summaryrefslogtreecommitdiff
path: root/tex/context/base
diff options
context:
space:
mode:
authorHans Hagen <pragma@wxs.nl>2013-05-20 02:00:00 +0200
committerHans Hagen <pragma@wxs.nl>2013-05-20 02:00:00 +0200
commitbd95a21d2b31a5fab1b4cc7c2b0334823fb3a3e9 (patch)
tree831128c411476f077eb7910d8c08f524d3ee43ec /tex/context/base
parent9a10021cd4cb23995ad3ffa915fc5b7f6890aaf8 (diff)
downloadcontext-bd95a21d2b31a5fab1b4cc7c2b0334823fb3a3e9.tar.gz
beta 2013.05.20 02:00
Diffstat (limited to 'tex/context/base')
-rw-r--r--tex/context/base/anch-pgr.lua1388
-rw-r--r--tex/context/base/anch-pos.lua2072
-rw-r--r--tex/context/base/attr-col.lua1076
-rw-r--r--tex/context/base/attr-eff.lua222
-rw-r--r--tex/context/base/attr-ini.lua334
-rw-r--r--tex/context/base/attr-lay.lua506
-rw-r--r--tex/context/base/attr-mkr.lua52
-rw-r--r--tex/context/base/attr-neg.lua196
-rw-r--r--tex/context/base/back-exp.lua4822
-rw-r--r--tex/context/base/back-ini.lua212
-rw-r--r--tex/context/base/bibl-bib.lua1532
-rw-r--r--tex/context/base/bibl-tra.lua494
-rw-r--r--tex/context/base/bibl-tst.lua42
-rw-r--r--tex/context/base/blob-ini.lua388
-rw-r--r--tex/context/base/buff-imp-default.lua84
-rw-r--r--tex/context/base/buff-imp-escaped.lua28
-rw-r--r--tex/context/base/buff-imp-lua.lua416
-rw-r--r--tex/context/base/buff-imp-mp.lua234
-rw-r--r--tex/context/base/buff-imp-nested.lua160
-rw-r--r--tex/context/base/buff-imp-parsed-xml.lua202
-rw-r--r--tex/context/base/buff-imp-tex.lua260
-rw-r--r--tex/context/base/buff-imp-xml.lua266
-rw-r--r--tex/context/base/buff-ini.lua732
-rw-r--r--tex/context/base/buff-par.lua368
-rw-r--r--tex/context/base/buff-ver.lua1536
-rw-r--r--tex/context/base/catc-ini.lua82
-rw-r--r--tex/context/base/char-cjk.lua730
-rw-r--r--tex/context/base/char-enc.lua372
-rw-r--r--tex/context/base/char-ent.lua4514
-rw-r--r--tex/context/base/char-ini.lua2316
-rw-r--r--tex/context/base/char-map.lua2144
-rw-r--r--tex/context/base/char-tex.lua422
-rw-r--r--tex/context/base/char-utf.lua1106
-rw-r--r--tex/context/base/chem-ini.lua86
-rw-r--r--tex/context/base/chem-str.lua1640
-rw-r--r--tex/context/base/cldf-bas.lua358
-rw-r--r--tex/context/base/cldf-com.lua72
-rw-r--r--tex/context/base/cldf-ini.lua2132
-rw-r--r--tex/context/base/cldf-int.lua442
-rw-r--r--tex/context/base/cldf-prs.lua108
-rw-r--r--tex/context/base/cldf-ver.lua150
-rw-r--r--tex/context/base/colo-icc.lua240
-rw-r--r--tex/context/base/colo-run.lua136
-rw-r--r--tex/context/base/cont-new.mkiv2
-rw-r--r--tex/context/base/context-version.pdfbin4134 -> 4127 bytes
-rw-r--r--tex/context/base/context.mkiv2
-rw-r--r--tex/context/base/core-ctx.lua694
-rw-r--r--tex/context/base/core-dat.lua538
-rw-r--r--tex/context/base/core-env.lua308
-rw-r--r--tex/context/base/core-sys.lua202
-rw-r--r--tex/context/base/core-two.lua314
-rw-r--r--tex/context/base/core-uti.lua588
-rw-r--r--tex/context/base/data-aux.lua124
-rw-r--r--tex/context/base/data-bin.lua54
-rw-r--r--tex/context/base/data-con.lua276
-rw-r--r--tex/context/base/data-crl.lua122
-rw-r--r--tex/context/base/data-ctx.lua18
-rw-r--r--tex/context/base/data-env.lua582
-rw-r--r--tex/context/base/data-exp.lua940
-rw-r--r--tex/context/base/data-fil.lua226
-rw-r--r--tex/context/base/data-gen.lua18
-rw-r--r--tex/context/base/data-ini.lua464
-rw-r--r--tex/context/base/data-inp.lua50
-rw-r--r--tex/context/base/data-lst.lua154
-rw-r--r--tex/context/base/data-lua.lua262
-rw-r--r--tex/context/base/data-met.lua266
-rw-r--r--tex/context/base/data-out.lua36
-rw-r--r--tex/context/base/data-pre.lua492
-rw-r--r--tex/context/base/data-sch.lua400
-rw-r--r--tex/context/base/data-tex.lua366
-rw-r--r--tex/context/base/data-tmf.lua146
-rw-r--r--tex/context/base/data-tmp.lua840
-rw-r--r--tex/context/base/data-tre.lua150
-rw-r--r--tex/context/base/data-use.lua202
-rw-r--r--tex/context/base/data-vir.lua168
-rw-r--r--tex/context/base/data-zip.lua528
-rw-r--r--tex/context/base/file-ini.lua74
-rw-r--r--tex/context/base/file-job.lua2002
-rw-r--r--tex/context/base/file-lib.lua130
-rw-r--r--tex/context/base/file-mod.lua362
-rw-r--r--tex/context/base/file-res.lua310
-rw-r--r--tex/context/base/file-syn.lua102
-rw-r--r--tex/context/base/font-afk.lua400
-rw-r--r--tex/context/base/font-afm.lua1942
-rw-r--r--tex/context/base/font-age.lua8230
-rw-r--r--tex/context/base/font-agl.lua1334
-rw-r--r--tex/context/base/font-aux.lua330
-rw-r--r--tex/context/base/font-chk.lua718
-rw-r--r--tex/context/base/font-cid.lua328
-rw-r--r--tex/context/base/font-col.lua476
-rw-r--r--tex/context/base/font-con.lua2660
-rw-r--r--tex/context/base/font-ctx.lua3638
-rw-r--r--tex/context/base/font-def.lua898
-rw-r--r--tex/context/base/font-enc.lua294
-rw-r--r--tex/context/base/font-enh.lua400
-rw-r--r--tex/context/base/font-ext.lua1892
-rw-r--r--tex/context/base/font-fbk.lua608
-rw-r--r--tex/context/base/font-gds.lua1504
-rw-r--r--tex/context/base/font-hsh.lua452
-rw-r--r--tex/context/base/font-ini.lua64
-rw-r--r--tex/context/base/font-ldr.lua140
-rw-r--r--tex/context/base/font-log.lua172
-rw-r--r--tex/context/base/font-lua.lua92
-rw-r--r--tex/context/base/font-map.lua658
-rw-r--r--tex/context/base/font-mis.lua222
-rw-r--r--tex/context/base/font-nod.lua868
-rw-r--r--tex/context/base/font-odk.lua1808
-rw-r--r--tex/context/base/font-otb.lua1314
-rw-r--r--tex/context/base/font-otc.lua666
-rw-r--r--tex/context/base/font-otd.lua522
-rw-r--r--tex/context/base/font-otf.lua4310
-rw-r--r--tex/context/base/font-oth.lua102
-rw-r--r--tex/context/base/font-oti.lua182
-rw-r--r--tex/context/base/font-otp.lua1754
-rw-r--r--tex/context/base/font-ott.lua2226
-rw-r--r--tex/context/base/font-sol.lua1768
-rw-r--r--tex/context/base/font-syn.lua3448
-rw-r--r--tex/context/base/font-tfm.lua304
-rw-r--r--tex/context/base/font-trt.lua114
-rw-r--r--tex/context/base/font-vf.lua410
-rw-r--r--tex/context/base/grph-epd.lua50
-rw-r--r--tex/context/base/grph-fil.lua142
-rw-r--r--tex/context/base/grph-inc.lua3218
-rw-r--r--tex/context/base/grph-raw.lua84
-rw-r--r--tex/context/base/grph-swf.lua188
-rw-r--r--tex/context/base/grph-u3d.lua102
-rw-r--r--tex/context/base/grph-wnd.lua94
-rw-r--r--tex/context/base/java-ini.lua452
-rw-r--r--tex/context/base/l-boolean.lua138
-rw-r--r--tex/context/base/l-dir.lua940
-rw-r--r--tex/context/base/l-file.lua1180
-rw-r--r--tex/context/base/l-function.lua22
-rw-r--r--tex/context/base/l-io.lua724
-rw-r--r--tex/context/base/l-lpeg.lua1704
-rw-r--r--tex/context/base/l-lua.lua300
-rw-r--r--tex/context/base/l-math.lua68
-rw-r--r--tex/context/base/l-md5.lua234
-rw-r--r--tex/context/base/l-number.lua414
-rw-r--r--tex/context/base/l-os.lua948
-rw-r--r--tex/context/base/l-package.lua680
-rw-r--r--tex/context/base/l-pdfview.lua286
-rw-r--r--tex/context/base/l-set.lua174
-rw-r--r--tex/context/base/l-string.lua410
-rw-r--r--tex/context/base/l-table.lua2724
-rw-r--r--tex/context/base/l-unicode.lua1884
-rw-r--r--tex/context/base/l-url.lua688
-rw-r--r--tex/context/base/l-xml.lua46
-rw-r--r--tex/context/base/lang-def.lua932
-rw-r--r--tex/context/base/lang-frq-de.lua24
-rw-r--r--tex/context/base/lang-frq-en.lua52
-rw-r--r--tex/context/base/lang-frq-nl.lua24
-rw-r--r--tex/context/base/lang-ini.lua752
-rw-r--r--tex/context/base/lang-lab.lua284
-rw-r--r--tex/context/base/lang-url.lua226
-rw-r--r--tex/context/base/lang-wrd.lua706
-rw-r--r--tex/context/base/layo-ini.lua122
-rw-r--r--tex/context/base/lpdf-ano.lua1506
-rw-r--r--tex/context/base/lpdf-enc.lua314
-rw-r--r--tex/context/base/lpdf-epa.lua452
-rw-r--r--tex/context/base/lpdf-epd.lua702
-rw-r--r--tex/context/base/lpdf-fld.lua2610
-rw-r--r--tex/context/base/lpdf-grp.lua488
-rw-r--r--tex/context/base/lpdf-ini.lua1644
-rw-r--r--tex/context/base/lpdf-mov.lua126
-rw-r--r--tex/context/base/lpdf-nod.lua272
-rw-r--r--tex/context/base/lpdf-ren.lua698
-rw-r--r--tex/context/base/lpdf-swf.lua612
-rw-r--r--tex/context/base/lpdf-tag.lua626
-rw-r--r--tex/context/base/lpdf-u3d.lua976
-rw-r--r--tex/context/base/lpdf-wid.lua1290
-rw-r--r--tex/context/base/luat-bwc.lua64
-rw-r--r--tex/context/base/luat-cbk.lua640
-rw-r--r--tex/context/base/luat-cnf.lua394
-rw-r--r--tex/context/base/luat-cod.lua362
-rw-r--r--tex/context/base/luat-env.lua352
-rw-r--r--tex/context/base/luat-exe.lua252
-rw-r--r--tex/context/base/luat-fio.lua234
-rw-r--r--tex/context/base/luat-fmt.lua280
-rw-r--r--tex/context/base/luat-ini.lua412
-rw-r--r--tex/context/base/luat-iop.lua390
-rw-r--r--tex/context/base/luat-lua.lua90
-rw-r--r--tex/context/base/luat-mac.lua868
-rw-r--r--tex/context/base/luat-run.lua316
-rw-r--r--tex/context/base/luat-sta.lua422
-rw-r--r--tex/context/base/luat-sto.lua338
-rw-r--r--tex/context/base/lxml-aux.lua1622
-rw-r--r--tex/context/base/lxml-css.lua316
-rw-r--r--tex/context/base/lxml-ctx.lua270
-rw-r--r--tex/context/base/lxml-dir.lua228
-rw-r--r--tex/context/base/lxml-ent.lua114
-rw-r--r--tex/context/base/lxml-inf.lua116
-rw-r--r--tex/context/base/lxml-lpt.lua2932
-rw-r--r--tex/context/base/lxml-mis.lua206
-rw-r--r--tex/context/base/lxml-sor.lua318
-rw-r--r--tex/context/base/lxml-tab.lua2734
-rw-r--r--tex/context/base/lxml-tex.lua3372
-rw-r--r--tex/context/base/lxml-xml.lua890
-rw-r--r--tex/context/base/m-chart.lua1832
-rw-r--r--tex/context/base/m-database.lua274
-rw-r--r--tex/context/base/m-markdown.lua1648
-rw-r--r--tex/context/base/m-pstricks.lua148
-rw-r--r--tex/context/base/m-spreadsheet.lua664
-rw-r--r--tex/context/base/m-steps.lua454
-rw-r--r--tex/context/base/math-act.lua808
-rw-r--r--tex/context/base/math-dim.lua480
-rw-r--r--tex/context/base/math-ext.lua394
-rw-r--r--tex/context/base/math-fbk.lua624
-rw-r--r--tex/context/base/math-frc.lua102
-rw-r--r--tex/context/base/math-map.lua1368
-rw-r--r--tex/context/base/math-noa.lua2384
-rw-r--r--tex/context/base/math-ren.lua138
-rw-r--r--tex/context/base/math-tag.lua690
-rw-r--r--tex/context/base/math-ttv.lua1602
-rw-r--r--tex/context/base/meta-fun.lua114
-rw-r--r--tex/context/base/meta-ini.lua330
-rw-r--r--tex/context/base/meta-pdf.lua1134
-rw-r--r--tex/context/base/meta-pdh.lua1220
-rw-r--r--tex/context/base/meta-tex.lua76
-rw-r--r--tex/context/base/mlib-ctx.lua356
-rw-r--r--tex/context/base/mlib-pdf.lua1060
-rw-r--r--tex/context/base/mlib-pps.lua2432
-rw-r--r--tex/context/base/mlib-run.lua1182
-rw-r--r--tex/context/base/mult-aux.lua308
-rw-r--r--tex/context/base/mult-chk.lua152
-rw-r--r--tex/context/base/mult-fun.lua202
-rw-r--r--tex/context/base/mult-ini.lua666
-rw-r--r--tex/context/base/mult-low.lua694
-rw-r--r--tex/context/base/mult-mps.lua230
-rw-r--r--tex/context/base/node-acc.lua280
-rw-r--r--tex/context/base/node-aux.lua778
-rw-r--r--tex/context/base/node-bck.lua322
-rw-r--r--tex/context/base/node-dir.lua618
-rw-r--r--tex/context/base/node-ext.lua60
-rw-r--r--tex/context/base/node-fin.lua2444
-rw-r--r--tex/context/base/node-fnt.lua452
-rw-r--r--tex/context/base/node-ini.lua842
-rw-r--r--tex/context/base/node-inj.lua1038
-rw-r--r--tex/context/base/node-mig.lua276
-rw-r--r--tex/context/base/node-pag.lua60
-rw-r--r--tex/context/base/node-pro.lua330
-rw-r--r--tex/context/base/node-ref.lua1170
-rw-r--r--tex/context/base/node-res.lua812
-rw-r--r--tex/context/base/node-rul.lua778
-rw-r--r--tex/context/base/node-ser.lua572
-rw-r--r--tex/context/base/node-shp.lua296
-rw-r--r--tex/context/base/node-snp.lua132
-rw-r--r--tex/context/base/node-tex.lua82
-rw-r--r--tex/context/base/node-tra.lua1058
-rw-r--r--tex/context/base/node-tsk.lua804
-rw-r--r--tex/context/base/node-tst.lua240
-rw-r--r--tex/context/base/node-typ.lua158
-rw-r--r--tex/context/base/pack-obj.lua154
-rw-r--r--tex/context/base/pack-rul.lua218
-rw-r--r--tex/context/base/page-flt.lua578
-rw-r--r--tex/context/base/page-inj.lua202
-rw-r--r--tex/context/base/page-ins.lua194
-rw-r--r--tex/context/base/page-lin.lua580
-rw-r--r--tex/context/base/page-mix.lua1390
-rw-r--r--tex/context/base/page-pst.lua156
-rw-r--r--tex/context/base/page-str.lua464
-rw-r--r--tex/context/base/regi-8859-1.lua52
-rw-r--r--tex/context/base/regi-8859-10.lua52
-rw-r--r--tex/context/base/regi-8859-11.lua52
-rw-r--r--tex/context/base/regi-8859-13.lua52
-rw-r--r--tex/context/base/regi-8859-14.lua52
-rw-r--r--tex/context/base/regi-8859-15.lua52
-rw-r--r--tex/context/base/regi-8859-16.lua52
-rw-r--r--tex/context/base/regi-8859-2.lua52
-rw-r--r--tex/context/base/regi-8859-3.lua52
-rw-r--r--tex/context/base/regi-8859-4.lua52
-rw-r--r--tex/context/base/regi-8859-5.lua52
-rw-r--r--tex/context/base/regi-8859-6.lua52
-rw-r--r--tex/context/base/regi-8859-7.lua52
-rw-r--r--tex/context/base/regi-8859-8.lua52
-rw-r--r--tex/context/base/regi-8859-9.lua52
-rw-r--r--tex/context/base/regi-cp1250.lua52
-rw-r--r--tex/context/base/regi-cp1251.lua52
-rw-r--r--tex/context/base/regi-cp1252.lua52
-rw-r--r--tex/context/base/regi-cp1253.lua52
-rw-r--r--tex/context/base/regi-cp1254.lua52
-rw-r--r--tex/context/base/regi-cp1255.lua52
-rw-r--r--tex/context/base/regi-cp1256.lua52
-rw-r--r--tex/context/base/regi-cp1257.lua52
-rw-r--r--tex/context/base/regi-cp1258.lua52
-rw-r--r--tex/context/base/regi-demo.lua44
-rw-r--r--tex/context/base/regi-ini.lua776
-rw-r--r--tex/context/base/s-fonts-coverage.lua226
-rw-r--r--tex/context/base/s-fonts-features.lua322
-rw-r--r--tex/context/base/s-fonts-goodies.lua234
-rw-r--r--tex/context/base/s-fonts-missing.lua202
-rw-r--r--tex/context/base/s-fonts-shapes.lua656
-rw-r--r--tex/context/base/s-fonts-system.lua136
-rw-r--r--tex/context/base/s-fonts-tables.lua624
-rw-r--r--tex/context/base/s-fonts-vectors.lua208
-rw-r--r--tex/context/base/s-languages-sorting.lua236
-rw-r--r--tex/context/base/s-languages-system.lua70
-rw-r--r--tex/context/base/s-math-coverage.lua360
-rw-r--r--tex/context/base/s-math-parameters.lua270
-rw-r--r--tex/context/base/s-pre-71.lua126
-rw-r--r--tex/context/base/scrn-but.lua38
-rw-r--r--tex/context/base/scrn-fld.lua170
-rw-r--r--tex/context/base/scrn-hlp.lua238
-rw-r--r--tex/context/base/scrn-ini.lua64
-rw-r--r--tex/context/base/scrn-pag.lua54
-rw-r--r--tex/context/base/scrn-ref.lua130
-rw-r--r--tex/context/base/scrn-wid.lua428
-rw-r--r--tex/context/base/scrp-cjk.lua1902
-rw-r--r--tex/context/base/scrp-eth.lua300
-rw-r--r--tex/context/base/scrp-ini.lua1268
-rw-r--r--tex/context/base/sort-ini.lua1330
-rw-r--r--tex/context/base/sort-lan.lua1850
-rw-r--r--tex/context/base/spac-adj.lua116
-rw-r--r--tex/context/base/spac-ali.lua268
-rw-r--r--tex/context/base/spac-chr.lua400
-rw-r--r--tex/context/base/spac-hor.lua62
-rw-r--r--tex/context/base/spac-ver.lua2716
-rw-r--r--tex/context/base/status-files.pdfbin24738 -> 24732 bytes
-rw-r--r--tex/context/base/status-lua.pdfbin211829 -> 211790 bytes
-rw-r--r--tex/context/base/strc-bkm.lua392
-rw-r--r--tex/context/base/strc-blk.lua304
-rw-r--r--tex/context/base/strc-con.lua18
-rw-r--r--tex/context/base/strc-doc.lua1912
-rw-r--r--tex/context/base/strc-flt.lua18
-rw-r--r--tex/context/base/strc-ini.lua676
-rw-r--r--tex/context/base/strc-itm.lua76
-rw-r--r--tex/context/base/strc-lev.lua102
-rw-r--r--tex/context/base/strc-lst.lua1690
-rw-r--r--tex/context/base/strc-mar.lua1392
-rw-r--r--tex/context/base/strc-not.lua894
-rw-r--r--tex/context/base/strc-num.lua1298
-rw-r--r--tex/context/base/strc-pag.lua626
-rw-r--r--tex/context/base/strc-ref.lua4316
-rw-r--r--tex/context/base/strc-reg.lua1724
-rw-r--r--tex/context/base/strc-rsc.lua308
-rw-r--r--tex/context/base/strc-syn.lua396
-rw-r--r--tex/context/base/strc-tag.lua708
-rw-r--r--tex/context/base/supp-box.lua224
-rw-r--r--tex/context/base/supp-ran.lua146
-rw-r--r--tex/context/base/symb-ini.lua100
-rw-r--r--tex/context/base/syst-aux.lua160
-rw-r--r--tex/context/base/syst-con.lua124
-rw-r--r--tex/context/base/syst-lua.lua246
-rw-r--r--tex/context/base/tabl-tbl.lua82
-rw-r--r--tex/context/base/tabl-xtb.lua1976
-rw-r--r--tex/context/base/task-ini.lua382
-rw-r--r--tex/context/base/toks-ini.lua682
-rw-r--r--tex/context/base/trac-ctx.lua96
-rw-r--r--tex/context/base/trac-deb.lua496
-rw-r--r--tex/context/base/trac-exp.lua458
-rw-r--r--tex/context/base/trac-fil.lua362
-rw-r--r--tex/context/base/trac-inf.lua386
-rw-r--r--tex/context/base/trac-jus.lua272
-rw-r--r--tex/context/base/trac-lmx.lua1464
-rw-r--r--tex/context/base/trac-log.lua1632
-rw-r--r--tex/context/base/trac-pro.lua416
-rw-r--r--tex/context/base/trac-set.lua758
-rw-r--r--tex/context/base/trac-tex.lua150
-rw-r--r--tex/context/base/trac-tim.lua276
-rw-r--r--tex/context/base/trac-vis.lua1852
-rw-r--r--tex/context/base/trac-xml.lua366
-rw-r--r--tex/context/base/type-ini.lua152
-rw-r--r--tex/context/base/typo-bld.lua370
-rw-r--r--tex/context/base/typo-brk.lua604
-rw-r--r--tex/context/base/typo-cap.lua662
-rw-r--r--tex/context/base/typo-cln.lua204
-rw-r--r--tex/context/base/typo-dig.lua324
-rw-r--r--tex/context/base/typo-dir.lua926
-rw-r--r--tex/context/base/typo-ini.lua22
-rw-r--r--tex/context/base/typo-itc.lua512
-rw-r--r--tex/context/base/typo-krn.lua670
-rw-r--r--tex/context/base/typo-lan.lua144
-rw-r--r--tex/context/base/typo-mar.lua1758
-rw-r--r--tex/context/base/typo-pag.lua358
-rw-r--r--tex/context/base/typo-par.lua362
-rw-r--r--tex/context/base/typo-prc.lua250
-rw-r--r--tex/context/base/typo-rep.lua256
-rw-r--r--tex/context/base/typo-spa.lua458
-rw-r--r--tex/context/base/unic-ini.lua38
-rw-r--r--tex/context/base/util-deb.lua256
-rw-r--r--tex/context/base/util-dim.lua898
-rw-r--r--tex/context/base/util-env.lua574
-rw-r--r--tex/context/base/util-fmt.lua152
-rw-r--r--tex/context/base/util-jsn.lua292
-rw-r--r--tex/context/base/util-lib.lua576
-rw-r--r--tex/context/base/util-lua.lua702
-rw-r--r--tex/context/base/util-mrg.lua456
-rw-r--r--tex/context/base/util-pck.lua288
-rw-r--r--tex/context/base/util-prs.lua1186
-rw-r--r--tex/context/base/util-ran.lua214
-rw-r--r--tex/context/base/util-seq.lua660
-rw-r--r--tex/context/base/util-soc.lua186
-rw-r--r--tex/context/base/util-sql-imp-client.lua512
-rw-r--r--tex/context/base/util-sql-imp-library.lua578
-rw-r--r--tex/context/base/util-sql-imp-swiglib.lua1010
-rw-r--r--tex/context/base/util-sql-loggers.lua554
-rw-r--r--tex/context/base/util-sql-sessions.lua698
-rw-r--r--tex/context/base/util-sql-tickets.lua1544
-rw-r--r--tex/context/base/util-sql-users.lua820
-rw-r--r--tex/context/base/util-sql.lua886
-rw-r--r--tex/context/base/util-sta.lua684
-rw-r--r--tex/context/base/util-sto.lua378
-rw-r--r--tex/context/base/util-str.lua1532
-rw-r--r--tex/context/base/util-tab.lua986
-rw-r--r--tex/context/base/util-tpl.lua348
-rw-r--r--tex/context/base/x-asciimath.lua540
-rw-r--r--tex/context/base/x-calcmath.lua724
-rw-r--r--tex/context/base/x-cals.lua436
-rw-r--r--tex/context/base/x-chemml.lua102
-rw-r--r--tex/context/base/x-ct.lua330
-rw-r--r--tex/context/base/x-ldx.lua682
-rw-r--r--tex/context/base/x-mathml.lua1658
411 files changed, 132070 insertions, 132070 deletions
diff --git a/tex/context/base/anch-pgr.lua b/tex/context/base/anch-pgr.lua
index 992b4deff..278448e3a 100644
--- a/tex/context/base/anch-pgr.lua
+++ b/tex/context/base/anch-pgr.lua
@@ -1,694 +1,694 @@
-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"
-}
-
--- todo: we need to clean up lists (of previous pages)
-
-local commands, context = commands, context
-
-local format = string.format
-local abs = math.abs
-local concat, sort = table.concat, table.sort
-local splitter = lpeg.splitat(":")
-local lpegmatch = lpeg.match
-
-local jobpositions = job.positions
-local formatters = string.formatters
-
-local report_graphics = logs.reporter("graphics")
-
-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 function regionarea(r)
- local rx, ry = r.x, r.y
- local rw = rx + r.w
- local rh = ry + r.h
- local rd = ry - r.d
- return {
- f_pair(rx, rh - ry),
- f_pair(rw, rh - ry),
- f_pair(rw, rd - ry),
- f_pair(rx, rd - ry),
- }
-end
-
--- we can use a 'local t, n' and reuse the table
-
-local eps = 2
-
-local function add(t,x,y,last,direction)
- local n = #t
- if n == 0 then
- t[n+1] = { x, y }
- else
- local tn = t[n]
- local lx = tn[1]
- local ly = tn[2]
- if x == lx and y == ly then
- -- quick skip
- elseif n == 1 then
--- if abs(lx-x) <= eps or abs(ly-y) <= eps then
- if abs(lx-x) > eps or abs(ly-y) > eps then
- t[n+1] = { x, y }
- end
- else
- local tm = t[n-1]
- local px = tm[1]
- local py = tm[2]
-if (direction == "down" and y > ly) or (direction == "up" and y < ly) then
- -- move back from too much hang
-else
- if abs(lx-px) <= eps and abs(lx-x) <= eps then
- if abs(ly-y) > eps then
- tn[2] = y
- end
- elseif abs(ly-py) <= eps and abs(ly-y) <= eps then
- if abs(lx-x) > eps then
- tn[1] = x
- end
- elseif not last then
- t[n+1] = { x, y }
- end
-end
- end
- end
-end
-
--- local function add(t,x,y,last)
--- t[#t+1] = { x, y }
--- end
-
-local function finish(t)
- local n = #t
- if n > 1 then
- local first = t[1]
- local last = t[n]
- if abs(first[1]-last[1]) <= eps and abs(first[2]-last[2]) <= eps then
- t[n] = nil
- end
- end
-end
-
-local function clip(t,ytop,ybot)
- local first, last = 1, #t
- for i=first,last do
- local y = t[i][2]
- if ytop < y then
- first = i
- end
- if ybot > y then
- last = i
- break
- end
- end
- local lp = { }
- lp[#lp+1] = { t[first][1], ytop }
- for i=first+1,last-1 do
- lp[#lp+1] = { t[i][1], t[i][2] }
- end
- lp[#lp+1] = { t[last][1], ybot }
- return lp
-end
-
--- todo: mark regions and free paragraphs in collected
-
-local function shapes(r,rx,ry,rw,rh,rd,lytop,lybot,rytop,rybot,obeyhang)
- -- we assume that we only hang per page and not cross pages
- -- which makes sense as hanging is only uses in special cases
- --
- -- we can remove data as soon as a page is done so we could
- -- remember per page and discard areas after each shipout
- local leftshape, rightshape
- leftshape = { { rx, rh } } -- spikes get removed so we can start at the edge
- rightshape = { { rw, rh } } -- even if we hang next
- local paragraphs = r.paragraphs
- local extending = false
- if paragraphs then
- for i=1,#paragraphs do
- local p = paragraphs[i]
- local ha = p.ha
- if obeyhang and ha and ha ~= 0 then
- local py = p.y
- local ph = p.h
- local pd = p.d
- local hi = p.hi
- local hang = ha * (ph + pd)
- local py_ph = py + ph
- -- ha < 0 hi < 0 : right top
- -- ha < 0 hi > 0 : left top
- if ha < 0 then
- if hi < 0 then -- right
- add(rightshape,rw, py_ph,"up")
- add(rightshape,rw + hi,py_ph,"up")
- add(rightshape,rw + hi,py_ph + hang,"up")
- add(rightshape,rw, py_ph + hang,"up")
- else
- -- left
- add(leftshape,rx,py_ph,"down")
- add(leftshape,rx + hi,py_ph,"down")
- add(leftshape,rx + hi,py_ph + hang,"down")
- add(leftshape,rx,py_ph + hang,"down")
- end
- else
- -- maybe some day
- end
- extending = true -- false
- else -- we need to clip to the next par
- local ps = p.ps
- if ps then
- local py = p.y
- local ph = p.h
- local pd = p.d
- local step = ph + pd
- local size = #ps * step
- local py_ph = py + ph
- add(leftshape,rx,py_ph,"up")
- add(rightshape,rw,py_ph,"down")
- for i=1,#ps do
- local p = ps[i]
- local l = p[1]
- local w = p[2]
- add(leftshape,rx + l, py_ph,"up")
- add(rightshape,rx + l + w, py_ph,"down")
- py_ph = py_ph - step
- add(leftshape,rx + l, py_ph,"up")
- add(rightshape,rx + l + w, py_ph,"down")
- end
- extending = true
- elseif extending then
- local py = p.y
- local ph = p.h
- local pd = p.d
- local py_ph = py + ph
- local py_pd = py - pd
- add(leftshape,leftshape[#leftshape][1],py_ph,"up")
- add(rightshape,rightshape[#rightshape][1],py_ph,"down")
- add(leftshape,rx,py_ph,"up") -- shouldn't this be py_pd
- add(rightshape,rw,py_ph,"down") -- shouldn't this be py_pd
- extending = false
- end
- end
- end
- end
- -- we can have a simple variant when no paragraphs
- if extending then
- -- not ok
- leftshape[#leftshape][2] = rd
- rightshape[#rightshape][2] = rw
- else
- add(leftshape,rx,rd,"up")
- add(rightshape,rw,rd,"down")
- end
- return clip(leftshape,lytop,lybot), clip(rightshape,rytop,rybot)
-end
-
--- local function shapes(r,rx,ry,rw,rh,rd,lytop,lybot,rytop,rybot,obeyhang)
--- local leftshape = { { rx, rh }, { rx, rd } }
--- local rightshape = { { rw, rh }, { rw, rd } }
--- return clip(leftshape,lytop,lybot), clip(rightshape,rytop,rybot)
--- end
-
-local function singlepart(b,e,r,left,right,obeyhang)
- local bx, by = b.x, b.y
- local ex, ey = e.x, e.y
- local rx, ry = r.x, r.y
- local rw = rx + r.w
- local rh = ry + r.h
- local rd = ry - r.d
- if left then
- rx = rx + left
- rw = rw - right
- end
- local bh = by + b.h
- local bd = by - b.d
- local eh = ey + e.h
- local ed = ey - e.d
- 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
- area = {
- f_pair(bx,bh-ry),
- f_pair(ex,eh-ry),
- f_pair(ex,ed-ry),
- f_pair(bx,bd-ry),
- }
- else
- area = { }
- local leftshapes, rightshapes = shapes(r,rx,ry,rw,rh,rd,bd,ed,bh,eh,obeyhang)
- add(area,bx,bh-ry)
- for i=1,#rightshapes do
- local ri = rightshapes[i]
- add(area,ri[1],ri[2]-ry)
- end
- add(area,ex,eh-ry)
- add(area,ex,ed-ry)
- for i=#leftshapes,1,-1 do
- local li = leftshapes[i]
- add(area,li[1],li[2]-ry)
- end
- add(area,bx,bd-ry)
- add(area,bx,bh-ry,true) -- finish last straight line (but no add as we cycle)
- finish(area)
- for i=1,#area do
- local a = area[i]
- area[i] = f_pair(a[1],a[2])
- end
- end
- return {
- location = "single",
- region = r,
- area = area,
- }
-end
-
-local function firstpart(b,r,left,right,obeyhang)
- local bx, by = b.x, b.y
- local rx, ry = r.x, r.y
- local rw = rx + r.w
- local rh = ry + r.h
- local rd = ry - r.d
- if left then
- rx = rx + left
- rw = rw - right
- end
- local bh = by + b.h
- local bd = by - b.d
- local area = { }
- local leftshapes, rightshapes = shapes(r,rx,ry,rw,rh,rd,bd,rd,bh,rd,obeyhang)
- add(area,bx,bh-ry)
- for i=1,#rightshapes do
- local ri = rightshapes[i]
- add(area,ri[1],ri[2]-ry)
- end
- for i=#leftshapes,1,-1 do
- local li = leftshapes[i]
- add(area,li[1],li[2]-ry)
- end
- add(area,bx,bd-ry)
- add(area,bx,bh-ry,true) -- finish last straight line (but no add as we cycle)
- finish(area)
- for i=1,#area do
- local a = area[i]
- area[i] = f_pair(a[1],a[2])
- end
- return {
- location = "first",
- region = r,
- area = area,
- }
-end
-
-local function middlepart(r,left,right,obeyhang)
- local rx, ry = r.x, r.y
- local rw = rx + r.w
- local rh = ry + r.h
- local rd = ry - r.d
- if left then
- rx = rx + left
- rw = rw - right
- end
- local area = { }
- local leftshapes, rightshapes = shapes(r,rx,ry,rw,rh,rd,rh,rd,rh,rd,obeyhang)
- for i=#leftshapes,1,-1 do
- local li = leftshapes[i]
- add(area,li[1],li[2]-ry)
- end
- for i=1,#rightshapes do
- local ri = rightshapes[i]
- add(area,ri[1],ri[2]-ry)
- end
- finish(area)
- for i=1,#area do
- local a = area[i]
- area[i] = f_pair(a[1],a[2])
- end
- return {
- location = "middle",
- region = r,
- area = area,
- }
-end
-
-local function lastpart(e,r,left,right,obeyhang)
- local ex, ey = e.x, e.y
- local rx, ry = r.x, r.y
- local rw = rx + r.w
- local rh = ry + r.h
- local rd = ry - r.d
- if left then
- rx = rx + left
- rw = rw - right
- end
- local eh = ey + e.h
- local ed = ey - e.d
- local area = { }
- -- two cases: till end and halfway e line
- local leftshapes, rightshapes = shapes(r,rx,ry,rw,rh,rd,rh,ed,rh,eh,obeyhang)
- for i=1,#rightshapes do
- local ri = rightshapes[i]
- add(area,ri[1],ri[2]-ry)
- end
- add(area,ex,eh-ry)
- add(area,ex,ed-ry)
- for i=#leftshapes,1,-1 do
- local li = leftshapes[i]
- add(area,li[1],li[2]-ry)
- end
- finish(area)
- for i=1,#area do
- local a = area[i]
- area[i] = f_pair(a[1],a[2])
- end
- return {
- location = "last",
- region = r,
- area = area,
- }
-end
-
-graphics = graphics or { }
-local backgrounds = { }
-
-graphics.backgrounds = backgrounds
-
-local function calculatemultipar(tag,obeyhang)
- 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_graphics("invalid tag %a",tag)
- return { }
- end
- local br = b.r
- local er = e.r
- if not br or not er then
- report_graphics("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_graphics("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 assymetrical doublesided layouts.
- local left = 0
- local right = 0
- local rc = b.c
- if rc then
- rc = collected[rc]
- if rc then
- local tb = collected[rc.r]
- if tb then
- left = -(tb.x - rc.x)
- right = (tb.w - rc.w - left) -- tb.x - rc.x
- end
- 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
- if bn then
- local bp = collected[f_p_tag(bn)]
- if bp then
- left = left + bp.ls
- right = right + bp.rs
- end
- end
- --
- if bindex == eindex then
- return {
- list = { [b.p] = { singlepart(b,e,collected[br],left,right,obeyhang) } },
- bpos = b,
- epos = e,
- }
- else
- local list = {
- [b.p] = { firstpart(b,collected[br],left,right,obeyhang) },
- }
- 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 p = r.p
- local pp = list[p]
- if pp then
- pp[#pp+1] = middlepart(r,left,right,obeyhang)
- else
- list[p] = { middlepart(r,left,right,obeyhang) }
- end
- end
- end
- local p = e.p
- local pp = list[p]
- if pp then
- pp[#pp+1] = lastpart(e,collected[er],left,right,obeyhang)
- else
- list[p] = { lastpart(e,collected[er],left,right,obeyhang) }
- end
- return {
- list = list,
- bpos = b,
- epos = e,
- }
- end
-end
-
--- local pending = { } -- needs gc
---
--- local function register(data,n,anchor)
--- local pa = pending[anchor]
--- if not pa then
--- pa = { }
--- pending[anchor] = pa
--- end
--- for page, pagedata in next, data do
--- local pap = pa[page]
--- if pap then
--- pap[#pap+1] = n
--- else
--- pa[page] = { n }
--- end
--- end
--- end
---
--- function backgrounds.registered(anchor,page)
--- local pa = pending[anchor]
--- if pa then
--- concat(pa,",")
--- else
--- return ""
--- end
--- end
-
-local pbg = { } -- will move to pending
-
-function backgrounds.calculatemultipar(n)
- if not pbg[n] then
- pbg[n] = calculatemultipar("pbg",n) or { }
- end
-end
-
-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 = [[
-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 = [[
-multilocs[%s] := %s ;
-multikind[%s] := "%s" ;
-multipars[%s] := (%--t--cycle) shifted - (%p,%p) ;
-]]
-
-local f_template_c = [[
-multiregs[%s] := (%--t--cycle) shifted - %s ;
-]]
-
-local f_template_d = [[
-setbounds currentpicture to multibox ;
-]]
-
-f_template_a = formatters[f_template_a]
-f_template_b = formatters[f_template_b]
-f_template_c = formatters[f_template_c]
-f_template_d = formatters[f_template_d]
-
-function backgrounds.fetchmultipar(n,anchor,page,obeyhang)
- local data = pbg[n]
- if not data then
- data = calculatemultipar(n,obeyhang)
- pbg[n] = data -- can be replaced by register
- -- register(data.list,n,anchor)
- end
- if data then
- local list = data.list
- if list then
- local pagedata = list[page]
- if pagedata then
- local nofmultipars = #pagedata
- -- report_graphics("fetching %a at page %s using anchor %a containing %s multipars",n,page,anchor,nofmultipars)
- local a = jobpositions.collected[anchor]
- if not a then
- report_graphics("missing anchor %a",anchor)
- else
- local trace = false
- local x, y, w, h, d = a.x, a.y, a.w, a.h, a.d
- local bpos = data.bpos
- local bh, bd = bpos.h, bpos.d
- local result = { f_template_a(nofmultipars,w,h+d,bh,bd,bh+bd) }
- for i=1,nofmultipars do
- local region = pagedata[i]
- result[#result+1] = f_template_b(
- i, multilocs[region.location],
- i, region.location,
- i, region.area, x, y-region.region.y)
- if trace then
- result[#result+1] = f_template_c(i, regionarea(region.region), offset)
- end
- end
- data[page] = nil
- result[#result+1] = f_template_d()
- result = concat(result,"\n")
- return result
- end
- end
- end
- end
- return f_template_a(0,"origin",0,0,0)
-end
-
-backgrounds.point = f_point
-backgrounds.pair = f_pair
-backgrounds.path = f_path
-
-function commands.fetchmultipar(n,anchor,page)
- context(backgrounds.fetchmultipar(n,anchor,page))
-end
-
-function commands.fetchmultishape(n,anchor,page)
- context(backgrounds.fetchmultipar(n,anchor,page,true))
-end
-
-local f_template_a = [[
-path posboxes[], posregions[] ;
-numeric pospages[] ;
-numeric nofposboxes ;
-nofposboxes := %s ;
-%t ;
-]]
-
-local f_template_b = [[
-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 ;
-]]
-
-f_template_a = formatters[f_template_a]
-f_template_b = formatters[f_template_b]
-
-function commands.fetchposboxes(tags,anchor,page) -- no caching (yet) / todo: anchor, page
- local collected = jobpositions.collected
- if type(tags) == "string" then
- tags = utilities.parsers.settings_to_array(tags)
- end
- local list, nofboxes = { }, 0
- for i=1,#tags do
- local tag= tags[i]
- local c = collected[tag]
- if c then
- local r = c.r
- if r then
- r = collected[r]
- if r then
- local rx, ry, rw, rh, rd = r.x, r.y, r.w, r.h, 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
- context(f_template_a(nofboxes,list))
-end
-
-local doifelse = commands.doifelse
-
-function commands.doifelsemultipar(n,page,obeyhang)
- local data = pbg[n]
- if not data then
- data = calculatemultipar(n,obeyhang)
- pbg[n] = data
- end
- if page then
- doifelse(data and data[page] and true)
- else
- doifelse(data and next(data) and true)
- end
-end
-
-function commands.doifelserangeonpage(first,last,page)
- local collected = jobpositions.collected
- local f = collected[first]
- if not f then
- doifelse(false)
- return
- end
- local l = collected[last]
- if not l then
- doifelse(false)
- return
- end
- doifelse(page >= f.p and page <= l.p)
-end
+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"
+}
+
+-- todo: we need to clean up lists (of previous pages)
+
+local commands, context = commands, context
+
+local format = string.format
+local abs = math.abs
+local concat, sort = table.concat, table.sort
+local splitter = lpeg.splitat(":")
+local lpegmatch = lpeg.match
+
+local jobpositions = job.positions
+local formatters = string.formatters
+
+local report_graphics = logs.reporter("graphics")
+
+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 function regionarea(r)
+ local rx, ry = r.x, r.y
+ local rw = rx + r.w
+ local rh = ry + r.h
+ local rd = ry - r.d
+ return {
+ f_pair(rx, rh - ry),
+ f_pair(rw, rh - ry),
+ f_pair(rw, rd - ry),
+ f_pair(rx, rd - ry),
+ }
+end
+
+-- we can use a 'local t, n' and reuse the table
+
+local eps = 2
+
+local function add(t,x,y,last,direction)
+ local n = #t
+ if n == 0 then
+ t[n+1] = { x, y }
+ else
+ local tn = t[n]
+ local lx = tn[1]
+ local ly = tn[2]
+ if x == lx and y == ly then
+ -- quick skip
+ elseif n == 1 then
+-- if abs(lx-x) <= eps or abs(ly-y) <= eps then
+ if abs(lx-x) > eps or abs(ly-y) > eps then
+ t[n+1] = { x, y }
+ end
+ else
+ local tm = t[n-1]
+ local px = tm[1]
+ local py = tm[2]
+if (direction == "down" and y > ly) or (direction == "up" and y < ly) then
+ -- move back from too much hang
+else
+ if abs(lx-px) <= eps and abs(lx-x) <= eps then
+ if abs(ly-y) > eps then
+ tn[2] = y
+ end
+ elseif abs(ly-py) <= eps and abs(ly-y) <= eps then
+ if abs(lx-x) > eps then
+ tn[1] = x
+ end
+ elseif not last then
+ t[n+1] = { x, y }
+ end
+end
+ end
+ end
+end
+
+-- local function add(t,x,y,last)
+-- t[#t+1] = { x, y }
+-- end
+
+local function finish(t)
+ local n = #t
+ if n > 1 then
+ local first = t[1]
+ local last = t[n]
+ if abs(first[1]-last[1]) <= eps and abs(first[2]-last[2]) <= eps then
+ t[n] = nil
+ end
+ end
+end
+
+local function clip(t,ytop,ybot)
+ local first, last = 1, #t
+ for i=first,last do
+ local y = t[i][2]
+ if ytop < y then
+ first = i
+ end
+ if ybot > y then
+ last = i
+ break
+ end
+ end
+ local lp = { }
+ lp[#lp+1] = { t[first][1], ytop }
+ for i=first+1,last-1 do
+ lp[#lp+1] = { t[i][1], t[i][2] }
+ end
+ lp[#lp+1] = { t[last][1], ybot }
+ return lp
+end
+
+-- todo: mark regions and free paragraphs in collected
+
+local function shapes(r,rx,ry,rw,rh,rd,lytop,lybot,rytop,rybot,obeyhang)
+ -- we assume that we only hang per page and not cross pages
+ -- which makes sense as hanging is only uses in special cases
+ --
+ -- we can remove data as soon as a page is done so we could
+ -- remember per page and discard areas after each shipout
+ local leftshape, rightshape
+ leftshape = { { rx, rh } } -- spikes get removed so we can start at the edge
+ rightshape = { { rw, rh } } -- even if we hang next
+ local paragraphs = r.paragraphs
+ local extending = false
+ if paragraphs then
+ for i=1,#paragraphs do
+ local p = paragraphs[i]
+ local ha = p.ha
+ if obeyhang and ha and ha ~= 0 then
+ local py = p.y
+ local ph = p.h
+ local pd = p.d
+ local hi = p.hi
+ local hang = ha * (ph + pd)
+ local py_ph = py + ph
+ -- ha < 0 hi < 0 : right top
+ -- ha < 0 hi > 0 : left top
+ if ha < 0 then
+ if hi < 0 then -- right
+ add(rightshape,rw, py_ph,"up")
+ add(rightshape,rw + hi,py_ph,"up")
+ add(rightshape,rw + hi,py_ph + hang,"up")
+ add(rightshape,rw, py_ph + hang,"up")
+ else
+ -- left
+ add(leftshape,rx,py_ph,"down")
+ add(leftshape,rx + hi,py_ph,"down")
+ add(leftshape,rx + hi,py_ph + hang,"down")
+ add(leftshape,rx,py_ph + hang,"down")
+ end
+ else
+ -- maybe some day
+ end
+ extending = true -- false
+ else -- we need to clip to the next par
+ local ps = p.ps
+ if ps then
+ local py = p.y
+ local ph = p.h
+ local pd = p.d
+ local step = ph + pd
+ local size = #ps * step
+ local py_ph = py + ph
+ add(leftshape,rx,py_ph,"up")
+ add(rightshape,rw,py_ph,"down")
+ for i=1,#ps do
+ local p = ps[i]
+ local l = p[1]
+ local w = p[2]
+ add(leftshape,rx + l, py_ph,"up")
+ add(rightshape,rx + l + w, py_ph,"down")
+ py_ph = py_ph - step
+ add(leftshape,rx + l, py_ph,"up")
+ add(rightshape,rx + l + w, py_ph,"down")
+ end
+ extending = true
+ elseif extending then
+ local py = p.y
+ local ph = p.h
+ local pd = p.d
+ local py_ph = py + ph
+ local py_pd = py - pd
+ add(leftshape,leftshape[#leftshape][1],py_ph,"up")
+ add(rightshape,rightshape[#rightshape][1],py_ph,"down")
+ add(leftshape,rx,py_ph,"up") -- shouldn't this be py_pd
+ add(rightshape,rw,py_ph,"down") -- shouldn't this be py_pd
+ extending = false
+ end
+ end
+ end
+ end
+ -- we can have a simple variant when no paragraphs
+ if extending then
+ -- not ok
+ leftshape[#leftshape][2] = rd
+ rightshape[#rightshape][2] = rw
+ else
+ add(leftshape,rx,rd,"up")
+ add(rightshape,rw,rd,"down")
+ end
+ return clip(leftshape,lytop,lybot), clip(rightshape,rytop,rybot)
+end
+
+-- local function shapes(r,rx,ry,rw,rh,rd,lytop,lybot,rytop,rybot,obeyhang)
+-- local leftshape = { { rx, rh }, { rx, rd } }
+-- local rightshape = { { rw, rh }, { rw, rd } }
+-- return clip(leftshape,lytop,lybot), clip(rightshape,rytop,rybot)
+-- end
+
+local function singlepart(b,e,r,left,right,obeyhang)
+ local bx, by = b.x, b.y
+ local ex, ey = e.x, e.y
+ local rx, ry = r.x, r.y
+ local rw = rx + r.w
+ local rh = ry + r.h
+ local rd = ry - r.d
+ if left then
+ rx = rx + left
+ rw = rw - right
+ end
+ local bh = by + b.h
+ local bd = by - b.d
+ local eh = ey + e.h
+ local ed = ey - e.d
+ 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
+ area = {
+ f_pair(bx,bh-ry),
+ f_pair(ex,eh-ry),
+ f_pair(ex,ed-ry),
+ f_pair(bx,bd-ry),
+ }
+ else
+ area = { }
+ local leftshapes, rightshapes = shapes(r,rx,ry,rw,rh,rd,bd,ed,bh,eh,obeyhang)
+ add(area,bx,bh-ry)
+ for i=1,#rightshapes do
+ local ri = rightshapes[i]
+ add(area,ri[1],ri[2]-ry)
+ end
+ add(area,ex,eh-ry)
+ add(area,ex,ed-ry)
+ for i=#leftshapes,1,-1 do
+ local li = leftshapes[i]
+ add(area,li[1],li[2]-ry)
+ end
+ add(area,bx,bd-ry)
+ add(area,bx,bh-ry,true) -- finish last straight line (but no add as we cycle)
+ finish(area)
+ for i=1,#area do
+ local a = area[i]
+ area[i] = f_pair(a[1],a[2])
+ end
+ end
+ return {
+ location = "single",
+ region = r,
+ area = area,
+ }
+end
+
+local function firstpart(b,r,left,right,obeyhang)
+ local bx, by = b.x, b.y
+ local rx, ry = r.x, r.y
+ local rw = rx + r.w
+ local rh = ry + r.h
+ local rd = ry - r.d
+ if left then
+ rx = rx + left
+ rw = rw - right
+ end
+ local bh = by + b.h
+ local bd = by - b.d
+ local area = { }
+ local leftshapes, rightshapes = shapes(r,rx,ry,rw,rh,rd,bd,rd,bh,rd,obeyhang)
+ add(area,bx,bh-ry)
+ for i=1,#rightshapes do
+ local ri = rightshapes[i]
+ add(area,ri[1],ri[2]-ry)
+ end
+ for i=#leftshapes,1,-1 do
+ local li = leftshapes[i]
+ add(area,li[1],li[2]-ry)
+ end
+ add(area,bx,bd-ry)
+ add(area,bx,bh-ry,true) -- finish last straight line (but no add as we cycle)
+ finish(area)
+ for i=1,#area do
+ local a = area[i]
+ area[i] = f_pair(a[1],a[2])
+ end
+ return {
+ location = "first",
+ region = r,
+ area = area,
+ }
+end
+
+local function middlepart(r,left,right,obeyhang)
+ local rx, ry = r.x, r.y
+ local rw = rx + r.w
+ local rh = ry + r.h
+ local rd = ry - r.d
+ if left then
+ rx = rx + left
+ rw = rw - right
+ end
+ local area = { }
+ local leftshapes, rightshapes = shapes(r,rx,ry,rw,rh,rd,rh,rd,rh,rd,obeyhang)
+ for i=#leftshapes,1,-1 do
+ local li = leftshapes[i]
+ add(area,li[1],li[2]-ry)
+ end
+ for i=1,#rightshapes do
+ local ri = rightshapes[i]
+ add(area,ri[1],ri[2]-ry)
+ end
+ finish(area)
+ for i=1,#area do
+ local a = area[i]
+ area[i] = f_pair(a[1],a[2])
+ end
+ return {
+ location = "middle",
+ region = r,
+ area = area,
+ }
+end
+
+local function lastpart(e,r,left,right,obeyhang)
+ local ex, ey = e.x, e.y
+ local rx, ry = r.x, r.y
+ local rw = rx + r.w
+ local rh = ry + r.h
+ local rd = ry - r.d
+ if left then
+ rx = rx + left
+ rw = rw - right
+ end
+ local eh = ey + e.h
+ local ed = ey - e.d
+ local area = { }
+ -- two cases: till end and halfway e line
+ local leftshapes, rightshapes = shapes(r,rx,ry,rw,rh,rd,rh,ed,rh,eh,obeyhang)
+ for i=1,#rightshapes do
+ local ri = rightshapes[i]
+ add(area,ri[1],ri[2]-ry)
+ end
+ add(area,ex,eh-ry)
+ add(area,ex,ed-ry)
+ for i=#leftshapes,1,-1 do
+ local li = leftshapes[i]
+ add(area,li[1],li[2]-ry)
+ end
+ finish(area)
+ for i=1,#area do
+ local a = area[i]
+ area[i] = f_pair(a[1],a[2])
+ end
+ return {
+ location = "last",
+ region = r,
+ area = area,
+ }
+end
+
+graphics = graphics or { }
+local backgrounds = { }
+
+graphics.backgrounds = backgrounds
+
+local function calculatemultipar(tag,obeyhang)
+ 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_graphics("invalid tag %a",tag)
+ return { }
+ end
+ local br = b.r
+ local er = e.r
+ if not br or not er then
+ report_graphics("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_graphics("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 assymetrical doublesided layouts.
+ local left = 0
+ local right = 0
+ local rc = b.c
+ if rc then
+ rc = collected[rc]
+ if rc then
+ local tb = collected[rc.r]
+ if tb then
+ left = -(tb.x - rc.x)
+ right = (tb.w - rc.w - left) -- tb.x - rc.x
+ end
+ 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
+ if bn then
+ local bp = collected[f_p_tag(bn)]
+ if bp then
+ left = left + bp.ls
+ right = right + bp.rs
+ end
+ end
+ --
+ if bindex == eindex then
+ return {
+ list = { [b.p] = { singlepart(b,e,collected[br],left,right,obeyhang) } },
+ bpos = b,
+ epos = e,
+ }
+ else
+ local list = {
+ [b.p] = { firstpart(b,collected[br],left,right,obeyhang) },
+ }
+ 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 p = r.p
+ local pp = list[p]
+ if pp then
+ pp[#pp+1] = middlepart(r,left,right,obeyhang)
+ else
+ list[p] = { middlepart(r,left,right,obeyhang) }
+ end
+ end
+ end
+ local p = e.p
+ local pp = list[p]
+ if pp then
+ pp[#pp+1] = lastpart(e,collected[er],left,right,obeyhang)
+ else
+ list[p] = { lastpart(e,collected[er],left,right,obeyhang) }
+ end
+ return {
+ list = list,
+ bpos = b,
+ epos = e,
+ }
+ end
+end
+
+-- local pending = { } -- needs gc
+--
+-- local function register(data,n,anchor)
+-- local pa = pending[anchor]
+-- if not pa then
+-- pa = { }
+-- pending[anchor] = pa
+-- end
+-- for page, pagedata in next, data do
+-- local pap = pa[page]
+-- if pap then
+-- pap[#pap+1] = n
+-- else
+-- pa[page] = { n }
+-- end
+-- end
+-- end
+--
+-- function backgrounds.registered(anchor,page)
+-- local pa = pending[anchor]
+-- if pa then
+-- concat(pa,",")
+-- else
+-- return ""
+-- end
+-- end
+
+local pbg = { } -- will move to pending
+
+function backgrounds.calculatemultipar(n)
+ if not pbg[n] then
+ pbg[n] = calculatemultipar("pbg",n) or { }
+ end
+end
+
+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 = [[
+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 = [[
+multilocs[%s] := %s ;
+multikind[%s] := "%s" ;
+multipars[%s] := (%--t--cycle) shifted - (%p,%p) ;
+]]
+
+local f_template_c = [[
+multiregs[%s] := (%--t--cycle) shifted - %s ;
+]]
+
+local f_template_d = [[
+setbounds currentpicture to multibox ;
+]]
+
+f_template_a = formatters[f_template_a]
+f_template_b = formatters[f_template_b]
+f_template_c = formatters[f_template_c]
+f_template_d = formatters[f_template_d]
+
+function backgrounds.fetchmultipar(n,anchor,page,obeyhang)
+ local data = pbg[n]
+ if not data then
+ data = calculatemultipar(n,obeyhang)
+ pbg[n] = data -- can be replaced by register
+ -- register(data.list,n,anchor)
+ end
+ if data then
+ local list = data.list
+ if list then
+ local pagedata = list[page]
+ if pagedata then
+ local nofmultipars = #pagedata
+ -- report_graphics("fetching %a at page %s using anchor %a containing %s multipars",n,page,anchor,nofmultipars)
+ local a = jobpositions.collected[anchor]
+ if not a then
+ report_graphics("missing anchor %a",anchor)
+ else
+ local trace = false
+ local x, y, w, h, d = a.x, a.y, a.w, a.h, a.d
+ local bpos = data.bpos
+ local bh, bd = bpos.h, bpos.d
+ local result = { f_template_a(nofmultipars,w,h+d,bh,bd,bh+bd) }
+ for i=1,nofmultipars do
+ local region = pagedata[i]
+ result[#result+1] = f_template_b(
+ i, multilocs[region.location],
+ i, region.location,
+ i, region.area, x, y-region.region.y)
+ if trace then
+ result[#result+1] = f_template_c(i, regionarea(region.region), offset)
+ end
+ end
+ data[page] = nil
+ result[#result+1] = f_template_d()
+ result = concat(result,"\n")
+ return result
+ end
+ end
+ end
+ end
+ return f_template_a(0,"origin",0,0,0)
+end
+
+backgrounds.point = f_point
+backgrounds.pair = f_pair
+backgrounds.path = f_path
+
+function commands.fetchmultipar(n,anchor,page)
+ context(backgrounds.fetchmultipar(n,anchor,page))
+end
+
+function commands.fetchmultishape(n,anchor,page)
+ context(backgrounds.fetchmultipar(n,anchor,page,true))
+end
+
+local f_template_a = [[
+path posboxes[], posregions[] ;
+numeric pospages[] ;
+numeric nofposboxes ;
+nofposboxes := %s ;
+%t ;
+]]
+
+local f_template_b = [[
+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 ;
+]]
+
+f_template_a = formatters[f_template_a]
+f_template_b = formatters[f_template_b]
+
+function commands.fetchposboxes(tags,anchor,page) -- no caching (yet) / todo: anchor, page
+ local collected = jobpositions.collected
+ if type(tags) == "string" then
+ tags = utilities.parsers.settings_to_array(tags)
+ end
+ local list, nofboxes = { }, 0
+ for i=1,#tags do
+ local tag= tags[i]
+ local c = collected[tag]
+ if c then
+ local r = c.r
+ if r then
+ r = collected[r]
+ if r then
+ local rx, ry, rw, rh, rd = r.x, r.y, r.w, r.h, 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
+ context(f_template_a(nofboxes,list))
+end
+
+local doifelse = commands.doifelse
+
+function commands.doifelsemultipar(n,page,obeyhang)
+ local data = pbg[n]
+ if not data then
+ data = calculatemultipar(n,obeyhang)
+ pbg[n] = data
+ end
+ if page then
+ doifelse(data and data[page] and true)
+ else
+ doifelse(data and next(data) and true)
+ end
+end
+
+function commands.doifelserangeonpage(first,last,page)
+ local collected = jobpositions.collected
+ local f = collected[first]
+ if not f then
+ doifelse(false)
+ return
+ end
+ local l = collected[last]
+ if not l then
+ doifelse(false)
+ return
+ end
+ doifelse(page >= f.p and page <= l.p)
+end
diff --git a/tex/context/base/anch-pos.lua b/tex/context/base/anch-pos.lua
index c94fd60a0..2697cecf4 100644
--- a/tex/context/base/anch-pos.lua
+++ b/tex/context/base/anch-pos.lua
@@ -1,1036 +1,1036 @@
-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
-
-local commands, context = commands, context
-
-local tostring, next, rawget, setmetatable = tostring, next, rawget, setmetatable
-local sort = table.sort
-local format, gmatch, match = string.format, string.gmatch, string.match
-local rawget = rawget
-local lpegmatch = lpeg.match
-local insert, remove = table.insert, table.remove
-local allocate, mark = utilities.storage.allocate, utilities.storage.mark
-local texsp, texcount, texbox, texdimen, texsetcount = tex.sp, tex.count, tex.box, tex.dimen, tex.setcount
------ texsp = string.todimen -- because we cache this is much faster but no rounding
-
-local pdf = pdf -- h and v are variables
-
-local setmetatableindex = table.setmetatableindex
-local new_latelua = nodes.pool.latelua
-local find_tail = node.slide
-
-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
-
-_plib_ = jobpositions -- might go
-
-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
- }
-}
-
-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_b_column = formatters["_plib_.b_col(%q)"]
-local f_e_column = formatters["_plib_.e_col()"]
-
-local f_enhance = formatters["_plib_.enhance(%q)"]
-local f_region = formatters["region:%s"]
-
-local f_b_region = formatters["_plib_.b_region(%q)"]
-local f_e_region = formatters["_plib_.e_region(%s)"]
-
-local f_tag_three = formatters["%s:%s:%s"]
-local f_tag_two = formatters["%s:%s"]
-
-local function sorter(a,b)
- return a.y > b.y
-end
-
-local nofusedregions = 0
-local nofmissingregions = 0
-local nofregular = 0
-
--- todo: register subsets and count them indepently
-
-local function initializer()
- tobesaved = jobpositions.tobesaved
- collected = jobpositions.collected
- -- enhance regions with paragraphs
- for tag, data in next, collected do
- local region = data.r
- if region then
- local r = collected[region]
- if r then
- local paragraphs = r.paragraphs
- if not paragraphs then
- r.paragraphs = { data }
- else
- paragraphs[#paragraphs+1] = data
- end
- nofusedregions = nofusedregions + 1
- else
- nofmissingregions = nofmissingregions + 1
- end
- else
- nofregular = nofregular + 1
- end
- setmetatable(data,default)
- end
- -- add metatable
- -- for tag, data in next, collected do
- -- setmetatable(data,default)
- -- end
- -- sort this data
- for tag, data in next, collected do
- local region = data.r
- if region then
- local r = collected[region]
- if r then
- local paragraphs = r.paragraphs
- if paragraphs and #paragraphs > 1 then
- sort(paragraphs,sorter)
- end
- end
- end
- -- so, we can be sparse and don't need 'or 0' code
- end
-end
-
-job.register('job.positions.collected', tobesaved, initializer)
-
-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 function setdim(name,w,h,d,extra) -- will be used when we move to sp allover
- local x = pdf.h
- local y = pdf.v
- if x == 0 then x = nil end
- if y == 0 then y = nil end
- if w == 0 then w = nil end
- if h == 0 then h = nil end
- if d == 0 then d = nil end
- if extra == "" then extra = nil end
- -- todo: sparse
- tobesaved[name] = {
- p = texcount.realpageno,
- x = x,
- y = y,
- w = w,
- h = h,
- d = d,
- e = extra,
- r = region,
- c = column,
- }
-end
-
-local function setall(name,p,x,y,w,h,d,extra)
- if x == 0 then x = nil end
- if y == 0 then y = nil end
- if w == 0 then w = nil end
- if h == 0 then h = nil end
- if d == 0 then d = nil end
- if extra == "" then extra = nil end
- -- todo: sparse
- tobesaved[name] = {
- p = p,
- x = x,
- y = y,
- w = w,
- h = h,
- d = d,
- e = extra,
- r = region,
- c = column,
- }
-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
- data.x = pdf.h
- end
- if data.y == true then
- data.y = pdf.v
- end
- if data.p == true then
- data.p = texcount.realpageno
- 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
-
-local function set(name,index,val)
- local data = enhance(val or index)
- if val 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
-
-jobpositions.setdim = setdim
-jobpositions.setall = setall
-jobpositions.set = set
-jobpositions.get = get
-
-commands.setpos = setall
-
--- will become private table (could also become attribute driven but too nasty
--- as attributes can bleed e.g. in margin stuff)
-
-function jobpositions.b_col(tag)
- tobesaved[tag] = {
- r = true,
- x = pdf.h,
- w = 0,
- }
- insert(columns,tag)
- column = tag
-end
-
-function jobpositions.e_col(tag)
- local t = tobesaved[column]
- if not t then
- -- something's wrong
- else
- t.w = pdf.h - t.x
- t.r = region
- end
- remove(columns)
- column = columns[#columns]
-end
-
-function commands.bcolumn(tag,register)
- insert(columns,tag)
- column = tag
- if register then
- context(new_latelua(f_b_column(tag)))
- end
-end
-
-function commands.ecolumn(register)
- if register then
- context(new_latelua(f_e_column()))
- end
- remove(columns)
- column = columns[#columns]
-end
-
--- regions
-
-function jobpositions.b_region(tag)
- local last = tobesaved[tag]
- last.x = pdf.h
-last.y = pdf.v
- last.p = texcount.realpageno
- insert(regions,tag)
- region = tag
-end
-
-function jobpositions.e_region(correct)
- local last = tobesaved[region]
-if correct then
- last.h = last.y - pdf.v
-end
- last.y = pdf.v
- remove(regions)
- region = regions[#regions]
-end
-
-function jobpositions.markregionbox(n,tag,correct)
- if not tag or tag == "" then
- nofregions = nofregions + 1
- tag = f_region(nofregions)
- end
- local box = texbox[n]
- local w = box.width
- local h = box.height
- local d = box.depth
- tobesaved[tag] = {
- p = true,
- x = true,
- y = pdf.v, -- true,
- w = w ~= 0 and w or nil,
- h = h ~= 0 and h or nil,
- d = d ~= 0 and d or nil,
- }
- local push = new_latelua(f_b_region(tag))
- local pop = new_latelua(f_e_region(tostring(correct))) -- todo: check if tostring is needed with formatter
- -- maybe we should construct a hbox first (needs experimenting) so that we can avoid some at the tex end
- local head = box.list
- if head then
- local tail = find_tail(head)
- head.prev = push
- push.next = head
- pop .prev = tail
- tail.next = pop
- else -- we can have a simple push/pop
- push.next = pop
- pop.prev = push
- end
- box.list = push
-end
-
-function jobpositions.enhance(name)
- enhance(tobesaved[name])
-end
-
-function commands.pos(name,t)
- tobesaved[name] = t
- context(new_latelua(f_enhance(name)))
-end
-
-local nofparagraphs = 0
-
-function commands.parpos() -- todo: relate to localpar (so this is an intermediate variant)
- nofparagraphs = nofparagraphs + 1
- texsetcount("global","c_anch_positions_paragraph",nofparagraphs)
- local strutbox = texbox.strutbox
- local t = {
- p = true,
- c = true,
- r = true,
- x = true,
- y = true,
- h = strutbox.height,
- d = strutbox.depth,
- hs = tex.hsize,
- }
- local leftskip = tex.leftskip.width
- local rightskip = tex.rightskip.width
- local hangindent = tex.hangindent
- local hangafter = tex.hangafter
- local parindent = tex.parindent
- local parshape = tex.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 tag = f_p_tag(nofparagraphs)
- tobesaved[tag] = t
- context(new_latelua(f_enhance(tag)))
-end
-
-function commands.posxy(name) -- can node.write be used here?
- tobesaved[name] = {
- p = true,
- c = column,
- r = true,
- x = true,
- y = true,
- n = nofparagraphs > 0 and nofparagraphs or nil,
- }
- context(new_latelua(f_enhance(name)))
-end
-
-function commands.poswhd(name,w,h,d)
- tobesaved[name] = {
- p = true,
- c = column,
- r = true,
- x = true,
- y = true,
- w = w,
- h = h,
- d = d,
- n = nofparagraphs > 0 and nofparagraphs or nil,
- }
- context(new_latelua(f_enhance(name)))
-end
-
-function commands.posplus(name,w,h,d,extra)
- tobesaved[name] = {
- p = true,
- c = column,
- r = true,
- x = true,
- y = true,
- w = w,
- h = h,
- d = d,
- n = nofparagraphs > 0 and nofparagraphs or nil,
- e = extra,
- }
- context(new_latelua(f_enhance(name)))
-end
-
-function commands.posstrut(name,w,h,d)
- local strutbox = texbox.strutbox
- tobesaved[name] = {
- p = true,
- c = column,
- r = true,
- x = true,
- y = true,
- h = strutbox.height,
- d = strutbox.depth,
- n = nofparagraphs > 0 and nofparagraphs or nil,
- }
- context(new_latelua(f_enhance(name)))
-end
-
-function jobpositions.getreserved(tag,n)
- if tag == v_column then
- local fulltag = f_tag_three(tag,texcount.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,texcount.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
-
-function jobpositions.page(id)
- local jpi = collected[id]
- return jpi and jpi.p
-end
-
-function jobpositions.region(id)
- local jpi = collected[id]
- return jpi and jpi.r or false
-end
-
-function jobpositions.column(id)
- local jpi = collected[id]
- return jpi and jpi.c or false
-end
-
-function jobpositions.paragraph(id)
- local jpi = collected[id]
- return jpi and jpi.n
-end
-
-jobpositions.p = jobpositions.page
-jobpositions.r = jobpositions.region
-jobpositions.c = jobpositions.column
-jobpositions.n = jobpositions.paragraph
-
-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.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
-
-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
-
-jobpositions.overlapping = overlapping
-jobpositions.onsamepage = onsamepage
-
--- interface
-
-commands.replacepospxywhd = jobpositions.replace
-commands.copyposition = jobpositions.copy
-
-function commands.MPp(id)
- local jpi = collected[id]
- if jpi then
- local p = jpi.p
- if p and p ~= true then
- context(p)
- return
- end
- end
- context('0')
-end
-
-function commands.MPx(id)
- local jpi = collected[id]
- 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
-
-function commands.MPy(id)
- local jpi = collected[id]
- 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
-
-function commands.MPw(id)
- local jpi = collected[id]
- if jpi then
- local w = jpi.w
- if w and w ~= 0 then
- context("%.5fpt",w*pt)
- return
- end
- end
- context('0pt')
-end
-
-function commands.MPh(id)
- local jpi = collected[id]
- if jpi then
- local h = jpi.h
- if h and h ~= 0 then
- context("%.5fpt",h*pt)
- return
- end
- end
- context('0pt')
-end
-
-function commands.MPd(id)
- local jpi = collected[id]
- if jpi then
- local d = jpi.d
- if d and d ~= 0 then
- context("%.5fpt",d*pt)
- return
- end
- end
- context('0pt')
-end
-
-function commands.MPxy(id)
- local jpi = collected[id]
- if jpi then
- context('(%.5fpt,%.5fpt)',
- jpi.x*pt,
- jpi.y*pt
- )
- else
- context('(0,0)')
- end
-end
-
-function commands.MPll(id)
- local jpi = collected[id]
- if jpi then
- context('(%.5fpt,%.5fpt)',
- jpi.x *pt,
- (jpi.y-jpi.d)*pt
- )
- else
- context('(0,0)') -- for mp only
- end
-end
-
-function commands.MPlr(id)
- local jpi = collected[id]
- 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
-
-function commands.MPur(id)
- local jpi = collected[id]
- 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
-
-function commands.MPul(id)
- local jpi = collected[id]
- 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
-
-commands.MPpos = MPpos
-
-function commands.MPn(id)
- local jpi = collected[id]
- if jpi then
- local n = jpi.n
- if n then
- context(n)
- return
- end
- end
- context(0)
-end
-
-function commands.MPc(id)
- local jpi = collected[id]
- if jpi then
- local c = jpi.c
- if c and p ~= true then
- context(c)
- return
- end
- end
- context(c) -- number
-end
-
-function commands.MPr(id)
- local jpi = collected[id]
- if jpi then
- local r = jpi.r
- if r and p ~= true then
- context(r)
- return
- end
- end
-end
-
-local function MPpardata(n)
- local t = collected[n]
- if not t then
- local tag = f_p_tag(n)
- 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
-
-commands.MPpardata = MPpardata
-
-function commands.MPposset(id) -- special helper, used in backgrounds
- local b = f_b_tag(id)
- local e = f_e_tag(id)
- local w = f_w_tag(id)
- local p = f_p_tag(jobpositions.n(b))
- MPpos(b) context(",") MPpos(e) context(",") MPpos(w) context(",") MPpos(p) context(",") MPpardata(p)
-end
-
-function commands.MPls(id)
- local t = collected[id]
- if t then
- context("%.5fpt",t.ls*pt)
- else
- context("0pt")
- end
-end
-
-function commands.MPrs(id)
- local t = collected[id]
- if t then
- context("%.5fpt",t.rs*pt)
- else
- context("0pt")
- end
-end
-
-local splitter = lpeg.tsplitat(",")
-
-function commands.MPplus(id,n,default)
- 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
- context(split[n] or default)
- return
- end
- end
- context(default)
-end
-
-function commands.MPrest(id,default)
- local jpi = collected[id]
- context(jpi and jpi.e or default)
-end
-
-function commands.MPxywhd(id)
- local t = collected[id]
- if t then
- context("%.5fpt,%.5fpt,%.5fpt,%.5fpt,%.5fpt",
- t.x*pt,
- t.y*pt,
- t.w*pt,
- t.h*pt,
- t.d*pt
- )
- else
- context("0,0,0,0,0") -- for mp only
- end
-end
-
-local doif, doifelse = commands.doif, commands.doifelse
-
-function commands.doifpositionelse(name)
- doifelse(collected[name])
-end
-
-function commands.doifposition(name)
- doif(collected[name])
-end
-
-function commands.doifpositiononpage(name,page) -- probably always realpageno
- local c = collected[name]
- doifelse(c and c.p == page)
-end
-
-function commands.doifoverlappingelse(one,two,overlappingmargin)
- doifelse(overlapping(one,two,overlappingmargin))
-end
-
-function commands.doifpositionsonsamepageelse(list,page)
- doifelse(onsamepage(list))
-end
-
-function commands.doifpositionsonthispageelse(list)
- doifelse(onsamepage(list,tostring(tex.count.realpageno)))
-end
-
-function commands.doifelsepositionsused()
- doifelse(next(collected))
-end
-
-commands.markcolumnbox = jobpositions.markcolumnbox
-commands.markregionbox = jobpositions.markregionbox
-
--- 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)
+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
+
+local commands, context = commands, context
+
+local tostring, next, rawget, setmetatable = tostring, next, rawget, setmetatable
+local sort = table.sort
+local format, gmatch, match = string.format, string.gmatch, string.match
+local rawget = rawget
+local lpegmatch = lpeg.match
+local insert, remove = table.insert, table.remove
+local allocate, mark = utilities.storage.allocate, utilities.storage.mark
+local texsp, texcount, texbox, texdimen, texsetcount = tex.sp, tex.count, tex.box, tex.dimen, tex.setcount
+----- texsp = string.todimen -- because we cache this is much faster but no rounding
+
+local pdf = pdf -- h and v are variables
+
+local setmetatableindex = table.setmetatableindex
+local new_latelua = nodes.pool.latelua
+local find_tail = node.slide
+
+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
+
+_plib_ = jobpositions -- might go
+
+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
+ }
+}
+
+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_b_column = formatters["_plib_.b_col(%q)"]
+local f_e_column = formatters["_plib_.e_col()"]
+
+local f_enhance = formatters["_plib_.enhance(%q)"]
+local f_region = formatters["region:%s"]
+
+local f_b_region = formatters["_plib_.b_region(%q)"]
+local f_e_region = formatters["_plib_.e_region(%s)"]
+
+local f_tag_three = formatters["%s:%s:%s"]
+local f_tag_two = formatters["%s:%s"]
+
+local function sorter(a,b)
+ return a.y > b.y
+end
+
+local nofusedregions = 0
+local nofmissingregions = 0
+local nofregular = 0
+
+-- todo: register subsets and count them indepently
+
+local function initializer()
+ tobesaved = jobpositions.tobesaved
+ collected = jobpositions.collected
+ -- enhance regions with paragraphs
+ for tag, data in next, collected do
+ local region = data.r
+ if region then
+ local r = collected[region]
+ if r then
+ local paragraphs = r.paragraphs
+ if not paragraphs then
+ r.paragraphs = { data }
+ else
+ paragraphs[#paragraphs+1] = data
+ end
+ nofusedregions = nofusedregions + 1
+ else
+ nofmissingregions = nofmissingregions + 1
+ end
+ else
+ nofregular = nofregular + 1
+ end
+ setmetatable(data,default)
+ end
+ -- add metatable
+ -- for tag, data in next, collected do
+ -- setmetatable(data,default)
+ -- end
+ -- sort this data
+ for tag, data in next, collected do
+ local region = data.r
+ if region then
+ local r = collected[region]
+ if r then
+ local paragraphs = r.paragraphs
+ if paragraphs and #paragraphs > 1 then
+ sort(paragraphs,sorter)
+ end
+ end
+ end
+ -- so, we can be sparse and don't need 'or 0' code
+ end
+end
+
+job.register('job.positions.collected', tobesaved, initializer)
+
+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 function setdim(name,w,h,d,extra) -- will be used when we move to sp allover
+ local x = pdf.h
+ local y = pdf.v
+ if x == 0 then x = nil end
+ if y == 0 then y = nil end
+ if w == 0 then w = nil end
+ if h == 0 then h = nil end
+ if d == 0 then d = nil end
+ if extra == "" then extra = nil end
+ -- todo: sparse
+ tobesaved[name] = {
+ p = texcount.realpageno,
+ x = x,
+ y = y,
+ w = w,
+ h = h,
+ d = d,
+ e = extra,
+ r = region,
+ c = column,
+ }
+end
+
+local function setall(name,p,x,y,w,h,d,extra)
+ if x == 0 then x = nil end
+ if y == 0 then y = nil end
+ if w == 0 then w = nil end
+ if h == 0 then h = nil end
+ if d == 0 then d = nil end
+ if extra == "" then extra = nil end
+ -- todo: sparse
+ tobesaved[name] = {
+ p = p,
+ x = x,
+ y = y,
+ w = w,
+ h = h,
+ d = d,
+ e = extra,
+ r = region,
+ c = column,
+ }
+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
+ data.x = pdf.h
+ end
+ if data.y == true then
+ data.y = pdf.v
+ end
+ if data.p == true then
+ data.p = texcount.realpageno
+ 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
+
+local function set(name,index,val)
+ local data = enhance(val or index)
+ if val 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
+
+jobpositions.setdim = setdim
+jobpositions.setall = setall
+jobpositions.set = set
+jobpositions.get = get
+
+commands.setpos = setall
+
+-- will become private table (could also become attribute driven but too nasty
+-- as attributes can bleed e.g. in margin stuff)
+
+function jobpositions.b_col(tag)
+ tobesaved[tag] = {
+ r = true,
+ x = pdf.h,
+ w = 0,
+ }
+ insert(columns,tag)
+ column = tag
+end
+
+function jobpositions.e_col(tag)
+ local t = tobesaved[column]
+ if not t then
+ -- something's wrong
+ else
+ t.w = pdf.h - t.x
+ t.r = region
+ end
+ remove(columns)
+ column = columns[#columns]
+end
+
+function commands.bcolumn(tag,register)
+ insert(columns,tag)
+ column = tag
+ if register then
+ context(new_latelua(f_b_column(tag)))
+ end
+end
+
+function commands.ecolumn(register)
+ if register then
+ context(new_latelua(f_e_column()))
+ end
+ remove(columns)
+ column = columns[#columns]
+end
+
+-- regions
+
+function jobpositions.b_region(tag)
+ local last = tobesaved[tag]
+ last.x = pdf.h
+last.y = pdf.v
+ last.p = texcount.realpageno
+ insert(regions,tag)
+ region = tag
+end
+
+function jobpositions.e_region(correct)
+ local last = tobesaved[region]
+if correct then
+ last.h = last.y - pdf.v
+end
+ last.y = pdf.v
+ remove(regions)
+ region = regions[#regions]
+end
+
+function jobpositions.markregionbox(n,tag,correct)
+ if not tag or tag == "" then
+ nofregions = nofregions + 1
+ tag = f_region(nofregions)
+ end
+ local box = texbox[n]
+ local w = box.width
+ local h = box.height
+ local d = box.depth
+ tobesaved[tag] = {
+ p = true,
+ x = true,
+ y = pdf.v, -- true,
+ w = w ~= 0 and w or nil,
+ h = h ~= 0 and h or nil,
+ d = d ~= 0 and d or nil,
+ }
+ local push = new_latelua(f_b_region(tag))
+ local pop = new_latelua(f_e_region(tostring(correct))) -- todo: check if tostring is needed with formatter
+ -- maybe we should construct a hbox first (needs experimenting) so that we can avoid some at the tex end
+ local head = box.list
+ if head then
+ local tail = find_tail(head)
+ head.prev = push
+ push.next = head
+ pop .prev = tail
+ tail.next = pop
+ else -- we can have a simple push/pop
+ push.next = pop
+ pop.prev = push
+ end
+ box.list = push
+end
+
+function jobpositions.enhance(name)
+ enhance(tobesaved[name])
+end
+
+function commands.pos(name,t)
+ tobesaved[name] = t
+ context(new_latelua(f_enhance(name)))
+end
+
+local nofparagraphs = 0
+
+function commands.parpos() -- todo: relate to localpar (so this is an intermediate variant)
+ nofparagraphs = nofparagraphs + 1
+ texsetcount("global","c_anch_positions_paragraph",nofparagraphs)
+ local strutbox = texbox.strutbox
+ local t = {
+ p = true,
+ c = true,
+ r = true,
+ x = true,
+ y = true,
+ h = strutbox.height,
+ d = strutbox.depth,
+ hs = tex.hsize,
+ }
+ local leftskip = tex.leftskip.width
+ local rightskip = tex.rightskip.width
+ local hangindent = tex.hangindent
+ local hangafter = tex.hangafter
+ local parindent = tex.parindent
+ local parshape = tex.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 tag = f_p_tag(nofparagraphs)
+ tobesaved[tag] = t
+ context(new_latelua(f_enhance(tag)))
+end
+
+function commands.posxy(name) -- can node.write be used here?
+ tobesaved[name] = {
+ p = true,
+ c = column,
+ r = true,
+ x = true,
+ y = true,
+ n = nofparagraphs > 0 and nofparagraphs or nil,
+ }
+ context(new_latelua(f_enhance(name)))
+end
+
+function commands.poswhd(name,w,h,d)
+ tobesaved[name] = {
+ p = true,
+ c = column,
+ r = true,
+ x = true,
+ y = true,
+ w = w,
+ h = h,
+ d = d,
+ n = nofparagraphs > 0 and nofparagraphs or nil,
+ }
+ context(new_latelua(f_enhance(name)))
+end
+
+function commands.posplus(name,w,h,d,extra)
+ tobesaved[name] = {
+ p = true,
+ c = column,
+ r = true,
+ x = true,
+ y = true,
+ w = w,
+ h = h,
+ d = d,
+ n = nofparagraphs > 0 and nofparagraphs or nil,
+ e = extra,
+ }
+ context(new_latelua(f_enhance(name)))
+end
+
+function commands.posstrut(name,w,h,d)
+ local strutbox = texbox.strutbox
+ tobesaved[name] = {
+ p = true,
+ c = column,
+ r = true,
+ x = true,
+ y = true,
+ h = strutbox.height,
+ d = strutbox.depth,
+ n = nofparagraphs > 0 and nofparagraphs or nil,
+ }
+ context(new_latelua(f_enhance(name)))
+end
+
+function jobpositions.getreserved(tag,n)
+ if tag == v_column then
+ local fulltag = f_tag_three(tag,texcount.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,texcount.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
+
+function jobpositions.page(id)
+ local jpi = collected[id]
+ return jpi and jpi.p
+end
+
+function jobpositions.region(id)
+ local jpi = collected[id]
+ return jpi and jpi.r or false
+end
+
+function jobpositions.column(id)
+ local jpi = collected[id]
+ return jpi and jpi.c or false
+end
+
+function jobpositions.paragraph(id)
+ local jpi = collected[id]
+ return jpi and jpi.n
+end
+
+jobpositions.p = jobpositions.page
+jobpositions.r = jobpositions.region
+jobpositions.c = jobpositions.column
+jobpositions.n = jobpositions.paragraph
+
+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.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
+
+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
+
+jobpositions.overlapping = overlapping
+jobpositions.onsamepage = onsamepage
+
+-- interface
+
+commands.replacepospxywhd = jobpositions.replace
+commands.copyposition = jobpositions.copy
+
+function commands.MPp(id)
+ local jpi = collected[id]
+ if jpi then
+ local p = jpi.p
+ if p and p ~= true then
+ context(p)
+ return
+ end
+ end
+ context('0')
+end
+
+function commands.MPx(id)
+ local jpi = collected[id]
+ 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
+
+function commands.MPy(id)
+ local jpi = collected[id]
+ 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
+
+function commands.MPw(id)
+ local jpi = collected[id]
+ if jpi then
+ local w = jpi.w
+ if w and w ~= 0 then
+ context("%.5fpt",w*pt)
+ return
+ end
+ end
+ context('0pt')
+end
+
+function commands.MPh(id)
+ local jpi = collected[id]
+ if jpi then
+ local h = jpi.h
+ if h and h ~= 0 then
+ context("%.5fpt",h*pt)
+ return
+ end
+ end
+ context('0pt')
+end
+
+function commands.MPd(id)
+ local jpi = collected[id]
+ if jpi then
+ local d = jpi.d
+ if d and d ~= 0 then
+ context("%.5fpt",d*pt)
+ return
+ end
+ end
+ context('0pt')
+end
+
+function commands.MPxy(id)
+ local jpi = collected[id]
+ if jpi then
+ context('(%.5fpt,%.5fpt)',
+ jpi.x*pt,
+ jpi.y*pt
+ )
+ else
+ context('(0,0)')
+ end
+end
+
+function commands.MPll(id)
+ local jpi = collected[id]
+ if jpi then
+ context('(%.5fpt,%.5fpt)',
+ jpi.x *pt,
+ (jpi.y-jpi.d)*pt
+ )
+ else
+ context('(0,0)') -- for mp only
+ end
+end
+
+function commands.MPlr(id)
+ local jpi = collected[id]
+ 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
+
+function commands.MPur(id)
+ local jpi = collected[id]
+ 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
+
+function commands.MPul(id)
+ local jpi = collected[id]
+ 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
+
+commands.MPpos = MPpos
+
+function commands.MPn(id)
+ local jpi = collected[id]
+ if jpi then
+ local n = jpi.n
+ if n then
+ context(n)
+ return
+ end
+ end
+ context(0)
+end
+
+function commands.MPc(id)
+ local jpi = collected[id]
+ if jpi then
+ local c = jpi.c
+ if c and p ~= true then
+ context(c)
+ return
+ end
+ end
+ context(c) -- number
+end
+
+function commands.MPr(id)
+ local jpi = collected[id]
+ if jpi then
+ local r = jpi.r
+ if r and p ~= true then
+ context(r)
+ return
+ end
+ end
+end
+
+local function MPpardata(n)
+ local t = collected[n]
+ if not t then
+ local tag = f_p_tag(n)
+ 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
+
+commands.MPpardata = MPpardata
+
+function commands.MPposset(id) -- special helper, used in backgrounds
+ local b = f_b_tag(id)
+ local e = f_e_tag(id)
+ local w = f_w_tag(id)
+ local p = f_p_tag(jobpositions.n(b))
+ MPpos(b) context(",") MPpos(e) context(",") MPpos(w) context(",") MPpos(p) context(",") MPpardata(p)
+end
+
+function commands.MPls(id)
+ local t = collected[id]
+ if t then
+ context("%.5fpt",t.ls*pt)
+ else
+ context("0pt")
+ end
+end
+
+function commands.MPrs(id)
+ local t = collected[id]
+ if t then
+ context("%.5fpt",t.rs*pt)
+ else
+ context("0pt")
+ end
+end
+
+local splitter = lpeg.tsplitat(",")
+
+function commands.MPplus(id,n,default)
+ 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
+ context(split[n] or default)
+ return
+ end
+ end
+ context(default)
+end
+
+function commands.MPrest(id,default)
+ local jpi = collected[id]
+ context(jpi and jpi.e or default)
+end
+
+function commands.MPxywhd(id)
+ local t = collected[id]
+ if t then
+ context("%.5fpt,%.5fpt,%.5fpt,%.5fpt,%.5fpt",
+ t.x*pt,
+ t.y*pt,
+ t.w*pt,
+ t.h*pt,
+ t.d*pt
+ )
+ else
+ context("0,0,0,0,0") -- for mp only
+ end
+end
+
+local doif, doifelse = commands.doif, commands.doifelse
+
+function commands.doifpositionelse(name)
+ doifelse(collected[name])
+end
+
+function commands.doifposition(name)
+ doif(collected[name])
+end
+
+function commands.doifpositiononpage(name,page) -- probably always realpageno
+ local c = collected[name]
+ doifelse(c and c.p == page)
+end
+
+function commands.doifoverlappingelse(one,two,overlappingmargin)
+ doifelse(overlapping(one,two,overlappingmargin))
+end
+
+function commands.doifpositionsonsamepageelse(list,page)
+ doifelse(onsamepage(list))
+end
+
+function commands.doifpositionsonthispageelse(list)
+ doifelse(onsamepage(list,tostring(tex.count.realpageno)))
+end
+
+function commands.doifelsepositionsused()
+ doifelse(next(collected))
+end
+
+commands.markcolumnbox = jobpositions.markcolumnbox
+commands.markregionbox = jobpositions.markregionbox
+
+-- 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)
diff --git a/tex/context/base/attr-col.lua b/tex/context/base/attr-col.lua
index 473bf8a74..7c6b7909b 100644
--- a/tex/context/base/attr-col.lua
+++ b/tex/context/base/attr-col.lua
@@ -1,538 +1,538 @@
-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 = type
-local format = string.format
-local concat = table.concat
-local min, max, floor = math.min, math.max, math.floor
-
-local attributes, nodes, utilities, logs, backends, storage = attributes, nodes, utilities, logs, backends, storage
-local commands, context, interfaces = commands, context, interfaces
-local tex = tex
-
-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 tasks = nodes.tasks
-local nodeinjections = backends.nodeinjections
-local registrations = backends.registrations
-local unsetvalue = attributes.unsetvalue
-
-local registerstorage = storage.register
-local formatters = string.formatters
-
--- 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 numbers = attributes.numbers
-local list = attributes.list
-
-registerstorage("attributes/colors/values", values, "attributes.colors.values")
-registerstorage("attributes/colors/registered", registered, "attributes.colors.registered")
-
-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
- return 1-r, 1-g, 1-b, 0
-end
-
-local function cmyktorgb(c,m,y,k)
- return 1.0 - min(1.0,c+k), 1.0 - min(1.0,m+k), 1.0 - min(1.0,y+k)
-end
-
-local function rgbtogray(r,g,b)
- if colors.weightgray then
- return .30*r + .59*g + .11*b
- else
- return r/3 + g/3 + b/3
- end
-end
-
-local function cmyktogray(c,m,y,k)
- return rgbtogray(cmyktorgb(c,m,y,k))
-end
-
--- not critical so not needed:
---
--- local function cmyktogray(c,m,y,k)
--- local r, g, b = 1.0 - min(1.0,c+k), 1.0 - min(1.0,m+k), 1.0 - min(1.0,y+k)
--- if colors.weightgray then
--- return .30*r + .59*g + .11*b
--- else
--- return r/3 + g/3 + b/3
--- end
--- end
-
--- http://en.wikipedia.org/wiki/HSI_color_space
--- http://nl.wikipedia.org/wiki/HSV_(kleurruimte)
-
-local function hsvtorgb(h,s,v)
- -- h = h % 360
- local hd = h/60
- local hf = floor(hd)
- local hi = hf % 6
- -- local f = hd - hi
- local f = hd - hf
- 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",hi,h,s,v)
- 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
-
-colors.rgbtocmyk = rgbtocmyk
-colors.rgbtogray = rgbtogray
-colors.cmyktorgb = cmyktorgb
-colors.cmyktogray = cmyktogray
-colors.rgbtohsv = rgbtohsv
-colors.hsvtorgb = hsvtorgb
-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)
- 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[8]
- 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)
- 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, rgb, cmyk = graycolor(v[2]), rgbcolor(v[3],v[4],v[5]), cmykcolor(v[6],v[7],v[8],v[9])
- d = { rgb, gray, rgb, cmyk }
- elseif model == 4 then
- local gray, rgb, cmyk = graycolor(v[2]), rgbcolor(v[3],v[4],v[5]), 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
-
-function colors.setmodel(name,weightgray)
- colors.model = name
- colors.default = models[name] or 1
- colors.weightgray = weightgray ~= false
- return colors.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)
- if value == false or not colors.supported then
- tasks.disableaction("shipouts","attributes.colors.handler")
- else
- tasks.enableaction("shipouts","attributes.colors.handler")
- end
-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
-
--- 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 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
- if value == false or not transparencies.supported then
- tasks.disableaction("shipouts","attributes.transparencies.handler")
- else
- tasks.enableaction("shipouts","attributes.transparencies.handler")
- end
-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
-
---- 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()
- tasks.enableaction("shipouts","attributes.colorintents.handler")
-end
-
--- interface
-
-commands.enablecolor = colors.enable
-commands.enabletransparency = transparencies.enable
-commands.enablecolorintents = colorintents.enable
-
-function commands.registercolor (...) context(colors .register(...)) end
-function commands.registertransparency(...) context(transparencies.register(...)) end
-function commands.registercolorintent (...) context(colorintents .register(...)) end
+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 = type
+local format = string.format
+local concat = table.concat
+local min, max, floor = math.min, math.max, math.floor
+
+local attributes, nodes, utilities, logs, backends, storage = attributes, nodes, utilities, logs, backends, storage
+local commands, context, interfaces = commands, context, interfaces
+local tex = tex
+
+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 tasks = nodes.tasks
+local nodeinjections = backends.nodeinjections
+local registrations = backends.registrations
+local unsetvalue = attributes.unsetvalue
+
+local registerstorage = storage.register
+local formatters = string.formatters
+
+-- 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 numbers = attributes.numbers
+local list = attributes.list
+
+registerstorage("attributes/colors/values", values, "attributes.colors.values")
+registerstorage("attributes/colors/registered", registered, "attributes.colors.registered")
+
+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
+ return 1-r, 1-g, 1-b, 0
+end
+
+local function cmyktorgb(c,m,y,k)
+ return 1.0 - min(1.0,c+k), 1.0 - min(1.0,m+k), 1.0 - min(1.0,y+k)
+end
+
+local function rgbtogray(r,g,b)
+ if colors.weightgray then
+ return .30*r + .59*g + .11*b
+ else
+ return r/3 + g/3 + b/3
+ end
+end
+
+local function cmyktogray(c,m,y,k)
+ return rgbtogray(cmyktorgb(c,m,y,k))
+end
+
+-- not critical so not needed:
+--
+-- local function cmyktogray(c,m,y,k)
+-- local r, g, b = 1.0 - min(1.0,c+k), 1.0 - min(1.0,m+k), 1.0 - min(1.0,y+k)
+-- if colors.weightgray then
+-- return .30*r + .59*g + .11*b
+-- else
+-- return r/3 + g/3 + b/3
+-- end
+-- end
+
+-- http://en.wikipedia.org/wiki/HSI_color_space
+-- http://nl.wikipedia.org/wiki/HSV_(kleurruimte)
+
+local function hsvtorgb(h,s,v)
+ -- h = h % 360
+ local hd = h/60
+ local hf = floor(hd)
+ local hi = hf % 6
+ -- local f = hd - hi
+ local f = hd - hf
+ 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",hi,h,s,v)
+ 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
+
+colors.rgbtocmyk = rgbtocmyk
+colors.rgbtogray = rgbtogray
+colors.cmyktorgb = cmyktorgb
+colors.cmyktogray = cmyktogray
+colors.rgbtohsv = rgbtohsv
+colors.hsvtorgb = hsvtorgb
+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)
+ 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[8]
+ 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)
+ 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, rgb, cmyk = graycolor(v[2]), rgbcolor(v[3],v[4],v[5]), cmykcolor(v[6],v[7],v[8],v[9])
+ d = { rgb, gray, rgb, cmyk }
+ elseif model == 4 then
+ local gray, rgb, cmyk = graycolor(v[2]), rgbcolor(v[3],v[4],v[5]), 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
+
+function colors.setmodel(name,weightgray)
+ colors.model = name
+ colors.default = models[name] or 1
+ colors.weightgray = weightgray ~= false
+ return colors.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)
+ if value == false or not colors.supported then
+ tasks.disableaction("shipouts","attributes.colors.handler")
+ else
+ tasks.enableaction("shipouts","attributes.colors.handler")
+ end
+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
+
+-- 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 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
+ if value == false or not transparencies.supported then
+ tasks.disableaction("shipouts","attributes.transparencies.handler")
+ else
+ tasks.enableaction("shipouts","attributes.transparencies.handler")
+ end
+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
+
+--- 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()
+ tasks.enableaction("shipouts","attributes.colorintents.handler")
+end
+
+-- interface
+
+commands.enablecolor = colors.enable
+commands.enabletransparency = transparencies.enable
+commands.enablecolorintents = colorintents.enable
+
+function commands.registercolor (...) context(colors .register(...)) end
+function commands.registertransparency(...) context(transparencies.register(...)) end
+function commands.registercolorintent (...) context(colorintents .register(...)) end
diff --git a/tex/context/base/attr-eff.lua b/tex/context/base/attr-eff.lua
index b0a987747..4dce5419a 100644
--- a/tex/context/base/attr-eff.lua
+++ b/tex/context/base/attr-eff.lua
@@ -1,111 +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 commands, interfaces = commands, interfaces
-local attributes, nodes, backends, utilities = attributes, nodes, backends, utilities
-local tex = tex
-
-local states = attributes.states
-local tasks = nodes.tasks
-local nodeinjections = backends.nodeinjections
-local settexattribute = tex.setattribute
-local allocate = utilities.storage.allocate
-local setmetatableindex = table.setmetatableindex
-local formatters = string.formatters
-
-local variables = interfaces.variables
-local v_normal = variables.normal
-
-attributes.effects = attributes.effects or { }
-local effects = attributes.effects
-
-local a_effect = attributes.private('effect')
-
-effects.data = allocate()
-effects.values = effects.values or { }
-effects.registered = effects.registered or { }
-effects.attribute = a_effect
-
-local data = effects.data
-local registered = effects.registered
-local values = effects.values
-
-local f_stamp = formatters["%s:%s:%s"]
-
-storage.register("attributes/effects/registered", registered, "attributes.effects.registered")
-storage.register("attributes/effects/values", values, "attributes.effects.values")
-
--- valid effects: normal inner outer both hidden (stretch,rulethickness,effect)
-
-local function effect(...) effect = nodeinjections.effect return effect(...) end
-
-local function extender(effects,key)
- if key == "none" then
- local d = effect(0,0,0)
- effects.none = d
- return d
- end
-end
-
-local function reviver(data,n)
- local e = values[n] -- we could nil values[n] now but hardly needed
- local d = effect(e[1],e[2],e[3])
- data[n] = d
- return d
-end
-
-setmetatableindex(effects, extender)
-setmetatableindex(effects.data, reviver)
-
-effects.handler = nodes.installattributehandler {
- name = "effect",
- namespace = effects,
- initializer = states.initialize,
- finalizer = states.finalize,
- processor = states.process,
-}
-
-local function register(specification)
- local alternative, stretch, rulethickness
- if specification then
- alternative = specification.alternative or v_normal
- stretch = specification.stretch or 0
- rulethickness = specification.rulethickness or 0
- else
- alternative = v_normal
- stretch = 0
- rulethickness = 0
- end
- local stamp = f_stamp(alternative,stretch,rulethickness)
- local n = registered[stamp]
- if not n then
- n = #values + 1
- values[n] = { alternative, stretch, rulethickness }
- registered[stamp] = n
- end
- return n
-end
-
-local function enable()
- tasks.enableaction("shipouts","attributes.effects.handler")
-end
-
-effects.register = register
-effects.enable = enable
-
--- interface
-
-local enabled = false
-
-function commands.triggereffect(specification)
- if not enabled then
- enable()
- enabled = true
- end
- settexattribute(a_effect,register(specification))
-end
+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 commands, interfaces = commands, interfaces
+local attributes, nodes, backends, utilities = attributes, nodes, backends, utilities
+local tex = tex
+
+local states = attributes.states
+local tasks = nodes.tasks
+local nodeinjections = backends.nodeinjections
+local settexattribute = tex.setattribute
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+local formatters = string.formatters
+
+local variables = interfaces.variables
+local v_normal = variables.normal
+
+attributes.effects = attributes.effects or { }
+local effects = attributes.effects
+
+local a_effect = attributes.private('effect')
+
+effects.data = allocate()
+effects.values = effects.values or { }
+effects.registered = effects.registered or { }
+effects.attribute = a_effect
+
+local data = effects.data
+local registered = effects.registered
+local values = effects.values
+
+local f_stamp = formatters["%s:%s:%s"]
+
+storage.register("attributes/effects/registered", registered, "attributes.effects.registered")
+storage.register("attributes/effects/values", values, "attributes.effects.values")
+
+-- valid effects: normal inner outer both hidden (stretch,rulethickness,effect)
+
+local function effect(...) effect = nodeinjections.effect return effect(...) end
+
+local function extender(effects,key)
+ if key == "none" then
+ local d = effect(0,0,0)
+ effects.none = d
+ return d
+ end
+end
+
+local function reviver(data,n)
+ local e = values[n] -- we could nil values[n] now but hardly needed
+ local d = effect(e[1],e[2],e[3])
+ data[n] = d
+ return d
+end
+
+setmetatableindex(effects, extender)
+setmetatableindex(effects.data, reviver)
+
+effects.handler = nodes.installattributehandler {
+ name = "effect",
+ namespace = effects,
+ initializer = states.initialize,
+ finalizer = states.finalize,
+ processor = states.process,
+}
+
+local function register(specification)
+ local alternative, stretch, rulethickness
+ if specification then
+ alternative = specification.alternative or v_normal
+ stretch = specification.stretch or 0
+ rulethickness = specification.rulethickness or 0
+ else
+ alternative = v_normal
+ stretch = 0
+ rulethickness = 0
+ end
+ local stamp = f_stamp(alternative,stretch,rulethickness)
+ local n = registered[stamp]
+ if not n then
+ n = #values + 1
+ values[n] = { alternative, stretch, rulethickness }
+ registered[stamp] = n
+ end
+ return n
+end
+
+local function enable()
+ tasks.enableaction("shipouts","attributes.effects.handler")
+end
+
+effects.register = register
+effects.enable = enable
+
+-- interface
+
+local enabled = false
+
+function commands.triggereffect(specification)
+ if not enabled then
+ enable()
+ enabled = true
+ end
+ settexattribute(a_effect,register(specification))
+end
diff --git a/tex/context/base/attr-ini.lua b/tex/context/base/attr-ini.lua
index cb8eecf77..206a86d79 100644
--- a/tex/context/base/attr-ini.lua
+++ b/tex/context/base/attr-ini.lua
@@ -1,167 +1,167 @@
-if not modules then modules = { } end modules ['attr-ini'] = {
- version = 1.001,
- comment = "companion to attr-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local commands, context, nodes, storage = commands, context, nodes, storage
-
-local next, type = next, type
-
---[[ldx--
-<p>We start with a registration system for atributes so that we can use the
-symbolic names later on.</p>
---ldx]]--
-
-attributes = attributes or { }
-local attributes = attributes
-
-local sharedstorage = storage.shared
-
-attributes.names = attributes.names or { }
-attributes.numbers = attributes.numbers or { }
-attributes.list = attributes.list or { }
-attributes.states = attributes.states or { }
-attributes.handlers = attributes.handlers or { }
-attributes.unsetvalue = -0x7FFFFFFF
-
-local names = attributes.names
-local numbers = attributes.numbers
-local list = attributes.list
-
-storage.register("attributes/names", names, "attributes.names")
-storage.register("attributes/numbers", numbers, "attributes.numbers")
-storage.register("attributes/list", list, "attributes.list")
-
-function attributes.define(name,number) -- at the tex end
- if not numbers[name] then
- numbers[name] = number
- names[number] = name
- list[number] = { }
- end
-end
-
---[[ldx--
-<p>We reserve this one as we really want it to be always set (faster).</p>
---ldx]]--
-
-names[0], numbers["fontdynamic"] = "fontdynamic", 0
-
---[[ldx--
-<p>We can use the attributes in the range 127-255 (outside user space). These
-are only used when no attribute is set at the \TEX\ end which normally
-happens in <l n='context'/>.</p>
---ldx]]--
-
-sharedstorage.attributes_last_private = sharedstorage.attributes_last_private or 127
-
--- to be considered (so that we can use an array access):
---
--- local private = { } attributes.private = private
---
--- setmetatable(private, {
--- __index = function(t,name)
--- local number = sharedstorage.attributes_last_private
--- if number < 1023 then -- tex.count.minallocatedattribute - 1
--- number = number + 1
--- sharedstorage.attributes_last_private = number
--- end
--- numbers[name], names[number], list[number] = number, name, { }
--- private[name] = number
--- return number
--- end,
--- __call = function(t,name)
--- return t[name]
--- end
--- } )
-
-function attributes.private(name) -- at the lua end (hidden from user)
- local number = numbers[name]
- if not number then
- local last = sharedstorage.attributes_last_private
- if last < 1023 then -- tex.count.minallocatedattribute - 1
- last = last + 1
- sharedstorage.attributes_last_private = last
- else
- report_attribute("no more room for private attributes")
- os.exit()
- end
- number = last
- numbers[name], names[number], list[number] = number, name, { }
- end
- return number
-end
-
--- tracers
-
-local report_attribute = logs.reporter("attributes")
-
-local function showlist(what,list)
- if list then
- local a = list.next
- local i = 0
- while a do
- local number, value = a.number, a.value
- i = i + 1
- report_attribute("%S %2i: attribute %3i, value %4i, name %a",what,i,number,value,names[number])
- a = a.next
- end
- end
-end
-
-function attributes.showcurrent()
- showlist("current",node.current_attr())
-end
-
-function attributes.ofnode(n)
- showlist(n,n.attr)
-end
-
--- interface
-
-commands.defineattribute = attributes.define
-commands.showattributes = attributes.showcurrent
-
-function commands.getprivateattribute(name)
- context(attributes.private(name))
-end
-
--- rather special
-
-local store = { }
-
-function commands.savecurrentattributes(name)
- name = name or ""
- local n = node.current_attr()
- n = n and n.next
- local t = { }
- while n do
- t[n.number] = n.value
- n = n.next
- end
- store[name] = {
- attr = t,
- font = font.current(),
- }
-end
-
-function commands.restorecurrentattributes(name)
- name = name or ""
- local t = store[name]
- if t then
- local attr = t.attr
- local font = t.font
- if attr then
- for k, v in next, attr do
- tex.attribute[k] = v
- end
- end
- if font then
- -- tex.font = font
- context.getvalue(fonts.hashes.csnames[font]) -- we don't have a direct way yet (will discuss it with taco)
- end
- end
- -- store[name] = nil
-end
+if not modules then modules = { } end modules ['attr-ini'] = {
+ version = 1.001,
+ comment = "companion to attr-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local commands, context, nodes, storage = commands, context, nodes, storage
+
+local next, type = next, type
+
+--[[ldx--
+<p>We start with a registration system for atributes so that we can use the
+symbolic names later on.</p>
+--ldx]]--
+
+attributes = attributes or { }
+local attributes = attributes
+
+local sharedstorage = storage.shared
+
+attributes.names = attributes.names or { }
+attributes.numbers = attributes.numbers or { }
+attributes.list = attributes.list or { }
+attributes.states = attributes.states or { }
+attributes.handlers = attributes.handlers or { }
+attributes.unsetvalue = -0x7FFFFFFF
+
+local names = attributes.names
+local numbers = attributes.numbers
+local list = attributes.list
+
+storage.register("attributes/names", names, "attributes.names")
+storage.register("attributes/numbers", numbers, "attributes.numbers")
+storage.register("attributes/list", list, "attributes.list")
+
+function attributes.define(name,number) -- at the tex end
+ if not numbers[name] then
+ numbers[name] = number
+ names[number] = name
+ list[number] = { }
+ end
+end
+
+--[[ldx--
+<p>We reserve this one as we really want it to be always set (faster).</p>
+--ldx]]--
+
+names[0], numbers["fontdynamic"] = "fontdynamic", 0
+
+--[[ldx--
+<p>We can use the attributes in the range 127-255 (outside user space). These
+are only used when no attribute is set at the \TEX\ end which normally
+happens in <l n='context'/>.</p>
+--ldx]]--
+
+sharedstorage.attributes_last_private = sharedstorage.attributes_last_private or 127
+
+-- to be considered (so that we can use an array access):
+--
+-- local private = { } attributes.private = private
+--
+-- setmetatable(private, {
+-- __index = function(t,name)
+-- local number = sharedstorage.attributes_last_private
+-- if number < 1023 then -- tex.count.minallocatedattribute - 1
+-- number = number + 1
+-- sharedstorage.attributes_last_private = number
+-- end
+-- numbers[name], names[number], list[number] = number, name, { }
+-- private[name] = number
+-- return number
+-- end,
+-- __call = function(t,name)
+-- return t[name]
+-- end
+-- } )
+
+function attributes.private(name) -- at the lua end (hidden from user)
+ local number = numbers[name]
+ if not number then
+ local last = sharedstorage.attributes_last_private
+ if last < 1023 then -- tex.count.minallocatedattribute - 1
+ last = last + 1
+ sharedstorage.attributes_last_private = last
+ else
+ report_attribute("no more room for private attributes")
+ os.exit()
+ end
+ number = last
+ numbers[name], names[number], list[number] = number, name, { }
+ end
+ return number
+end
+
+-- tracers
+
+local report_attribute = logs.reporter("attributes")
+
+local function showlist(what,list)
+ if list then
+ local a = list.next
+ local i = 0
+ while a do
+ local number, value = a.number, a.value
+ i = i + 1
+ report_attribute("%S %2i: attribute %3i, value %4i, name %a",what,i,number,value,names[number])
+ a = a.next
+ end
+ end
+end
+
+function attributes.showcurrent()
+ showlist("current",node.current_attr())
+end
+
+function attributes.ofnode(n)
+ showlist(n,n.attr)
+end
+
+-- interface
+
+commands.defineattribute = attributes.define
+commands.showattributes = attributes.showcurrent
+
+function commands.getprivateattribute(name)
+ context(attributes.private(name))
+end
+
+-- rather special
+
+local store = { }
+
+function commands.savecurrentattributes(name)
+ name = name or ""
+ local n = node.current_attr()
+ n = n and n.next
+ local t = { }
+ while n do
+ t[n.number] = n.value
+ n = n.next
+ end
+ store[name] = {
+ attr = t,
+ font = font.current(),
+ }
+end
+
+function commands.restorecurrentattributes(name)
+ name = name or ""
+ local t = store[name]
+ if t then
+ local attr = t.attr
+ local font = t.font
+ if attr then
+ for k, v in next, attr do
+ tex.attribute[k] = v
+ end
+ end
+ if font then
+ -- tex.font = font
+ context.getvalue(fonts.hashes.csnames[font]) -- we don't have a direct way yet (will discuss it with taco)
+ end
+ end
+ -- store[name] = nil
+end
diff --git a/tex/context/base/attr-lay.lua b/tex/context/base/attr-lay.lua
index 60907bdf3..4bcc70b0c 100644
--- a/tex/context/base/attr-lay.lua
+++ b/tex/context/base/attr-lay.lua
@@ -1,253 +1,253 @@
-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, nodes, utilities, logs, backends = attributes, nodes, utilities, logs, backends
-local commands, context, interfaces = commands, context, interfaces
-local tex = tex
-
-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 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 tasks = nodes.tasks
-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 nodepool = nodes.pool
-
-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_begin = layerstacker.resolve_begin
-viewerlayers.resolve_step = layerstacker.resolve_step
-viewerlayers.resolve_end = layerstacker.resolve_end
-
-function commands.cleanuplayers()
- layerstacker.clean()
- -- todo
-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.stacked,
- processor = states.stacker,
-}
-
-local stack, enabled, global = { }, false, false
-
-function viewerlayers.enable(value)
- if value == false or not viewerlayers.supported then
- if enabled then
- tasks.disableaction("shipouts","attributes.viewerlayers.handler")
- end
- enabled = false
- else
- if not enabled then
- tasks.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
- 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
-
-commands.defineviewerlayer = viewerlayers.define
-commands.startviewerlayer = viewerlayers.start
-commands.stopviewerlayer = viewerlayers.stop
-
-function commands.definedviewerlayer(settings)
- viewerlayers.define(settings)
- context(register(settings.tag,true)) -- true forces a use
-end
-
-function commands.registeredviewerlayer(name)
- context(register(name,true)) -- true forces a use
-end
+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, nodes, utilities, logs, backends = attributes, nodes, utilities, logs, backends
+local commands, context, interfaces = commands, context, interfaces
+local tex = tex
+
+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 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 tasks = nodes.tasks
+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 nodepool = nodes.pool
+
+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_begin = layerstacker.resolve_begin
+viewerlayers.resolve_step = layerstacker.resolve_step
+viewerlayers.resolve_end = layerstacker.resolve_end
+
+function commands.cleanuplayers()
+ layerstacker.clean()
+ -- todo
+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.stacked,
+ processor = states.stacker,
+}
+
+local stack, enabled, global = { }, false, false
+
+function viewerlayers.enable(value)
+ if value == false or not viewerlayers.supported then
+ if enabled then
+ tasks.disableaction("shipouts","attributes.viewerlayers.handler")
+ end
+ enabled = false
+ else
+ if not enabled then
+ tasks.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
+ 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
+
+commands.defineviewerlayer = viewerlayers.define
+commands.startviewerlayer = viewerlayers.start
+commands.stopviewerlayer = viewerlayers.stop
+
+function commands.definedviewerlayer(settings)
+ viewerlayers.define(settings)
+ context(register(settings.tag,true)) -- true forces a use
+end
+
+function commands.registeredviewerlayer(name)
+ context(register(name,true)) -- true forces a use
+end
diff --git a/tex/context/base/attr-mkr.lua b/tex/context/base/attr-mkr.lua
index 87a8e2015..976598fa0 100644
--- a/tex/context/base/attr-mkr.lua
+++ b/tex/context/base/attr-mkr.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['attr-mkr'] = {
- version = 1.001,
- comment = "companion to attr-mkr.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local markers = nodes.markers or { }
-nodes.markers = markers
-
-local cache = { }
-local numbers = attributes.numbers
-local a_unknown = attributes.private("marker:unknown")
-
-table.setmetatableindex(cache,function(t,k)
- local k = "marker:" .. k
- local v = numbers[k] or a_unknown
- t[k] = v
- return v
-end)
-
-function markers.get(n,name)
- local a = cache[name]
- return a and n[a] or nil
-end
+if not modules then modules = { } end modules ['attr-mkr'] = {
+ version = 1.001,
+ comment = "companion to attr-mkr.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local markers = nodes.markers or { }
+nodes.markers = markers
+
+local cache = { }
+local numbers = attributes.numbers
+local a_unknown = attributes.private("marker:unknown")
+
+table.setmetatableindex(cache,function(t,k)
+ local k = "marker:" .. k
+ local v = numbers[k] or a_unknown
+ t[k] = v
+ return v
+end)
+
+function markers.get(n,name)
+ local a = cache[name]
+ return a and n[a] or nil
+end
diff --git a/tex/context/base/attr-neg.lua b/tex/context/base/attr-neg.lua
index d37490f11..c32cec956 100644
--- a/tex/context/base/attr-neg.lua
+++ b/tex/context/base/attr-neg.lua
@@ -1,98 +1,98 @@
-if not modules then modules = { } end modules ['attr-neg'] = {
- version = 1.001,
- comment = "companion to attr-neg.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
-
-local format = string.format
-
-local attributes, nodes, utilities, logs, backends = attributes, nodes, utilities, logs, backends
-local commands, context, interfaces = commands, context, interfaces
-local tex = tex
-
-local states = attributes.states
-local tasks = nodes.tasks
-local nodeinjections = backends.nodeinjections
-local settexattribute = tex.setattribute
-local variables = interfaces.variables
-local allocate = utilities.storage.allocate
-local setmetatableindex = table.setmetatableindex
-
---- negative / positive
-
-attributes.negatives = attributes.negatives or { }
-local negatives = attributes.negatives
-
-local a_negative = attributes.private("negative")
-
-local v_none = interfaces.variables.none
-
-negatives.data = allocate()
-negatives.attribute = a_negative
-
-negatives.registered = allocate {
- [variables.positive] = 1,
- [variables.negative] = 2,
-}
-
-local data = negatives.data
-local registered = negatives.registered
-
-local function extender(negatives,key)
- if key == "none" then -- v_none then
- local d = data[1]
- negatives.none = d
- return d
- end
-end
-
-local function reviver(data,n)
- if n == 1 then
- local d = nodeinjections.positive() -- called once
- data[1] = d
- return d
- elseif n == 2 then
- local d = nodeinjections.negative() -- called once
- data[2] = d
- return d
- end
-end
-
-setmetatableindex(negatives, extender)
-setmetatableindex(negatives.data, reviver)
-
-negatives.handler = nodes.installattributehandler {
- name = "negative",
- namespace = negatives,
- initializer = states.initialize,
- finalizer = states.finalize,
- processor = states.process,
-}
-
-local function register(stamp)
- return registered[stamp] or registered.positive
-end
-
-local function enable()
- tasks.enableaction("shipouts","attributes.negatives.handler")
-end
-
-negatives.register = register
-negatives.enable = enable
-
--- interface
-
-local enabled = false
-
-function commands.triggernegative(stamp)
- if not enabled then
- enable()
- enabled = true
- end
- settexattribute(a_negative,register(stamp))
-end
+if not modules then modules = { } end modules ['attr-neg'] = {
+ version = 1.001,
+ comment = "companion to attr-neg.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
+
+local format = string.format
+
+local attributes, nodes, utilities, logs, backends = attributes, nodes, utilities, logs, backends
+local commands, context, interfaces = commands, context, interfaces
+local tex = tex
+
+local states = attributes.states
+local tasks = nodes.tasks
+local nodeinjections = backends.nodeinjections
+local settexattribute = tex.setattribute
+local variables = interfaces.variables
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+
+--- negative / positive
+
+attributes.negatives = attributes.negatives or { }
+local negatives = attributes.negatives
+
+local a_negative = attributes.private("negative")
+
+local v_none = interfaces.variables.none
+
+negatives.data = allocate()
+negatives.attribute = a_negative
+
+negatives.registered = allocate {
+ [variables.positive] = 1,
+ [variables.negative] = 2,
+}
+
+local data = negatives.data
+local registered = negatives.registered
+
+local function extender(negatives,key)
+ if key == "none" then -- v_none then
+ local d = data[1]
+ negatives.none = d
+ return d
+ end
+end
+
+local function reviver(data,n)
+ if n == 1 then
+ local d = nodeinjections.positive() -- called once
+ data[1] = d
+ return d
+ elseif n == 2 then
+ local d = nodeinjections.negative() -- called once
+ data[2] = d
+ return d
+ end
+end
+
+setmetatableindex(negatives, extender)
+setmetatableindex(negatives.data, reviver)
+
+negatives.handler = nodes.installattributehandler {
+ name = "negative",
+ namespace = negatives,
+ initializer = states.initialize,
+ finalizer = states.finalize,
+ processor = states.process,
+}
+
+local function register(stamp)
+ return registered[stamp] or registered.positive
+end
+
+local function enable()
+ tasks.enableaction("shipouts","attributes.negatives.handler")
+end
+
+negatives.register = register
+negatives.enable = enable
+
+-- interface
+
+local enabled = false
+
+function commands.triggernegative(stamp)
+ if not enabled then
+ enable()
+ enabled = true
+ end
+ settexattribute(a_negative,register(stamp))
+end
diff --git a/tex/context/base/back-exp.lua b/tex/context/base/back-exp.lua
index 4d61e64c7..4d219a18b 100644
--- a/tex/context/base/back-exp.lua
+++ b/tex/context/base/back-exp.lua
@@ -1,2411 +1,2411 @@
-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"
-}
-
--- 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
--- delimited -> left/right string (needs marking)
-
--- 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)
-
-local next, type = next, type
-local format, match, concat, rep, sub, gsub, gmatch, find = string.format, string.match, table.concat, string.rep, string.sub, string.gsub, string.gmatch, string.find
-local validstring = string.valid
-local lpegmatch = lpeg.match
-local utfchar, utfbyte, utfvalues = utf.char, utf.byte, utf.values
-local insert, remove = table.insert, table.remove
-local fromunicode16 = fonts.mappings.fromunicode16
-local sortedhash = table.sortedhash
-local formatters = string.formatters
-
-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 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 settings_to_array = utilities.parsers.settings_to_array
-
-local setmetatableindex = table.setmetatableindex
-local tasks = nodes.tasks
-local fontchar = fonts.hashes.characters
-local fontquads = fonts.hashes.quads
-local languagenames = languages.numbers
-
-local nodecodes = nodes.nodecodes
-local skipcodes = nodes.skipcodes
-local whatsitcodes = nodes.whatsitcodes
-local listcodes = nodes.listcodes
-
-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 insert_code = nodecodes.insert
-local whatsit_code = nodecodes.whatsit
-local refximage_code = whatsitcodes.pdfrefximage
-local localpar_code = whatsitcodes.localpar
-
-local userskip_code = skipcodes.userskip
-local rightskip_code = skipcodes.rightskip
-local parfillskip_code = skipcodes.parfillskip
-local spaceskip_code = skipcodes.spaceskip
-local xspaceskip_code = skipcodes.xspaceskip
-
-local line_code = listcodes.line
-
-local a_characters = attributes.private('characters')
-local a_exportstatus = attributes.private('exportstatus')
-
-local a_tagged = attributes.private('tagged')
-local a_taggedpar = attributes.private("taggedpar")
-local a_image = attributes.private('image')
-local a_reference = attributes.private('reference')
-
-local a_textblock = attributes.private("textblock")
-
-local traverse_id = node.traverse_id
-local traverse_nodes = node.traverse
-local slide_nodelist = node.slide
-local texattribute = tex.attribute
-local texdimen = tex.dimen
-local texcount = tex.count
-local locate_node = nodes.locate
-
-local references = structures.references
-local structurestags = structures.tags
-local taglist = structurestags.taglist
-local properties = structurestags.properties
-local userdata = structurestags.userdata -- might be combines with taglist
-local tagdata = structurestags.data
-local tagmetadata = structurestags.metadata
-local detailedtag = structurestags.detailedtag
-
-local starttiming = statistics.starttiming
-local stoptiming = statistics.stoptiming
-
--- todo: more locals (and optimize)
-
-local exportversion = "0.30"
-
-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
-
-local attributehash = { } -- to be considered: set the values at the tex end
-local hyphencode = 0xAD
-local hyphen = utfchar(0xAD) -- todo: also emdash etc
-local colonsplitter = lpeg.splitat(":")
-local dashsplitter = lpeg.splitat("-")
-local threshold = 65536
-local indexing = false
-local keephyphens = false
-
-local finetuning = { }
-
-local treestack = { }
-local nesting = { }
-local currentdepth = 0
-
-local tree = { data = { }, fulltag == "root" } -- root
-local treeroot = tree
-local treehash = { }
-local extras = { }
-local checks = { }
-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 entityremapper = utf.remapper(entities)
-
-local alignmapping = {
- flushright = "right",
- middle = "center",
- flushleft = "left",
-}
-
-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",
-}
-
-local defaultnature = "mixed" -- "inline"
-
-setmetatableindex(used, function(t,k)
- if k then
- local v = { }
- t[k] = v
- return v
- end
-end)
-
-setmetatableindex(specialspaces, function(t,k)
- local v = utfchar(k)
- t[k] = v
- entities[v] = formatters["&#x%X;"](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",
-}
-
-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 formatters[' %s="%s"'](key,gsub(value,".",attribentities))
- else
- return ""
- end
-end
-
--- local P, C, Cc = lpeg.P, lpeg.C, lpeg.Cc
---
--- local dash, colon = P("-"), P(":")
---
--- local precolon, predash, rest = P((1-colon)^1), P((1-dash )^1), P(1)^1
---
--- local tagsplitter = C(precolon) * colon * C(predash) * dash * C(rest) +
--- C(predash) * dash * Cc(nil) * C(rest)
-
-local listdata = { }
-
-local function 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
- listdata[m.kind .. ":" .. m.name .. "-" .. tag] = ci
- end
- end
-end
-
-local spaces = utilities.strings.newrepeater(" ",-1)
-
-function structurestags.setattributehash(fulltag,key,value) -- public hash
- if type(fulltag) == "number" then
- fulltag = taglist[fulltag]
- if fulltag then
- fulltag = fulltag[#fulltag]
- end
- end
- if fulltag then
- local ah = attributehash[fulltag] -- could be metatable magic
- if not ah then
- ah = { }
- attributehash[fulltag] = ah
- end
- ah[key] = value
- end
-end
-
-
--- experiment: styles and images
---
--- officially we should convert to bp but we round anyway
-
-local usedstyles = { }
-
--- /* padding : ; */
--- /* text-justify : inter-word ; */
-
-local documenttemplate = [[
-document {
- font-size : %s !important ;
- max-width : %s !important ;
- text-align : %s !important ;
- hyphens : %s !important ;
-}
-]]
-
-local styletemplate = [[
-%s[detail='%s'] {
- font-style : %s ;
- font-variant : %s ;
- font-weight : %s ;
- font-family : %s ;
- color : %s ;
-}]]
-
-local function allusedstyles(xmlfile)
- local result = { format("/* styles for file %s */",xmlfile) }
- --
- local bodyfont = finetuning.bodyfont
- local width = finetuning.width
- local hyphen = finetuning.hyphen
- local align = finetuning.align
- --
- if not bodyfont or bodyfont == "" then
- bodyfont = "12pt"
- elseif type(bodyfont) == "number" then
- bodyfont = number.todimen(bodyfont,"pt","%ipt") or "12pt"
- end
- if not width or width == "" then
- width = "50em"
- elseif type(width) == "number" then
- width = number.todimen(width,"pt","%ipt") or "50em"
- end
- if hyphen == variables.yes then
- hyphen = "manual"
- else
- hyphen = "inherited"
- end
- if align then
- align = numbertoallign[align]
- end
- if not align then
- align = hyphens and "justify" or "inherited"
- end
- --
- result[#result+1] = format(documenttemplate,bodyfont,width,align,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)
- result[#result+1] = formatters[styletemplate](element,detail,
- s.style or "inherit",
- s.variant or "inherit",
- s.weight or "inherit",
- s.family or "inherit",
- c or "inherit")
- end
- end
- return concat(result,"\n\n")
-end
-
-local usedimages = { }
-
-local imagetemplate = [[
-%s[id="%s"] {
- display : block ;
- background-image : url(%s) ;
- background-size : 100%% auto ;
- background-repeat : no-repeat ;
- width : %s ;
- height : %s ;
-}]]
-
-local function allusedimages(xmlfile)
- local result = { format("/* images for file %s */",xmlfile) }
- for element, details in sortedhash(usedimages) do
- for detail, data in sortedhash(details) do
- local name = data.name
- if file.suffix(name) == "pdf" then
- -- temp hack .. we will have a remapper
- name = file.replacesuffix(name,"svg")
- end
- result[#result+1] = formatters[imagetemplate](element,detail,name,data.width,data.height)
- end
- end
- return concat(result,"\n\n")
-end
-
-local function uniqueusedimages()
- local unique = { }
- for element, details in next, usedimages do
- for detail, data in next, details do
- local name = data.name
- if file.suffix(name) == "pdf" then
- unique[file.replacesuffix(name,"svg")] = name
- else
- unique[name] = name
- end
- end
- end
- return unique
-end
-
---
-
-properties.vspace = { export = "break", nature = "display" }
------------------ = { export = "pagebreak", nature = "display" }
-
-local function makebreaklist(list)
- nofbreaks = nofbreaks + 1
- local t = { }
- if list then
- for i=1,#list do
- t[i] = list[i]
- end
- end
- t[#t+1] = "break-" .. nofbreaks -- maybe no number
- return 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
-
-local fields = { "title", "subtitle", "author", "keywords" }
-
-local function checkdocument(root)
- local data = root.data
- if data then
- for i=1,#data do
- local di = data[i]
- if di.content then
- -- ok
- elseif di.tg == "ignore" then
- di.element = ""
- checkdocument(di)
- else
- -- can't happen
- end
- end
- end
-end
-
-function extras.document(result,element,detail,n,fulltag,di)
- result[#result+1] = format(" language=%q",languagenames[tex.count.mainlanguagenumber])
- if not less_state then
- result[#result+1] = format(" file=%q",tex.jobname)
- result[#result+1] = format(" date=%q",os.date())
- result[#result+1] = format(" context=%q",environment.version)
- result[#result+1] = format(" version=%q",exportversion)
- result[#result+1] = format(" xmlns:m=%q","http://www.w3.org/1998/Math/MathML")
- local identity = interactions.general.getidentity()
- for i=1,#fields do
- local key = fields[i]
- local value = identity[key]
- if value and value ~= "" then
- result[#result+1] = formatters[" %s=%q"](key,value)
- end
- end
- end
- checkdocument(di)
-end
-
-local itemgroups = { }
-
-function structurestags.setitemgroup(current,packed,symbol)
- itemgroups[detailedtag("itemgroup",current)] = {
- packed = packed,
- symbol = symbol,
- }
-end
-
-function extras.itemgroup(result,element,detail,n,fulltag,di)
- local hash = itemgroups[fulltag]
- if hash then
- local v = hash.packed
- if v then
- result[#result+1] = " packed='yes'"
- end
- local v = hash.symbol
- if v then
- result[#result+1] = attribute("symbol",v)
- end
- end
-end
-
-local synonyms = { }
-
-function structurestags.setsynonym(current,tag)
- synonyms[detailedtag("synonym",current)] = tag
-end
-
-function extras.synonym(result,element,detail,n,fulltag,di)
- local tag = synonyms[fulltag]
- if tag then
- result[#result+1] = formatters[" tag='%s'"](tag)
- end
-end
-
-local sortings = { }
-
-function structurestags.setsorting(current,tag)
- sortings[detailedtag("sorting",current)] = tag
-end
-
-function extras.sorting(result,element,detail,n,fulltag,di)
- local tag = sortings[fulltag]
- if tag then
- result[#result+1] = formatters[" tag='%s'"](tag)
- end
-end
-
-usedstyles.highlight = { }
-
-function structurestags.sethighlight(current,style,color) -- we assume global styles
- usedstyles.highlight[current] = {
- style = style, -- xml.css.fontspecification(style),
- color = color, -- xml.css.colorspec(color),
- }
-end
-
-local descriptions = { }
-local symbols = { }
-local linked = { }
-
-function structurestags.setdescription(tag,n)
- local nd = structures.notes.get(tag,n) -- todo: use listdata instead
- if nd then
- local references = nd.references
- descriptions[references and references.internal] = detailedtag("description",tag)
- end
-end
-
-function structurestags.setdescriptionsymbol(tag,n)
- local nd = structures.notes.get(tag,n) -- todo: use listdata instead
- if nd then
- local references = nd.references
- symbols[references and references.internal] = detailedtag("descriptionsymbol",tag)
- end
-end
-
-function finalizers.descriptions(tree)
- local n = 0
- for id, tag in next, descriptions do
- local sym = symbols[id]
- if sym then
- n = n + 1
- linked[tag] = n
- linked[sym] = n
- end
- end
-end
-
-function extras.description(result,element,detail,n,fulltag,di)
- local id = linked[fulltag]
- if id then
- result[#result+1] = formatters[" insert='%s'"](id) -- maybe just fulltag
- end
-end
-
-function extras.descriptionsymbol(result,element,detail,n,fulltag,di)
- local id = linked[fulltag]
- if id then
- result[#result+1] = formatters[" insert='%s'"](id)
- end
-end
-
-usedimages.image = { }
-
-function structurestags.setfigure(name,page,width,height)
- usedimages.image[detailedtag("image")] = {
- name = name,
- page = page,
- width = number.todimen(width,"cm","%0.3fcm"),
- height = number.todimen(height,"cm","%0.3fcm"),
- }
-end
-
-function extras.image(result,element,detail,n,fulltag,di)
- local data = usedimages.image[fulltag]
- if data then
- result[#result+1] = attribute("name",data.name)
- if tonumber(data.page) > 1 then
- result[#result+1] = formatters[" page='%s'"](data.page)
- end
- result[#result+1] = formatters[" id='%s' width='%s' height='%s'"](fulltag,data.width,data.height)
- end
-end
-
-local combinations = { }
-
-function structurestags.setcombination(nx,ny)
- combinations[detailedtag("combination")] = {
- nx = nx,
- ny = ny,
- }
-end
-
-function extras.combination(result,element,detail,n,fulltag,di)
- local data = combinations[fulltag]
- if data then
- result[#result+1] = formatters[" nx='%s' ny='%s'"](data.nx,data.ny)
- end
-end
-
--- quite some code deals with exporting references --
-
-local evaluators = { }
-local specials = { }
-
-evaluators.inner = function(result,var)
- local inner = var.inner
- if inner then
- result[#result+1] = attribute("location",inner)
- end
-end
-
-evaluators.outer = function(result,var)
- local file, url = references.checkedfileorurl(var.outer,var.outer)
- if url then
- result[#result+1] = attribute("url",url)
- elseif file then
- result[#result+1] = attribute("file",file)
- end
-end
-
-evaluators["outer with inner"] = function(result,var)
- local file = references.checkedfile(var.f)
- if file then
- result[#result+1] = attribute("file",file)
- end
- local inner = var.inner
- if inner then
- result[#result+1] = attribute("location",inner)
- end
-end
-
-evaluators.special = function(result,var)
- local handler = specials[var.special]
- if handler then
- handler(result,var)
- end
-end
-
-evaluators["special outer with operation"] = evaluators.special
-evaluators["special operation"] = evaluators.special
-evaluators["special operation with arguments"] = evaluators.special
-
-function specials.url(result,var)
- local url = references.checkedurl(var.operation)
- if url then
- result[#result+1] = attribute("url",url)
- end
-end
-
-function specials.file(result,var)
- local file = references.checkedfile(var.operation)
- if file then
- result[#result+1] = attribute("file",file)
- end
-end
-
-function specials.fileorurl(result,var)
- local file, url = references.checkedfileorurl(var.operation,var.operation)
- if url then
- result[#result+1] = attribute("url",url)
- elseif file then
- result[#result+1] = attribute("file",file)
- end
-end
-
-function specials.internal(result,var)
- local internal = references.checkedurl(var.operation)
- if internal then
- result[#result+1] = formatters[" location='aut:%s'"](internal)
- end
-end
-
-local referencehash = { }
-
-local function adddestination(result,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
- result[#result+1] = formatters[" prefix='%s'"](prefix)
- end
- result[#result+1] = formatters[" destination='%s'"](reference)
- for i=1,#references do
- local r = references[i]
- local e = evaluators[r.kind]
- if e then
- e(result,r)
- end
- end
- end
- end
-end
-
-local function addreference(result,references)
- if references then
- local reference = references.reference
- if reference and reference ~= "" then
- local prefix = references.prefix
- if prefix and prefix ~= "" then
- result[#result+1] = formatters[" prefix='%s'"](prefix)
- end
- result[#result+1] = formatters[" reference='%s'"](reference)
- end
- local internal = references.internal
- if internal and internal ~= "" then
- result[#result+1] = formatters[" location='aut:%s'"](internal)
- end
- end
-end
-
-function extras.link(result,element,detail,n,fulltag,di)
- -- for instance in lists a link has nested elements and no own text
- local reference = referencehash[fulltag]
- if reference then
- adddestination(result,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 extras.link(result,element,detail,n,fulltag,di) then
- return true
- end
- end
- end
- end
- end
-end
-
--- no settings, as these are obscure ones
-
-local automathrows = true directives.register("backend.export.math.autorows", function(v) automathrows = v end)
-local automathapply = true directives.register("backend.export.math.autoapply", function(v) automathapply = v end)
-local automathnumber = true directives.register("backend.export.math.autonumber", function(v) automathnumber = v end)
-local automathstrip = true directives.register("backend.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)
- 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 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
- local nucleus, superscript, subscript
- for i=1,ndata do
- local di = data[i]
- if not di then
- -- weird
- elseif di.content then
- -- text
- elseif not nucleus then
- nucleus = i
- elseif not superscript then
- superscript = i
- elseif not subscript then
- subscript = i
- else
- -- error
- end
- end
- if superscript and subscript then
- local sup, sub = data[superscript], data[subscript]
- data[superscript], data[subscript] = sub, sup
- -- sub.__o__, sup.__o__ = subscript, superscript
- sub.__i__, sup.__i__ = superscript, subscript
- end
- elseif roottg == "mfenced" then
- local new, n = { }, 0
- local attributes = { }
- root.attributes = attributes
- for i=1,ndata do
- local di = data[i]
- if not di then
- -- weird
- elseif di.content then
- n = n + 1
- new[n] = di
- else
- local tg = di.tg
- if tg == "mleft" then
- attributes.left = tostring(di.data[1].data[1].content)
- elseif tg == "mmiddle" then
- attributes.middle = tostring(di.data[1].data[1].content)
- elseif tg == "mright" then
- attributes.right = tostring(di.data[1].data[1].content)
- else
- n = n + 1
- di.__i__ = n
- new[n] = di
- end
- end
- end
- root.data = new
- ndata = n
- end
- if ndata == 0 then
- return
- elseif ndata == 1 then
- local d = data[1]
- if not d then
- return
- elseif d.content then
- return
- elseif #root.data == 1 then
- local tg = d.tg
- if automathrows and roottg == "mrow" then
- -- maybe just always ! check spec first
- if tg == "mrow" or tg == "mfenced" or tg == "mfrac" or tg == "mroot" or tg == "msqrt"then
- root.skip = "comment"
- elseif tg == "mo" 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
- local detail = di.detail
- if tg == "math" then
- -- di.element = "mrow" -- when properties
- di.skip = "comment"
- checkmath(di)
- i = i + 1
- elseif tg == "mover" or tg == "munder" or tg == "munderover" then
- if detail == "accent" then
- di.attributes = { accent = "true" }
- di.detail = nil
- end
- checkmath(di)
- i = i + 1
- elseif tg == "mroot" then
- if #di.data == 1 then
- -- else firefox complains
- di.element = "msqrt"
- end
- checkmath(di)
- i = i + 1
- elseif tg == "break" then
- di.skip = "comment"
- i = i + 1
- elseif tg == "mrow" and detail then
- di.detail = nil
- checkmath(di)
- di = {
- element = "maction",
- nature = "display",
- attributes = { actiontype = detail },
- data = { di },
- n = 0,
- }
- data[i] = di
- i = i + 1
- elseif detail then
- -- no checkmath(di) here
- local category = tonumber(detail) or 0
- 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
- else -- can be string or boolean
- if parenttg ~= "mtext" and di == " " then
- data[i] = false
- end
- i = i + 1
- end
- end
- end
-end
-
-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 di = data[i]
- if di and not di.content then
- di = stripmath(di)
- end
- if di then
- local content = di.content
- if not content then
- n = n + 1
- di.__i__ = n
- data[n] = di
- elseif content == " " or content == "" then
- -- skip
- else
- n = n + 1
- data[n] = di
- 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)
- local hash = attributehash[di.fulltag]
- local mode = (hash and hash.mode) == "display" and "block" or "inline"
- di.attributes = {
- display = mode
- }
- -- can be option if needed:
- if mode == "inline" then
- di.nature = "mixed" -- else spacing problem (maybe inline)
- else
- di.nature = "display"
- end
- if automathstrip then
- stripmath(di)
- end
- checkmath(di)
-end
-
-local a, z, A, Z = 0x61, 0x7A, 0x41, 0x5A
-
-function extras.mi(result,element,detail,n,fulltag,di) -- 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
-
-function extras.section(result,element,detail,n,fulltag,di)
- local data = listdata[fulltag]
- if data then
- addreference(result,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 extras.section(result,element,detail,n,ft,di) then
- return true
- end
- end
- end
- end
- end
-end
-
-function extras.float(result,element,detail,n,fulltag,di)
- local data = listdata[fulltag]
- if data then
- addreference(result,data.references)
- return true
- else
- local data = di.data
- if data then
- for i=1,#data do
- local di = data[i]
- if di and extras.section(result,element,detail,n,di.fulltag,di) then
- return true
- end
- end
- end
- end
-end
-
-local tabledata = { }
-
-function structurestags.settablecell(rows,columns,align)
- if align > 0 or rows > 1 or columns > 1 then
- tabledata[detailedtag("tablecell")] = {
- rows = rows,
- columns = columns,
- align = align,
- }
- end
-end
-
-function extras.tablecell(result,element,detail,n,fulltag,di)
- local hash = tabledata[fulltag]
- if hash then
- local v = hash.columns
- if v and v > 1 then
- result[#result+1] = formatters[" columns='%s'"](v)
- end
- local v = hash.rows
- if v and v > 1 then
- result[#result+1] = formatters[" rows='%s'"](v)
- end
- local v = hash.align
- if not v or v == 0 then
- -- normal
- elseif v == 1 then -- use numbertoalign here
- result[#result+1] = " align='flushright'"
- elseif v == 2 then
- result[#result+1] = " align='middle'"
- elseif v == 3 then
- result[#result+1] = " align='flushleft'"
- end
- end
-end
-
-local tabulatedata = { }
-
-function structurestags.settabulatecell(align)
- if align > 0 then
- tabulatedata[detailedtag("tabulatecell")] = {
- align = align,
- }
- end
-end
-
-function extras.tabulate(result,element,detail,n,fulltag,di)
- local data = di.data
- for i=1,#data do
- local di = data[i]
- if di.tg == "tabulaterow" then
- local did = di.data
- local content = false
- for i=1,#did do
- local d = did[i].data
- if d and #d > 0 and d[1].content then
- content = true
- break
- end
- end
- if not content then
- di.element = "" -- or simply remove
- end
- end
- end
-end
-
-function extras.tabulatecell(result,element,detail,n,fulltag,di)
- local hash = tabulatedata[fulltag]
- if hash then
- local v = hash.align
- if not v or v == 0 then
- -- normal
- elseif v == 1 then
- result[#result+1] = " align='flushleft'"
- elseif v == 2 then
- result[#result+1] = " align='flushright'"
- elseif v == 3 then
- result[#result+1] = " align='middle'"
- end
- end
-end
-
--- flusher
-
-local linedone = false -- can go ... we strip newlines anyway
-local inlinedepth = 0
-
--- todo: #result -> nofresult
-
-local function emptytag(result,element,nature,depth,di) -- currently only break but at some point
- local a = di.attributes -- we might add detail etc
- if a then -- happens seldom
- if linedone then
- result[#result+1] = formatters["%w<%s"](depth,namespaced[element])
- else
- result[#result+1] = formatters["\n%w<%s"](depth,namespaced[element])
- end
- for k, v in next, a do
- result[#result+1] = formatters[" %s=%q"](k,v)
- end
- result[#result+1] = "/>\n"
- else
- if linedone then
- result[#result+1] = formatters["%w<%s/>\n"](depth,namespaced[element])
- else
- result[#result+1] = formatters["\n%w<%s/>\n"](depth,namespaced[element])
- end
- end
- linedone = false
-end
-
-local function begintag(result,element,nature,depth,di,skip)
- -- if needed we can use a local result with xresult
- local detail = di.detail
- local n = di.n
- local fulltag = di.fulltag
- local comment = di.comment
- if nature == "inline" then
- linedone = false
- inlinedepth = inlinedepth + 1
- if show_comment and comment then
- result[#result+1] = formatters["<!-- %s -->"](comment)
- end
- elseif nature == "mixed" then
- if inlinedepth > 0 then
- if show_comment and comment then
- result[#result+1] = formatters["<!-- %s -->"](comment)
- end
- elseif linedone then
- result[#result+1] = spaces[depth]
- if show_comment and comment then
- result[#result+1] = formatters["<!-- %s -->"](comment)
- end
- else
- result[#result+1] = formatters["\n%w"](depth)
- linedone = false
- if show_comment and comment then
- result[#result+1] = formatters["<!-- %s -->\n%w"](comment,depth)
- end
- end
- inlinedepth = inlinedepth + 1
- else
- if inlinedepth > 0 then
- if show_comment and comment then
- result[#result+1] = formatters["<!-- %s -->"](comment)
- end
- elseif linedone then
- result[#result+1] = spaces[depth]
- if show_comment and comment then
- result[#result+1] = formatters["<!-- %s -->"](comment)
- end
- else
- result[#result+1] = formatters["\n%w"](depth) -- can introduced extra line in mixed+mixed (filtered later on)
- linedone = false
- if show_comment and comment then
- result[#result+1] = formatters["<!-- %s -->\n%w"](comment,depth)
- end
- end
- end
- if skip == "comment" then
- if show_comment then
- result[#result+1] = formatters["<!-- begin %s -->"](namespaced[element])
- end
- elseif skip then
- -- ignore
- else
- result[#result+1] = formatters["<%s"](namespaced[element])
- if detail then
- result[#result+1] = formatters[" detail=%q"](detail)
- end
- if indexing and n then
- result[#result+1] = formatters[" n=%q"](n)
- end
- local extra = extras[element]
- if extra then
- extra(result,element,detail,n,fulltag,di)
- end
- local u = userdata[fulltag]
- if u then
- for k, v in next, u do
- result[#result+1] = formatters[" %s=%q"](k,v)
- end
- end
- local a = di.attributes
- if a then
- for k, v in next, a do
- result[#result+1] = formatters[" %s=%q"](k,v)
- end
- end
- result[#result+1] = ">"
- end
- if inlinedepth > 0 then
- elseif nature == "display" then
- result[#result+1] = "\n"
- linedone = true
- end
- used[element][detail or ""] = nature -- for template css
- local metadata = tagmetadata[fulltag]
- if metadata then
- if not linedone then
- result[#result+1] = "\n"
- linedone = true
- end
- result[#result+1] = formatters["%w<metadata>\n"](depth)
- for k, v in table.sortedpairs(metadata) do
- v = entityremapper(v)
- result[#result+1] = formatters["%w<metavariable name=%q>%s</metavariable>\n"](depth+1,k,v)
- end
- result[#result+1] = formatters["%w</metadata>\n"](depth)
- end
-end
-
-local function endtag(result,element,nature,depth,skip)
- if nature == "display" then
- if inlinedepth == 0 then
- if not linedone then
- result[#result+1] = "\n"
- end
- if skip == "comment" then
- if show_comment then
- result[#result+1] = formatters["%w<!-- end %s -->\n"](depth,namespaced[element])
- end
- elseif skip then
- -- ignore
- else
- result[#result+1] = formatters["%w</%s>\n"](depth,namespaced[element])
- end
- linedone = true
- else
- if skip == "comment" then
- if show_comment then
- result[#result+1] = formatters["<!-- end %s -->"](namespaced[element])
- end
- elseif skip then
- -- ignore
- else
- result[#result+1] = formatters["</%s>"](namespaced[element])
- end
- end
- else
- inlinedepth = inlinedepth - 1
- if skip == "comment" then
- if show_comment then
- result[#result+1] = formatters["<!-- end %s -->"](namespaced[element])
- end
- elseif skip then
- -- ignore
- else
- result[#result+1] = formatters["</%s>"](namespaced[element])
- end
- linedone = false
- end
-end
-
-local function flushtree(result,data,nature,depth)
- depth = depth + 1
- local nofdata = #data
- for i=1,nofdata do
- local di = data[i]
- if not di then -- hm, di can be string
- -- whatever
- elseif di.content then
- -- already has breaks
- local content = entityremapper(di.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] = formatters["<c n='%s'>%s</c>"](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] = formatters["<c n='%s'>%s</c>"](di.parnumber or 0,content)
- else
- result[#result+1] = content
- end
- end
- linedone = false
- 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,depth,di)
- elseif element == "" or di.skip == "ignore" then
- -- skip
- else
- if di.before then
- flushtree(result,di.before,nature,depth)
- end
- local natu = di.nature
- local skip = di.skip
- if di.breaknode then
- emptytag(result,"break","display",depth,di)
- end
- begintag(result,element,natu,depth,di,skip)
- flushtree(result,di.data,natu,depth)
- -- if sub(result[#result],-1) == " " and natu ~= "inline" then
- -- result[#result] = sub(result[#result],1,-2)
- -- end
- endtag(result,element,natu,depth,skip)
- if di.after then
- flushtree(result,di.after,nature,depth)
- 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.content then
- 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
- prevnature = "inline"
- prevparnumber = parnumber
- 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"
- elseif element == "" or di.skip == "ignore" then
- -- skip
- 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
- prevelement = element
- breaktree(di,tree,element)
- end
- nofnewdata = nofnewdata + 1
- newdata[nofnewdata] = di
- 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
- 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()
- for tag, trees in next, treehash do
- local d = trees[1].data
- 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.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 indextree(tree)
- local data = tree.data
- if data then
- local n, new = 0, { }
- 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
- end
-end
-
-local function checktree(tree)
- local data = tree.data
- if data then
- for i=1,#data do
- local d = data[i]
- if type(d) == "table" then
- local check = checks[d.tg]
- if check then
- check(d)
- end
- checktree(d)
- end
- end
- end
-end
-
--- collector code
-
-local function push(fulltag,depth)
- local tag, n = lpegmatch(dashsplitter,fulltag)
- local tg, detail = lpegmatch(colonsplitter,tag)
- local element, nature
- if detail then
- local pd = properties[tag]
- local pt = properties[tg]
- element = pd and pd.export or pt and pt.export or tg
- nature = pd and pd.nature or pt and pt.nature or defaultnature
- else
- local p = properties[tg]
- element = p and p.export or tg
- nature = p and p.nature or "inline"
- end
- local treedata = tree.data
- local t = {
- tg = tg,
- fulltag = fulltag,
- detail = detail,
- n = tonumber(n), -- more efficient
- element = element,
- nature = nature,
- data = { },
- attribute = currentattribute,
- parnumber = currentparagraph,
- }
- 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=%a paragraph=%a index=%a detail=%a>",currentdepth-1,fulltag,currentattribute or 0,currentparagraph or 0,#treedata,detail)
- else
- report_export("%w<%s trigger=%a paragraph=%a index=%a>",currentdepth-1,fulltag,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()
- local top = nesting[currentdepth]
- tree = treestack[currentdepth]
- currentdepth = currentdepth - 1
- if trace_export then
- if top then
- report_export("%w</%s>",currentdepth,top)
- else
- report_export("</%s>",top)
- end
- 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 current then
- 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
-end
-
-local function pushcontent(currentparagraph,newparagraph)
- if nofcurrentcontent > 0 then
- if currentparagraph 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 currentparagraph we could push, remove spaces, pop
- elseif somespace[content] and currentparagraph then
- -- omit; when currentparagraph we could push, remove spaces, pop
- else
- local olddepth, newdepth
- local list = taglist[currentattribute]
- if list then
- olddepth, newdepth = pushentry(list)
- end
- local td = tree.data
- local nd = #td
- td[nd+1] = { parnumber = currentparagraph, content = content }
- if trace_export then
- report_export("%w<!-- start content with length %s -->",currentdepth,#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
- nofcurrentcontent = 0
- end
- if currentparagraph then
- pushentry(makebreaklist(currentnesting))
- if trace_export then
- report_export("%w<!-- break added betweep paragraph %a and %a -->",currentdepth,currentparagraph,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
-
--- whatsit_code localpar_code
-
-local function collectresults(head,list) -- is last used (we also have currentattribute)
- local p
- for n in traverse_nodes(head) do
- local id = n.id -- 14: image, 8: literal (mp)
- if id == glyph_code then
- local at = n[a_tagged]
- 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
- -- we could add tonunicodes for ligatures (todo)
- local components = n.components
- if components then -- we loose data
- collectresults(components,nil)
- else
- local c = n.char
- if last ~= at then
- local tl = taglist[at]
- pushcontent()
- currentnesting = tl
- currentparagraph = n[a_taggedpar]
- 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 = n[a_reference]
- if r then
- referencehash[tl[#tl]] = r -- fulltag
- end
- --
- elseif last then
- local ap = n[a_taggedpar]
- if ap ~= currentparagraph then
- pushcontent(currentparagraph,ap)
- pushentry(currentnesting)
- currentattribute = last
- currentparagraph = ap
- 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 = 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 = 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[n.font]
- if fc then
- fc = fc and fc[c]
- if fc then
- local u = fc.tounicode
- if u and u ~= "" then
- nofcurrentcontent = nofcurrentcontent + 1
- currentcontent[nofcurrentcontent] = utfchar(fromunicode16(u))
- else
- nofcurrentcontent = nofcurrentcontent + 1
- currentcontent[nofcurrentcontent] = utfchar(c)
- end
- else -- weird, happens in hz (we really need to get rid of the pseudo fonts)
- nofcurrentcontent = nofcurrentcontent + 1
- currentcontent[nofcurrentcontent] = utfchar(c)
- end
- else
- nofcurrentcontent = nofcurrentcontent + 1
- currentcontent[nofcurrentcontent] = utfchar(c)
- end
- end
- end
- end
- elseif id == disc_code then -- probably too late
- if keephyphens then
- local pre = n.pre
- if pre and not pre.next and pre.id == glyph_code and pre.char == hyphencode then
- nofcurrentcontent = nofcurrentcontent + 1
- currentcontent[nofcurrentcontent] = hyphen
- end
- end
- collectresults(n.replace,nil)
- elseif id == glue_code then
- -- we need to distinguish between hskips and vskips
- local ca = n[a_characters]
- if ca == 0 then
- -- skip this one ... already converted special character (node-acc)
- elseif ca then
- local a = n[a_tagged]
- 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 = n[a_taggedpar]
- currentattribute = a
- last = a
- pushentry(currentnesting)
- -- no reference check (see above)
- elseif last then
- local ap = n[a_taggedpar]
- 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
- else
- local subtype = n.subtype
- if subtype == userskip_code then
- if n.spec.width > threshold then
- if last and not somespace[currentcontent[nofcurrentcontent]] then
- local a = n[a_tagged]
- 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 = n[a_tagged]
- 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 not somespace[r] then
- local a = n[a_tagged]
- 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 paragaph endings (crossings) elsewhere and we quit here
- -- as we don't want the rightskip space addition
- return
- end
- end
- elseif id == hlist_code or id == vlist_code then
- local ai = n[a_image]
- if ai then
- local at = n[a_tagged]
- 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
- collectresults(n.list,n)
- end
- elseif id == kern_code then
- local kern = n.kern
- if kern > 0 then
- local limit = threshold
- if p and p.id == glyph_code then
- limit = fontquads[p.font] / 4
- end
- if kern > limit then
- if last and not somespace[currentcontent[nofcurrentcontent]] then
- local a = n[a_tagged]
- 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]
- pushentry(currentnesting)
- currentattribute = last
- end
- end
- end
- end
- end
- p = n
- 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, true
-end
-
-function builders.paragraphs.tag(head)
- noftextblocks = noftextblocks + 1
- for n in traverse_id(hlist_code,head) do
- local subtype = n.subtype
- if subtype == line_code then
- n[a_textblock] = noftextblocks
- elseif subtype == glue_code or subtype == kern_code then
- n[a_textblock] = 0
- end
- end
- return false
-end
-
--- encoding='utf-8'
-
-local xmlpreamble = [[
-<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
-
-<!-- input filename : %- 17s -->
-<!-- processing date : %- 17s -->
-<!-- context version : %- 17s -->
-<!-- exporter version : %- 17s -->
-
-]]
-
-local function wholepreamble()
- return format(xmlpreamble,tex.jobname,os.date(),environment.version,exportversion)
-end
-
-
-local csspreamble = [[
-<?xml-stylesheet type="text/css" href="%s"?>
-]]
-
-local function allusedstylesheets(xmlfile,cssfiles,files)
- local result = { }
- for i=1,#cssfiles do
- local cssfile = cssfiles[i]
- if type(cssfile) ~= "string" or cssfile == variables.yes or cssfile == "" or cssfile == xmlfile then
- cssfile = file.replacesuffix(xmlfile,"css")
- else
- cssfile = file.addsuffix(cssfile,"css")
- end
- files[#files+1] = cssfile
- report_export("adding css reference '%s'",cssfile)
- result[#result+1] = format(csspreamble,cssfile)
- end
- return concat(result)
-end
-
-local e_template = [[
-%s {
- display: %s ;
-}]]
-
-local d_template = [[
-%s[detail=%s] {
- display: %s ;
-}]]
-
-local displaymapping = {
- inline = "inline",
- display = "block",
- mixed = "inline",
-}
-
-local function allusedelements(xmlfile)
- local result = { format("/* template for file %s */",xmlfile) }
- for element, details in sortedhash(used) do
- result[#result+1] = format("/* category: %s */",element)
- for detail, nature in sortedhash(details) do
- local d = displaymapping[nature or "display"] or "block"
- if detail == "" then
- result[#result+1] = formatters[e_template](element,d)
- else
- result[#result+1] = formatters[d_template](element,detail,d)
- end
- end
- end
- return concat(result,"\n\n")
-end
-
-local function allcontent(tree)
- local result = { }
- flushtree(result,tree.data,"display",0) -- we need to collect images
- result = concat(result)
- 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 xmlwrap = xml.wrap
- for e in xml.collected(xmltree,"/document") do
- e.at["xmlns:xhtml"] = "http://www.w3.org/1999/xhtml"
- break
- end
- -- todo: inject xhtmlpreamble (xmlns should have be enough)
- local wrapper = { tg = "a", ns = "xhtml", at = { href = "unknown" } }
- for e in xml.collected(xmltree,"link") do
- local at = e.at
- local href
- if at.location then
- href = "#" .. gsub(at.location,":","_")
- elseif at.url then
- href = at.url
- elseif at.file then
- href = at.file
- end
- if href then
- wrapper.at.href = href
- xmlwrap(e,wrapper)
- end
- end
- local wrapper = { tg = "a", ns = "xhtml", at = { name = "unknown" } }
- for e in xml.collected(xmltree,"!link[@location]") do
- local location = e.at.location
- if location then
- wrapper.at.name = gsub(location,":","_")
- xmlwrap(e,wrapper)
- end
- end
- return xmltree
- else
- return xml.convert("<?xml version='1.0'?>\n<error>invalid xhtml tree</error>")
- end
-end
-
-local cssfile, xhtmlfile = nil, nil
-
-directives.register("backend.export.css", function(v) cssfile = v end)
-directives.register("backend.export.xhtml",function(v) xhtmlfile = v end)
-
-local function stopexport(v)
- starttiming(treehash)
- --
- finishexport()
- --
- collapsetree(tree)
- indextree(tree)
- checktree(tree)
- breaktree(tree)
- finalizetree(tree)
- --
- hashlistdata()
- --
- if type(v) ~= "string" or v == variables.yes or v == "" then
- v = tex.jobname
- end
- local basename = file.basename(v)
- local xmlfile = file.addsuffix(basename,"export")
- --
- local imagefilename = file.addsuffix(file.removesuffix(xmlfile) .. "-images","css")
- local stylefilename = file.addsuffix(file.removesuffix(xmlfile) .. "-styles","css")
- local templatefilename = file.replacesuffix(xmlfile,"template")
- local specificationfilename = file.replacesuffix(xmlfile,"specification")
- --
- if xhtml and not cssfile then
- cssfile = true
- end
- local cssfiles = { }
- if cssfile then
- if cssfile == true then
- cssfiles = { "export-example.css" }
- else
- cssfiles = settings_to_array(cssfile or "")
- end
- insert(cssfiles,1,imagefilename)
- insert(cssfiles,1,stylefilename)
- end
- cssfiles = table.unique(cssfiles)
- --
- local result = allcontent(tree) -- also does some housekeeping and data collecting
- --
- local files = {
- }
- local results = concat {
- wholepreamble(),
- allusedstylesheets(xmlfile,cssfiles,files), -- ads to files
- result,
- }
- --
- files = table.unique(files)
- --
- report_export("saving xml data in %a",xmlfile)
- io.savedata(xmlfile,results)
- --
- report_export("saving css image definitions in %a",imagefilename)
- io.savedata(imagefilename,allusedimages(xmlfile))
- --
- report_export("saving css style definitions in %a",stylefilename)
- io.savedata(stylefilename,allusedstyles(xmlfile))
- --
- report_export("saving css template in %a",templatefilename)
- io.savedata(templatefilename,allusedelements(xmlfile))
- --
- if xhtmlfile then
- if type(v) ~= "string" or xhtmlfile == true or xhtmlfile == variables.yes or xhtmlfile == "" or xhtmlfile == xmlfile then
- xhtmlfile = file.replacesuffix(xmlfile,"xhtml")
- else
- xhtmlfile = file.addsuffix(xhtmlfile,"xhtml")
- end
- files[#files+1] = xhtmlfile
- report_export("saving xhtml variant in %a",xhtmlfile)
- local xmltree = cleanxhtmltree(xml.convert(results))
- xml.save(xmltree,xhtmlfile)
- -- looking at identity is somewhat redundant as we also inherit from interaction
- -- at the tex end
- local identity = interactions.general.getidentity()
- local specification = {
- name = file.removesuffix(v),
- identifier = os.uuid(),
- images = uniqueusedimages(),
- root = xhtmlfile,
- files = files,
- language = languagenames[tex.count.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),
- }
- report_export("saving specification in %a (mtxrun --script epub --make %s)",specificationfilename,specificationfilename)
- io.savedata(specificationfilename,table.serialize(specification,true))
- end
- stoptiming(treehash)
-end
-
-local appendaction = nodes.tasks.appendaction
-local enableaction = nodes.tasks.enableaction
-
-function commands.setupexport(t)
- table.merge(finetuning,t)
- keephyphens = finetuning.hyphen == variables.yes
-end
-
-local function startexport(v)
- if v and not exporting then
- report_export("enabling export to xml")
--- not yet known in task-ini
- appendaction("shipouts","normalizers", "nodes.handlers.export")
--- enableaction("shipouts","nodes.handlers.export")
- enableaction("shipouts","nodes.handlers.accessibility")
- enableaction("math", "noads.handlers.tags")
---~ appendaction("finalizers","lists","builders.paragraphs.tag")
---~ enableaction("finalizers","builders.paragraphs.tag")
- luatex.registerstopactions(function() stopexport(v) end)
- exporting = true
- end
-end
-
-directives.register("backend.export",startexport) -- maybe .name
-
-statistics.register("xml exporting time", function()
- if exporting then
- return format("%s seconds, version %s", statistics.elapsedtime(treehash),exportversion)
- end
-end)
-
--- These are called at the tex end:
-
-commands.settagitemgroup = structurestags.setitemgroup
-commands.settagsynonym = structurestags.setsynonym
-commands.settagsorting = structurestags.setsorting
-commands.settagdescription = structurestags.setdescription
-commands.settagdescriptionsymbol = structurestags.setdescriptionsymbol
-commands.settaghighlight = structurestags.sethighlight
-commands.settagfigure = structurestags.setfigure
-commands.settagcombination = structurestags.setcombination
-commands.settagtablecell = structurestags.settablecell
-commands.settagtabulatecell = structurestags.settabulatecell
+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"
+}
+
+-- 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
+-- delimited -> left/right string (needs marking)
+
+-- 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)
+
+local next, type = next, type
+local format, match, concat, rep, sub, gsub, gmatch, find = string.format, string.match, table.concat, string.rep, string.sub, string.gsub, string.gmatch, string.find
+local validstring = string.valid
+local lpegmatch = lpeg.match
+local utfchar, utfbyte, utfvalues = utf.char, utf.byte, utf.values
+local insert, remove = table.insert, table.remove
+local fromunicode16 = fonts.mappings.fromunicode16
+local sortedhash = table.sortedhash
+local formatters = string.formatters
+
+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 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 settings_to_array = utilities.parsers.settings_to_array
+
+local setmetatableindex = table.setmetatableindex
+local tasks = nodes.tasks
+local fontchar = fonts.hashes.characters
+local fontquads = fonts.hashes.quads
+local languagenames = languages.numbers
+
+local nodecodes = nodes.nodecodes
+local skipcodes = nodes.skipcodes
+local whatsitcodes = nodes.whatsitcodes
+local listcodes = nodes.listcodes
+
+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 insert_code = nodecodes.insert
+local whatsit_code = nodecodes.whatsit
+local refximage_code = whatsitcodes.pdfrefximage
+local localpar_code = whatsitcodes.localpar
+
+local userskip_code = skipcodes.userskip
+local rightskip_code = skipcodes.rightskip
+local parfillskip_code = skipcodes.parfillskip
+local spaceskip_code = skipcodes.spaceskip
+local xspaceskip_code = skipcodes.xspaceskip
+
+local line_code = listcodes.line
+
+local a_characters = attributes.private('characters')
+local a_exportstatus = attributes.private('exportstatus')
+
+local a_tagged = attributes.private('tagged')
+local a_taggedpar = attributes.private("taggedpar")
+local a_image = attributes.private('image')
+local a_reference = attributes.private('reference')
+
+local a_textblock = attributes.private("textblock")
+
+local traverse_id = node.traverse_id
+local traverse_nodes = node.traverse
+local slide_nodelist = node.slide
+local texattribute = tex.attribute
+local texdimen = tex.dimen
+local texcount = tex.count
+local locate_node = nodes.locate
+
+local references = structures.references
+local structurestags = structures.tags
+local taglist = structurestags.taglist
+local properties = structurestags.properties
+local userdata = structurestags.userdata -- might be combines with taglist
+local tagdata = structurestags.data
+local tagmetadata = structurestags.metadata
+local detailedtag = structurestags.detailedtag
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+
+-- todo: more locals (and optimize)
+
+local exportversion = "0.30"
+
+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
+
+local attributehash = { } -- to be considered: set the values at the tex end
+local hyphencode = 0xAD
+local hyphen = utfchar(0xAD) -- todo: also emdash etc
+local colonsplitter = lpeg.splitat(":")
+local dashsplitter = lpeg.splitat("-")
+local threshold = 65536
+local indexing = false
+local keephyphens = false
+
+local finetuning = { }
+
+local treestack = { }
+local nesting = { }
+local currentdepth = 0
+
+local tree = { data = { }, fulltag == "root" } -- root
+local treeroot = tree
+local treehash = { }
+local extras = { }
+local checks = { }
+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 entityremapper = utf.remapper(entities)
+
+local alignmapping = {
+ flushright = "right",
+ middle = "center",
+ flushleft = "left",
+}
+
+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",
+}
+
+local defaultnature = "mixed" -- "inline"
+
+setmetatableindex(used, function(t,k)
+ if k then
+ local v = { }
+ t[k] = v
+ return v
+ end
+end)
+
+setmetatableindex(specialspaces, function(t,k)
+ local v = utfchar(k)
+ t[k] = v
+ entities[v] = formatters["&#x%X;"](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",
+}
+
+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 formatters[' %s="%s"'](key,gsub(value,".",attribentities))
+ else
+ return ""
+ end
+end
+
+-- local P, C, Cc = lpeg.P, lpeg.C, lpeg.Cc
+--
+-- local dash, colon = P("-"), P(":")
+--
+-- local precolon, predash, rest = P((1-colon)^1), P((1-dash )^1), P(1)^1
+--
+-- local tagsplitter = C(precolon) * colon * C(predash) * dash * C(rest) +
+-- C(predash) * dash * Cc(nil) * C(rest)
+
+local listdata = { }
+
+local function 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
+ listdata[m.kind .. ":" .. m.name .. "-" .. tag] = ci
+ end
+ end
+end
+
+local spaces = utilities.strings.newrepeater(" ",-1)
+
+function structurestags.setattributehash(fulltag,key,value) -- public hash
+ if type(fulltag) == "number" then
+ fulltag = taglist[fulltag]
+ if fulltag then
+ fulltag = fulltag[#fulltag]
+ end
+ end
+ if fulltag then
+ local ah = attributehash[fulltag] -- could be metatable magic
+ if not ah then
+ ah = { }
+ attributehash[fulltag] = ah
+ end
+ ah[key] = value
+ end
+end
+
+
+-- experiment: styles and images
+--
+-- officially we should convert to bp but we round anyway
+
+local usedstyles = { }
+
+-- /* padding : ; */
+-- /* text-justify : inter-word ; */
+
+local documenttemplate = [[
+document {
+ font-size : %s !important ;
+ max-width : %s !important ;
+ text-align : %s !important ;
+ hyphens : %s !important ;
+}
+]]
+
+local styletemplate = [[
+%s[detail='%s'] {
+ font-style : %s ;
+ font-variant : %s ;
+ font-weight : %s ;
+ font-family : %s ;
+ color : %s ;
+}]]
+
+local function allusedstyles(xmlfile)
+ local result = { format("/* styles for file %s */",xmlfile) }
+ --
+ local bodyfont = finetuning.bodyfont
+ local width = finetuning.width
+ local hyphen = finetuning.hyphen
+ local align = finetuning.align
+ --
+ if not bodyfont or bodyfont == "" then
+ bodyfont = "12pt"
+ elseif type(bodyfont) == "number" then
+ bodyfont = number.todimen(bodyfont,"pt","%ipt") or "12pt"
+ end
+ if not width or width == "" then
+ width = "50em"
+ elseif type(width) == "number" then
+ width = number.todimen(width,"pt","%ipt") or "50em"
+ end
+ if hyphen == variables.yes then
+ hyphen = "manual"
+ else
+ hyphen = "inherited"
+ end
+ if align then
+ align = numbertoallign[align]
+ end
+ if not align then
+ align = hyphens and "justify" or "inherited"
+ end
+ --
+ result[#result+1] = format(documenttemplate,bodyfont,width,align,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)
+ result[#result+1] = formatters[styletemplate](element,detail,
+ s.style or "inherit",
+ s.variant or "inherit",
+ s.weight or "inherit",
+ s.family or "inherit",
+ c or "inherit")
+ end
+ end
+ return concat(result,"\n\n")
+end
+
+local usedimages = { }
+
+local imagetemplate = [[
+%s[id="%s"] {
+ display : block ;
+ background-image : url(%s) ;
+ background-size : 100%% auto ;
+ background-repeat : no-repeat ;
+ width : %s ;
+ height : %s ;
+}]]
+
+local function allusedimages(xmlfile)
+ local result = { format("/* images for file %s */",xmlfile) }
+ for element, details in sortedhash(usedimages) do
+ for detail, data in sortedhash(details) do
+ local name = data.name
+ if file.suffix(name) == "pdf" then
+ -- temp hack .. we will have a remapper
+ name = file.replacesuffix(name,"svg")
+ end
+ result[#result+1] = formatters[imagetemplate](element,detail,name,data.width,data.height)
+ end
+ end
+ return concat(result,"\n\n")
+end
+
+local function uniqueusedimages()
+ local unique = { }
+ for element, details in next, usedimages do
+ for detail, data in next, details do
+ local name = data.name
+ if file.suffix(name) == "pdf" then
+ unique[file.replacesuffix(name,"svg")] = name
+ else
+ unique[name] = name
+ end
+ end
+ end
+ return unique
+end
+
+--
+
+properties.vspace = { export = "break", nature = "display" }
+----------------- = { export = "pagebreak", nature = "display" }
+
+local function makebreaklist(list)
+ nofbreaks = nofbreaks + 1
+ local t = { }
+ if list then
+ for i=1,#list do
+ t[i] = list[i]
+ end
+ end
+ t[#t+1] = "break-" .. nofbreaks -- maybe no number
+ return 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
+
+local fields = { "title", "subtitle", "author", "keywords" }
+
+local function checkdocument(root)
+ local data = root.data
+ if data then
+ for i=1,#data do
+ local di = data[i]
+ if di.content then
+ -- ok
+ elseif di.tg == "ignore" then
+ di.element = ""
+ checkdocument(di)
+ else
+ -- can't happen
+ end
+ end
+ end
+end
+
+function extras.document(result,element,detail,n,fulltag,di)
+ result[#result+1] = format(" language=%q",languagenames[tex.count.mainlanguagenumber])
+ if not less_state then
+ result[#result+1] = format(" file=%q",tex.jobname)
+ result[#result+1] = format(" date=%q",os.date())
+ result[#result+1] = format(" context=%q",environment.version)
+ result[#result+1] = format(" version=%q",exportversion)
+ result[#result+1] = format(" xmlns:m=%q","http://www.w3.org/1998/Math/MathML")
+ local identity = interactions.general.getidentity()
+ for i=1,#fields do
+ local key = fields[i]
+ local value = identity[key]
+ if value and value ~= "" then
+ result[#result+1] = formatters[" %s=%q"](key,value)
+ end
+ end
+ end
+ checkdocument(di)
+end
+
+local itemgroups = { }
+
+function structurestags.setitemgroup(current,packed,symbol)
+ itemgroups[detailedtag("itemgroup",current)] = {
+ packed = packed,
+ symbol = symbol,
+ }
+end
+
+function extras.itemgroup(result,element,detail,n,fulltag,di)
+ local hash = itemgroups[fulltag]
+ if hash then
+ local v = hash.packed
+ if v then
+ result[#result+1] = " packed='yes'"
+ end
+ local v = hash.symbol
+ if v then
+ result[#result+1] = attribute("symbol",v)
+ end
+ end
+end
+
+local synonyms = { }
+
+function structurestags.setsynonym(current,tag)
+ synonyms[detailedtag("synonym",current)] = tag
+end
+
+function extras.synonym(result,element,detail,n,fulltag,di)
+ local tag = synonyms[fulltag]
+ if tag then
+ result[#result+1] = formatters[" tag='%s'"](tag)
+ end
+end
+
+local sortings = { }
+
+function structurestags.setsorting(current,tag)
+ sortings[detailedtag("sorting",current)] = tag
+end
+
+function extras.sorting(result,element,detail,n,fulltag,di)
+ local tag = sortings[fulltag]
+ if tag then
+ result[#result+1] = formatters[" tag='%s'"](tag)
+ end
+end
+
+usedstyles.highlight = { }
+
+function structurestags.sethighlight(current,style,color) -- we assume global styles
+ usedstyles.highlight[current] = {
+ style = style, -- xml.css.fontspecification(style),
+ color = color, -- xml.css.colorspec(color),
+ }
+end
+
+local descriptions = { }
+local symbols = { }
+local linked = { }
+
+function structurestags.setdescription(tag,n)
+ local nd = structures.notes.get(tag,n) -- todo: use listdata instead
+ if nd then
+ local references = nd.references
+ descriptions[references and references.internal] = detailedtag("description",tag)
+ end
+end
+
+function structurestags.setdescriptionsymbol(tag,n)
+ local nd = structures.notes.get(tag,n) -- todo: use listdata instead
+ if nd then
+ local references = nd.references
+ symbols[references and references.internal] = detailedtag("descriptionsymbol",tag)
+ end
+end
+
+function finalizers.descriptions(tree)
+ local n = 0
+ for id, tag in next, descriptions do
+ local sym = symbols[id]
+ if sym then
+ n = n + 1
+ linked[tag] = n
+ linked[sym] = n
+ end
+ end
+end
+
+function extras.description(result,element,detail,n,fulltag,di)
+ local id = linked[fulltag]
+ if id then
+ result[#result+1] = formatters[" insert='%s'"](id) -- maybe just fulltag
+ end
+end
+
+function extras.descriptionsymbol(result,element,detail,n,fulltag,di)
+ local id = linked[fulltag]
+ if id then
+ result[#result+1] = formatters[" insert='%s'"](id)
+ end
+end
+
+usedimages.image = { }
+
+function structurestags.setfigure(name,page,width,height)
+ usedimages.image[detailedtag("image")] = {
+ name = name,
+ page = page,
+ width = number.todimen(width,"cm","%0.3fcm"),
+ height = number.todimen(height,"cm","%0.3fcm"),
+ }
+end
+
+function extras.image(result,element,detail,n,fulltag,di)
+ local data = usedimages.image[fulltag]
+ if data then
+ result[#result+1] = attribute("name",data.name)
+ if tonumber(data.page) > 1 then
+ result[#result+1] = formatters[" page='%s'"](data.page)
+ end
+ result[#result+1] = formatters[" id='%s' width='%s' height='%s'"](fulltag,data.width,data.height)
+ end
+end
+
+local combinations = { }
+
+function structurestags.setcombination(nx,ny)
+ combinations[detailedtag("combination")] = {
+ nx = nx,
+ ny = ny,
+ }
+end
+
+function extras.combination(result,element,detail,n,fulltag,di)
+ local data = combinations[fulltag]
+ if data then
+ result[#result+1] = formatters[" nx='%s' ny='%s'"](data.nx,data.ny)
+ end
+end
+
+-- quite some code deals with exporting references --
+
+local evaluators = { }
+local specials = { }
+
+evaluators.inner = function(result,var)
+ local inner = var.inner
+ if inner then
+ result[#result+1] = attribute("location",inner)
+ end
+end
+
+evaluators.outer = function(result,var)
+ local file, url = references.checkedfileorurl(var.outer,var.outer)
+ if url then
+ result[#result+1] = attribute("url",url)
+ elseif file then
+ result[#result+1] = attribute("file",file)
+ end
+end
+
+evaluators["outer with inner"] = function(result,var)
+ local file = references.checkedfile(var.f)
+ if file then
+ result[#result+1] = attribute("file",file)
+ end
+ local inner = var.inner
+ if inner then
+ result[#result+1] = attribute("location",inner)
+ end
+end
+
+evaluators.special = function(result,var)
+ local handler = specials[var.special]
+ if handler then
+ handler(result,var)
+ end
+end
+
+evaluators["special outer with operation"] = evaluators.special
+evaluators["special operation"] = evaluators.special
+evaluators["special operation with arguments"] = evaluators.special
+
+function specials.url(result,var)
+ local url = references.checkedurl(var.operation)
+ if url then
+ result[#result+1] = attribute("url",url)
+ end
+end
+
+function specials.file(result,var)
+ local file = references.checkedfile(var.operation)
+ if file then
+ result[#result+1] = attribute("file",file)
+ end
+end
+
+function specials.fileorurl(result,var)
+ local file, url = references.checkedfileorurl(var.operation,var.operation)
+ if url then
+ result[#result+1] = attribute("url",url)
+ elseif file then
+ result[#result+1] = attribute("file",file)
+ end
+end
+
+function specials.internal(result,var)
+ local internal = references.checkedurl(var.operation)
+ if internal then
+ result[#result+1] = formatters[" location='aut:%s'"](internal)
+ end
+end
+
+local referencehash = { }
+
+local function adddestination(result,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
+ result[#result+1] = formatters[" prefix='%s'"](prefix)
+ end
+ result[#result+1] = formatters[" destination='%s'"](reference)
+ for i=1,#references do
+ local r = references[i]
+ local e = evaluators[r.kind]
+ if e then
+ e(result,r)
+ end
+ end
+ end
+ end
+end
+
+local function addreference(result,references)
+ if references then
+ local reference = references.reference
+ if reference and reference ~= "" then
+ local prefix = references.prefix
+ if prefix and prefix ~= "" then
+ result[#result+1] = formatters[" prefix='%s'"](prefix)
+ end
+ result[#result+1] = formatters[" reference='%s'"](reference)
+ end
+ local internal = references.internal
+ if internal and internal ~= "" then
+ result[#result+1] = formatters[" location='aut:%s'"](internal)
+ end
+ end
+end
+
+function extras.link(result,element,detail,n,fulltag,di)
+ -- for instance in lists a link has nested elements and no own text
+ local reference = referencehash[fulltag]
+ if reference then
+ adddestination(result,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 extras.link(result,element,detail,n,fulltag,di) then
+ return true
+ end
+ end
+ end
+ end
+ end
+end
+
+-- no settings, as these are obscure ones
+
+local automathrows = true directives.register("backend.export.math.autorows", function(v) automathrows = v end)
+local automathapply = true directives.register("backend.export.math.autoapply", function(v) automathapply = v end)
+local automathnumber = true directives.register("backend.export.math.autonumber", function(v) automathnumber = v end)
+local automathstrip = true directives.register("backend.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)
+ 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 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
+ local nucleus, superscript, subscript
+ for i=1,ndata do
+ local di = data[i]
+ if not di then
+ -- weird
+ elseif di.content then
+ -- text
+ elseif not nucleus then
+ nucleus = i
+ elseif not superscript then
+ superscript = i
+ elseif not subscript then
+ subscript = i
+ else
+ -- error
+ end
+ end
+ if superscript and subscript then
+ local sup, sub = data[superscript], data[subscript]
+ data[superscript], data[subscript] = sub, sup
+ -- sub.__o__, sup.__o__ = subscript, superscript
+ sub.__i__, sup.__i__ = superscript, subscript
+ end
+ elseif roottg == "mfenced" then
+ local new, n = { }, 0
+ local attributes = { }
+ root.attributes = attributes
+ for i=1,ndata do
+ local di = data[i]
+ if not di then
+ -- weird
+ elseif di.content then
+ n = n + 1
+ new[n] = di
+ else
+ local tg = di.tg
+ if tg == "mleft" then
+ attributes.left = tostring(di.data[1].data[1].content)
+ elseif tg == "mmiddle" then
+ attributes.middle = tostring(di.data[1].data[1].content)
+ elseif tg == "mright" then
+ attributes.right = tostring(di.data[1].data[1].content)
+ else
+ n = n + 1
+ di.__i__ = n
+ new[n] = di
+ end
+ end
+ end
+ root.data = new
+ ndata = n
+ end
+ if ndata == 0 then
+ return
+ elseif ndata == 1 then
+ local d = data[1]
+ if not d then
+ return
+ elseif d.content then
+ return
+ elseif #root.data == 1 then
+ local tg = d.tg
+ if automathrows and roottg == "mrow" then
+ -- maybe just always ! check spec first
+ if tg == "mrow" or tg == "mfenced" or tg == "mfrac" or tg == "mroot" or tg == "msqrt"then
+ root.skip = "comment"
+ elseif tg == "mo" 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
+ local detail = di.detail
+ if tg == "math" then
+ -- di.element = "mrow" -- when properties
+ di.skip = "comment"
+ checkmath(di)
+ i = i + 1
+ elseif tg == "mover" or tg == "munder" or tg == "munderover" then
+ if detail == "accent" then
+ di.attributes = { accent = "true" }
+ di.detail = nil
+ end
+ checkmath(di)
+ i = i + 1
+ elseif tg == "mroot" then
+ if #di.data == 1 then
+ -- else firefox complains
+ di.element = "msqrt"
+ end
+ checkmath(di)
+ i = i + 1
+ elseif tg == "break" then
+ di.skip = "comment"
+ i = i + 1
+ elseif tg == "mrow" and detail then
+ di.detail = nil
+ checkmath(di)
+ di = {
+ element = "maction",
+ nature = "display",
+ attributes = { actiontype = detail },
+ data = { di },
+ n = 0,
+ }
+ data[i] = di
+ i = i + 1
+ elseif detail then
+ -- no checkmath(di) here
+ local category = tonumber(detail) or 0
+ 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
+ else -- can be string or boolean
+ if parenttg ~= "mtext" and di == " " then
+ data[i] = false
+ end
+ i = i + 1
+ end
+ end
+ end
+end
+
+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 di = data[i]
+ if di and not di.content then
+ di = stripmath(di)
+ end
+ if di then
+ local content = di.content
+ if not content then
+ n = n + 1
+ di.__i__ = n
+ data[n] = di
+ elseif content == " " or content == "" then
+ -- skip
+ else
+ n = n + 1
+ data[n] = di
+ 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)
+ local hash = attributehash[di.fulltag]
+ local mode = (hash and hash.mode) == "display" and "block" or "inline"
+ di.attributes = {
+ display = mode
+ }
+ -- can be option if needed:
+ if mode == "inline" then
+ di.nature = "mixed" -- else spacing problem (maybe inline)
+ else
+ di.nature = "display"
+ end
+ if automathstrip then
+ stripmath(di)
+ end
+ checkmath(di)
+end
+
+local a, z, A, Z = 0x61, 0x7A, 0x41, 0x5A
+
+function extras.mi(result,element,detail,n,fulltag,di) -- 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
+
+function extras.section(result,element,detail,n,fulltag,di)
+ local data = listdata[fulltag]
+ if data then
+ addreference(result,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 extras.section(result,element,detail,n,ft,di) then
+ return true
+ end
+ end
+ end
+ end
+ end
+end
+
+function extras.float(result,element,detail,n,fulltag,di)
+ local data = listdata[fulltag]
+ if data then
+ addreference(result,data.references)
+ return true
+ else
+ local data = di.data
+ if data then
+ for i=1,#data do
+ local di = data[i]
+ if di and extras.section(result,element,detail,n,di.fulltag,di) then
+ return true
+ end
+ end
+ end
+ end
+end
+
+local tabledata = { }
+
+function structurestags.settablecell(rows,columns,align)
+ if align > 0 or rows > 1 or columns > 1 then
+ tabledata[detailedtag("tablecell")] = {
+ rows = rows,
+ columns = columns,
+ align = align,
+ }
+ end
+end
+
+function extras.tablecell(result,element,detail,n,fulltag,di)
+ local hash = tabledata[fulltag]
+ if hash then
+ local v = hash.columns
+ if v and v > 1 then
+ result[#result+1] = formatters[" columns='%s'"](v)
+ end
+ local v = hash.rows
+ if v and v > 1 then
+ result[#result+1] = formatters[" rows='%s'"](v)
+ end
+ local v = hash.align
+ if not v or v == 0 then
+ -- normal
+ elseif v == 1 then -- use numbertoalign here
+ result[#result+1] = " align='flushright'"
+ elseif v == 2 then
+ result[#result+1] = " align='middle'"
+ elseif v == 3 then
+ result[#result+1] = " align='flushleft'"
+ end
+ end
+end
+
+local tabulatedata = { }
+
+function structurestags.settabulatecell(align)
+ if align > 0 then
+ tabulatedata[detailedtag("tabulatecell")] = {
+ align = align,
+ }
+ end
+end
+
+function extras.tabulate(result,element,detail,n,fulltag,di)
+ local data = di.data
+ for i=1,#data do
+ local di = data[i]
+ if di.tg == "tabulaterow" then
+ local did = di.data
+ local content = false
+ for i=1,#did do
+ local d = did[i].data
+ if d and #d > 0 and d[1].content then
+ content = true
+ break
+ end
+ end
+ if not content then
+ di.element = "" -- or simply remove
+ end
+ end
+ end
+end
+
+function extras.tabulatecell(result,element,detail,n,fulltag,di)
+ local hash = tabulatedata[fulltag]
+ if hash then
+ local v = hash.align
+ if not v or v == 0 then
+ -- normal
+ elseif v == 1 then
+ result[#result+1] = " align='flushleft'"
+ elseif v == 2 then
+ result[#result+1] = " align='flushright'"
+ elseif v == 3 then
+ result[#result+1] = " align='middle'"
+ end
+ end
+end
+
+-- flusher
+
+local linedone = false -- can go ... we strip newlines anyway
+local inlinedepth = 0
+
+-- todo: #result -> nofresult
+
+local function emptytag(result,element,nature,depth,di) -- currently only break but at some point
+ local a = di.attributes -- we might add detail etc
+ if a then -- happens seldom
+ if linedone then
+ result[#result+1] = formatters["%w<%s"](depth,namespaced[element])
+ else
+ result[#result+1] = formatters["\n%w<%s"](depth,namespaced[element])
+ end
+ for k, v in next, a do
+ result[#result+1] = formatters[" %s=%q"](k,v)
+ end
+ result[#result+1] = "/>\n"
+ else
+ if linedone then
+ result[#result+1] = formatters["%w<%s/>\n"](depth,namespaced[element])
+ else
+ result[#result+1] = formatters["\n%w<%s/>\n"](depth,namespaced[element])
+ end
+ end
+ linedone = false
+end
+
+local function begintag(result,element,nature,depth,di,skip)
+ -- if needed we can use a local result with xresult
+ local detail = di.detail
+ local n = di.n
+ local fulltag = di.fulltag
+ local comment = di.comment
+ if nature == "inline" then
+ linedone = false
+ inlinedepth = inlinedepth + 1
+ if show_comment and comment then
+ result[#result+1] = formatters["<!-- %s -->"](comment)
+ end
+ elseif nature == "mixed" then
+ if inlinedepth > 0 then
+ if show_comment and comment then
+ result[#result+1] = formatters["<!-- %s -->"](comment)
+ end
+ elseif linedone then
+ result[#result+1] = spaces[depth]
+ if show_comment and comment then
+ result[#result+1] = formatters["<!-- %s -->"](comment)
+ end
+ else
+ result[#result+1] = formatters["\n%w"](depth)
+ linedone = false
+ if show_comment and comment then
+ result[#result+1] = formatters["<!-- %s -->\n%w"](comment,depth)
+ end
+ end
+ inlinedepth = inlinedepth + 1
+ else
+ if inlinedepth > 0 then
+ if show_comment and comment then
+ result[#result+1] = formatters["<!-- %s -->"](comment)
+ end
+ elseif linedone then
+ result[#result+1] = spaces[depth]
+ if show_comment and comment then
+ result[#result+1] = formatters["<!-- %s -->"](comment)
+ end
+ else
+ result[#result+1] = formatters["\n%w"](depth) -- can introduced extra line in mixed+mixed (filtered later on)
+ linedone = false
+ if show_comment and comment then
+ result[#result+1] = formatters["<!-- %s -->\n%w"](comment,depth)
+ end
+ end
+ end
+ if skip == "comment" then
+ if show_comment then
+ result[#result+1] = formatters["<!-- begin %s -->"](namespaced[element])
+ end
+ elseif skip then
+ -- ignore
+ else
+ result[#result+1] = formatters["<%s"](namespaced[element])
+ if detail then
+ result[#result+1] = formatters[" detail=%q"](detail)
+ end
+ if indexing and n then
+ result[#result+1] = formatters[" n=%q"](n)
+ end
+ local extra = extras[element]
+ if extra then
+ extra(result,element,detail,n,fulltag,di)
+ end
+ local u = userdata[fulltag]
+ if u then
+ for k, v in next, u do
+ result[#result+1] = formatters[" %s=%q"](k,v)
+ end
+ end
+ local a = di.attributes
+ if a then
+ for k, v in next, a do
+ result[#result+1] = formatters[" %s=%q"](k,v)
+ end
+ end
+ result[#result+1] = ">"
+ end
+ if inlinedepth > 0 then
+ elseif nature == "display" then
+ result[#result+1] = "\n"
+ linedone = true
+ end
+ used[element][detail or ""] = nature -- for template css
+ local metadata = tagmetadata[fulltag]
+ if metadata then
+ if not linedone then
+ result[#result+1] = "\n"
+ linedone = true
+ end
+ result[#result+1] = formatters["%w<metadata>\n"](depth)
+ for k, v in table.sortedpairs(metadata) do
+ v = entityremapper(v)
+ result[#result+1] = formatters["%w<metavariable name=%q>%s</metavariable>\n"](depth+1,k,v)
+ end
+ result[#result+1] = formatters["%w</metadata>\n"](depth)
+ end
+end
+
+local function endtag(result,element,nature,depth,skip)
+ if nature == "display" then
+ if inlinedepth == 0 then
+ if not linedone then
+ result[#result+1] = "\n"
+ end
+ if skip == "comment" then
+ if show_comment then
+ result[#result+1] = formatters["%w<!-- end %s -->\n"](depth,namespaced[element])
+ end
+ elseif skip then
+ -- ignore
+ else
+ result[#result+1] = formatters["%w</%s>\n"](depth,namespaced[element])
+ end
+ linedone = true
+ else
+ if skip == "comment" then
+ if show_comment then
+ result[#result+1] = formatters["<!-- end %s -->"](namespaced[element])
+ end
+ elseif skip then
+ -- ignore
+ else
+ result[#result+1] = formatters["</%s>"](namespaced[element])
+ end
+ end
+ else
+ inlinedepth = inlinedepth - 1
+ if skip == "comment" then
+ if show_comment then
+ result[#result+1] = formatters["<!-- end %s -->"](namespaced[element])
+ end
+ elseif skip then
+ -- ignore
+ else
+ result[#result+1] = formatters["</%s>"](namespaced[element])
+ end
+ linedone = false
+ end
+end
+
+local function flushtree(result,data,nature,depth)
+ depth = depth + 1
+ local nofdata = #data
+ for i=1,nofdata do
+ local di = data[i]
+ if not di then -- hm, di can be string
+ -- whatever
+ elseif di.content then
+ -- already has breaks
+ local content = entityremapper(di.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] = formatters["<c n='%s'>%s</c>"](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] = formatters["<c n='%s'>%s</c>"](di.parnumber or 0,content)
+ else
+ result[#result+1] = content
+ end
+ end
+ linedone = false
+ 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,depth,di)
+ elseif element == "" or di.skip == "ignore" then
+ -- skip
+ else
+ if di.before then
+ flushtree(result,di.before,nature,depth)
+ end
+ local natu = di.nature
+ local skip = di.skip
+ if di.breaknode then
+ emptytag(result,"break","display",depth,di)
+ end
+ begintag(result,element,natu,depth,di,skip)
+ flushtree(result,di.data,natu,depth)
+ -- if sub(result[#result],-1) == " " and natu ~= "inline" then
+ -- result[#result] = sub(result[#result],1,-2)
+ -- end
+ endtag(result,element,natu,depth,skip)
+ if di.after then
+ flushtree(result,di.after,nature,depth)
+ 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.content then
+ 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
+ prevnature = "inline"
+ prevparnumber = parnumber
+ 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"
+ elseif element == "" or di.skip == "ignore" then
+ -- skip
+ 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
+ prevelement = element
+ breaktree(di,tree,element)
+ end
+ nofnewdata = nofnewdata + 1
+ newdata[nofnewdata] = di
+ 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
+ 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()
+ for tag, trees in next, treehash do
+ local d = trees[1].data
+ 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.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 indextree(tree)
+ local data = tree.data
+ if data then
+ local n, new = 0, { }
+ 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
+ end
+end
+
+local function checktree(tree)
+ local data = tree.data
+ if data then
+ for i=1,#data do
+ local d = data[i]
+ if type(d) == "table" then
+ local check = checks[d.tg]
+ if check then
+ check(d)
+ end
+ checktree(d)
+ end
+ end
+ end
+end
+
+-- collector code
+
+local function push(fulltag,depth)
+ local tag, n = lpegmatch(dashsplitter,fulltag)
+ local tg, detail = lpegmatch(colonsplitter,tag)
+ local element, nature
+ if detail then
+ local pd = properties[tag]
+ local pt = properties[tg]
+ element = pd and pd.export or pt and pt.export or tg
+ nature = pd and pd.nature or pt and pt.nature or defaultnature
+ else
+ local p = properties[tg]
+ element = p and p.export or tg
+ nature = p and p.nature or "inline"
+ end
+ local treedata = tree.data
+ local t = {
+ tg = tg,
+ fulltag = fulltag,
+ detail = detail,
+ n = tonumber(n), -- more efficient
+ element = element,
+ nature = nature,
+ data = { },
+ attribute = currentattribute,
+ parnumber = currentparagraph,
+ }
+ 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=%a paragraph=%a index=%a detail=%a>",currentdepth-1,fulltag,currentattribute or 0,currentparagraph or 0,#treedata,detail)
+ else
+ report_export("%w<%s trigger=%a paragraph=%a index=%a>",currentdepth-1,fulltag,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()
+ local top = nesting[currentdepth]
+ tree = treestack[currentdepth]
+ currentdepth = currentdepth - 1
+ if trace_export then
+ if top then
+ report_export("%w</%s>",currentdepth,top)
+ else
+ report_export("</%s>",top)
+ end
+ 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 current then
+ 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
+end
+
+local function pushcontent(currentparagraph,newparagraph)
+ if nofcurrentcontent > 0 then
+ if currentparagraph 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 currentparagraph we could push, remove spaces, pop
+ elseif somespace[content] and currentparagraph then
+ -- omit; when currentparagraph we could push, remove spaces, pop
+ else
+ local olddepth, newdepth
+ local list = taglist[currentattribute]
+ if list then
+ olddepth, newdepth = pushentry(list)
+ end
+ local td = tree.data
+ local nd = #td
+ td[nd+1] = { parnumber = currentparagraph, content = content }
+ if trace_export then
+ report_export("%w<!-- start content with length %s -->",currentdepth,#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
+ nofcurrentcontent = 0
+ end
+ if currentparagraph then
+ pushentry(makebreaklist(currentnesting))
+ if trace_export then
+ report_export("%w<!-- break added betweep paragraph %a and %a -->",currentdepth,currentparagraph,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
+
+-- whatsit_code localpar_code
+
+local function collectresults(head,list) -- is last used (we also have currentattribute)
+ local p
+ for n in traverse_nodes(head) do
+ local id = n.id -- 14: image, 8: literal (mp)
+ if id == glyph_code then
+ local at = n[a_tagged]
+ 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
+ -- we could add tonunicodes for ligatures (todo)
+ local components = n.components
+ if components then -- we loose data
+ collectresults(components,nil)
+ else
+ local c = n.char
+ if last ~= at then
+ local tl = taglist[at]
+ pushcontent()
+ currentnesting = tl
+ currentparagraph = n[a_taggedpar]
+ 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 = n[a_reference]
+ if r then
+ referencehash[tl[#tl]] = r -- fulltag
+ end
+ --
+ elseif last then
+ local ap = n[a_taggedpar]
+ if ap ~= currentparagraph then
+ pushcontent(currentparagraph,ap)
+ pushentry(currentnesting)
+ currentattribute = last
+ currentparagraph = ap
+ 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 = 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 = 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[n.font]
+ if fc then
+ fc = fc and fc[c]
+ if fc then
+ local u = fc.tounicode
+ if u and u ~= "" then
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = utfchar(fromunicode16(u))
+ else
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = utfchar(c)
+ end
+ else -- weird, happens in hz (we really need to get rid of the pseudo fonts)
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = utfchar(c)
+ end
+ else
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = utfchar(c)
+ end
+ end
+ end
+ end
+ elseif id == disc_code then -- probably too late
+ if keephyphens then
+ local pre = n.pre
+ if pre and not pre.next and pre.id == glyph_code and pre.char == hyphencode then
+ nofcurrentcontent = nofcurrentcontent + 1
+ currentcontent[nofcurrentcontent] = hyphen
+ end
+ end
+ collectresults(n.replace,nil)
+ elseif id == glue_code then
+ -- we need to distinguish between hskips and vskips
+ local ca = n[a_characters]
+ if ca == 0 then
+ -- skip this one ... already converted special character (node-acc)
+ elseif ca then
+ local a = n[a_tagged]
+ 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 = n[a_taggedpar]
+ currentattribute = a
+ last = a
+ pushentry(currentnesting)
+ -- no reference check (see above)
+ elseif last then
+ local ap = n[a_taggedpar]
+ 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
+ else
+ local subtype = n.subtype
+ if subtype == userskip_code then
+ if n.spec.width > threshold then
+ if last and not somespace[currentcontent[nofcurrentcontent]] then
+ local a = n[a_tagged]
+ 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 = n[a_tagged]
+ 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 not somespace[r] then
+ local a = n[a_tagged]
+ 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 paragaph endings (crossings) elsewhere and we quit here
+ -- as we don't want the rightskip space addition
+ return
+ end
+ end
+ elseif id == hlist_code or id == vlist_code then
+ local ai = n[a_image]
+ if ai then
+ local at = n[a_tagged]
+ 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
+ collectresults(n.list,n)
+ end
+ elseif id == kern_code then
+ local kern = n.kern
+ if kern > 0 then
+ local limit = threshold
+ if p and p.id == glyph_code then
+ limit = fontquads[p.font] / 4
+ end
+ if kern > limit then
+ if last and not somespace[currentcontent[nofcurrentcontent]] then
+ local a = n[a_tagged]
+ 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]
+ pushentry(currentnesting)
+ currentattribute = last
+ end
+ end
+ end
+ end
+ end
+ p = n
+ 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, true
+end
+
+function builders.paragraphs.tag(head)
+ noftextblocks = noftextblocks + 1
+ for n in traverse_id(hlist_code,head) do
+ local subtype = n.subtype
+ if subtype == line_code then
+ n[a_textblock] = noftextblocks
+ elseif subtype == glue_code or subtype == kern_code then
+ n[a_textblock] = 0
+ end
+ end
+ return false
+end
+
+-- encoding='utf-8'
+
+local xmlpreamble = [[
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+
+<!-- input filename : %- 17s -->
+<!-- processing date : %- 17s -->
+<!-- context version : %- 17s -->
+<!-- exporter version : %- 17s -->
+
+]]
+
+local function wholepreamble()
+ return format(xmlpreamble,tex.jobname,os.date(),environment.version,exportversion)
+end
+
+
+local csspreamble = [[
+<?xml-stylesheet type="text/css" href="%s"?>
+]]
+
+local function allusedstylesheets(xmlfile,cssfiles,files)
+ local result = { }
+ for i=1,#cssfiles do
+ local cssfile = cssfiles[i]
+ if type(cssfile) ~= "string" or cssfile == variables.yes or cssfile == "" or cssfile == xmlfile then
+ cssfile = file.replacesuffix(xmlfile,"css")
+ else
+ cssfile = file.addsuffix(cssfile,"css")
+ end
+ files[#files+1] = cssfile
+ report_export("adding css reference '%s'",cssfile)
+ result[#result+1] = format(csspreamble,cssfile)
+ end
+ return concat(result)
+end
+
+local e_template = [[
+%s {
+ display: %s ;
+}]]
+
+local d_template = [[
+%s[detail=%s] {
+ display: %s ;
+}]]
+
+local displaymapping = {
+ inline = "inline",
+ display = "block",
+ mixed = "inline",
+}
+
+local function allusedelements(xmlfile)
+ local result = { format("/* template for file %s */",xmlfile) }
+ for element, details in sortedhash(used) do
+ result[#result+1] = format("/* category: %s */",element)
+ for detail, nature in sortedhash(details) do
+ local d = displaymapping[nature or "display"] or "block"
+ if detail == "" then
+ result[#result+1] = formatters[e_template](element,d)
+ else
+ result[#result+1] = formatters[d_template](element,detail,d)
+ end
+ end
+ end
+ return concat(result,"\n\n")
+end
+
+local function allcontent(tree)
+ local result = { }
+ flushtree(result,tree.data,"display",0) -- we need to collect images
+ result = concat(result)
+ 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 xmlwrap = xml.wrap
+ for e in xml.collected(xmltree,"/document") do
+ e.at["xmlns:xhtml"] = "http://www.w3.org/1999/xhtml"
+ break
+ end
+ -- todo: inject xhtmlpreamble (xmlns should have be enough)
+ local wrapper = { tg = "a", ns = "xhtml", at = { href = "unknown" } }
+ for e in xml.collected(xmltree,"link") do
+ local at = e.at
+ local href
+ if at.location then
+ href = "#" .. gsub(at.location,":","_")
+ elseif at.url then
+ href = at.url
+ elseif at.file then
+ href = at.file
+ end
+ if href then
+ wrapper.at.href = href
+ xmlwrap(e,wrapper)
+ end
+ end
+ local wrapper = { tg = "a", ns = "xhtml", at = { name = "unknown" } }
+ for e in xml.collected(xmltree,"!link[@location]") do
+ local location = e.at.location
+ if location then
+ wrapper.at.name = gsub(location,":","_")
+ xmlwrap(e,wrapper)
+ end
+ end
+ return xmltree
+ else
+ return xml.convert("<?xml version='1.0'?>\n<error>invalid xhtml tree</error>")
+ end
+end
+
+local cssfile, xhtmlfile = nil, nil
+
+directives.register("backend.export.css", function(v) cssfile = v end)
+directives.register("backend.export.xhtml",function(v) xhtmlfile = v end)
+
+local function stopexport(v)
+ starttiming(treehash)
+ --
+ finishexport()
+ --
+ collapsetree(tree)
+ indextree(tree)
+ checktree(tree)
+ breaktree(tree)
+ finalizetree(tree)
+ --
+ hashlistdata()
+ --
+ if type(v) ~= "string" or v == variables.yes or v == "" then
+ v = tex.jobname
+ end
+ local basename = file.basename(v)
+ local xmlfile = file.addsuffix(basename,"export")
+ --
+ local imagefilename = file.addsuffix(file.removesuffix(xmlfile) .. "-images","css")
+ local stylefilename = file.addsuffix(file.removesuffix(xmlfile) .. "-styles","css")
+ local templatefilename = file.replacesuffix(xmlfile,"template")
+ local specificationfilename = file.replacesuffix(xmlfile,"specification")
+ --
+ if xhtml and not cssfile then
+ cssfile = true
+ end
+ local cssfiles = { }
+ if cssfile then
+ if cssfile == true then
+ cssfiles = { "export-example.css" }
+ else
+ cssfiles = settings_to_array(cssfile or "")
+ end
+ insert(cssfiles,1,imagefilename)
+ insert(cssfiles,1,stylefilename)
+ end
+ cssfiles = table.unique(cssfiles)
+ --
+ local result = allcontent(tree) -- also does some housekeeping and data collecting
+ --
+ local files = {
+ }
+ local results = concat {
+ wholepreamble(),
+ allusedstylesheets(xmlfile,cssfiles,files), -- ads to files
+ result,
+ }
+ --
+ files = table.unique(files)
+ --
+ report_export("saving xml data in %a",xmlfile)
+ io.savedata(xmlfile,results)
+ --
+ report_export("saving css image definitions in %a",imagefilename)
+ io.savedata(imagefilename,allusedimages(xmlfile))
+ --
+ report_export("saving css style definitions in %a",stylefilename)
+ io.savedata(stylefilename,allusedstyles(xmlfile))
+ --
+ report_export("saving css template in %a",templatefilename)
+ io.savedata(templatefilename,allusedelements(xmlfile))
+ --
+ if xhtmlfile then
+ if type(v) ~= "string" or xhtmlfile == true or xhtmlfile == variables.yes or xhtmlfile == "" or xhtmlfile == xmlfile then
+ xhtmlfile = file.replacesuffix(xmlfile,"xhtml")
+ else
+ xhtmlfile = file.addsuffix(xhtmlfile,"xhtml")
+ end
+ files[#files+1] = xhtmlfile
+ report_export("saving xhtml variant in %a",xhtmlfile)
+ local xmltree = cleanxhtmltree(xml.convert(results))
+ xml.save(xmltree,xhtmlfile)
+ -- looking at identity is somewhat redundant as we also inherit from interaction
+ -- at the tex end
+ local identity = interactions.general.getidentity()
+ local specification = {
+ name = file.removesuffix(v),
+ identifier = os.uuid(),
+ images = uniqueusedimages(),
+ root = xhtmlfile,
+ files = files,
+ language = languagenames[tex.count.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),
+ }
+ report_export("saving specification in %a (mtxrun --script epub --make %s)",specificationfilename,specificationfilename)
+ io.savedata(specificationfilename,table.serialize(specification,true))
+ end
+ stoptiming(treehash)
+end
+
+local appendaction = nodes.tasks.appendaction
+local enableaction = nodes.tasks.enableaction
+
+function commands.setupexport(t)
+ table.merge(finetuning,t)
+ keephyphens = finetuning.hyphen == variables.yes
+end
+
+local function startexport(v)
+ if v and not exporting then
+ report_export("enabling export to xml")
+-- not yet known in task-ini
+ appendaction("shipouts","normalizers", "nodes.handlers.export")
+-- enableaction("shipouts","nodes.handlers.export")
+ enableaction("shipouts","nodes.handlers.accessibility")
+ enableaction("math", "noads.handlers.tags")
+--~ appendaction("finalizers","lists","builders.paragraphs.tag")
+--~ enableaction("finalizers","builders.paragraphs.tag")
+ luatex.registerstopactions(function() stopexport(v) end)
+ exporting = true
+ end
+end
+
+directives.register("backend.export",startexport) -- maybe .name
+
+statistics.register("xml exporting time", function()
+ if exporting then
+ return format("%s seconds, version %s", statistics.elapsedtime(treehash),exportversion)
+ end
+end)
+
+-- These are called at the tex end:
+
+commands.settagitemgroup = structurestags.setitemgroup
+commands.settagsynonym = structurestags.setsynonym
+commands.settagsorting = structurestags.setsorting
+commands.settagdescription = structurestags.setdescription
+commands.settagdescriptionsymbol = structurestags.setdescriptionsymbol
+commands.settaghighlight = structurestags.sethighlight
+commands.settagfigure = structurestags.setfigure
+commands.settagcombination = structurestags.setcombination
+commands.settagtablecell = structurestags.settablecell
+commands.settagtabulatecell = structurestags.settabulatecell
diff --git a/tex/context/base/back-ini.lua b/tex/context/base/back-ini.lua
index 1568e3564..bdd931abd 100644
--- a/tex/context/base/back-ini.lua
+++ b/tex/context/base/back-ini.lua
@@ -1,106 +1,106 @@
-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
-local sind, cosd = math.sind, math.cosd
-
-backends = backends or { }
-local backends = backends
-
-local trace_backend = false trackers.register("backend.initializers", function(v) trace_finalizers = v end)
-
-local report_backend = logs.reporter("backend","initializing")
-
-local allocate = utilities.storage.allocate
-local setmetatableindex = table.setmetatableindex
-
-local function nothing() return nil end
-
-backends.nothing = nothing
-
-local nodeinjections = { }
-local codeinjections = { }
-local registrations = { }
-local tables = allocate()
-
-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_backend then
- if backend.comment then
- report_backend("initializing backend %a, %a",what,backend.comment)
- else
- report_backend("initializing backend %a",what)
- end
- end
- backends.current = what
- for category, default in next, defaults do
- local target, plugin = backends[category], backend[category]
- setmetatableindex(plugin, default)
- setmetatableindex(target, plugin)
- end
- elseif trace_backend then
- report_backend("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)
-
-local comment = { "comment", "" }
-
-tables.vfspecials = allocate {
- red = comment,
- green = comment,
- blue = comment,
- black = comment,
- startslant = comment,
- stopslant = comment,
-}
-
--- experimental code --
-
-function commands.pdfrotation(a) -- somewhat weird here
- local s, c = sind(a), cosd(a)
- context("%0.6f %0.6f %0.6f %0.6f",c,s,-s,c)
-end
-
+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
+local sind, cosd = math.sind, math.cosd
+
+backends = backends or { }
+local backends = backends
+
+local trace_backend = false trackers.register("backend.initializers", function(v) trace_finalizers = v end)
+
+local report_backend = logs.reporter("backend","initializing")
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+
+local function nothing() return nil end
+
+backends.nothing = nothing
+
+local nodeinjections = { }
+local codeinjections = { }
+local registrations = { }
+local tables = allocate()
+
+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_backend then
+ if backend.comment then
+ report_backend("initializing backend %a, %a",what,backend.comment)
+ else
+ report_backend("initializing backend %a",what)
+ end
+ end
+ backends.current = what
+ for category, default in next, defaults do
+ local target, plugin = backends[category], backend[category]
+ setmetatableindex(plugin, default)
+ setmetatableindex(target, plugin)
+ end
+ elseif trace_backend then
+ report_backend("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)
+
+local comment = { "comment", "" }
+
+tables.vfspecials = allocate {
+ red = comment,
+ green = comment,
+ blue = comment,
+ black = comment,
+ startslant = comment,
+ stopslant = comment,
+}
+
+-- experimental code --
+
+function commands.pdfrotation(a) -- somewhat weird here
+ local s, c = sind(a), cosd(a)
+ context("%0.6f %0.6f %0.6f %0.6f",c,s,-s,c)
+end
+
diff --git a/tex/context/base/bibl-bib.lua b/tex/context/base/bibl-bib.lua
index ab38a0b28..c86a0c0c2 100644
--- a/tex/context/base/bibl-bib.lua
+++ b/tex/context/base/bibl-bib.lua
@@ -1,766 +1,766 @@
-if not modules then modules = { } end modules ['bibl-bib'] = {
- version = 1.001,
- comment = "this module is the basis for the lxml-* ones",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
---[[ldx--
-<p>This is a prelude to integrated bibliography support. This file just loads
-bibtex files and converts them to xml so that the we access the content
-in a convenient way. Actually handling the data takes place elsewhere.</p>
---ldx]]--
-
-local lower, format, gsub, concat = string.lower, string.format, string.gsub, table.concat
-local next = next
-local utfchar = utf.char
-local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
-local textoutf = characters and characters.tex.toutf
-local variables = interfaces and interfaces.variables
-local settings_to_hash = utilities.parsers.settings_to_hash
-local finalizers = xml.finalizers.tex
-local xmlfilter, xmltext, getid = xml.filter, xml.text, lxml.getid
-local formatters = string.formatters
-
-local P, R, S, C, Cc, Cs, Ct = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct
-
-local trace_bibxml = false trackers.register("publications.bibxml", function(v) trace_bibtex = v end)
-
-local report_xml = logs.reporter("publications","xml")
-
-bibtex = bibtex or { }
-local bibtex = bibtex
-
-bibtex.statistics = bibtex.statistics or { }
-local bibtexstats = bibtex.statistics
-
-bibtexstats.nofbytes = 0
-bibtexstats.nofdefinitions = 0
-bibtexstats.nofshortcuts = 0
-
-local defaultshortcuts = {
- jan = "1",
- feb = "2",
- mar = "3",
- apr = "4",
- may = "5",
- jun = "6",
- jul = "7",
- aug = "8",
- sep = "9",
- oct = "10",
- nov = "11",
- dec = "12",
-}
-
-local shortcuts = { }
-local data = { }
-local entries
-
--- Currently we expand shortcuts and for large ones (like the acknowledgements
--- in tugboat.bib this is not that efficient. However, eventually strings get
--- hashed again.
-
-local function do_shortcut(tag,key,value)
- bibtexstats.nofshortcuts = bibtexstats.nofshortcuts + 1
- if lower(tag) == "@string" then
- shortcuts[key] = value
- end
-end
-
-local function do_definition(tag,key,tab) -- maybe check entries here (saves memory)
- if not entries or entries[key] then
- bibtexstats.nofdefinitions = bibtexstats.nofdefinitions + 1
- local t = { }
- for i=1,#tab,2 do
- t[tab[i]] = tab[i+1]
- end
- local p = data[tag]
- if not p then
- data[tag] = { [key] = t }
- else
- p[key] = t
- end
- end
-end
-
-local function resolve(s)
- return shortcuts[s] or defaultshortcuts[s] or s -- can be number
-end
-
-local percent = P("%")
-local start = P("@")
-local comma = P(",")
-local hash = P("#")
-local escape = P("\\")
-local single = P("'")
-local double = P('"')
-local left = P('{')
-local right = P('}')
-local both = left + right
-local lineending = S("\n\r")
-local space = S(" \t\n\r\f")
-local spacing = space^0
-local equal = P("=")
-local collapsed = (space^1)/ " "
-
-local function add(a,b) if b then return a..b else return a end end
-
-local keyword = C((R("az","AZ","09") + S("@_:-"))^1) -- C((1-space)^1)
-local s_quoted = ((escape*single) + collapsed + (1-single))^0
-local d_quoted = ((escape*double) + collapsed + (1-double))^0
-local balanced = lpegpatterns.balanced
-
-local s_value = (single/"") * s_quoted * (single/"")
-local d_value = (double/"") * d_quoted * (double/"")
-local b_value = (left /"") * balanced * (right /"")
-local r_value = keyword/resolve
-
-local somevalue = s_value + d_value + b_value + r_value
-local value = Cs((somevalue * ((spacing * hash * spacing)/"" * somevalue)^0))
-
-local assignment = spacing * keyword * spacing * equal * spacing * value * spacing
-local shortcut = keyword * spacing * left * spacing * (assignment * comma^0)^0 * spacing * right
-local definition = keyword * spacing * left * spacing * keyword * comma * Ct((assignment * comma^0)^0) * spacing * right
-local comment = keyword * spacing * left * (1-right)^0 * spacing * right
-local forget = percent^1 * (1-lineending)^0
-
--- todo \%
-
-local grammar = (space + forget + shortcut/do_shortcut + definition/do_definition + comment + 1)^0
-
-function bibtex.convert(session,content)
- statistics.starttiming(bibtex)
- data, shortcuts, entries = session.data, session.shortcuts, session.entries
- bibtexstats.nofbytes = bibtexstats.nofbytes + #content
- session.nofbytes = session.nofbytes + #content
- lpegmatch(grammar,content or "")
- statistics.stoptiming(bibtex)
-end
-
-function bibtex.load(session,filename)
- local filename = resolvers.findfile(filename,"bib")
- if filename ~= "" then
- local data = io.loaddata(filename) or ""
- if data == "" then
- report_xml("empty file %a, no conversion to xml",filename)
- elseif trace_bibxml then
- report_xml("converting file %a to xml",filename)
- end
- bibtex.convert(session,data)
- end
-end
-
-function bibtex.new()
- return {
- data = { },
- shortcuts = { },
- xml = xml.convert("<?xml version='1.0' standalone='yes'?>\n<bibtex></bibtex>"),
- nofbytes = 0,
- entries = nil,
- loaded = false,
- }
-end
-
-local p_escaped = lpegpatterns.xml.escaped
-
-local ihatethis = {
- f = "\\f",
- n = "\\n",
- r = "\\r",
- s = "\\s",
- t = "\\t",
- v = "\\v",
- z = "\\z",
-}
-
-local command = P("\\")/"" * Cc("\\bibtexcommand{") * (R("az","AZ")^1) * Cc("}")
-local any = P(1)
-local done = P(-1)
-local one_l = P("{") / ""
-local one_r = P("}") / ""
-local two_l = P("{{") / ""
-local two_r = P("}}") / ""
-
-local filter = Cs(
- two_l * (command + any - two_r - done)^0 * two_r * done +
- one_l * (command + any - one_r - done)^0 * one_r * done +
- (command + any )^0
-)
-
-function bibtex.toxml(session,options)
- if session.loaded then
- return
- else
- session.loaded = true
- end
- -- we can always speed this up if needed
- -- format slows down things a bit but who cares
- statistics.starttiming(bibtex)
- local result, r = { }, 0
- local options = settings_to_hash(options)
- local convert = options.convert -- todo: interface
- local strip = options.strip -- todo: interface
- local entries = session.entries
- r = r + 1 ; result[r] = "<?xml version='1.0' standalone='yes'?>"
- r = r + 1 ; result[r] = "<bibtex>"
- for id, categories in next, session.data do
- id = lower(gsub(id,"^@",""))
- for name, entry in next, categories do
- if not entries or entries[name] then
- r = r + 1 ; result[r] = formatters["<entry tag='%s' category='%s'>"](lower(name),id)
- for key, value in next, entry do
- value = gsub(value,"\\(.)",ihatethis) -- this really needs checking
- value = lpegmatch(p_escaped,value)
- if value ~= "" then
- if convert then
- value = textoutf(value,true)
- end
- if strip then
- -- as there is no proper namespace in bibtex we need this
- -- kind of hackery ... bibtex databases are quite unportable
- value = lpegmatch(filter,value) or value
- end
- r = r + 1 ; result[r] = formatters[" <field name='%s'>%s</field>"](key,value)
- end
- end
- r = r + 1 ; result[r] = "</entry>"
- end
- end
- end
- r = r + 1 ; result[r] = "</bibtex>"
- result = concat(result,"\n")
- -- alternatively we could use lxml.convert
- session.xml = xml.convert(result, {
- resolve_entities = true,
- resolve_predefined_entities = true, -- in case we have escaped entities
- -- unify_predefined_entities = true, -- &#038; -> &amp;
- utfize_entities = true,
- } )
- session.data = nil
- session.shortcuts = nil
- statistics.stoptiming(bibtex)
-end
-
-statistics.register("bibtex load time", function()
- local nofbytes = bibtexstats.nofbytes
- if nofbytes > 0 then
- return format("%s seconds (%s bytes, %s definitions, %s shortcuts)",
- statistics.elapsedtime(bibtex),nofbytes,bibtexstats.nofdefinitions,bibtexstats.nofshortcuts)
- else
- return nil
- end
-end)
-
---~ str = [[
---~ @COMMENT { CRAP }
---~ @STRING{ hans = "h a n s" }
---~ @STRING{ taco = "t a c o" }
---~ @SOMETHING{ key1, abc = "t a c o" , def = "h a n s" }
---~ @SOMETHING{ key2, abc = hans # taco }
---~ @SOMETHING{ key3, abc = "hans" # taco }
---~ @SOMETHING{ key4, abc = hans # "taco" }
---~ @SOMETHING{ key5, abc = hans # taco # "hans" # "taco"}
---~ @SOMETHING{ key6, abc = {oeps {oeps} oeps} }
---~ ]]
-
---~ local session = bibtex.new()
---~ bibtex.convert(session,str)
---~ bibtex.toxml(session)
---~ print(session.nofbytes,statistics.elapsedtime(bibtex))
-
---~ local session = bibtex.new()
---~ bibtex.load(session,"IEEEabrv.bib")
---~ bibtex.load(session,"IEEEfull.bib")
---~ bibtex.load(session,"IEEEexample.bib")
---~ bibtex.toxml(session)
---~ print(session.nofbytes,statistics.elapsedtime(bibtex))
-
---~ local session = bibtex.new()
---~ bibtex.load(session,"gut.bib")
---~ bibtex.load(session,"komoedie.bib")
---~ bibtex.load(session,"texbook1.bib")
---~ bibtex.load(session,"texbook2.bib")
---~ bibtex.load(session,"texbook3.bib")
---~ bibtex.load(session,"texgraph.bib")
---~ bibtex.load(session,"texjourn.bib")
---~ bibtex.load(session,"texnique.bib")
---~ bibtex.load(session,"tugboat.bib")
---~ bibtex.toxml(session)
---~ print(session.nofbytes,statistics.elapsedtime(bibtex))
-
---~ print(table.serialize(session.data))
---~ print(table.serialize(session.shortcuts))
---~ print(xml.serialize(session.xml))
-
-if not characters then dofile(resolvers.findfile("char-def.lua")) end
-
-local chardata = characters.data
-local concat = table.concat
-
-local lpeg = lpeg
-
-local P, Ct, lpegmatch, lpegpatterns = lpeg.P, lpeg.Ct, lpeg.match, lpeg.patterns
-
-local space, comma = P(" "), P(",")
-
-local andsplitter = lpeg.tsplitat(space^1 * "and" * space^1)
-local commasplitter = lpeg.tsplitat(space^0 * comma * space^0)
-local spacesplitter = lpeg.tsplitat(space^1)
-local firstcharacter = lpegpatterns.utf8byte
-
-local function is_upper(str)
- local first = lpegmatch(firstcharacter,str)
- local okay = chardata[first]
- return okay and okay.category == "lu"
-end
-
-local function splitauthors(str)
- local authors = lpegmatch(andsplitter,str)
- for i=1,#authors do
- local firstnames, vons, surnames, initials, juniors, words
- local author = authors[i]
- local split = lpegmatch(commasplitter,author)
- local n = #split
- if n == 1 then
- --~ First von Last
- words = lpegmatch(spacesplitter,author)
- firstnames, vons, surnames = { }, { }, { }
- local i, n = 1, #words
- while i <= n do
- local w = words[i]
- if is_upper(w) then
- firstnames[#firstnames+1], i = w, i + 1
- else
- break
- end
- end
- while i <= n do
- local w = words[i]
- if is_upper(w) then
- break
- else
- vons[#vons+1], i = w, i + 1
- end
- end
- while i <= n do
- surnames[#surnames+1], i = words[i], i + 1
- end
- elseif n == 2 then
- --~ von Last, First
- words = lpegmatch(spacesplitter,split[2])
- surnames = lpegmatch(spacesplitter,split[1])
- firstnames, vons = { }, { }
- local i, n = 1, #words
- while i <= n do
- local w = words[i]
- if is_upper(w) then
- firstnames[#firstnames+1], i = w, i + 1
- else
- break
- end
- end
- while i <= n do
- vons[#vons+1], i = words[i], i + 1
- end
- else
- --~ von Last, Jr ,First
- firstnames = lpegmatch(spacesplitter,split[1])
- juniors = lpegmatch(spacesplitter,split[2])
- surnames = lpegmatch(spacesplitter,split[3])
- if n > 3 then
- -- error
- end
- end
- if #surnames == 0 then
- surnames[1] = firstnames[#firstnames]
- firstnames[#firstnames] = nil
- end
- if firstnames then
- initials = { }
- for i=1,#firstnames do
- initials[i] = utfchar(lpegmatch(firstcharacter,firstnames[i]))
- end
- end
- authors[i] = {
- original = author,
- firstnames = firstnames,
- vons = vons,
- surnames = surnames,
- initials = initials,
- juniors = juniors,
- }
- end
- authors.original = str
- return authors
-end
-
-local function the_initials(initials,symbol)
- local t, symbol = { }, symbol or "."
- for i=1,#initials do
- t[i] = initials[i] .. symbol
- end
- return t
-end
-
--- authors
-
-bibtex.authors = bibtex.authors or { }
-
-local authors = bibtex.authors
-
-local defaultsettings = {
- firstnamesep = " ",
- vonsep = " ",
- surnamesep = " ",
- juniorsep = " ",
- surnamejuniorsep = ", ",
- juniorjuniorsep = ", ",
- surnamefirstnamesep = ", ",
- surnameinitialsep = ", ",
- namesep = ", ",
- lastnamesep = " and ",
- finalnamesep = " and ",
-}
-
-function authors.normal(author,settings)
- local firstnames, vons, surnames, juniors = author.firstnames, author.vons, author.surnames, author.juniors
- local result, settings = { }, settings or defaultsettings
- if firstnames and #firstnames > 0 then
- result[#result+1] = concat(firstnames," ")
- result[#result+1] = settings.firstnamesep or defaultsettings.firstnamesep
- end
- if vons and #vons > 0 then
- result[#result+1] = concat(vons," ")
- result[#result+1] = settings.vonsep or defaultsettings.vonsep
- end
- if surnames then
- result[#result+1] = concat(surnames," ")
- end
- if juniors and #juniors > 0 then
- result[#result+1] = concat(juniors," ")
- result[#result+1] = settings.surnamesep or defaultsettings.surnamesep
- end
- return concat(result)
-end
-
-function authors.normalshort(author,settings)
- local firstnames, vons, surnames, juniors = author.firstnames, author.vons, author.surnames, author.juniors
- local result, settings = { }, settings or defaultsettings
- if firstnames and #firstnames > 0 then
- result[#result+1] = concat(firstnames," ")
- result[#result+1] = settings.firstnamesep or defaultsettings.firstnamesep
- end
- if vons and #vons > 0 then
- result[#result+1] = concat(vons," ")
- result[#result+1] = settings.vonsep or defaultsettings.vonsep
- end
- if surnames then
- result[#result+1] = concat(surnames," ")
- end
- if juniors and #juniors > 0 then
- result[#result+1] = concat(juniors," ")
- result[#result+1] = settings.surnamejuniorsep or defaultsettings.surnamejuniorsep
- end
- return concat(result)
-end
-
-function authors.inverted(author,settings)
- local firstnames, vons, surnames, juniors = author.firstnames, author.vons, author.surnames, author.juniors
- local result, settings = { }, settings or defaultsettings
- if vons and #vons > 0 then
- result[#result+1] = concat(vons," ")
- result[#result+1] = settings.vonsep or defaultsettings.vonsep
- end
- if surnames then
- result[#result+1] = concat(surnames," ")
- end
- if juniors and #juniors > 0 then
- result[#result+1] = settings.juniorjuniorsep or defaultsettings.juniorjuniorsep
- result[#result+1] = concat(juniors," ")
- end
- if firstnames and #firstnames > 0 then
- result[#result+1] = settings.surnamefirstnamesep or defaultsettings.surnamefirstnamesep
- result[#result+1] = concat(firstnames," ")
- end
- return concat(result)
-end
-
-function authors.invertedshort(author,settings)
- local vons, surnames, initials, juniors = author.vons, author.surnames, author.initials, author.juniors
- local result, settings = { }, settings or defaultsettings
- if vons and #vons > 0 then
- result[#result+1] = concat(vons," ")
- result[#result+1] = settings.vonsep or defaultsettings.vonsep
- end
- if surnames then
- result[#result+1] = concat(surnames," ")
- end
- if juniors and #juniors > 0 then
- result[#result+1] = settings.juniorjuniorsep or defaultsettings.juniorjuniorsep
- result[#result+1] = concat(juniors," ")
- end
- if initials and #initials > 0 then
- result[#result+1] = settings.surnameinitialsep or defaultsettings.surnameinitialsep
- result[#result+1] = concat(the_initials(initials)," ")
- end
- return concat(result)
-end
-
-local lastconcatsize = 1
-
-local function bibtexconcat(t,settings)
- local namesep = settings.namesep or defaultsettings.namesep or ", "
- local lastnamesep = settings.lastnamesep or defaultsettings.lastnamesep or namesep
- local finalnamesep = settings.finalnamesep or defaultsettings.finalnamesep or lastnamesep
- local lastconcatsize = #t
- if lastconcatsize > 2 then
- local s = { }
- for i=1,lastconcatsize-2 do
- s[i] = t[i] .. namesep
- end
- s[lastconcatsize-1], s[lastconcatsize] = t[lastconcatsize-1] .. finalnamesep, t[lastconcatsize]
- return concat(s)
- elseif lastconcatsize > 1 then
- return concat(t,lastnamesep)
- elseif lastconcatsize > 0 then
- return t[1]
- else
- return ""
- end
-end
-
-function authors.concat(author,combiner,what,settings)
- if type(combiner) == "string" then
- combiner = authors[combiner or "normal"] or authors.normal
- end
- local split = splitauthors(author)
- local setting = settings[what]
- local etallimit, etaldisplay, etaltext = 1000, 1000, ""
- if setting then
- etallimit = settings.etallimit or 1000
- etaldisplay = settings.etaldisplay or etallimit
- etalltext = settings.etaltext or ""
- end
- local max = #split
- if max > etallimit and etaldisplay < max then
- max = etaldisplay
- end
- for i=1,max do
- split[i] = combiner(split[i],settings)
- end
- local result = bibtexconcat(split,settings)
- if max < #split then
- return result
- else
- return result .. etaltext
- end
-end
-
-function authors.short(author,year)
- local result = { }
- if author then
- local authors = splitauthors(author)
- for a=1,#authors do
- local aa = authors[a]
- local initials = aa.initials
- for i=1,#initials do
- result[#result+1] = initials[i]
- end
- local surnames = aa.surnames
- for s=1,#surnames do
- result[#result+1] = utfchar(lpegmatch(firstcharacter,surnames[s]))
- end
- end
- end
- if year then
- result[#result+1] = year
- end
- return concat(result)
-end
-
--- We can consider creating a hashtable key -> entry but I wonder if
--- pays off.
-
-local function collectauthoryears(id,list)
- list = settings_to_hash(list)
- id = getid(id)
- local found = { }
- for e in xml.collected(id,"/bibtex/entry") do
- if list[e.at.tag] then
- local year = xmlfilter(e,"xml:///field[@name='year']/text()")
- local author = xmlfilter(e,"xml:///field[@name='author']/text()")
- if author and year then
- local a = found[author]
- if not a then
- a = { }
- found[author] = a
- end
- local y = a[year]
- if not y then
- y = { }
- a[year] = y
- end
- y[#y+1] = e
- end
- end
- end
- -- found = { author = { year_1 = { e1, e2, e3 } } }
- local done = { }
- for author, years in next, found do
- local yrs = { }
- for year, entries in next, years do
- if subyears then
- -- -- add letters to all entries of an author and if so shouldn't
- -- -- we tag all years of an author as soon as we do this?
- -- if #entries > 1 then
- -- for i=1,#years do
- -- local entry = years[i]
- -- -- years[i] = year .. string.char(i + string.byte("0") - 1)
- -- end
- -- end
- else
- yrs[#yrs+1] = year
- end
- end
- done[author] = yrs
- end
- return done
-end
-
-local method, settings = "normal", { }
-
-function authors.setsettings(s)
- settings = s or settings
-end
-
-if commands then
-
- local sessions = { }
-
- function commands.definebibtexsession(name)
- sessions[name] = bibtex.new()
- end
-
- function commands.preparebibtexsession(name,xmlname,options)
- bibtex.toxml(sessions[name],options)
- lxml.register(xmlname,sessions[name].xml)
- end
-
- function commands.registerbibtexfile(name,filename)
- bibtex.load(sessions[name],filename)
- end
-
- function commands.registerbibtexentry(name,entry)
- local session = sessions[name]
- local entries = session.entries
- if not entries then
- session.entries = { [entry] = true } -- here we can keep more info
- else
- entries[entry] = true
- end
- end
-
- -- commands.bibtexconcat = bibtexconcat
-
- -- finalizers can be rather dumb as we have just text and no embedded xml
-
- function finalizers.bibtexconcat(collected,method,what)
- if collected then
- local author = collected[1].dt[1] or ""
- if author ~= "" then
- context(authors.concat(author,method,what,settings))
- end
- end
- end
-
- function finalizers.bibtexshort(collected)
- if collected then
- local c = collected[1]
- local year = xmlfilter(c,"xml://field[@name='year']/text()")
- local author = xmlfilter(c,"xml://field[@name='author']/text()")
- context(authors.short(author,year))
- end
- end
-
- -- experiment:
-
- --~ -- alternative approach: keep data at the tex end
-
- --~ local function xbibtexconcat(t,sep,finalsep,lastsep)
- --~ local n = #t
- --~ if n > 0 then
- --~ context(t[1])
- --~ if n > 1 then
- --~ if n > 2 then
- --~ for i=2,n-1 do
- --~ context.bibtexpublicationsparameter("sep")
- --~ context(t[i])
- --~ end
- --~ context.bibtexpublicationsparameter("finalsep")
- --~ else
- --~ context.bibtexpublicationsparameter("lastsep")
- --~ end
- --~ context(t[n])
- --~ end
- --~ end
- --~ end
-
- -- todo : sort
-
- -- todo: choose between bibtex or commands namespace
-
- function bibtex.authorref(id,list)
- local result = collectauthoryears(id,list,method,what)
- for author, years in next, result do
- context(authors.concat(author,method,what,settings))
- end
- end
-
- function bibtex.authoryearref(id,list)
- local result = collectauthoryears(id,list,method,what)
- for author, years in next, result do
- context("%s (%s)",authors.concat(author,method,what,settings),concat(years,", "))
- end
- end
-
- function bibtex.authoryearsref(id,list)
- local result = collectauthoryears(id,list,method,what)
- for author, years in next, result do
- context("(%s, %s)",authors.concat(author,method,what,settings),concat(years,", "))
- end
- end
-
- function bibtex.singularorplural(singular,plural)
- if lastconcatsize and lastconcatsize > 1 then
- context(plural)
- else
- context(singular)
- end
- end
-
-end
-
-
---~ local function test(sample)
---~ local authors = splitauthors(sample)
---~ print(table.serialize(authors))
---~ for i=1,#authors do
---~ local author = authors[i]
---~ print(normalauthor (author,settings))
---~ print(normalshortauthor (author,settings))
---~ print(invertedauthor (author,settings))
---~ print(invertedshortauthor(author,settings))
---~ end
---~ print(concatauthors(sample,settings,normalauthor))
---~ print(concatauthors(sample,settings,normalshortauthor))
---~ print(concatauthors(sample,settings,invertedauthor))
---~ print(concatauthors(sample,settings,invertedshortauthor))
---~ end
-
---~ local sample_a = "Hagen, Hans and Hoekwater, Taco Whoever T. Ex. and Henkel Hut, Hartmut Harald von der"
---~ local sample_b = "Hans Hagen and Taco Whoever T. Ex. Hoekwater and Hartmut Harald von der Henkel Hut"
-
---~ test(sample_a)
---~ test(sample_b)
+if not modules then modules = { } end modules ['bibl-bib'] = {
+ version = 1.001,
+ comment = "this module is the basis for the lxml-* ones",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[ldx--
+<p>This is a prelude to integrated bibliography support. This file just loads
+bibtex files and converts them to xml so that the we access the content
+in a convenient way. Actually handling the data takes place elsewhere.</p>
+--ldx]]--
+
+local lower, format, gsub, concat = string.lower, string.format, string.gsub, table.concat
+local next = next
+local utfchar = utf.char
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+local textoutf = characters and characters.tex.toutf
+local variables = interfaces and interfaces.variables
+local settings_to_hash = utilities.parsers.settings_to_hash
+local finalizers = xml.finalizers.tex
+local xmlfilter, xmltext, getid = xml.filter, xml.text, lxml.getid
+local formatters = string.formatters
+
+local P, R, S, C, Cc, Cs, Ct = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct
+
+local trace_bibxml = false trackers.register("publications.bibxml", function(v) trace_bibtex = v end)
+
+local report_xml = logs.reporter("publications","xml")
+
+bibtex = bibtex or { }
+local bibtex = bibtex
+
+bibtex.statistics = bibtex.statistics or { }
+local bibtexstats = bibtex.statistics
+
+bibtexstats.nofbytes = 0
+bibtexstats.nofdefinitions = 0
+bibtexstats.nofshortcuts = 0
+
+local defaultshortcuts = {
+ jan = "1",
+ feb = "2",
+ mar = "3",
+ apr = "4",
+ may = "5",
+ jun = "6",
+ jul = "7",
+ aug = "8",
+ sep = "9",
+ oct = "10",
+ nov = "11",
+ dec = "12",
+}
+
+local shortcuts = { }
+local data = { }
+local entries
+
+-- Currently we expand shortcuts and for large ones (like the acknowledgements
+-- in tugboat.bib this is not that efficient. However, eventually strings get
+-- hashed again.
+
+local function do_shortcut(tag,key,value)
+ bibtexstats.nofshortcuts = bibtexstats.nofshortcuts + 1
+ if lower(tag) == "@string" then
+ shortcuts[key] = value
+ end
+end
+
+local function do_definition(tag,key,tab) -- maybe check entries here (saves memory)
+ if not entries or entries[key] then
+ bibtexstats.nofdefinitions = bibtexstats.nofdefinitions + 1
+ local t = { }
+ for i=1,#tab,2 do
+ t[tab[i]] = tab[i+1]
+ end
+ local p = data[tag]
+ if not p then
+ data[tag] = { [key] = t }
+ else
+ p[key] = t
+ end
+ end
+end
+
+local function resolve(s)
+ return shortcuts[s] or defaultshortcuts[s] or s -- can be number
+end
+
+local percent = P("%")
+local start = P("@")
+local comma = P(",")
+local hash = P("#")
+local escape = P("\\")
+local single = P("'")
+local double = P('"')
+local left = P('{')
+local right = P('}')
+local both = left + right
+local lineending = S("\n\r")
+local space = S(" \t\n\r\f")
+local spacing = space^0
+local equal = P("=")
+local collapsed = (space^1)/ " "
+
+local function add(a,b) if b then return a..b else return a end end
+
+local keyword = C((R("az","AZ","09") + S("@_:-"))^1) -- C((1-space)^1)
+local s_quoted = ((escape*single) + collapsed + (1-single))^0
+local d_quoted = ((escape*double) + collapsed + (1-double))^0
+local balanced = lpegpatterns.balanced
+
+local s_value = (single/"") * s_quoted * (single/"")
+local d_value = (double/"") * d_quoted * (double/"")
+local b_value = (left /"") * balanced * (right /"")
+local r_value = keyword/resolve
+
+local somevalue = s_value + d_value + b_value + r_value
+local value = Cs((somevalue * ((spacing * hash * spacing)/"" * somevalue)^0))
+
+local assignment = spacing * keyword * spacing * equal * spacing * value * spacing
+local shortcut = keyword * spacing * left * spacing * (assignment * comma^0)^0 * spacing * right
+local definition = keyword * spacing * left * spacing * keyword * comma * Ct((assignment * comma^0)^0) * spacing * right
+local comment = keyword * spacing * left * (1-right)^0 * spacing * right
+local forget = percent^1 * (1-lineending)^0
+
+-- todo \%
+
+local grammar = (space + forget + shortcut/do_shortcut + definition/do_definition + comment + 1)^0
+
+function bibtex.convert(session,content)
+ statistics.starttiming(bibtex)
+ data, shortcuts, entries = session.data, session.shortcuts, session.entries
+ bibtexstats.nofbytes = bibtexstats.nofbytes + #content
+ session.nofbytes = session.nofbytes + #content
+ lpegmatch(grammar,content or "")
+ statistics.stoptiming(bibtex)
+end
+
+function bibtex.load(session,filename)
+ local filename = resolvers.findfile(filename,"bib")
+ if filename ~= "" then
+ local data = io.loaddata(filename) or ""
+ if data == "" then
+ report_xml("empty file %a, no conversion to xml",filename)
+ elseif trace_bibxml then
+ report_xml("converting file %a to xml",filename)
+ end
+ bibtex.convert(session,data)
+ end
+end
+
+function bibtex.new()
+ return {
+ data = { },
+ shortcuts = { },
+ xml = xml.convert("<?xml version='1.0' standalone='yes'?>\n<bibtex></bibtex>"),
+ nofbytes = 0,
+ entries = nil,
+ loaded = false,
+ }
+end
+
+local p_escaped = lpegpatterns.xml.escaped
+
+local ihatethis = {
+ f = "\\f",
+ n = "\\n",
+ r = "\\r",
+ s = "\\s",
+ t = "\\t",
+ v = "\\v",
+ z = "\\z",
+}
+
+local command = P("\\")/"" * Cc("\\bibtexcommand{") * (R("az","AZ")^1) * Cc("}")
+local any = P(1)
+local done = P(-1)
+local one_l = P("{") / ""
+local one_r = P("}") / ""
+local two_l = P("{{") / ""
+local two_r = P("}}") / ""
+
+local filter = Cs(
+ two_l * (command + any - two_r - done)^0 * two_r * done +
+ one_l * (command + any - one_r - done)^0 * one_r * done +
+ (command + any )^0
+)
+
+function bibtex.toxml(session,options)
+ if session.loaded then
+ return
+ else
+ session.loaded = true
+ end
+ -- we can always speed this up if needed
+ -- format slows down things a bit but who cares
+ statistics.starttiming(bibtex)
+ local result, r = { }, 0
+ local options = settings_to_hash(options)
+ local convert = options.convert -- todo: interface
+ local strip = options.strip -- todo: interface
+ local entries = session.entries
+ r = r + 1 ; result[r] = "<?xml version='1.0' standalone='yes'?>"
+ r = r + 1 ; result[r] = "<bibtex>"
+ for id, categories in next, session.data do
+ id = lower(gsub(id,"^@",""))
+ for name, entry in next, categories do
+ if not entries or entries[name] then
+ r = r + 1 ; result[r] = formatters["<entry tag='%s' category='%s'>"](lower(name),id)
+ for key, value in next, entry do
+ value = gsub(value,"\\(.)",ihatethis) -- this really needs checking
+ value = lpegmatch(p_escaped,value)
+ if value ~= "" then
+ if convert then
+ value = textoutf(value,true)
+ end
+ if strip then
+ -- as there is no proper namespace in bibtex we need this
+ -- kind of hackery ... bibtex databases are quite unportable
+ value = lpegmatch(filter,value) or value
+ end
+ r = r + 1 ; result[r] = formatters[" <field name='%s'>%s</field>"](key,value)
+ end
+ end
+ r = r + 1 ; result[r] = "</entry>"
+ end
+ end
+ end
+ r = r + 1 ; result[r] = "</bibtex>"
+ result = concat(result,"\n")
+ -- alternatively we could use lxml.convert
+ session.xml = xml.convert(result, {
+ resolve_entities = true,
+ resolve_predefined_entities = true, -- in case we have escaped entities
+ -- unify_predefined_entities = true, -- &#038; -> &amp;
+ utfize_entities = true,
+ } )
+ session.data = nil
+ session.shortcuts = nil
+ statistics.stoptiming(bibtex)
+end
+
+statistics.register("bibtex load time", function()
+ local nofbytes = bibtexstats.nofbytes
+ if nofbytes > 0 then
+ return format("%s seconds (%s bytes, %s definitions, %s shortcuts)",
+ statistics.elapsedtime(bibtex),nofbytes,bibtexstats.nofdefinitions,bibtexstats.nofshortcuts)
+ else
+ return nil
+ end
+end)
+
+--~ str = [[
+--~ @COMMENT { CRAP }
+--~ @STRING{ hans = "h a n s" }
+--~ @STRING{ taco = "t a c o" }
+--~ @SOMETHING{ key1, abc = "t a c o" , def = "h a n s" }
+--~ @SOMETHING{ key2, abc = hans # taco }
+--~ @SOMETHING{ key3, abc = "hans" # taco }
+--~ @SOMETHING{ key4, abc = hans # "taco" }
+--~ @SOMETHING{ key5, abc = hans # taco # "hans" # "taco"}
+--~ @SOMETHING{ key6, abc = {oeps {oeps} oeps} }
+--~ ]]
+
+--~ local session = bibtex.new()
+--~ bibtex.convert(session,str)
+--~ bibtex.toxml(session)
+--~ print(session.nofbytes,statistics.elapsedtime(bibtex))
+
+--~ local session = bibtex.new()
+--~ bibtex.load(session,"IEEEabrv.bib")
+--~ bibtex.load(session,"IEEEfull.bib")
+--~ bibtex.load(session,"IEEEexample.bib")
+--~ bibtex.toxml(session)
+--~ print(session.nofbytes,statistics.elapsedtime(bibtex))
+
+--~ local session = bibtex.new()
+--~ bibtex.load(session,"gut.bib")
+--~ bibtex.load(session,"komoedie.bib")
+--~ bibtex.load(session,"texbook1.bib")
+--~ bibtex.load(session,"texbook2.bib")
+--~ bibtex.load(session,"texbook3.bib")
+--~ bibtex.load(session,"texgraph.bib")
+--~ bibtex.load(session,"texjourn.bib")
+--~ bibtex.load(session,"texnique.bib")
+--~ bibtex.load(session,"tugboat.bib")
+--~ bibtex.toxml(session)
+--~ print(session.nofbytes,statistics.elapsedtime(bibtex))
+
+--~ print(table.serialize(session.data))
+--~ print(table.serialize(session.shortcuts))
+--~ print(xml.serialize(session.xml))
+
+if not characters then dofile(resolvers.findfile("char-def.lua")) end
+
+local chardata = characters.data
+local concat = table.concat
+
+local lpeg = lpeg
+
+local P, Ct, lpegmatch, lpegpatterns = lpeg.P, lpeg.Ct, lpeg.match, lpeg.patterns
+
+local space, comma = P(" "), P(",")
+
+local andsplitter = lpeg.tsplitat(space^1 * "and" * space^1)
+local commasplitter = lpeg.tsplitat(space^0 * comma * space^0)
+local spacesplitter = lpeg.tsplitat(space^1)
+local firstcharacter = lpegpatterns.utf8byte
+
+local function is_upper(str)
+ local first = lpegmatch(firstcharacter,str)
+ local okay = chardata[first]
+ return okay and okay.category == "lu"
+end
+
+local function splitauthors(str)
+ local authors = lpegmatch(andsplitter,str)
+ for i=1,#authors do
+ local firstnames, vons, surnames, initials, juniors, words
+ local author = authors[i]
+ local split = lpegmatch(commasplitter,author)
+ local n = #split
+ if n == 1 then
+ --~ First von Last
+ words = lpegmatch(spacesplitter,author)
+ firstnames, vons, surnames = { }, { }, { }
+ local i, n = 1, #words
+ while i <= n do
+ local w = words[i]
+ if is_upper(w) then
+ firstnames[#firstnames+1], i = w, i + 1
+ else
+ break
+ end
+ end
+ while i <= n do
+ local w = words[i]
+ if is_upper(w) then
+ break
+ else
+ vons[#vons+1], i = w, i + 1
+ end
+ end
+ while i <= n do
+ surnames[#surnames+1], i = words[i], i + 1
+ end
+ elseif n == 2 then
+ --~ von Last, First
+ words = lpegmatch(spacesplitter,split[2])
+ surnames = lpegmatch(spacesplitter,split[1])
+ firstnames, vons = { }, { }
+ local i, n = 1, #words
+ while i <= n do
+ local w = words[i]
+ if is_upper(w) then
+ firstnames[#firstnames+1], i = w, i + 1
+ else
+ break
+ end
+ end
+ while i <= n do
+ vons[#vons+1], i = words[i], i + 1
+ end
+ else
+ --~ von Last, Jr ,First
+ firstnames = lpegmatch(spacesplitter,split[1])
+ juniors = lpegmatch(spacesplitter,split[2])
+ surnames = lpegmatch(spacesplitter,split[3])
+ if n > 3 then
+ -- error
+ end
+ end
+ if #surnames == 0 then
+ surnames[1] = firstnames[#firstnames]
+ firstnames[#firstnames] = nil
+ end
+ if firstnames then
+ initials = { }
+ for i=1,#firstnames do
+ initials[i] = utfchar(lpegmatch(firstcharacter,firstnames[i]))
+ end
+ end
+ authors[i] = {
+ original = author,
+ firstnames = firstnames,
+ vons = vons,
+ surnames = surnames,
+ initials = initials,
+ juniors = juniors,
+ }
+ end
+ authors.original = str
+ return authors
+end
+
+local function the_initials(initials,symbol)
+ local t, symbol = { }, symbol or "."
+ for i=1,#initials do
+ t[i] = initials[i] .. symbol
+ end
+ return t
+end
+
+-- authors
+
+bibtex.authors = bibtex.authors or { }
+
+local authors = bibtex.authors
+
+local defaultsettings = {
+ firstnamesep = " ",
+ vonsep = " ",
+ surnamesep = " ",
+ juniorsep = " ",
+ surnamejuniorsep = ", ",
+ juniorjuniorsep = ", ",
+ surnamefirstnamesep = ", ",
+ surnameinitialsep = ", ",
+ namesep = ", ",
+ lastnamesep = " and ",
+ finalnamesep = " and ",
+}
+
+function authors.normal(author,settings)
+ local firstnames, vons, surnames, juniors = author.firstnames, author.vons, author.surnames, author.juniors
+ local result, settings = { }, settings or defaultsettings
+ if firstnames and #firstnames > 0 then
+ result[#result+1] = concat(firstnames," ")
+ result[#result+1] = settings.firstnamesep or defaultsettings.firstnamesep
+ end
+ if vons and #vons > 0 then
+ result[#result+1] = concat(vons," ")
+ result[#result+1] = settings.vonsep or defaultsettings.vonsep
+ end
+ if surnames then
+ result[#result+1] = concat(surnames," ")
+ end
+ if juniors and #juniors > 0 then
+ result[#result+1] = concat(juniors," ")
+ result[#result+1] = settings.surnamesep or defaultsettings.surnamesep
+ end
+ return concat(result)
+end
+
+function authors.normalshort(author,settings)
+ local firstnames, vons, surnames, juniors = author.firstnames, author.vons, author.surnames, author.juniors
+ local result, settings = { }, settings or defaultsettings
+ if firstnames and #firstnames > 0 then
+ result[#result+1] = concat(firstnames," ")
+ result[#result+1] = settings.firstnamesep or defaultsettings.firstnamesep
+ end
+ if vons and #vons > 0 then
+ result[#result+1] = concat(vons," ")
+ result[#result+1] = settings.vonsep or defaultsettings.vonsep
+ end
+ if surnames then
+ result[#result+1] = concat(surnames," ")
+ end
+ if juniors and #juniors > 0 then
+ result[#result+1] = concat(juniors," ")
+ result[#result+1] = settings.surnamejuniorsep or defaultsettings.surnamejuniorsep
+ end
+ return concat(result)
+end
+
+function authors.inverted(author,settings)
+ local firstnames, vons, surnames, juniors = author.firstnames, author.vons, author.surnames, author.juniors
+ local result, settings = { }, settings or defaultsettings
+ if vons and #vons > 0 then
+ result[#result+1] = concat(vons," ")
+ result[#result+1] = settings.vonsep or defaultsettings.vonsep
+ end
+ if surnames then
+ result[#result+1] = concat(surnames," ")
+ end
+ if juniors and #juniors > 0 then
+ result[#result+1] = settings.juniorjuniorsep or defaultsettings.juniorjuniorsep
+ result[#result+1] = concat(juniors," ")
+ end
+ if firstnames and #firstnames > 0 then
+ result[#result+1] = settings.surnamefirstnamesep or defaultsettings.surnamefirstnamesep
+ result[#result+1] = concat(firstnames," ")
+ end
+ return concat(result)
+end
+
+function authors.invertedshort(author,settings)
+ local vons, surnames, initials, juniors = author.vons, author.surnames, author.initials, author.juniors
+ local result, settings = { }, settings or defaultsettings
+ if vons and #vons > 0 then
+ result[#result+1] = concat(vons," ")
+ result[#result+1] = settings.vonsep or defaultsettings.vonsep
+ end
+ if surnames then
+ result[#result+1] = concat(surnames," ")
+ end
+ if juniors and #juniors > 0 then
+ result[#result+1] = settings.juniorjuniorsep or defaultsettings.juniorjuniorsep
+ result[#result+1] = concat(juniors," ")
+ end
+ if initials and #initials > 0 then
+ result[#result+1] = settings.surnameinitialsep or defaultsettings.surnameinitialsep
+ result[#result+1] = concat(the_initials(initials)," ")
+ end
+ return concat(result)
+end
+
+local lastconcatsize = 1
+
+local function bibtexconcat(t,settings)
+ local namesep = settings.namesep or defaultsettings.namesep or ", "
+ local lastnamesep = settings.lastnamesep or defaultsettings.lastnamesep or namesep
+ local finalnamesep = settings.finalnamesep or defaultsettings.finalnamesep or lastnamesep
+ local lastconcatsize = #t
+ if lastconcatsize > 2 then
+ local s = { }
+ for i=1,lastconcatsize-2 do
+ s[i] = t[i] .. namesep
+ end
+ s[lastconcatsize-1], s[lastconcatsize] = t[lastconcatsize-1] .. finalnamesep, t[lastconcatsize]
+ return concat(s)
+ elseif lastconcatsize > 1 then
+ return concat(t,lastnamesep)
+ elseif lastconcatsize > 0 then
+ return t[1]
+ else
+ return ""
+ end
+end
+
+function authors.concat(author,combiner,what,settings)
+ if type(combiner) == "string" then
+ combiner = authors[combiner or "normal"] or authors.normal
+ end
+ local split = splitauthors(author)
+ local setting = settings[what]
+ local etallimit, etaldisplay, etaltext = 1000, 1000, ""
+ if setting then
+ etallimit = settings.etallimit or 1000
+ etaldisplay = settings.etaldisplay or etallimit
+ etalltext = settings.etaltext or ""
+ end
+ local max = #split
+ if max > etallimit and etaldisplay < max then
+ max = etaldisplay
+ end
+ for i=1,max do
+ split[i] = combiner(split[i],settings)
+ end
+ local result = bibtexconcat(split,settings)
+ if max < #split then
+ return result
+ else
+ return result .. etaltext
+ end
+end
+
+function authors.short(author,year)
+ local result = { }
+ if author then
+ local authors = splitauthors(author)
+ for a=1,#authors do
+ local aa = authors[a]
+ local initials = aa.initials
+ for i=1,#initials do
+ result[#result+1] = initials[i]
+ end
+ local surnames = aa.surnames
+ for s=1,#surnames do
+ result[#result+1] = utfchar(lpegmatch(firstcharacter,surnames[s]))
+ end
+ end
+ end
+ if year then
+ result[#result+1] = year
+ end
+ return concat(result)
+end
+
+-- We can consider creating a hashtable key -> entry but I wonder if
+-- pays off.
+
+local function collectauthoryears(id,list)
+ list = settings_to_hash(list)
+ id = getid(id)
+ local found = { }
+ for e in xml.collected(id,"/bibtex/entry") do
+ if list[e.at.tag] then
+ local year = xmlfilter(e,"xml:///field[@name='year']/text()")
+ local author = xmlfilter(e,"xml:///field[@name='author']/text()")
+ if author and year then
+ local a = found[author]
+ if not a then
+ a = { }
+ found[author] = a
+ end
+ local y = a[year]
+ if not y then
+ y = { }
+ a[year] = y
+ end
+ y[#y+1] = e
+ end
+ end
+ end
+ -- found = { author = { year_1 = { e1, e2, e3 } } }
+ local done = { }
+ for author, years in next, found do
+ local yrs = { }
+ for year, entries in next, years do
+ if subyears then
+ -- -- add letters to all entries of an author and if so shouldn't
+ -- -- we tag all years of an author as soon as we do this?
+ -- if #entries > 1 then
+ -- for i=1,#years do
+ -- local entry = years[i]
+ -- -- years[i] = year .. string.char(i + string.byte("0") - 1)
+ -- end
+ -- end
+ else
+ yrs[#yrs+1] = year
+ end
+ end
+ done[author] = yrs
+ end
+ return done
+end
+
+local method, settings = "normal", { }
+
+function authors.setsettings(s)
+ settings = s or settings
+end
+
+if commands then
+
+ local sessions = { }
+
+ function commands.definebibtexsession(name)
+ sessions[name] = bibtex.new()
+ end
+
+ function commands.preparebibtexsession(name,xmlname,options)
+ bibtex.toxml(sessions[name],options)
+ lxml.register(xmlname,sessions[name].xml)
+ end
+
+ function commands.registerbibtexfile(name,filename)
+ bibtex.load(sessions[name],filename)
+ end
+
+ function commands.registerbibtexentry(name,entry)
+ local session = sessions[name]
+ local entries = session.entries
+ if not entries then
+ session.entries = { [entry] = true } -- here we can keep more info
+ else
+ entries[entry] = true
+ end
+ end
+
+ -- commands.bibtexconcat = bibtexconcat
+
+ -- finalizers can be rather dumb as we have just text and no embedded xml
+
+ function finalizers.bibtexconcat(collected,method,what)
+ if collected then
+ local author = collected[1].dt[1] or ""
+ if author ~= "" then
+ context(authors.concat(author,method,what,settings))
+ end
+ end
+ end
+
+ function finalizers.bibtexshort(collected)
+ if collected then
+ local c = collected[1]
+ local year = xmlfilter(c,"xml://field[@name='year']/text()")
+ local author = xmlfilter(c,"xml://field[@name='author']/text()")
+ context(authors.short(author,year))
+ end
+ end
+
+ -- experiment:
+
+ --~ -- alternative approach: keep data at the tex end
+
+ --~ local function xbibtexconcat(t,sep,finalsep,lastsep)
+ --~ local n = #t
+ --~ if n > 0 then
+ --~ context(t[1])
+ --~ if n > 1 then
+ --~ if n > 2 then
+ --~ for i=2,n-1 do
+ --~ context.bibtexpublicationsparameter("sep")
+ --~ context(t[i])
+ --~ end
+ --~ context.bibtexpublicationsparameter("finalsep")
+ --~ else
+ --~ context.bibtexpublicationsparameter("lastsep")
+ --~ end
+ --~ context(t[n])
+ --~ end
+ --~ end
+ --~ end
+
+ -- todo : sort
+
+ -- todo: choose between bibtex or commands namespace
+
+ function bibtex.authorref(id,list)
+ local result = collectauthoryears(id,list,method,what)
+ for author, years in next, result do
+ context(authors.concat(author,method,what,settings))
+ end
+ end
+
+ function bibtex.authoryearref(id,list)
+ local result = collectauthoryears(id,list,method,what)
+ for author, years in next, result do
+ context("%s (%s)",authors.concat(author,method,what,settings),concat(years,", "))
+ end
+ end
+
+ function bibtex.authoryearsref(id,list)
+ local result = collectauthoryears(id,list,method,what)
+ for author, years in next, result do
+ context("(%s, %s)",authors.concat(author,method,what,settings),concat(years,", "))
+ end
+ end
+
+ function bibtex.singularorplural(singular,plural)
+ if lastconcatsize and lastconcatsize > 1 then
+ context(plural)
+ else
+ context(singular)
+ end
+ end
+
+end
+
+
+--~ local function test(sample)
+--~ local authors = splitauthors(sample)
+--~ print(table.serialize(authors))
+--~ for i=1,#authors do
+--~ local author = authors[i]
+--~ print(normalauthor (author,settings))
+--~ print(normalshortauthor (author,settings))
+--~ print(invertedauthor (author,settings))
+--~ print(invertedshortauthor(author,settings))
+--~ end
+--~ print(concatauthors(sample,settings,normalauthor))
+--~ print(concatauthors(sample,settings,normalshortauthor))
+--~ print(concatauthors(sample,settings,invertedauthor))
+--~ print(concatauthors(sample,settings,invertedshortauthor))
+--~ end
+
+--~ local sample_a = "Hagen, Hans and Hoekwater, Taco Whoever T. Ex. and Henkel Hut, Hartmut Harald von der"
+--~ local sample_b = "Hans Hagen and Taco Whoever T. Ex. Hoekwater and Hartmut Harald von der Henkel Hut"
+
+--~ test(sample_a)
+--~ test(sample_b)
diff --git a/tex/context/base/bibl-tra.lua b/tex/context/base/bibl-tra.lua
index 7111108e8..6a7016023 100644
--- a/tex/context/base/bibl-tra.lua
+++ b/tex/context/base/bibl-tra.lua
@@ -1,247 +1,247 @@
-if not modules then modules = { } end modules ['bibl-tra'] = {
- version = 1.001,
- comment = "this module is the basis for the lxml-* ones",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-bibtex = bibtex or { }
-local bibtex = bibtex
-
-bibtex.hacks = bibtex.hacks or { }
-local hacks = bibtex.hacks
-
-local match, gmatch, format, concat, sort = string.match, string.gmatch, string.format, table.concat, table.sort
-local variables, constants = interfaces.variables, interfaces.constants
-
-local trace_bibtex = false trackers.register("publications.bibtex", function(v) trace_bibtex = v end)
-
-local report_tex = logs.reporter("publications","tex")
-
-local context, structures = context, structures
-
-local references = structures.references
-local sections = structures.sections
-
-local list, done, alldone, used, registered, ordered = { }, { }, { }, { }, { }, { }
-local mode = 0
-
-local template = utilities.strings.striplong([[
- \citation{*}
- \bibstyle{cont-%s}
- \bibdata{%s}
-]])
-
-local bibtexbin = environment.arguments.mlbibtex and "mlbibcontext" or "bibtex"
-
-directives.register("publications.usemlbibtex", function(v)
- bibtexbin = v and "mlbibcontext" or "bibtex"
-end)
-
-function hacks.process(settings)
- local style = settings.style or ""
- local database = settings.database or ""
- local jobname = tex.jobname
- if database ~= "" then
- interfaces.showmessage("publications",3)
- io.savedata(file.addsuffix(jobname,"aux"),format(template,style,database))
- if trace_bibtex then
- report_tex("processing bibtex file %a using %a",jobname,bibtexbin)
- end
- os.execute(format("%s %q",bibtexbin,jobname))
- -- purge 'm
- end
-end
-
-function hacks.register(str)
- if trace_bibtex then
- report_tex("registering bibtex entry %a",str)
- end
- registered[#registered+1] = str
- ordered[str] = #registered
-end
-
-function hacks.nofregistered()
- return #registered
-end
-
-function hacks.reset(m)
- mode, list, done = m, { }, { }
-end
-
-function hacks.add(str,listindex)
- if not str or mode == 0 then
- -- skip
- elseif mode == 1 then
- -- all locals but no duplicates
- local sc = sections.currentid()
- if done[str] ~= sc then
- done[str], alldone[str] = sc, true
- list[#list+1] = { str, listindex }
- end
- elseif mode == 2 then
- -- all locals but no preceding
- local sc = sections.currentid()
- if not alldone[str] and done[str] ~= sc then
- done[str], alldone[str] = sc, true
- list[#list+1] = { str, listindex }
- end
- end
-end
-
-local function compare(a,b) -- quite some checking for non-nil
- local aa, bb = a and a[1], b and b[1]
- if aa and bb then
- local oa, ob = ordered[aa], ordered[bb]
- return oa and ob and oa < ob
- end
- return false
-end
-
-function hacks.flush(sortvariant)
- if sortvariant == "" or sortvariant == variables.cite or sortvariant == "default" then
- -- order is cite order i.e. same as list
- else
- sort(list,compare)
- end
- for i=1,#list do
- context.doprocessbibtexentry(list[i][1])
- end
-end
-
-function hacks.filterall()
- for i=1,#registered do
- list[i] = { registered[i], i }
- end
-end
-
-function hacks.registerplaced(str)
- used[str] = true
-end
-
-function hacks.doifalreadyplaced(str)
- commands.doifelse(used[str])
-end
-
--- we ask for <n>:tag but when we can't find it we go back
--- to look for previous definitions, and when not found again
--- we look forward
-
-local function compare(a,b)
- local aa, bb = a and a[3], b and b[3]
- return aa and bb and aa < bb
-end
-
-function hacks.resolve(prefix,block,reference) -- maybe already feed it split
- -- needs checking (the prefix in relation to components)
- local subsets
- local collected = references.collected
- if prefix and prefix ~= "" then
- subsets = { collected[prefix] or collected[""] }
- else
- local components = references.productdata.components
- local subset = collected[""]
- if subset then
- subsets = { subset }
- else
- subsets = { }
- end
- for i=1,#components do
- local subset = collected[components[i]]
- if subset then
- subsets[#subsets+1] = subset
- end
- end
- end
- if #subsets > 0 then
- local result, nofresult, done = { }, 0, { }
- block = tonumber(block)
- for i=1,#subsets do
- local subset = subsets[i]
- for rest in gmatch(reference,"[^, ]+") do
- local blk, tag, found = block, nil, nil
- if block then
- tag = blk .. ":" .. rest
- found = subset[tag]
- if not found then
- for i=block-1,1,-1 do
- tag = i .. ":" .. rest
- found = subset[tag]
- if found then
- blk = i
- break
- end
- end
- end
- end
- if not found then
- blk = "*"
- tag = blk .. ":" .. rest
- found = subset[tag]
- end
- if found then
- local current = tonumber(found.entries and found.entries.text) -- tonumber needed
- if current and not done[current] then
- nofresult = nofresult + 1
- result[nofresult] = { blk, rest, current }
- done[current] = true
- end
- end
- end
- end
- -- todo: ranges so the interface will change
- sort(result,compare)
- local first, last, firsti, lasti, firstr, lastr
- local collected, nofcollected = { }, 0
- for i=1,nofresult do
- local r = result[i]
- local current = r[3]
- if not first then
- first, last, firsti, lasti, firstr, lastr = current, current, i, i, r, r
- elseif current == last + 1 then
- last, lasti, lastr = current, i, r
- else
- if last > first + 1 then
- nofcollected = nofcollected + 1
- collected[nofcollected] = { firstr[1], firstr[2], lastr[1], lastr[2] }
- else
- nofcollected = nofcollected + 1
- collected[nofcollected] = { firstr[1], firstr[2] }
- if last > first then
- nofcollected = nofcollected + 1
- collected[nofcollected] = { lastr[1], lastr[2] }
- end
- end
- first, last, firsti, lasti, firstr, lastr = current, current, i, i, r, r
- end
- end
- if first and last then
- if last > first + 1 then
- nofcollected = nofcollected + 1
- collected[nofcollected] = { firstr[1], firstr[2], lastr[1], lastr[2] }
- else
- nofcollected = nofcollected + 1
- collected[nofcollected] = { firstr[1], firstr[2] }
- if last > first then
- nofcollected = nofcollected + 1
- collected[nofcollected] = { lastr[1], lastr[2] }
- end
- end
- end
- if nofcollected > 0 then
- for i=1,nofcollected do
- local c = collected[i]
- if c[3] then
- context.dowithbibtexnumrefrange(#collected,i,prefix,c[1],c[2],c[3],c[4])
- else
- context.dowithbibtexnumref(#collected,i,prefix,c[1],c[2])
- end
- end
- else
- context.nobibtexnumref("error 1")
- end
- else
- context.nobibtexnumref("error 2")
- end
-end
+if not modules then modules = { } end modules ['bibl-tra'] = {
+ version = 1.001,
+ comment = "this module is the basis for the lxml-* ones",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+bibtex = bibtex or { }
+local bibtex = bibtex
+
+bibtex.hacks = bibtex.hacks or { }
+local hacks = bibtex.hacks
+
+local match, gmatch, format, concat, sort = string.match, string.gmatch, string.format, table.concat, table.sort
+local variables, constants = interfaces.variables, interfaces.constants
+
+local trace_bibtex = false trackers.register("publications.bibtex", function(v) trace_bibtex = v end)
+
+local report_tex = logs.reporter("publications","tex")
+
+local context, structures = context, structures
+
+local references = structures.references
+local sections = structures.sections
+
+local list, done, alldone, used, registered, ordered = { }, { }, { }, { }, { }, { }
+local mode = 0
+
+local template = utilities.strings.striplong([[
+ \citation{*}
+ \bibstyle{cont-%s}
+ \bibdata{%s}
+]])
+
+local bibtexbin = environment.arguments.mlbibtex and "mlbibcontext" or "bibtex"
+
+directives.register("publications.usemlbibtex", function(v)
+ bibtexbin = v and "mlbibcontext" or "bibtex"
+end)
+
+function hacks.process(settings)
+ local style = settings.style or ""
+ local database = settings.database or ""
+ local jobname = tex.jobname
+ if database ~= "" then
+ interfaces.showmessage("publications",3)
+ io.savedata(file.addsuffix(jobname,"aux"),format(template,style,database))
+ if trace_bibtex then
+ report_tex("processing bibtex file %a using %a",jobname,bibtexbin)
+ end
+ os.execute(format("%s %q",bibtexbin,jobname))
+ -- purge 'm
+ end
+end
+
+function hacks.register(str)
+ if trace_bibtex then
+ report_tex("registering bibtex entry %a",str)
+ end
+ registered[#registered+1] = str
+ ordered[str] = #registered
+end
+
+function hacks.nofregistered()
+ return #registered
+end
+
+function hacks.reset(m)
+ mode, list, done = m, { }, { }
+end
+
+function hacks.add(str,listindex)
+ if not str or mode == 0 then
+ -- skip
+ elseif mode == 1 then
+ -- all locals but no duplicates
+ local sc = sections.currentid()
+ if done[str] ~= sc then
+ done[str], alldone[str] = sc, true
+ list[#list+1] = { str, listindex }
+ end
+ elseif mode == 2 then
+ -- all locals but no preceding
+ local sc = sections.currentid()
+ if not alldone[str] and done[str] ~= sc then
+ done[str], alldone[str] = sc, true
+ list[#list+1] = { str, listindex }
+ end
+ end
+end
+
+local function compare(a,b) -- quite some checking for non-nil
+ local aa, bb = a and a[1], b and b[1]
+ if aa and bb then
+ local oa, ob = ordered[aa], ordered[bb]
+ return oa and ob and oa < ob
+ end
+ return false
+end
+
+function hacks.flush(sortvariant)
+ if sortvariant == "" or sortvariant == variables.cite or sortvariant == "default" then
+ -- order is cite order i.e. same as list
+ else
+ sort(list,compare)
+ end
+ for i=1,#list do
+ context.doprocessbibtexentry(list[i][1])
+ end
+end
+
+function hacks.filterall()
+ for i=1,#registered do
+ list[i] = { registered[i], i }
+ end
+end
+
+function hacks.registerplaced(str)
+ used[str] = true
+end
+
+function hacks.doifalreadyplaced(str)
+ commands.doifelse(used[str])
+end
+
+-- we ask for <n>:tag but when we can't find it we go back
+-- to look for previous definitions, and when not found again
+-- we look forward
+
+local function compare(a,b)
+ local aa, bb = a and a[3], b and b[3]
+ return aa and bb and aa < bb
+end
+
+function hacks.resolve(prefix,block,reference) -- maybe already feed it split
+ -- needs checking (the prefix in relation to components)
+ local subsets
+ local collected = references.collected
+ if prefix and prefix ~= "" then
+ subsets = { collected[prefix] or collected[""] }
+ else
+ local components = references.productdata.components
+ local subset = collected[""]
+ if subset then
+ subsets = { subset }
+ else
+ subsets = { }
+ end
+ for i=1,#components do
+ local subset = collected[components[i]]
+ if subset then
+ subsets[#subsets+1] = subset
+ end
+ end
+ end
+ if #subsets > 0 then
+ local result, nofresult, done = { }, 0, { }
+ block = tonumber(block)
+ for i=1,#subsets do
+ local subset = subsets[i]
+ for rest in gmatch(reference,"[^, ]+") do
+ local blk, tag, found = block, nil, nil
+ if block then
+ tag = blk .. ":" .. rest
+ found = subset[tag]
+ if not found then
+ for i=block-1,1,-1 do
+ tag = i .. ":" .. rest
+ found = subset[tag]
+ if found then
+ blk = i
+ break
+ end
+ end
+ end
+ end
+ if not found then
+ blk = "*"
+ tag = blk .. ":" .. rest
+ found = subset[tag]
+ end
+ if found then
+ local current = tonumber(found.entries and found.entries.text) -- tonumber needed
+ if current and not done[current] then
+ nofresult = nofresult + 1
+ result[nofresult] = { blk, rest, current }
+ done[current] = true
+ end
+ end
+ end
+ end
+ -- todo: ranges so the interface will change
+ sort(result,compare)
+ local first, last, firsti, lasti, firstr, lastr
+ local collected, nofcollected = { }, 0
+ for i=1,nofresult do
+ local r = result[i]
+ local current = r[3]
+ if not first then
+ first, last, firsti, lasti, firstr, lastr = current, current, i, i, r, r
+ elseif current == last + 1 then
+ last, lasti, lastr = current, i, r
+ else
+ if last > first + 1 then
+ nofcollected = nofcollected + 1
+ collected[nofcollected] = { firstr[1], firstr[2], lastr[1], lastr[2] }
+ else
+ nofcollected = nofcollected + 1
+ collected[nofcollected] = { firstr[1], firstr[2] }
+ if last > first then
+ nofcollected = nofcollected + 1
+ collected[nofcollected] = { lastr[1], lastr[2] }
+ end
+ end
+ first, last, firsti, lasti, firstr, lastr = current, current, i, i, r, r
+ end
+ end
+ if first and last then
+ if last > first + 1 then
+ nofcollected = nofcollected + 1
+ collected[nofcollected] = { firstr[1], firstr[2], lastr[1], lastr[2] }
+ else
+ nofcollected = nofcollected + 1
+ collected[nofcollected] = { firstr[1], firstr[2] }
+ if last > first then
+ nofcollected = nofcollected + 1
+ collected[nofcollected] = { lastr[1], lastr[2] }
+ end
+ end
+ end
+ if nofcollected > 0 then
+ for i=1,nofcollected do
+ local c = collected[i]
+ if c[3] then
+ context.dowithbibtexnumrefrange(#collected,i,prefix,c[1],c[2],c[3],c[4])
+ else
+ context.dowithbibtexnumref(#collected,i,prefix,c[1],c[2])
+ end
+ end
+ else
+ context.nobibtexnumref("error 1")
+ end
+ else
+ context.nobibtexnumref("error 2")
+ end
+end
diff --git a/tex/context/base/bibl-tst.lua b/tex/context/base/bibl-tst.lua
index 569f583c8..5ff8f4570 100644
--- a/tex/context/base/bibl-tst.lua
+++ b/tex/context/base/bibl-tst.lua
@@ -1,21 +1,21 @@
-dofile("bibl-bib.lua")
-
-local session = bibtex.new()
-
-bibtex.load(session,"gut.bib")
-bibtex.load(session,"komoedie.bib")
-bibtex.load(session,"texbook1.bib")
-bibtex.load(session,"texbook2.bib")
-bibtex.load(session,"texbook3.bib")
-bibtex.load(session,"texgraph.bib")
-bibtex.load(session,"texjourn.bib")
-bibtex.load(session,"texnique.bib")
-bibtex.load(session,"tugboat.bib")
-print(bibtex.size,statistics.elapsedtime(bibtex))
-bibtex.toxml(session)
-print(bibtex.size,statistics.elapsedtime(bibtex))
-
---~ print(table.serialize(session.data))
---~ print(table.serialize(session.shortcuts))
---~ print(xml.serialize(session.xml))
-
+dofile("bibl-bib.lua")
+
+local session = bibtex.new()
+
+bibtex.load(session,"gut.bib")
+bibtex.load(session,"komoedie.bib")
+bibtex.load(session,"texbook1.bib")
+bibtex.load(session,"texbook2.bib")
+bibtex.load(session,"texbook3.bib")
+bibtex.load(session,"texgraph.bib")
+bibtex.load(session,"texjourn.bib")
+bibtex.load(session,"texnique.bib")
+bibtex.load(session,"tugboat.bib")
+print(bibtex.size,statistics.elapsedtime(bibtex))
+bibtex.toxml(session)
+print(bibtex.size,statistics.elapsedtime(bibtex))
+
+--~ print(table.serialize(session.data))
+--~ print(table.serialize(session.shortcuts))
+--~ print(xml.serialize(session.xml))
+
diff --git a/tex/context/base/blob-ini.lua b/tex/context/base/blob-ini.lua
index 148651b21..4debaf94c 100644
--- a/tex/context/base/blob-ini.lua
+++ b/tex/context/base/blob-ini.lua
@@ -1,194 +1,194 @@
-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"
-}
-
--- Experimental ... names and functionality will change ... just a
--- place to collect code, so:
---
--- DON'T USE THESE FUNCTIONS AS THEY WILL CHANGE!
---
--- 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 t_tonodes = typesetters.tonodes
-local t_hpack = typesetters.hpack
-
-local flush_node_list = node.flush_list
-local hpack_node_list = node.hpack
-local vpack_node_list = node.vpack
-local write_node = node.write
-
-blobs = blobs or { }
-
-local newline = lpegpatterns.newline
-local space = lpegpatterns.spacer
-local spacing = newline * space^0
-local content = (space^1)/" " + (1-spacing)
-
-local ctxtextcapture = lpeg.Ct ( ( -- needs checking (see elsewhere)
- space^0 * (
- newline^2 * space^0 * lpeg.Cc("")
- + newline * space^0 * lpeg.Cc(" ")
- + lpeg.Cs(content^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
- local list = t.list
- if typ == "string" then
- local pars = lpegmatch(ctxtextcapture,str)
- local noflist = #list
- for p=1,#pars do
- local str = pars[p]
- if #str == 0 then
- noflist = noflist + 1
- list[noflist] = { head = nil, tail = nil }
- else
- local l = list[noflist]
- if not l then
- l = { head = nil, tail = nil }
- noflist = noflist + 1
- list[noflist] = l
- end
- local head, tail = t_tonodes(str,nil,nil)
- if head then
- if l.head then
- l.tail.next = head
- head.prev = l.tail
- l.tail = tail
- else
- l.head, l.tail = head, tail
- end
- end
- end
- 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 = node.vpack(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:
-
-function commands.widthofstring(str)
- local l = t_hpack(str)
- context(number.todimen(l.width))
- flush_node_list(l)
-end
-
--- less efficient:
---
--- function commands.widthof(str)
--- local b = blobs.new()
--- blobs.append(b,str)
--- blobs.pack(b)
--- local w = blobs.dimensions(b)
--- context(number.todimen(w))
--- blobs.dispose(b)
--- end
+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"
+}
+
+-- Experimental ... names and functionality will change ... just a
+-- place to collect code, so:
+--
+-- DON'T USE THESE FUNCTIONS AS THEY WILL CHANGE!
+--
+-- 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 t_tonodes = typesetters.tonodes
+local t_hpack = typesetters.hpack
+
+local flush_node_list = node.flush_list
+local hpack_node_list = node.hpack
+local vpack_node_list = node.vpack
+local write_node = node.write
+
+blobs = blobs or { }
+
+local newline = lpegpatterns.newline
+local space = lpegpatterns.spacer
+local spacing = newline * space^0
+local content = (space^1)/" " + (1-spacing)
+
+local ctxtextcapture = lpeg.Ct ( ( -- needs checking (see elsewhere)
+ space^0 * (
+ newline^2 * space^0 * lpeg.Cc("")
+ + newline * space^0 * lpeg.Cc(" ")
+ + lpeg.Cs(content^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
+ local list = t.list
+ if typ == "string" then
+ local pars = lpegmatch(ctxtextcapture,str)
+ local noflist = #list
+ for p=1,#pars do
+ local str = pars[p]
+ if #str == 0 then
+ noflist = noflist + 1
+ list[noflist] = { head = nil, tail = nil }
+ else
+ local l = list[noflist]
+ if not l then
+ l = { head = nil, tail = nil }
+ noflist = noflist + 1
+ list[noflist] = l
+ end
+ local head, tail = t_tonodes(str,nil,nil)
+ if head then
+ if l.head then
+ l.tail.next = head
+ head.prev = l.tail
+ l.tail = tail
+ else
+ l.head, l.tail = head, tail
+ end
+ end
+ end
+ 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 = node.vpack(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:
+
+function commands.widthofstring(str)
+ local l = t_hpack(str)
+ context(number.todimen(l.width))
+ flush_node_list(l)
+end
+
+-- less efficient:
+--
+-- function commands.widthof(str)
+-- local b = blobs.new()
+-- blobs.append(b,str)
+-- blobs.pack(b)
+-- local w = blobs.dimensions(b)
+-- context(number.todimen(w))
+-- blobs.dispose(b)
+-- end
diff --git a/tex/context/base/buff-imp-default.lua b/tex/context/base/buff-imp-default.lua
index fd1634616..72a49d625 100644
--- a/tex/context/base/buff-imp-default.lua
+++ b/tex/context/base/buff-imp-default.lua
@@ -1,42 +1,42 @@
-if not modules then modules = { } end modules ['buff-imp-default'] = {
- version = 1.001,
- comment = "companion to buff-imp-default.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local patterns, P, V = lpeg.patterns, lpeg.P, lpeg.V
-local makepattern = visualizers.makepattern
-
-local handler = visualizers.newhandler()
-
-local grammar = { "visualizer",
-
- -- basic
-
- signal = makepattern(handler,"signal", visualizers.signalpattern),
- emptyline = makepattern(handler,"emptyline",patterns.emptyline),
- beginline = makepattern(handler,"beginline",patterns.beginline),
- newline = makepattern(handler,"newline", patterns.newline),
- space = makepattern(handler,"space", patterns.space),
- default = makepattern(handler,"default", patterns.utf8char),
- content = makepattern(handler,"default", patterns.somecontent), -- not too efficient
-
- -- handy
-
- -- line = V("newline") * V("emptyline")^0 * V("beginline"),
- line = V("newline") * V("emptyline")^0 * V("beginline") + V("emptyline") + V("newline"),
- whitespace = (V("space") + V("line"))^1,
- optionalwhitespace = (V("space") + V("line"))^0,
-
- -- used
-
- pattern = V("line") + V("space") + V("signal") + V("content"),
- visualizer = V("pattern")^1
-
-}
-
-local parser = P(grammar)
-
-visualizers.register("default", { parser = parser, handler = handler, grammar = grammar })
+if not modules then modules = { } end modules ['buff-imp-default'] = {
+ version = 1.001,
+ comment = "companion to buff-imp-default.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local patterns, P, V = lpeg.patterns, lpeg.P, lpeg.V
+local makepattern = visualizers.makepattern
+
+local handler = visualizers.newhandler()
+
+local grammar = { "visualizer",
+
+ -- basic
+
+ signal = makepattern(handler,"signal", visualizers.signalpattern),
+ emptyline = makepattern(handler,"emptyline",patterns.emptyline),
+ beginline = makepattern(handler,"beginline",patterns.beginline),
+ newline = makepattern(handler,"newline", patterns.newline),
+ space = makepattern(handler,"space", patterns.space),
+ default = makepattern(handler,"default", patterns.utf8char),
+ content = makepattern(handler,"default", patterns.somecontent), -- not too efficient
+
+ -- handy
+
+ -- line = V("newline") * V("emptyline")^0 * V("beginline"),
+ line = V("newline") * V("emptyline")^0 * V("beginline") + V("emptyline") + V("newline"),
+ whitespace = (V("space") + V("line"))^1,
+ optionalwhitespace = (V("space") + V("line"))^0,
+
+ -- used
+
+ pattern = V("line") + V("space") + V("signal") + V("content"),
+ visualizer = V("pattern")^1
+
+}
+
+local parser = P(grammar)
+
+visualizers.register("default", { parser = parser, handler = handler, grammar = grammar })
diff --git a/tex/context/base/buff-imp-escaped.lua b/tex/context/base/buff-imp-escaped.lua
index 5a15c736a..159921e2a 100644
--- a/tex/context/base/buff-imp-escaped.lua
+++ b/tex/context/base/buff-imp-escaped.lua
@@ -1,14 +1,14 @@
-if not modules then modules = { } end modules ['buff-imp-escaped'] = {
- version = 1.001,
- comment = "companion to buff-imp-escaped.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-visualizers.registerescapepattern("/BTEX/ETEX","/BTEX","/ETEX")
-
-visualizers.register("escaped", {
- parser = visualizers.escapepatterns["/BTEX/ETEX"],
- handler = visualizers.newhandler(),
-})
+if not modules then modules = { } end modules ['buff-imp-escaped'] = {
+ version = 1.001,
+ comment = "companion to buff-imp-escaped.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+visualizers.registerescapepattern("/BTEX/ETEX","/BTEX","/ETEX")
+
+visualizers.register("escaped", {
+ parser = visualizers.escapepatterns["/BTEX/ETEX"],
+ handler = visualizers.newhandler(),
+})
diff --git a/tex/context/base/buff-imp-lua.lua b/tex/context/base/buff-imp-lua.lua
index 4f47c0de9..1147666cc 100644
--- a/tex/context/base/buff-imp-lua.lua
+++ b/tex/context/base/buff-imp-lua.lua
@@ -1,208 +1,208 @@
-if not modules then modules = { } end modules ['buff-imp-lua'] = {
- version = 1.001,
- comment = "companion to buff-imp-lua.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- borrowed from scite
---
--- depricated:
---
--- gcinfo unpack getfenv setfenv loadlib
--- table.maxn table.getn table.setn
--- math.log10 math.mod math.modf math.fmod
-
-local format, tohash = string.format, table.tohash
-local P, S, V, patterns = lpeg.P, lpeg.S, lpeg.V, lpeg.patterns
-local C, Cs, Cg, Cb, Cmt, Carg = lpeg.C, lpeg.Cs, lpeg.Cg, lpeg.Cb, lpeg.Cmt, lpeg.Carg
-
-local core = tohash {
- "and", "break", "do", "else", "elseif", "end", "false", "for", "function",
- "if", "in", "local", "nil", "not", "or", "repeat", "return", "then",
- "true", "until", "while"
-}
-
-local base = tohash {
- "assert", "collectgarbage", "dofile", "error", "loadfile",
- "loadstring", "print", "rawget", "rawset", "require", "tonumber",
- "tostring", "type", "_G", "getmetatable", "ipairs", "next", "pairs",
- "pcall", "rawequal", "setmetatable", "xpcall", "module", "select",
-}
-
-local libraries = {
- coroutine = tohash {
- "create", "resume", "status", "wrap", "yield", "running",
- },
- package = tohash{
- "cpath", "loaded", "loadlib", "path", "config", "preload", "seeall",
- },
- io = tohash{
- "close", "flush", "input", "lines", "open", "output", "read", "tmpfile",
- "type", "write", "stdin", "stdout", "stderr", "popen",
- },
- math = tohash{
- "abs", "acos", "asin", "atan", "atan2", "ceil", "cos", "deg", "exp",
- "floor ", "ldexp", "log", "max", "min", "pi", "pow", "rad", "random",
- "randomseed", "sin", "sqrt", "tan", "cosh", "sinh", "tanh", "huge",
- },
- string = tohash{
- "byte", "char", "dump", "find", "len", "lower", "rep", "sub", "upper",
- "format", "gfind", "gsub", "gmatch", "match", "reverse",
- },
- table = tohash{
- "concat", "foreach", "foreachi", "sort", "insert", "remove", "pack",
- "unpack",
- },
- os = tohash{
- "clock", "date", "difftime", "execute", "exit", "getenv", "remove",
- "rename", "setlocale", "time", "tmpname",
- },
- lpeg = tohash{
- "print", "match", "locale", "type", "version", "setmaxstack",
- "P", "R", "S", "C", "V", "Cs", "Ct", "Cs", "Cp", "Carg",
- "Cg", "Cb", "Cmt", "Cf", "B",
- },
- -- bit
- -- debug
-}
-
-local context = context
-local verbatim = context.verbatim
-local makepattern = visualizers.makepattern
-
-local LuaSnippet = context.LuaSnippet
-local startLuaSnippet = context.startLuaSnippet
-local stopLuaSnippet = context.stopLuaSnippet
-
-local LuaSnippetBoundary = verbatim.LuaSnippetBoundary
-local LuaSnippetQuote = verbatim.LuaSnippetQuote
-local LuaSnippetString = verbatim.LuaSnippetString
-local LuaSnippetSpecial = verbatim.LuaSnippetSpecial
-local LuaSnippetComment = verbatim.LuaSnippetComment
-local LuaSnippetNameCore = verbatim.LuaSnippetNameCore
-local LuaSnippetNameBase = verbatim.LuaSnippetNameBase
-local LuaSnippetNameLibraries = verbatim.LuaSnippetNameLibraries
-local LuaSnippetName = verbatim.LuaSnippetName
-
-local namespace
-
-local function visualizename_a(s)
- if core[s] then
- namespace = nil
- LuaSnippetNameCore(s)
- elseif base[s] then
- namespace = nil
- LuaSnippetNameBase(s)
- else
- namespace = libraries[s]
- if namespace then
- LuaSnippetNameLibraries(s)
- else
- LuaSnippetName(s)
- end
- end
-end
-
-local function visualizename_b(s)
- if namespace and namespace[s] then
- namespace = nil
- LuaSnippetNameLibraries(s)
- else
- LuaSnippetName(s)
- end
-end
-
-local function visualizename_c(s)
- LuaSnippetName(s)
-end
-
-local handler = visualizers.newhandler {
- startinline = function() LuaSnippet(false,"{") end,
- stopinline = function() context("}") end,
- startdisplay = function() startLuaSnippet() end,
- stopdisplay = function() stopLuaSnippet() end ,
- boundary = function(s) LuaSnippetBoundary(s) end,
- special = function(s) LuaSnippetSpecial(s) end,
- comment = function(s) LuaSnippetComment(s) end,
- quote = function(s) LuaSnippetQuote(s) end,
- string = function(s) LuaSnippetString(s) end,
- period = function(s) verbatim(s) end,
- name_a = visualizename_a,
- name_b = visualizename_b,
- name_c = visualizename_c,
-}
-
-local comment = P("--")
-local name = (patterns.letter + patterns.underscore)
- * (patterns.letter + patterns.underscore + patterns.digit)^0
-local boundary = S('()[]{}')
-local special = S("-+/*^%=#") + P("..")
-
--- The following longstring parser is taken from Roberto's documentation
--- that can be found at http://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html.
-
-local equals = P("=")^0
-local open = P("[") * Cg(equals, "init") * P("[") * P("\n")^-1 -- maybe better: patterns.newline^-1
-local close = P("]") * C(equals) * P("]")
-local closeeq = Cmt(close * Cb("init"), function(s,i,a,b) return a == b end) -- wrong return value
-local longstring = open * Cs((P(1) - closeeq)^0) * close * Carg(1)
-
-local function long(content,equals,settings)
- handler.boundary(format("[%s[",equals or ""))
- visualizers.write(content,settings) -- unhandled
- handler.boundary(format("]%s]",equals or ""))
-end
-
-local grammar = visualizers.newgrammar("default", { "visualizer",
- sstring =
- makepattern(handler,"quote",patterns.dquote)
- * (V("whitespace") + makepattern(handler,"string",1-patterns.dquote))^0 -- patterns.nodquote
- * makepattern(handler,"quote",patterns.dquote),
- dstring =
- makepattern(handler,"quote",patterns.squote)
- * (V("whitespace") + makepattern(handler,"string",1-patterns.squote))^0 -- patterns.nosquote
- * makepattern(handler,"quote",patterns.squote),
- longstring =
- longstring / long,
- comment =
- makepattern(handler,"comment",comment)
- * (V("space") + V("content"))^0,
- longcomment =
- makepattern(handler,"comment",comment)
- * longstring / long,
- name =
- makepattern(handler,"name_a",name)
- * ( V("optionalwhitespace")
- * makepattern(handler,"default",patterns.period)
- * V("optionalwhitespace")
- * makepattern(handler,"name_b",name)
- )^-1
- * ( V("optionalwhitespace")
- * makepattern(handler,"default",patterns.period)
- * V("optionalwhitespace")
- * makepattern(handler,"name_c",name)
- )^0,
-
- pattern =
- V("longcomment")
- + V("comment")
- + V("longstring")
- + V("dstring")
- + V("sstring")
- + V("name")
- + makepattern(handler,"boundary",boundary)
- + makepattern(handler,"special",special)
-
- + V("space")
- + V("line")
- + V("default"),
-
- visualizer =
- V("pattern")^1
-} )
-
-local parser = P(grammar)
-
-visualizers.register("lua", { parser = parser, handler = handler, grammar = grammar } )
+if not modules then modules = { } end modules ['buff-imp-lua'] = {
+ version = 1.001,
+ comment = "companion to buff-imp-lua.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- borrowed from scite
+--
+-- depricated:
+--
+-- gcinfo unpack getfenv setfenv loadlib
+-- table.maxn table.getn table.setn
+-- math.log10 math.mod math.modf math.fmod
+
+local format, tohash = string.format, table.tohash
+local P, S, V, patterns = lpeg.P, lpeg.S, lpeg.V, lpeg.patterns
+local C, Cs, Cg, Cb, Cmt, Carg = lpeg.C, lpeg.Cs, lpeg.Cg, lpeg.Cb, lpeg.Cmt, lpeg.Carg
+
+local core = tohash {
+ "and", "break", "do", "else", "elseif", "end", "false", "for", "function",
+ "if", "in", "local", "nil", "not", "or", "repeat", "return", "then",
+ "true", "until", "while"
+}
+
+local base = tohash {
+ "assert", "collectgarbage", "dofile", "error", "loadfile",
+ "loadstring", "print", "rawget", "rawset", "require", "tonumber",
+ "tostring", "type", "_G", "getmetatable", "ipairs", "next", "pairs",
+ "pcall", "rawequal", "setmetatable", "xpcall", "module", "select",
+}
+
+local libraries = {
+ coroutine = tohash {
+ "create", "resume", "status", "wrap", "yield", "running",
+ },
+ package = tohash{
+ "cpath", "loaded", "loadlib", "path", "config", "preload", "seeall",
+ },
+ io = tohash{
+ "close", "flush", "input", "lines", "open", "output", "read", "tmpfile",
+ "type", "write", "stdin", "stdout", "stderr", "popen",
+ },
+ math = tohash{
+ "abs", "acos", "asin", "atan", "atan2", "ceil", "cos", "deg", "exp",
+ "floor ", "ldexp", "log", "max", "min", "pi", "pow", "rad", "random",
+ "randomseed", "sin", "sqrt", "tan", "cosh", "sinh", "tanh", "huge",
+ },
+ string = tohash{
+ "byte", "char", "dump", "find", "len", "lower", "rep", "sub", "upper",
+ "format", "gfind", "gsub", "gmatch", "match", "reverse",
+ },
+ table = tohash{
+ "concat", "foreach", "foreachi", "sort", "insert", "remove", "pack",
+ "unpack",
+ },
+ os = tohash{
+ "clock", "date", "difftime", "execute", "exit", "getenv", "remove",
+ "rename", "setlocale", "time", "tmpname",
+ },
+ lpeg = tohash{
+ "print", "match", "locale", "type", "version", "setmaxstack",
+ "P", "R", "S", "C", "V", "Cs", "Ct", "Cs", "Cp", "Carg",
+ "Cg", "Cb", "Cmt", "Cf", "B",
+ },
+ -- bit
+ -- debug
+}
+
+local context = context
+local verbatim = context.verbatim
+local makepattern = visualizers.makepattern
+
+local LuaSnippet = context.LuaSnippet
+local startLuaSnippet = context.startLuaSnippet
+local stopLuaSnippet = context.stopLuaSnippet
+
+local LuaSnippetBoundary = verbatim.LuaSnippetBoundary
+local LuaSnippetQuote = verbatim.LuaSnippetQuote
+local LuaSnippetString = verbatim.LuaSnippetString
+local LuaSnippetSpecial = verbatim.LuaSnippetSpecial
+local LuaSnippetComment = verbatim.LuaSnippetComment
+local LuaSnippetNameCore = verbatim.LuaSnippetNameCore
+local LuaSnippetNameBase = verbatim.LuaSnippetNameBase
+local LuaSnippetNameLibraries = verbatim.LuaSnippetNameLibraries
+local LuaSnippetName = verbatim.LuaSnippetName
+
+local namespace
+
+local function visualizename_a(s)
+ if core[s] then
+ namespace = nil
+ LuaSnippetNameCore(s)
+ elseif base[s] then
+ namespace = nil
+ LuaSnippetNameBase(s)
+ else
+ namespace = libraries[s]
+ if namespace then
+ LuaSnippetNameLibraries(s)
+ else
+ LuaSnippetName(s)
+ end
+ end
+end
+
+local function visualizename_b(s)
+ if namespace and namespace[s] then
+ namespace = nil
+ LuaSnippetNameLibraries(s)
+ else
+ LuaSnippetName(s)
+ end
+end
+
+local function visualizename_c(s)
+ LuaSnippetName(s)
+end
+
+local handler = visualizers.newhandler {
+ startinline = function() LuaSnippet(false,"{") end,
+ stopinline = function() context("}") end,
+ startdisplay = function() startLuaSnippet() end,
+ stopdisplay = function() stopLuaSnippet() end ,
+ boundary = function(s) LuaSnippetBoundary(s) end,
+ special = function(s) LuaSnippetSpecial(s) end,
+ comment = function(s) LuaSnippetComment(s) end,
+ quote = function(s) LuaSnippetQuote(s) end,
+ string = function(s) LuaSnippetString(s) end,
+ period = function(s) verbatim(s) end,
+ name_a = visualizename_a,
+ name_b = visualizename_b,
+ name_c = visualizename_c,
+}
+
+local comment = P("--")
+local name = (patterns.letter + patterns.underscore)
+ * (patterns.letter + patterns.underscore + patterns.digit)^0
+local boundary = S('()[]{}')
+local special = S("-+/*^%=#") + P("..")
+
+-- The following longstring parser is taken from Roberto's documentation
+-- that can be found at http://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html.
+
+local equals = P("=")^0
+local open = P("[") * Cg(equals, "init") * P("[") * P("\n")^-1 -- maybe better: patterns.newline^-1
+local close = P("]") * C(equals) * P("]")
+local closeeq = Cmt(close * Cb("init"), function(s,i,a,b) return a == b end) -- wrong return value
+local longstring = open * Cs((P(1) - closeeq)^0) * close * Carg(1)
+
+local function long(content,equals,settings)
+ handler.boundary(format("[%s[",equals or ""))
+ visualizers.write(content,settings) -- unhandled
+ handler.boundary(format("]%s]",equals or ""))
+end
+
+local grammar = visualizers.newgrammar("default", { "visualizer",
+ sstring =
+ makepattern(handler,"quote",patterns.dquote)
+ * (V("whitespace") + makepattern(handler,"string",1-patterns.dquote))^0 -- patterns.nodquote
+ * makepattern(handler,"quote",patterns.dquote),
+ dstring =
+ makepattern(handler,"quote",patterns.squote)
+ * (V("whitespace") + makepattern(handler,"string",1-patterns.squote))^0 -- patterns.nosquote
+ * makepattern(handler,"quote",patterns.squote),
+ longstring =
+ longstring / long,
+ comment =
+ makepattern(handler,"comment",comment)
+ * (V("space") + V("content"))^0,
+ longcomment =
+ makepattern(handler,"comment",comment)
+ * longstring / long,
+ name =
+ makepattern(handler,"name_a",name)
+ * ( V("optionalwhitespace")
+ * makepattern(handler,"default",patterns.period)
+ * V("optionalwhitespace")
+ * makepattern(handler,"name_b",name)
+ )^-1
+ * ( V("optionalwhitespace")
+ * makepattern(handler,"default",patterns.period)
+ * V("optionalwhitespace")
+ * makepattern(handler,"name_c",name)
+ )^0,
+
+ pattern =
+ V("longcomment")
+ + V("comment")
+ + V("longstring")
+ + V("dstring")
+ + V("sstring")
+ + V("name")
+ + makepattern(handler,"boundary",boundary)
+ + makepattern(handler,"special",special)
+
+ + V("space")
+ + V("line")
+ + V("default"),
+
+ visualizer =
+ V("pattern")^1
+} )
+
+local parser = P(grammar)
+
+visualizers.register("lua", { parser = parser, handler = handler, grammar = grammar } )
diff --git a/tex/context/base/buff-imp-mp.lua b/tex/context/base/buff-imp-mp.lua
index a535b8c80..34e3459c6 100644
--- a/tex/context/base/buff-imp-mp.lua
+++ b/tex/context/base/buff-imp-mp.lua
@@ -1,117 +1,117 @@
-if not modules then modules = { } end modules ['buff-imp-mp'] = {
- version = 1.001,
- comment = "companion to buff-imp-mp.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- Now that we also use lpeg lexers in scite, we can share the keywords
--- so we have moved the keyword lists to mult-mps.lua. Don't confuse the
--- scite lexers with the ones we use here. Of course all those lexers
--- boil down to doing similar things, but here we need more control over
--- the rendering and have a different way of nesting. It is no coincidence
--- that the coloring looks similar: both are derived from earlier lexing (in
--- texedit, mkii and the c++ scite lexer).
---
--- In the meantime we have lpeg based lexers in scite! And, as all this
--- lexing boils down to the same principles (associating symbolic rendering
--- with ranges of characters) and as the scite lexers do nesting, it makes
--- sense at some point to share code. However, keep in mind that the pretty
--- printers are also supposed to support invalid code (for educational
--- purposes). The scite lexers are more recent and there a different color
--- scheme is used. So, we might move away from the traditional coloring.
-
-local P, S, V, patterns = lpeg.P, lpeg.S, lpeg.V, lpeg.patterns
-
-local context = context
-local verbatim = context.verbatim
-local makepattern = visualizers.makepattern
-
-local MetapostSnippet = context.MetapostSnippet
-local startMetapostSnippet = context.startMetapostSnippet
-local stopMetapostSnippet = context.stopMetapostSnippet
-
-local MetapostSnippetConstructor = verbatim.MetapostSnippetConstructor
-local MetapostSnippetBoundary = verbatim.MetapostSnippetBoundary
-local MetapostSnippetSpecial = verbatim.MetapostSnippetSpecial
-local MetapostSnippetComment = verbatim.MetapostSnippetComment
-local MetapostSnippetNamePrimitive = verbatim.MetapostSnippetNamePrimitive
-local MetapostSnippetNamePlain = verbatim.MetapostSnippetNamePlain
-local MetapostSnippetNameMetafun = verbatim.MetapostSnippetNameMetafun
-local MetapostSnippetName = verbatim.MetapostSnippetName
-
-local primitives, plain, metafun
-
-local function initialize()
- local mps = dofile(resolvers.findfile("mult-mps.lua","tex")) or {
- primitives = { },
- plain = { },
- metafun = { },
- }
- primitives = table.tohash(mps.primitives)
- plain = table.tohash(mps.plain)
- metafun = table.tohash(mps.metafun)
-end
-
-local function visualizename(s)
- if not primitives then
- initialize()
- end
- if primitives[s] then
- MetapostSnippetNamePrimitive(s)
- elseif plain[s] then
- MetapostSnippetNamePlain(s)
- elseif metafun[s] then
- MetapostSnippetNameMetafun(s)
- else
- MetapostSnippetName(s)
- end
-end
-
-local handler = visualizers.newhandler {
- startinline = function() MetapostSnippet(false,"{") end,
- stopinline = function() context("}") end,
- startdisplay = function() startMetapostSnippet() end,
- stopdisplay = function() stopMetapostSnippet() end ,
- constructor = function(s) MetapostSnippetConstructor(s) end,
- boundary = function(s) MetapostSnippetBoundary(s) end,
- special = function(s) MetapostSnippetSpecial(s) end,
- comment = function(s) MetapostSnippetComment(s) end,
- string = function(s) MetapostSnippetString(s) end,
- quote = function(s) MetapostSnippetQuote(s) end,
- name = visualizename,
-}
-
-local comment = S("%")
-local name = (patterns.letter + S("_"))^1
-local constructor = S("$@#")
-local boundary = S('()[]:=<>;"')
-local special = S("-+/*|`!?^&%.,")
-
-local grammar = visualizers.newgrammar("default", { "visualizer",
-
- comment = makepattern(handler,"comment",comment)
- * (V("space") + V("content"))^0,
- dstring = makepattern(handler,"quote",patterns.dquote)
- * makepattern(handler,"string",patterns.nodquote)
- * makepattern(handler,"quote",patterns.dquote),
- name = makepattern(handler,"name",name),
- constructor = makepattern(handler,"constructor",constructor),
- boundary = makepattern(handler,"boundary",boundary),
- special = makepattern(handler,"special",special),
-
- pattern =
- V("comment") + V("dstring") + V("name") + V("constructor") + V("boundary") + V("special")
- + V("newline") * V("emptyline")^0 * V("beginline")
- + V("space")
- + V("default"),
-
- visualizer =
- V("pattern")^1
-
-} )
-
-local parser = P(grammar)
-
-visualizers.register("mp", { parser = parser, handler = handler, grammar = grammar } )
+if not modules then modules = { } end modules ['buff-imp-mp'] = {
+ version = 1.001,
+ comment = "companion to buff-imp-mp.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Now that we also use lpeg lexers in scite, we can share the keywords
+-- so we have moved the keyword lists to mult-mps.lua. Don't confuse the
+-- scite lexers with the ones we use here. Of course all those lexers
+-- boil down to doing similar things, but here we need more control over
+-- the rendering and have a different way of nesting. It is no coincidence
+-- that the coloring looks similar: both are derived from earlier lexing (in
+-- texedit, mkii and the c++ scite lexer).
+--
+-- In the meantime we have lpeg based lexers in scite! And, as all this
+-- lexing boils down to the same principles (associating symbolic rendering
+-- with ranges of characters) and as the scite lexers do nesting, it makes
+-- sense at some point to share code. However, keep in mind that the pretty
+-- printers are also supposed to support invalid code (for educational
+-- purposes). The scite lexers are more recent and there a different color
+-- scheme is used. So, we might move away from the traditional coloring.
+
+local P, S, V, patterns = lpeg.P, lpeg.S, lpeg.V, lpeg.patterns
+
+local context = context
+local verbatim = context.verbatim
+local makepattern = visualizers.makepattern
+
+local MetapostSnippet = context.MetapostSnippet
+local startMetapostSnippet = context.startMetapostSnippet
+local stopMetapostSnippet = context.stopMetapostSnippet
+
+local MetapostSnippetConstructor = verbatim.MetapostSnippetConstructor
+local MetapostSnippetBoundary = verbatim.MetapostSnippetBoundary
+local MetapostSnippetSpecial = verbatim.MetapostSnippetSpecial
+local MetapostSnippetComment = verbatim.MetapostSnippetComment
+local MetapostSnippetNamePrimitive = verbatim.MetapostSnippetNamePrimitive
+local MetapostSnippetNamePlain = verbatim.MetapostSnippetNamePlain
+local MetapostSnippetNameMetafun = verbatim.MetapostSnippetNameMetafun
+local MetapostSnippetName = verbatim.MetapostSnippetName
+
+local primitives, plain, metafun
+
+local function initialize()
+ local mps = dofile(resolvers.findfile("mult-mps.lua","tex")) or {
+ primitives = { },
+ plain = { },
+ metafun = { },
+ }
+ primitives = table.tohash(mps.primitives)
+ plain = table.tohash(mps.plain)
+ metafun = table.tohash(mps.metafun)
+end
+
+local function visualizename(s)
+ if not primitives then
+ initialize()
+ end
+ if primitives[s] then
+ MetapostSnippetNamePrimitive(s)
+ elseif plain[s] then
+ MetapostSnippetNamePlain(s)
+ elseif metafun[s] then
+ MetapostSnippetNameMetafun(s)
+ else
+ MetapostSnippetName(s)
+ end
+end
+
+local handler = visualizers.newhandler {
+ startinline = function() MetapostSnippet(false,"{") end,
+ stopinline = function() context("}") end,
+ startdisplay = function() startMetapostSnippet() end,
+ stopdisplay = function() stopMetapostSnippet() end ,
+ constructor = function(s) MetapostSnippetConstructor(s) end,
+ boundary = function(s) MetapostSnippetBoundary(s) end,
+ special = function(s) MetapostSnippetSpecial(s) end,
+ comment = function(s) MetapostSnippetComment(s) end,
+ string = function(s) MetapostSnippetString(s) end,
+ quote = function(s) MetapostSnippetQuote(s) end,
+ name = visualizename,
+}
+
+local comment = S("%")
+local name = (patterns.letter + S("_"))^1
+local constructor = S("$@#")
+local boundary = S('()[]:=<>;"')
+local special = S("-+/*|`!?^&%.,")
+
+local grammar = visualizers.newgrammar("default", { "visualizer",
+
+ comment = makepattern(handler,"comment",comment)
+ * (V("space") + V("content"))^0,
+ dstring = makepattern(handler,"quote",patterns.dquote)
+ * makepattern(handler,"string",patterns.nodquote)
+ * makepattern(handler,"quote",patterns.dquote),
+ name = makepattern(handler,"name",name),
+ constructor = makepattern(handler,"constructor",constructor),
+ boundary = makepattern(handler,"boundary",boundary),
+ special = makepattern(handler,"special",special),
+
+ pattern =
+ V("comment") + V("dstring") + V("name") + V("constructor") + V("boundary") + V("special")
+ + V("newline") * V("emptyline")^0 * V("beginline")
+ + V("space")
+ + V("default"),
+
+ visualizer =
+ V("pattern")^1
+
+} )
+
+local parser = P(grammar)
+
+visualizers.register("mp", { parser = parser, handler = handler, grammar = grammar } )
diff --git a/tex/context/base/buff-imp-nested.lua b/tex/context/base/buff-imp-nested.lua
index 16b8ac67a..019cd996d 100644
--- a/tex/context/base/buff-imp-nested.lua
+++ b/tex/context/base/buff-imp-nested.lua
@@ -1,80 +1,80 @@
-if not modules then modules = { } end modules ['buff-imp-nested'] = {
- version = 1.001,
- comment = "companion to buff-imp-nested.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local lpegmatch, patterns = lpeg.match, lpeg.patterns
-local P, V, Carg = lpeg.P, lpeg.V, lpeg.Carg
-
-local context = context
-local verbatim = context.verbatim
-local variables = interfaces.variables
-
-local makepattern = visualizers.makepattern
-local getvisualizer = visualizers.getvisualizer
-
-local nested = nil
-
-local donestedtypingstart = context.donestedtypingstart
-local donestedtypingstop = context.donestedtypingstop
-
-local v_none = variables.none
-local v_slanted = variables.slanted
-
-local handler = visualizers.newhandler {
- initialize = function(settings)
- local option = settings and settings.option
- if not option or option == "" then
- nested = nil
- elseif option == v_slanted then
- nested = nil
- elseif option == v_none then
- nested = nil
- else
- nested = getvisualizer(option,"direct")
- end
- end,
- open = function()
- donestedtypingstart()
- end,
- close = function()
- donestedtypingstop()
- end,
- content = function(s)
- if nested then
- nested(s)
- else
- verbatim(s)
- end
- end,
-}
-
-local open = P("<<")
-local close = P(">>")
-local rest = (1 - open - close - patterns.space - patterns.newline)^1
-
-local grammar = visualizers.newgrammar("default", {
-
- initialize = patterns.beginofstring * Carg(1) / handler.initialize,
-
- open = makepattern(handler,"open",open),
- close = makepattern(handler,"close",close),
- rest = makepattern(handler,"content",rest),
-
- nested = V("open") * (V("pattern")^0) * V("close"),
- pattern = V("line") + V("space") + V("nested") + V("rest"),
-
- visualizer = V("initialize") * (V("pattern")^1)
-
-} )
-
-local parser = P(grammar)
-
-visualizers.register("nested", { parser = parser, handler = handler, grammar = grammar } )
-
--- lpeg.match(parser,[[<<tf<<sl>>tf<<sl>>tf>>]]) context.par()
--- lpeg.match(parser,[[<<tf<<sl<<tf>>sl>>tf>>]]) context.par()
--- lpeg.match(parser,[[sl<<tf<<sl>>tf>>sl]]) context.par()
+if not modules then modules = { } end modules ['buff-imp-nested'] = {
+ version = 1.001,
+ comment = "companion to buff-imp-nested.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local lpegmatch, patterns = lpeg.match, lpeg.patterns
+local P, V, Carg = lpeg.P, lpeg.V, lpeg.Carg
+
+local context = context
+local verbatim = context.verbatim
+local variables = interfaces.variables
+
+local makepattern = visualizers.makepattern
+local getvisualizer = visualizers.getvisualizer
+
+local nested = nil
+
+local donestedtypingstart = context.donestedtypingstart
+local donestedtypingstop = context.donestedtypingstop
+
+local v_none = variables.none
+local v_slanted = variables.slanted
+
+local handler = visualizers.newhandler {
+ initialize = function(settings)
+ local option = settings and settings.option
+ if not option or option == "" then
+ nested = nil
+ elseif option == v_slanted then
+ nested = nil
+ elseif option == v_none then
+ nested = nil
+ else
+ nested = getvisualizer(option,"direct")
+ end
+ end,
+ open = function()
+ donestedtypingstart()
+ end,
+ close = function()
+ donestedtypingstop()
+ end,
+ content = function(s)
+ if nested then
+ nested(s)
+ else
+ verbatim(s)
+ end
+ end,
+}
+
+local open = P("<<")
+local close = P(">>")
+local rest = (1 - open - close - patterns.space - patterns.newline)^1
+
+local grammar = visualizers.newgrammar("default", {
+
+ initialize = patterns.beginofstring * Carg(1) / handler.initialize,
+
+ open = makepattern(handler,"open",open),
+ close = makepattern(handler,"close",close),
+ rest = makepattern(handler,"content",rest),
+
+ nested = V("open") * (V("pattern")^0) * V("close"),
+ pattern = V("line") + V("space") + V("nested") + V("rest"),
+
+ visualizer = V("initialize") * (V("pattern")^1)
+
+} )
+
+local parser = P(grammar)
+
+visualizers.register("nested", { parser = parser, handler = handler, grammar = grammar } )
+
+-- lpeg.match(parser,[[<<tf<<sl>>tf<<sl>>tf>>]]) context.par()
+-- lpeg.match(parser,[[<<tf<<sl<<tf>>sl>>tf>>]]) context.par()
+-- lpeg.match(parser,[[sl<<tf<<sl>>tf>>sl]]) context.par()
diff --git a/tex/context/base/buff-imp-parsed-xml.lua b/tex/context/base/buff-imp-parsed-xml.lua
index ef80fccb4..22611ac8a 100644
--- a/tex/context/base/buff-imp-parsed-xml.lua
+++ b/tex/context/base/buff-imp-parsed-xml.lua
@@ -1,101 +1,101 @@
-if not modules then modules = { } end modules ['buff-imp-parsed-xml'] = {
- version = 1.001,
- comment = "companion to buff-imp-parsed-xml.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 context = context
-local verbatim = context.verbatim
-
-local write = visualizers.write
-local writespace = visualizers.writespace
-local writeargument = visualizers.writeargument
-
-local ParsedXmlSnippetKey = context.ParsedXmlSnippetKey
-local ParsedXmlSnippetValue = context.ParsedXmlSnippetValue
-
-local ParsedXmlSnippetElement = verbatim.ParsedXmlSnippetElement
-local ParsedXmlSnippetInstruction = verbatim.ParsedXmlSnippetInstruction
-local ParsedXmlSnippetComment = verbatim.ParsedXmlSnippetComment
-local ParsedXmlSnippetCdata = verbatim.ParsedXmlSnippetCdata
-local ParsedXmlSnippetDoctype = verbatim.ParsedXmlSnippetDoctype
-
-local startParsedXmlSnippet = context.startParsedXmlSnippet
-local stopParsedXmlSnippet = context.stopParsedXmlSnippet
-
-local parsedxmlhandler = xml.newhandlers { -- todo: treat spaces and tabs
- name = "parsedxml",
- handle = function(...)
- print("error:",...) -- we need a handler as fallback, even if not used
- end,
- functions = {
- ["@el@"] = function(e,handler)
- local at = e.at
- if at and next(at) then
- ParsedXmlSnippetElement(format("<%s",e.tg))
- for k, v in next, at do
- writespace()
- ParsedXmlSnippetKey()
- writeargument(k)
- verbatim("=")
- ParsedXmlSnippetValue()
- writeargument(format("%q",k))
- end
- ParsedXmlSnippetElement(">")
- else
- ParsedXmlSnippetElement(format("<%s>",e.tg))
- end
- handler.serialize(e.dt,handler)
- ParsedXmlSnippetElement(format("</%s>",e.tg))
- end,
- ["@pi@"] = function(e,handler)
- ParsedXmlSnippetInstruction("<?")
- write(e.dt[1])
- ParsedXmlSnippetInstruction("?>")
- end ,
- ["@cm@"] = function(e,handler)
- ParsedXmlSnippetComment("<!--")
- write(e.dt[1])
- ParsedXmlSnippetComment("-->")
- end,
- ["@cd@"] = function(e,handler)
- ParsedXmlSnippetCdata("<![CDATA[")
- write(e.dt[1])
- ParsedXmlSnippetCdata("]]>")
- end,
- ["@dt@"] = function(e,handler)
- ParsedXmlSnippetDoctype("<!DOCTYPE")
- write(e.dt[1])
- ParsedXmlSnippetDoctype(">")
- end,
- ["@tx@"] = function(s,handler)
- write(s)
- end,
- }
-}
-
-local function parsedxml(root,pattern)
- if root then
- if pattern then
- root = xml.filter(root,pattern)
- end
- if root then
- context.startParsedXmlSnippet()
- xml.serialize(root,parsedxmlhandler)
- context.stopParsedXmlSnippet()
- end
- end
-end
-
-local function parser(str,settings)
- parsedxml(xml.convert(str),settings and settings.pattern)
-end
-
-visualizers.parsedxml = parsedxml -- for use at the lua end (maybe namespace needed)
-
-visualizers.register("parsed-xml", { parser = parser } )
-
+if not modules then modules = { } end modules ['buff-imp-parsed-xml'] = {
+ version = 1.001,
+ comment = "companion to buff-imp-parsed-xml.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 context = context
+local verbatim = context.verbatim
+
+local write = visualizers.write
+local writespace = visualizers.writespace
+local writeargument = visualizers.writeargument
+
+local ParsedXmlSnippetKey = context.ParsedXmlSnippetKey
+local ParsedXmlSnippetValue = context.ParsedXmlSnippetValue
+
+local ParsedXmlSnippetElement = verbatim.ParsedXmlSnippetElement
+local ParsedXmlSnippetInstruction = verbatim.ParsedXmlSnippetInstruction
+local ParsedXmlSnippetComment = verbatim.ParsedXmlSnippetComment
+local ParsedXmlSnippetCdata = verbatim.ParsedXmlSnippetCdata
+local ParsedXmlSnippetDoctype = verbatim.ParsedXmlSnippetDoctype
+
+local startParsedXmlSnippet = context.startParsedXmlSnippet
+local stopParsedXmlSnippet = context.stopParsedXmlSnippet
+
+local parsedxmlhandler = xml.newhandlers { -- todo: treat spaces and tabs
+ name = "parsedxml",
+ handle = function(...)
+ print("error:",...) -- we need a handler as fallback, even if not used
+ end,
+ functions = {
+ ["@el@"] = function(e,handler)
+ local at = e.at
+ if at and next(at) then
+ ParsedXmlSnippetElement(format("<%s",e.tg))
+ for k, v in next, at do
+ writespace()
+ ParsedXmlSnippetKey()
+ writeargument(k)
+ verbatim("=")
+ ParsedXmlSnippetValue()
+ writeargument(format("%q",k))
+ end
+ ParsedXmlSnippetElement(">")
+ else
+ ParsedXmlSnippetElement(format("<%s>",e.tg))
+ end
+ handler.serialize(e.dt,handler)
+ ParsedXmlSnippetElement(format("</%s>",e.tg))
+ end,
+ ["@pi@"] = function(e,handler)
+ ParsedXmlSnippetInstruction("<?")
+ write(e.dt[1])
+ ParsedXmlSnippetInstruction("?>")
+ end ,
+ ["@cm@"] = function(e,handler)
+ ParsedXmlSnippetComment("<!--")
+ write(e.dt[1])
+ ParsedXmlSnippetComment("-->")
+ end,
+ ["@cd@"] = function(e,handler)
+ ParsedXmlSnippetCdata("<![CDATA[")
+ write(e.dt[1])
+ ParsedXmlSnippetCdata("]]>")
+ end,
+ ["@dt@"] = function(e,handler)
+ ParsedXmlSnippetDoctype("<!DOCTYPE")
+ write(e.dt[1])
+ ParsedXmlSnippetDoctype(">")
+ end,
+ ["@tx@"] = function(s,handler)
+ write(s)
+ end,
+ }
+}
+
+local function parsedxml(root,pattern)
+ if root then
+ if pattern then
+ root = xml.filter(root,pattern)
+ end
+ if root then
+ context.startParsedXmlSnippet()
+ xml.serialize(root,parsedxmlhandler)
+ context.stopParsedXmlSnippet()
+ end
+ end
+end
+
+local function parser(str,settings)
+ parsedxml(xml.convert(str),settings and settings.pattern)
+end
+
+visualizers.parsedxml = parsedxml -- for use at the lua end (maybe namespace needed)
+
+visualizers.register("parsed-xml", { parser = parser } )
+
diff --git a/tex/context/base/buff-imp-tex.lua b/tex/context/base/buff-imp-tex.lua
index cf7ea9796..29fd8c0c5 100644
--- a/tex/context/base/buff-imp-tex.lua
+++ b/tex/context/base/buff-imp-tex.lua
@@ -1,130 +1,130 @@
-if not modules then modules = { } end modules ['buff-imp-tex'] = {
- version = 1.001,
- comment = "companion to v-tex.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local P, S, V, patterns = lpeg.P, lpeg.S, lpeg.V, lpeg.patterns
-
-local context = context
-local verbatim = context.verbatim
-local makepattern = visualizers.makepattern
-local makenested = visualizers.makenested
-local getvisualizer = visualizers.getvisualizer
-
-local TexSnippet = context.TexSnippet
-local startTexSnippet = context.startTexSnippet
-local stopTexSnippet = context.stopTexSnippet
-
-local TexSnippetName = verbatim.TexSnippetName
-local TexSnippetGroup = verbatim.TexSnippetGroup
-local TexSnippetBoundary = verbatim.TexSnippetBoundary
-local TexSnippetSpecial = verbatim.TexSnippetSpecial
-local TexSnippetComment = verbatim.TexSnippetComment
-
-local handler = visualizers.newhandler {
- startinline = function() TexSnippet(false,"{") end,
- stopinline = function() context("}") end,
- startdisplay = function() startTexSnippet() end,
- stopdisplay = function() stopTexSnippet() end ,
- name = function(s) TexSnippetName(s) end,
- group = function(s) TexSnippetGroup(s) end,
- boundary = function(s) TexSnippetBoundary(s) end,
- special = function(s) TexSnippetSpecial(s) end,
- comment = function(s) TexSnippetComment(s) end,
-}
-
--- todo: unicode letters in control sequences (slow as we need to test the nature)
-
-local comment = S("%")
-local name = P("\\") * (patterns.letter + S("@!?"))^1
-local escape = P("\\") * (patterns.anything - patterns.newline)^-1 -- else we get \n
-local group = S("${}")
-local boundary = S('[]()<>#="')
-local special = S("/^_-&+'`|")
-
-local p_comment = makepattern(handler,"comment",comment)
- * (V("space") + V("content"))^0
-local p_name = makepattern(handler,"name",name)
-local p_escape = makepattern(handler,"name",escape)
-local p_group = makepattern(handler,"group",group)
-local p_boundary = makepattern(handler,"boundary",boundary)
-local p_special = makepattern(handler,"special",special)
-local p_somespace = V("newline") * V("emptyline")^0 * V("beginline")
- + V("space")
-
---~ local pattern = visualizers.pattern
-
-local grammar = visualizers.newgrammar("default", { "visualizer",
-
- comment = p_comment,
- name = p_name,
- escape = p_escape,
- group = p_group,
- boundary = p_boundary,
- special = p_special,
- somespace = p_somespace,
-
- pattern = V("comment")
- + V("name") + V("escape") + V("group") + V("boundary") + V("special")
- + V("newline") * V("emptyline")^0 * V("beginline")
- + V("space")
- + V("default"),
-
- visualizer = V("pattern")^1
-
-} )
-
-local parser = P(grammar)
-
-visualizers.register("tex", { parser = parser, handler = handler, grammar = grammar } )
-
-local function makecommand(handler,how,start,left,right)
- local c, l, r, f = P(start), P(left), P(right), how
- local n = ( P { l * ((1 - (l + r)) + V(1))^0 * r } + P(1-r) )^0
- if type(how) == "string" then
- f = function(s) getvisualizer(how,"direct")(s) end
- end
- return makepattern(handler,"name",c)
- * V("somespace")^0
- * makepattern(handler,"group",l)
- * (n/f)
- * makepattern(handler,"group",r)
-end
-
-local grammar = visualizers.newgrammar("default", { "visualizer",
-
- comment = p_comment,
- name = p_name,
- escape = p_escape,
- group = p_group,
- boundary = p_boundary,
- special = p_special,
- somespace = p_somespace,
-
- mpcode = makenested(handler,"mp","\\startMPcode","\\stopMPcode")
- + makenested(handler,"mp","\\startMPgraphic","\\stopMPgraphic")
- + makenested(handler,"mp","\\startuseMPgraphic","\\stopuseMPgraphic")
- + makenested(handler,"mp","\\startreusableMPgraphic","\\stopreusableMPgraphic")
- + makenested(handler,"mp","\\startuniqueMPgraphic","\\stopuniqueMPgraphic")
- + makenested(handler,"mp","\\startMPpage","\\stopMPpage"),
-
- luacode = makenested (handler,"lua","\\startluacode","\\stopluacode")
- + makecommand(handler,"lua","\\ctxlua","{","}"),
-
- pattern = V("comment")
- + V("mpcode") + V("luacode")
- + V("name") + V("escape") + V("group") + V("boundary") + V("special")
- + V("newline") * V("emptyline")^0 * V("beginline")
- + V("space")
- + V("default"),
-
- visualizer = V("pattern")^1
-
-} )
-
-local parser = P(grammar)
-
-visualizers.register("context", { parser = parser, handler = handler, grammar = grammar } )
+if not modules then modules = { } end modules ['buff-imp-tex'] = {
+ version = 1.001,
+ comment = "companion to v-tex.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local P, S, V, patterns = lpeg.P, lpeg.S, lpeg.V, lpeg.patterns
+
+local context = context
+local verbatim = context.verbatim
+local makepattern = visualizers.makepattern
+local makenested = visualizers.makenested
+local getvisualizer = visualizers.getvisualizer
+
+local TexSnippet = context.TexSnippet
+local startTexSnippet = context.startTexSnippet
+local stopTexSnippet = context.stopTexSnippet
+
+local TexSnippetName = verbatim.TexSnippetName
+local TexSnippetGroup = verbatim.TexSnippetGroup
+local TexSnippetBoundary = verbatim.TexSnippetBoundary
+local TexSnippetSpecial = verbatim.TexSnippetSpecial
+local TexSnippetComment = verbatim.TexSnippetComment
+
+local handler = visualizers.newhandler {
+ startinline = function() TexSnippet(false,"{") end,
+ stopinline = function() context("}") end,
+ startdisplay = function() startTexSnippet() end,
+ stopdisplay = function() stopTexSnippet() end ,
+ name = function(s) TexSnippetName(s) end,
+ group = function(s) TexSnippetGroup(s) end,
+ boundary = function(s) TexSnippetBoundary(s) end,
+ special = function(s) TexSnippetSpecial(s) end,
+ comment = function(s) TexSnippetComment(s) end,
+}
+
+-- todo: unicode letters in control sequences (slow as we need to test the nature)
+
+local comment = S("%")
+local name = P("\\") * (patterns.letter + S("@!?"))^1
+local escape = P("\\") * (patterns.anything - patterns.newline)^-1 -- else we get \n
+local group = S("${}")
+local boundary = S('[]()<>#="')
+local special = S("/^_-&+'`|")
+
+local p_comment = makepattern(handler,"comment",comment)
+ * (V("space") + V("content"))^0
+local p_name = makepattern(handler,"name",name)
+local p_escape = makepattern(handler,"name",escape)
+local p_group = makepattern(handler,"group",group)
+local p_boundary = makepattern(handler,"boundary",boundary)
+local p_special = makepattern(handler,"special",special)
+local p_somespace = V("newline") * V("emptyline")^0 * V("beginline")
+ + V("space")
+
+--~ local pattern = visualizers.pattern
+
+local grammar = visualizers.newgrammar("default", { "visualizer",
+
+ comment = p_comment,
+ name = p_name,
+ escape = p_escape,
+ group = p_group,
+ boundary = p_boundary,
+ special = p_special,
+ somespace = p_somespace,
+
+ pattern = V("comment")
+ + V("name") + V("escape") + V("group") + V("boundary") + V("special")
+ + V("newline") * V("emptyline")^0 * V("beginline")
+ + V("space")
+ + V("default"),
+
+ visualizer = V("pattern")^1
+
+} )
+
+local parser = P(grammar)
+
+visualizers.register("tex", { parser = parser, handler = handler, grammar = grammar } )
+
+local function makecommand(handler,how,start,left,right)
+ local c, l, r, f = P(start), P(left), P(right), how
+ local n = ( P { l * ((1 - (l + r)) + V(1))^0 * r } + P(1-r) )^0
+ if type(how) == "string" then
+ f = function(s) getvisualizer(how,"direct")(s) end
+ end
+ return makepattern(handler,"name",c)
+ * V("somespace")^0
+ * makepattern(handler,"group",l)
+ * (n/f)
+ * makepattern(handler,"group",r)
+end
+
+local grammar = visualizers.newgrammar("default", { "visualizer",
+
+ comment = p_comment,
+ name = p_name,
+ escape = p_escape,
+ group = p_group,
+ boundary = p_boundary,
+ special = p_special,
+ somespace = p_somespace,
+
+ mpcode = makenested(handler,"mp","\\startMPcode","\\stopMPcode")
+ + makenested(handler,"mp","\\startMPgraphic","\\stopMPgraphic")
+ + makenested(handler,"mp","\\startuseMPgraphic","\\stopuseMPgraphic")
+ + makenested(handler,"mp","\\startreusableMPgraphic","\\stopreusableMPgraphic")
+ + makenested(handler,"mp","\\startuniqueMPgraphic","\\stopuniqueMPgraphic")
+ + makenested(handler,"mp","\\startMPpage","\\stopMPpage"),
+
+ luacode = makenested (handler,"lua","\\startluacode","\\stopluacode")
+ + makecommand(handler,"lua","\\ctxlua","{","}"),
+
+ pattern = V("comment")
+ + V("mpcode") + V("luacode")
+ + V("name") + V("escape") + V("group") + V("boundary") + V("special")
+ + V("newline") * V("emptyline")^0 * V("beginline")
+ + V("space")
+ + V("default"),
+
+ visualizer = V("pattern")^1
+
+} )
+
+local parser = P(grammar)
+
+visualizers.register("context", { parser = parser, handler = handler, grammar = grammar } )
diff --git a/tex/context/base/buff-imp-xml.lua b/tex/context/base/buff-imp-xml.lua
index 298a98ac0..0c48ed3b0 100644
--- a/tex/context/base/buff-imp-xml.lua
+++ b/tex/context/base/buff-imp-xml.lua
@@ -1,133 +1,133 @@
-if not modules then modules = { } end modules ['buff-imp-xml'] = {
- version = 1.001,
- comment = "companion to v-xml.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local P, S, V, patterns = lpeg.P, lpeg.S, lpeg.V, lpeg.patterns
-
-local context = context
-local verbatim = context.verbatim
-local makepattern = visualizers.makepattern
-
-local XmlSnippet = context.XmlSnippet
-local startXmlSnippet = context.startXmlSnippet
-local stopXmlSnippet = context.stopXmlSnippet
-
-local XmlSnippetName = verbatim.XmlSnippetName
-local XmlSnippetKey = verbatim.XmlSnippetKey
-local XmlSnippetBoundary = verbatim.XmlSnippetBoundary
-local XmlSnippetString = verbatim.XmlSnippetString
-local XmlSnippetEqual = verbatim.XmlSnippetEqual
-local XmlSnippetEntity = verbatim.XmlSnippetEntity
-local XmlSnippetComment = verbatim.XmlSnippetComment
-local XmlSnippetCdata = verbatim.XmlSnippetCdata
-
-local handler = visualizers.newhandler {
- startinline = function() XmlSnippet(false,"{") end,
- stopinline = function() context("}") end,
- startdisplay = function() startXmlSnippet() end,
- stopdisplay = function() stopXmlSnippet () end,
- name = function(s) XmlSnippetName(s) end,
- key = function(s) XmlSnippetKey(s) end,
- boundary = function(s) XmlSnippetBoundary(s) end,
- string = function(s) XmlSnippetString(s) end,
- equal = function(s) XmlSnippetEqual(s) end,
- entity = function(s) XmlSnippetEntity(s) end,
- comment = function(s) XmlSnippetComment(s) end,
- cdata = function(s) XmlSnippetCdata(s) end,
-}
-
-local comment = P("--")
-local name = (patterns.letter + patterns.digit + S('_-.'))^1
-local entity = P("&") * (1-P(";"))^1 * P(";")
-local openbegin = P("<")
-local openend = P("</")
-local closebegin = P("/>") + P(">")
-local closeend = P(">")
-local opencomment = P("<!--")
-local closecomment = P("-->")
-local openinstruction = P("<?")
-local closeinstruction = P("?>")
-local opencdata = P("<![CDATA[")
-local closecdata = P("]]>")
-
-local grammar = visualizers.newgrammar("default", { "visualizer",
- sstring =
- makepattern(handler,"string",patterns.dquote)
- * (V("whitespace") + makepattern(handler,"default",1-patterns.dquote))^0
- * makepattern(handler,"string",patterns.dquote),
- dstring =
- makepattern(handler,"string",patterns.squote)
- * (V("whitespace") + makepattern(handler,"default",1-patterns.squote))^0
- * makepattern(handler,"string",patterns.squote),
- entity =
- makepattern(handler,"entity",entity),
- name =
- makepattern(handler,"name",name)
- * (
- makepattern(handler,"default",patterns.colon)
- * makepattern(handler,"name",name)
- )^0,
- key =
- makepattern(handler,"key",name)
- * (
- makepattern(handler,"default",patterns.colon)
- * makepattern(handler,"key",name)
- )^0,
- attributes = (
- V("optionalwhitespace")
- * V("key")
- * V("optionalwhitespace")
- * makepattern(handler,"equal",patterns.equal)
- * V("optionalwhitespace")
- * (V("dstring") + V("sstring"))
- * V("optionalwhitespace")
- )^0,
- open =
- makepattern(handler,"boundary",openbegin)
- * V("name")
- * V("optionalwhitespace")
- * V("attributes")
- * makepattern(handler,"boundary",closebegin),
- close =
- makepattern(handler,"boundary",openend)
- * V("name")
- * V("optionalwhitespace")
- * makepattern(handler,"boundary",closeend),
- comment =
- makepattern(handler,"boundary",opencomment)
- * (V("whitespace") + makepattern(handler,"comment",(1-closecomment)^1))^0 -- slow
- * makepattern(handler,"boundary",closecomment),
- cdata =
- makepattern(handler,"boundary",opencdata)
- * (V("whitespace") + makepattern(handler,"comment",(1-closecdata)^1))^0 -- slow
- * makepattern(handler,"boundary",closecdata),
- instruction =
- makepattern(handler,"boundary",openinstruction)
- * V("name")
- * V("optionalwhitespace")
- * V("attributes")
- * V("optionalwhitespace")
- * makepattern(handler,"boundary",closeinstruction),
-
- pattern =
- V("comment")
- + V("instruction")
- + V("cdata")
- + V("close")
- + V("open")
- + V("entity")
- + V("space")
- + V("line")
- + V("default"),
-
- visualizer =
- V("pattern")^1
-} )
-
-local parser = P(grammar)
-
-visualizers.register("xml", { parser = parser, handler = handler, grammar = grammar } )
+if not modules then modules = { } end modules ['buff-imp-xml'] = {
+ version = 1.001,
+ comment = "companion to v-xml.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local P, S, V, patterns = lpeg.P, lpeg.S, lpeg.V, lpeg.patterns
+
+local context = context
+local verbatim = context.verbatim
+local makepattern = visualizers.makepattern
+
+local XmlSnippet = context.XmlSnippet
+local startXmlSnippet = context.startXmlSnippet
+local stopXmlSnippet = context.stopXmlSnippet
+
+local XmlSnippetName = verbatim.XmlSnippetName
+local XmlSnippetKey = verbatim.XmlSnippetKey
+local XmlSnippetBoundary = verbatim.XmlSnippetBoundary
+local XmlSnippetString = verbatim.XmlSnippetString
+local XmlSnippetEqual = verbatim.XmlSnippetEqual
+local XmlSnippetEntity = verbatim.XmlSnippetEntity
+local XmlSnippetComment = verbatim.XmlSnippetComment
+local XmlSnippetCdata = verbatim.XmlSnippetCdata
+
+local handler = visualizers.newhandler {
+ startinline = function() XmlSnippet(false,"{") end,
+ stopinline = function() context("}") end,
+ startdisplay = function() startXmlSnippet() end,
+ stopdisplay = function() stopXmlSnippet () end,
+ name = function(s) XmlSnippetName(s) end,
+ key = function(s) XmlSnippetKey(s) end,
+ boundary = function(s) XmlSnippetBoundary(s) end,
+ string = function(s) XmlSnippetString(s) end,
+ equal = function(s) XmlSnippetEqual(s) end,
+ entity = function(s) XmlSnippetEntity(s) end,
+ comment = function(s) XmlSnippetComment(s) end,
+ cdata = function(s) XmlSnippetCdata(s) end,
+}
+
+local comment = P("--")
+local name = (patterns.letter + patterns.digit + S('_-.'))^1
+local entity = P("&") * (1-P(";"))^1 * P(";")
+local openbegin = P("<")
+local openend = P("</")
+local closebegin = P("/>") + P(">")
+local closeend = P(">")
+local opencomment = P("<!--")
+local closecomment = P("-->")
+local openinstruction = P("<?")
+local closeinstruction = P("?>")
+local opencdata = P("<![CDATA[")
+local closecdata = P("]]>")
+
+local grammar = visualizers.newgrammar("default", { "visualizer",
+ sstring =
+ makepattern(handler,"string",patterns.dquote)
+ * (V("whitespace") + makepattern(handler,"default",1-patterns.dquote))^0
+ * makepattern(handler,"string",patterns.dquote),
+ dstring =
+ makepattern(handler,"string",patterns.squote)
+ * (V("whitespace") + makepattern(handler,"default",1-patterns.squote))^0
+ * makepattern(handler,"string",patterns.squote),
+ entity =
+ makepattern(handler,"entity",entity),
+ name =
+ makepattern(handler,"name",name)
+ * (
+ makepattern(handler,"default",patterns.colon)
+ * makepattern(handler,"name",name)
+ )^0,
+ key =
+ makepattern(handler,"key",name)
+ * (
+ makepattern(handler,"default",patterns.colon)
+ * makepattern(handler,"key",name)
+ )^0,
+ attributes = (
+ V("optionalwhitespace")
+ * V("key")
+ * V("optionalwhitespace")
+ * makepattern(handler,"equal",patterns.equal)
+ * V("optionalwhitespace")
+ * (V("dstring") + V("sstring"))
+ * V("optionalwhitespace")
+ )^0,
+ open =
+ makepattern(handler,"boundary",openbegin)
+ * V("name")
+ * V("optionalwhitespace")
+ * V("attributes")
+ * makepattern(handler,"boundary",closebegin),
+ close =
+ makepattern(handler,"boundary",openend)
+ * V("name")
+ * V("optionalwhitespace")
+ * makepattern(handler,"boundary",closeend),
+ comment =
+ makepattern(handler,"boundary",opencomment)
+ * (V("whitespace") + makepattern(handler,"comment",(1-closecomment)^1))^0 -- slow
+ * makepattern(handler,"boundary",closecomment),
+ cdata =
+ makepattern(handler,"boundary",opencdata)
+ * (V("whitespace") + makepattern(handler,"comment",(1-closecdata)^1))^0 -- slow
+ * makepattern(handler,"boundary",closecdata),
+ instruction =
+ makepattern(handler,"boundary",openinstruction)
+ * V("name")
+ * V("optionalwhitespace")
+ * V("attributes")
+ * V("optionalwhitespace")
+ * makepattern(handler,"boundary",closeinstruction),
+
+ pattern =
+ V("comment")
+ + V("instruction")
+ + V("cdata")
+ + V("close")
+ + V("open")
+ + V("entity")
+ + V("space")
+ + V("line")
+ + V("default"),
+
+ visualizer =
+ V("pattern")^1
+} )
+
+local parser = P(grammar)
+
+visualizers.register("xml", { parser = parser, handler = handler, grammar = grammar } )
diff --git a/tex/context/base/buff-ini.lua b/tex/context/base/buff-ini.lua
index 358c0f2a7..475d23efe 100644
--- a/tex/context/base/buff-ini.lua
+++ b/tex/context/base/buff-ini.lua
@@ -1,366 +1,366 @@
-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 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_grabbing = logs.reporter("buffers","grabbing")
-
-local context, commands = context, commands
-
-local concat = table.concat
-local type, next, load = type, next, load
-local sub, format = string.sub, string.format
-local splitlines, validstring = string.splitlines, string.valid
-local P, Cs, patterns, lpegmatch = lpeg.P, lpeg.Cs, lpeg.patterns, lpeg.match
-
-local variables = interfaces.variables
-local settings_to_array = utilities.parsers.settings_to_array
-local formatters = string.formatters
-
-local v_yes = variables.yes
-
-local catcodenumbers = catcodes.numbers
-
-local ctxcatcodes = catcodenumbers.ctxcatcodes
-local txtcatcodes = catcodenumbers.txtcatcodes
-
-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 }
-end
-
-local function append(name,str)
- local buffer = cache[name]
- if buffer then
- buffer.data = buffer.data .. str
- else
- cache[name] = { data = str }
- end
-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 collectcontent(names,separator) -- no print
- if type(names) == "string" then
- names = settings_to_array(names)
- end
- local nnames = #names
- if nnames == 0 then
- return getcontent("") -- default buffer
- elseif nnames == 1 then
- return getcontent(names[1])
- else
- local t, n = { }, 0
- for i=1,nnames do
- local c = getcontent(names[i])
- if c ~= "" then
- n = n + 1
- t[n] = c
- end
- end
- return concat(t,separator or "\n") -- was \r
- end
-end
-
-local function loadcontent(names) -- no print
- if type(names) == "string" then
- names = settings_to_array(names)
- end
- local nnames = #names
- local ok = false
- if nnames == 0 then
- ok = load(getcontent("")) -- default buffer
- elseif nnames == 1 then
- ok = load(getcontent(names[1]))
- else
- -- lua 5.2 chunked load
- local i = 0
- ok = load(function()
- while true do
- i = i + 1
- if i > nnames then
- return nil
- end
- local c = getcontent(names[i])
- if c == "" then
- -- would trigger end of load
- else
- return c
- end
- end
- end)
- end
- if ok then
- return ok()
- elseif nnames == 0 then
- report_buffers("invalid lua code in default buffer")
- else
- report_buffers("invalid lua code in buffer %a",concat(names,","))
- end
-end
-
-
-buffers.raw = getcontent
-buffers.erase = erase
-buffers.assign = assign
-buffers.append = append
-buffers.exists = exists
-buffers.getcontent = getcontent
-buffers.getlines = getlines
-buffers.collectcontent = collectcontent
-buffers.loadcontent = loadcontent
-
--- the context interface
-
-commands.erasebuffer = erase
-commands.assignbuffer = assign
-
-local anything = patterns.anything
-local alwaysmatched = patterns.alwaysmatched
-
-local function countnesting(b,e)
- local n
- local g = P(b) / function() n = n + 1 end
- + P(e) / function() n = n - 1 end
- + anything
- 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 indentatio 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 getmargin = (Cs(P(" ")^1)*P(-1)+1)^1
-local eol = patterns.eol
-local whatever = (P(1)-eol)^0 * eol^1
-
-local strippers = { }
-
-local function undent(str) -- new version, needs testing
- local margin = lpegmatch(getmargin,str)
- if type(margin) ~= "string" then
- return str
- end
- local indent = #margin
- if indent == 0 then
- return str
- end
- local stripper = strippers[indent]
- if not stripper then
- stripper = Cs((P(margin)/"" * whatever + eol^1)^1)
- strippers[indent] = stripper
- end
- return lpegmatch(stripper,str) or str
-end
-
-function commands.grabbuffer(name,begintag,endtag,bufferdata,catcodes) -- 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 autoundent then
- dn = undent(dn)
- end
- end
- assign(name,dn,catcodes)
- commands.doifelse(more)
-end
-
--- The optional prefix hack is there for the typesetbuffer feature and
--- in mkii we needed that (this hidden feature is used in a manual).
-
-local function prepared(name,list,prefix) -- list 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 then
- local name = file.addsuffix(name,"tmp")
- return tex.jobname .. "-" .. name, content
- else
- return name, content
- end
-end
-
-local capsule = "\\starttext\n%s\n\\stoptext\n"
-local command = "context %s"
-
-function commands.runbuffer(name,list,encapsulate)
- local name, content = prepared(name,list)
- if encapsulate then
- content = format(capsule,content)
- end
- local data = io.loaddata(name)
- if data ~= content then
- if trace_run then
- report_buffers("changes in %a, processing forced",name)
- end
- io.savedata(name,content)
- os.execute(format(command,name))
- elseif trace_run then
- report_buffers("no changes in %a, not processed",name)
- end
-end
-
-function commands.savebuffer(list,name,prefix) -- name is optional
- local name, content = prepared(name,list,prefix==v_yes)
- io.savedata(name,content)
-end
-
-function commands.getbuffer(name)
- local str = getcontent(name)
- if str ~= "" then
- context.viafile(str,formatters["buffer.%s"](validstring(name,"noname")))
- end
-end
-
-function commands.getbuffermkvi(name) -- rather direct !
- context.viafile(resolvers.macros.preprocessed(getcontent(name)),formatters["buffer.%s.mkiv"](validstring(name,"noname")))
-end
-
-function commands.gettexbuffer(name)
- local buffer = name and cache[name]
- if buffer and buffer.data ~= "" then
- context.pushcatcodetable()
- if buffer.catcodes == txtcatcodes then
- context.setcatcodetable(txtcatcodes)
- else
- context.setcatcodetable(ctxcatcodes)
- end
- -- context(function() context.viafile(buffer.data) end)
- context.getbuffer { name } -- viafile flushes too soon
- context.popcatcodetable()
- end
-end
-
-commands.getbufferctxlua = loadcontent
-
-function commands.doifelsebuffer(name)
- commands.doifelse(exists(name))
-end
-
--- 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.
-
--- function commands.feedback(names)
--- texprint(ctxcatcodes,splitlines(collectcontent(names)))
--- end
-
-function commands.feedback(names) -- bad name, maybe rename to injectbuffercontent
- context.printlines(collectcontent(names))
-end
+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 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_grabbing = logs.reporter("buffers","grabbing")
+
+local context, commands = context, commands
+
+local concat = table.concat
+local type, next, load = type, next, load
+local sub, format = string.sub, string.format
+local splitlines, validstring = string.splitlines, string.valid
+local P, Cs, patterns, lpegmatch = lpeg.P, lpeg.Cs, lpeg.patterns, lpeg.match
+
+local variables = interfaces.variables
+local settings_to_array = utilities.parsers.settings_to_array
+local formatters = string.formatters
+
+local v_yes = variables.yes
+
+local catcodenumbers = catcodes.numbers
+
+local ctxcatcodes = catcodenumbers.ctxcatcodes
+local txtcatcodes = catcodenumbers.txtcatcodes
+
+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 }
+end
+
+local function append(name,str)
+ local buffer = cache[name]
+ if buffer then
+ buffer.data = buffer.data .. str
+ else
+ cache[name] = { data = str }
+ end
+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 collectcontent(names,separator) -- no print
+ if type(names) == "string" then
+ names = settings_to_array(names)
+ end
+ local nnames = #names
+ if nnames == 0 then
+ return getcontent("") -- default buffer
+ elseif nnames == 1 then
+ return getcontent(names[1])
+ else
+ local t, n = { }, 0
+ for i=1,nnames do
+ local c = getcontent(names[i])
+ if c ~= "" then
+ n = n + 1
+ t[n] = c
+ end
+ end
+ return concat(t,separator or "\n") -- was \r
+ end
+end
+
+local function loadcontent(names) -- no print
+ if type(names) == "string" then
+ names = settings_to_array(names)
+ end
+ local nnames = #names
+ local ok = false
+ if nnames == 0 then
+ ok = load(getcontent("")) -- default buffer
+ elseif nnames == 1 then
+ ok = load(getcontent(names[1]))
+ else
+ -- lua 5.2 chunked load
+ local i = 0
+ ok = load(function()
+ while true do
+ i = i + 1
+ if i > nnames then
+ return nil
+ end
+ local c = getcontent(names[i])
+ if c == "" then
+ -- would trigger end of load
+ else
+ return c
+ end
+ end
+ end)
+ end
+ if ok then
+ return ok()
+ elseif nnames == 0 then
+ report_buffers("invalid lua code in default buffer")
+ else
+ report_buffers("invalid lua code in buffer %a",concat(names,","))
+ end
+end
+
+
+buffers.raw = getcontent
+buffers.erase = erase
+buffers.assign = assign
+buffers.append = append
+buffers.exists = exists
+buffers.getcontent = getcontent
+buffers.getlines = getlines
+buffers.collectcontent = collectcontent
+buffers.loadcontent = loadcontent
+
+-- the context interface
+
+commands.erasebuffer = erase
+commands.assignbuffer = assign
+
+local anything = patterns.anything
+local alwaysmatched = patterns.alwaysmatched
+
+local function countnesting(b,e)
+ local n
+ local g = P(b) / function() n = n + 1 end
+ + P(e) / function() n = n - 1 end
+ + anything
+ 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 indentatio 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 getmargin = (Cs(P(" ")^1)*P(-1)+1)^1
+local eol = patterns.eol
+local whatever = (P(1)-eol)^0 * eol^1
+
+local strippers = { }
+
+local function undent(str) -- new version, needs testing
+ local margin = lpegmatch(getmargin,str)
+ if type(margin) ~= "string" then
+ return str
+ end
+ local indent = #margin
+ if indent == 0 then
+ return str
+ end
+ local stripper = strippers[indent]
+ if not stripper then
+ stripper = Cs((P(margin)/"" * whatever + eol^1)^1)
+ strippers[indent] = stripper
+ end
+ return lpegmatch(stripper,str) or str
+end
+
+function commands.grabbuffer(name,begintag,endtag,bufferdata,catcodes) -- 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 autoundent then
+ dn = undent(dn)
+ end
+ end
+ assign(name,dn,catcodes)
+ commands.doifelse(more)
+end
+
+-- The optional prefix hack is there for the typesetbuffer feature and
+-- in mkii we needed that (this hidden feature is used in a manual).
+
+local function prepared(name,list,prefix) -- list 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 then
+ local name = file.addsuffix(name,"tmp")
+ return tex.jobname .. "-" .. name, content
+ else
+ return name, content
+ end
+end
+
+local capsule = "\\starttext\n%s\n\\stoptext\n"
+local command = "context %s"
+
+function commands.runbuffer(name,list,encapsulate)
+ local name, content = prepared(name,list)
+ if encapsulate then
+ content = format(capsule,content)
+ end
+ local data = io.loaddata(name)
+ if data ~= content then
+ if trace_run then
+ report_buffers("changes in %a, processing forced",name)
+ end
+ io.savedata(name,content)
+ os.execute(format(command,name))
+ elseif trace_run then
+ report_buffers("no changes in %a, not processed",name)
+ end
+end
+
+function commands.savebuffer(list,name,prefix) -- name is optional
+ local name, content = prepared(name,list,prefix==v_yes)
+ io.savedata(name,content)
+end
+
+function commands.getbuffer(name)
+ local str = getcontent(name)
+ if str ~= "" then
+ context.viafile(str,formatters["buffer.%s"](validstring(name,"noname")))
+ end
+end
+
+function commands.getbuffermkvi(name) -- rather direct !
+ context.viafile(resolvers.macros.preprocessed(getcontent(name)),formatters["buffer.%s.mkiv"](validstring(name,"noname")))
+end
+
+function commands.gettexbuffer(name)
+ local buffer = name and cache[name]
+ if buffer and buffer.data ~= "" then
+ context.pushcatcodetable()
+ if buffer.catcodes == txtcatcodes then
+ context.setcatcodetable(txtcatcodes)
+ else
+ context.setcatcodetable(ctxcatcodes)
+ end
+ -- context(function() context.viafile(buffer.data) end)
+ context.getbuffer { name } -- viafile flushes too soon
+ context.popcatcodetable()
+ end
+end
+
+commands.getbufferctxlua = loadcontent
+
+function commands.doifelsebuffer(name)
+ commands.doifelse(exists(name))
+end
+
+-- 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.
+
+-- function commands.feedback(names)
+-- texprint(ctxcatcodes,splitlines(collectcontent(names)))
+-- end
+
+function commands.feedback(names) -- bad name, maybe rename to injectbuffercontent
+ context.printlines(collectcontent(names))
+end
diff --git a/tex/context/base/buff-par.lua b/tex/context/base/buff-par.lua
index e0d32274f..2c1cd40e9 100644
--- a/tex/context/base/buff-par.lua
+++ b/tex/context/base/buff-par.lua
@@ -1,184 +1,184 @@
-if not modules then modules = { } end modules ['buff-par'] = {
- 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 context, commands = context, commands
-
-local insert, remove, find, gmatch = table.insert, table.remove, string.find, string.gmatch
-local strip, format = string.strip, string.format
-
-local trace_parallel = false trackers.register("buffers.parallel", function(v) trace_parallel = v end)
-
-local report_parallel = logs.reporter("buffers","parallel")
-
-local variables = interfaces.variables
-
-local parallel = buffers.parallel or { }
-buffers.parallel = parallel
-
-local settings_to_array = utilities.parsers.settings_to_array
-
-local data = { }
-
-function parallel.define(category,tags)
- local tags = settings_to_array(tags)
- local entries = { }
- data[category] = {
- tags = tags,
- entries = entries,
- }
- for i=1,#tags do
- entries[tags[i]] = {
- lines = { },
- number = 0,
- }
- end
-end
-
-function parallel.reset(category,tags)
- if not tags or tags == "" or tags == variables.all then
- tags = table.keys(entries)
- else
- tags = settings_to_array(tags)
- end
- for i=1,#tags do
- entries[tags[i]] = {
- lines = { },
- number = 0,
- }
- end
-end
-
-function parallel.next(category)
- local dc = data[category]
- local tags = dc.tags
- local entries = dc.entries
- for i=1,#tags do
- insert(entries[tags[i]].lines, { })
- end
-end
-
-function parallel.save(category,tag,content)
- local dc = data[category]
- if not dc then
- return
- end
- local entries = dc.entries[tag]
- if not entries then
- return
- end
- local lines = entries.lines
- if not lines then
- return
- end
- local line = lines[#lines]
- if not line then
- return
- end
- -- maybe no strip
- -- use lpeg
- if find(content,"%s*%[") then
- local done = false
- for label, content in gmatch(content,"%s*%[(.-)%]%s*([^%[]+)") do
- if done then
- line = { }
- insert(lines,line)
- else
- done = true
- end
- if trace_parallel and label ~= "" then
- report_parallel("reference found of category %a, tag %a, label %a",category,tag,label)
- end
- line.label = label
- line.content = strip(content)
- end
- else
- line.content = strip(content)
- line.label = ""
- end
-end
-
-function parallel.hassomecontent(category,tags)
- local dc = data[category]
- if not dc then
- return false
- end
- local entries = dc.entries
- if not tags or tags == "" or tags == variables.all then
- tags = table.keys(entries)
- else
- tags = utilities.parsers.settings_to_array(tags)
- end
- for t=1,#tags do
- local tag = tags[t]
- local lines = entries[tag].lines
- for i=1,#lines do
- local content = lines[i].content
- if content and content ~= "" then
- return true
- end
- end
- end
- return false
-end
-
-local save = resolvers.savers.byscheme
-
-function parallel.place(category,tags,options)
- local dc = data[category]
- if not dc then
- return
- end
- local entries = dc.entries
- local tags = utilities.parsers.settings_to_array(tags)
- local options = utilities.parsers.settings_to_hash(options)
- local start, n, criterium = options.start, options.n, options.criterium
- start, n = start and tonumber(start), n and tonumber(n)
- local max = 1
- if n then
- max = n
- elseif criterium == variables.all then
- max = 0
- for t=1,#tags do
- local tag = tags[t]
- local lines = entries[tag].lines
- if #lines > max then
- max = #lines
- end
- end
- end
- for i=1,max do
- for t=1,#tags do
- local tag = tags[t]
- local entry = entries[tag]
- if entry then
- local lines = entry.lines
- local number = entry.number + 1
- entry.number = number
- local line = remove(lines,1)
- if line and line.content then
- local content = format("\\input{%s}",save("virtual","parallel",line.content))
- context.doflushparallel(tag,1,number,line.label,content)
- else
- context.doflushparallel(tag,0,number,"","")
- end
- end
- end
- end
-end
-
--- interface
-
-commands.defineparallel = parallel.define
-commands.nextparallel = parallel.next
-commands.saveparallel = parallel.save
-commands.placeparallel = parallel.place
-commands.resetparallel = parallel.reset
-
-function commands.doifelseparallel(category,tags)
- commands.doifelse(parallel.hassomecontent(category,tags))
-end
+if not modules then modules = { } end modules ['buff-par'] = {
+ 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 context, commands = context, commands
+
+local insert, remove, find, gmatch = table.insert, table.remove, string.find, string.gmatch
+local strip, format = string.strip, string.format
+
+local trace_parallel = false trackers.register("buffers.parallel", function(v) trace_parallel = v end)
+
+local report_parallel = logs.reporter("buffers","parallel")
+
+local variables = interfaces.variables
+
+local parallel = buffers.parallel or { }
+buffers.parallel = parallel
+
+local settings_to_array = utilities.parsers.settings_to_array
+
+local data = { }
+
+function parallel.define(category,tags)
+ local tags = settings_to_array(tags)
+ local entries = { }
+ data[category] = {
+ tags = tags,
+ entries = entries,
+ }
+ for i=1,#tags do
+ entries[tags[i]] = {
+ lines = { },
+ number = 0,
+ }
+ end
+end
+
+function parallel.reset(category,tags)
+ if not tags or tags == "" or tags == variables.all then
+ tags = table.keys(entries)
+ else
+ tags = settings_to_array(tags)
+ end
+ for i=1,#tags do
+ entries[tags[i]] = {
+ lines = { },
+ number = 0,
+ }
+ end
+end
+
+function parallel.next(category)
+ local dc = data[category]
+ local tags = dc.tags
+ local entries = dc.entries
+ for i=1,#tags do
+ insert(entries[tags[i]].lines, { })
+ end
+end
+
+function parallel.save(category,tag,content)
+ local dc = data[category]
+ if not dc then
+ return
+ end
+ local entries = dc.entries[tag]
+ if not entries then
+ return
+ end
+ local lines = entries.lines
+ if not lines then
+ return
+ end
+ local line = lines[#lines]
+ if not line then
+ return
+ end
+ -- maybe no strip
+ -- use lpeg
+ if find(content,"%s*%[") then
+ local done = false
+ for label, content in gmatch(content,"%s*%[(.-)%]%s*([^%[]+)") do
+ if done then
+ line = { }
+ insert(lines,line)
+ else
+ done = true
+ end
+ if trace_parallel and label ~= "" then
+ report_parallel("reference found of category %a, tag %a, label %a",category,tag,label)
+ end
+ line.label = label
+ line.content = strip(content)
+ end
+ else
+ line.content = strip(content)
+ line.label = ""
+ end
+end
+
+function parallel.hassomecontent(category,tags)
+ local dc = data[category]
+ if not dc then
+ return false
+ end
+ local entries = dc.entries
+ if not tags or tags == "" or tags == variables.all then
+ tags = table.keys(entries)
+ else
+ tags = utilities.parsers.settings_to_array(tags)
+ end
+ for t=1,#tags do
+ local tag = tags[t]
+ local lines = entries[tag].lines
+ for i=1,#lines do
+ local content = lines[i].content
+ if content and content ~= "" then
+ return true
+ end
+ end
+ end
+ return false
+end
+
+local save = resolvers.savers.byscheme
+
+function parallel.place(category,tags,options)
+ local dc = data[category]
+ if not dc then
+ return
+ end
+ local entries = dc.entries
+ local tags = utilities.parsers.settings_to_array(tags)
+ local options = utilities.parsers.settings_to_hash(options)
+ local start, n, criterium = options.start, options.n, options.criterium
+ start, n = start and tonumber(start), n and tonumber(n)
+ local max = 1
+ if n then
+ max = n
+ elseif criterium == variables.all then
+ max = 0
+ for t=1,#tags do
+ local tag = tags[t]
+ local lines = entries[tag].lines
+ if #lines > max then
+ max = #lines
+ end
+ end
+ end
+ for i=1,max do
+ for t=1,#tags do
+ local tag = tags[t]
+ local entry = entries[tag]
+ if entry then
+ local lines = entry.lines
+ local number = entry.number + 1
+ entry.number = number
+ local line = remove(lines,1)
+ if line and line.content then
+ local content = format("\\input{%s}",save("virtual","parallel",line.content))
+ context.doflushparallel(tag,1,number,line.label,content)
+ else
+ context.doflushparallel(tag,0,number,"","")
+ end
+ end
+ end
+ end
+end
+
+-- interface
+
+commands.defineparallel = parallel.define
+commands.nextparallel = parallel.next
+commands.saveparallel = parallel.save
+commands.placeparallel = parallel.place
+commands.resetparallel = parallel.reset
+
+function commands.doifelseparallel(category,tags)
+ commands.doifelse(parallel.hassomecontent(category,tags))
+end
diff --git a/tex/context/base/buff-ver.lua b/tex/context/base/buff-ver.lua
index 30525b456..e327a59dd 100644
--- a/tex/context/base/buff-ver.lua
+++ b/tex/context/base/buff-ver.lua
@@ -1,768 +1,768 @@
-if not modules then modules = { } end modules ['buff-ver'] = {
- version = 1.001,
- comment = "companion to buff-ver.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- The default visualizers have reserved names starting with buff-imp-*. Users are
--- supposed to use different names for their own variants.
---
--- todo: skip=auto
-
-local type, next, rawset, rawget, setmetatable, getmetatable = type, next, rawset, rawget, setmetatable, getmetatable
-local format, lower, upper,match, find, sub = string.format, string.lower, string.upper, string.match, string.find, string.sub
-local splitlines = string.splitlines
-local concat = table.concat
-local C, P, R, S, V, Carg, Cc, Cs = lpeg.C, lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.Carg, lpeg.Cc, lpeg.Cs
-local patterns, lpegmatch, is_lpeg = lpeg.patterns, lpeg.match, lpeg.is_lpeg
-
-local context, commands = context, commands
-
-local trace_visualize = false trackers.register("buffers.visualize", function(v) trace_visualize = v end)
-local report_visualizers = logs.reporter("buffers","visualizers")
-
-local allocate = utilities.storage.allocate
-
-visualizers = visualizers or { }
-local specifications = allocate()
-visualizers.specifications = specifications
-
-local tabtospace = utilities.strings.tabtospace
-local variables = interfaces.variables
-local settings_to_array = utilities.parsers.settings_to_array
-local variables = interfaces.variables
-local findfile = resolvers.findfile
-local addsuffix = file.addsuffix
-
-local v_auto = variables.auto
-local v_yes = variables.yes
-
--- beware, all macros have an argument:
-
-local doinlineverbatimnewline = context.doinlineverbatimnewline
-local doinlineverbatimbeginline = context.doinlineverbatimbeginline
-local doinlineverbatimemptyline = context.doinlineverbatimemptyline
-local doinlineverbatimstart = context.doinlineverbatimstart
-local doinlineverbatimstop = context.doinlineverbatimstop
-
-local dodisplayverbatimnewline = context.dodisplayverbatimnewline
-local dodisplayverbatimbeginline = context.dodisplayverbatimbeginline
-local dodisplayverbatimemptyline = context.dodisplayverbatimemptyline
-local dodisplayverbatimstart = context.dodisplayverbatimstart
-local dodisplayverbatimstop = context.dodisplayverbatimstop
-
-local verbatim = context.verbatim
-local doverbatimspace = context.doverbatimspace
-
-local CargOne = Carg(1)
-
-local function f_emptyline(s,settings)
- if settings and settings.nature == "inline" then
- doinlineverbatimemptyline()
- else
- dodisplayverbatimemptyline()
- end
-end
-
-local function f_beginline(s,settings)
- if settings and settings.nature == "inline" then
- doinlineverbatimbeginline()
- else
- dodisplayverbatimbeginline()
- end
-end
-
-local function f_newline(s,settings)
- if settings and settings.nature == "inline" then
- doinlineverbatimnewline()
- else
- dodisplayverbatimnewline()
- end
-end
-
-local function f_start(s,settings)
- if settings and settings.nature == "inline" then
- doinlineverbatimstart()
- else
- dodisplayverbatimstart()
- end
-end
-
-local function f_stop(s,settings)
- if settings and settings.nature == "inline" then
- doinlineverbatimstop()
- else
- dodisplayverbatimstop()
- end
-end
-
-local function f_default(s) -- (s,settings)
- verbatim(s)
-end
-
-local function f_space() -- (s,settings)
- doverbatimspace()
-end
-
-local function f_signal() -- (s,settings)
- -- we use these for special purposes
-end
-
-local signal = "\000"
-
-visualizers.signal = signal
-visualizers.signalpattern = P(signal)
-
-local functions = { __index = {
- emptyline = f_emptyline,
- newline = f_newline,
- default = f_default,
- beginline = f_beginline,
- space = f_space,
- start = f_start,
- stop = f_stop,
- signal = f_signal,
- }
-}
-
-local handlers = { }
-
-function visualizers.newhandler(name,data)
- local tname, tdata = type(name), type(data)
- if tname == "table" then -- (data)
- setmetatable(name,getmetatable(name) or functions)
- return name
- elseif tname == "string" then
- if tdata == "string" then -- ("name","parent")
- local result = { }
- setmetatable(result,getmetatable(handlers[data]) or functions)
- handlers[name] = result
- return result
- elseif tdata == "table" then -- ("name",data)
- setmetatable(data,getmetatable(data) or functions)
- handlers[name] = data
- return data
- else -- ("name")
- local result = { }
- setmetatable(result,functions)
- handlers[name] = result
- return result
- end
- else -- ()
- local result = { }
- setmetatable(result,functions)
- return result
- end
-end
-
-function visualizers.newgrammar(name,t)
- name = lower(name)
- t = t or { }
- local g = visualizers.specifications[name]
- g = g and g.grammar
- if g then
- if trace_visualize then
- report_visualizers("cloning grammar %a",name)
- end
- for k,v in next, g do
- if not t[k] then
- t[k] = v
- end
- if is_lpeg(v) then
- t[name..":"..k] = v
- end
- end
- end
- return t
-end
-
-local function getvisualizer(method,nature)
- method = lower(method)
- local m = specifications[method] or specifications.default
- if nature then
- if trace_visualize then
- report_visualizers("getting visualizer %a with nature %a",method,nature)
- end
- return m and (m[nature] or m.parser) or nil
- else
- if trace_visualize then
- report_visualizers("getting visualizer %a",method)
- end
- return m and m.parser or nil
- end
-end
-
-local fallback = context.verbatim
-
-local function makepattern(visualizer,replacement,pattern)
- if not pattern then
- report_visualizers("error in visualizer %a",replacement)
- return patterns.alwaystrue
- else
- if type(visualizer) == "table" and type(replacement) == "string" then
- replacement = visualizer[replacement] or fallback
- else
- replacement = fallback
- end
- return (C(pattern) * CargOne) / replacement
- end
-end
-
-local function makenested(handler,how,start,stop)
- local b, e, f = P(start), P(stop), how
- if type(how) == "string" then
- f = function(s) getvisualizer(how,"direct")(s) end
- end
- return makepattern(handler,"name",b)
- * ((1-e)^1/f)
- * makepattern(handler,"name",e)
-end
-
-visualizers.pattern = makepattern
-visualizers.makepattern = makepattern
-visualizers.makenested = makenested
-
-function visualizers.load(name)
- name = lower(name)
- if rawget(specifications,name) == nil then
- name = lower(name)
- local texname = findfile(format("buff-imp-%s.mkiv",name))
- local luaname = findfile(format("buff-imp-%s.lua" ,name))
- if texname == "" or luaname == "" then
- -- assume a user specific file
- luaname = findfile(addsuffix(name,"mkiv"))
- texname = findfile(addsuffix(name,"lua" ))
- end
- if texname == "" or luaname == "" then
- if trace_visualize then
- report_visualizers("unknown visualizer %a",name)
- end
- else
- if trace_visualize then
- report_visualizers("loading visualizer %a",name)
- end
- lua.registercode(luaname)
- context.input(texname)
- end
- if rawget(specifications,name) == nil then
- rawset(specifications,name,false)
- end
- end
-end
-
-function visualizers.register(name,specification)
- name = lower(name)
- if trace_visualize then
- report_visualizers("registering visualizer %a",name)
- end
- specifications[name] = specification
- local parser, handler = specification.parser, specification.handler
- local displayparser = specification.display or parser
- local inlineparser = specification.inline or parser
- local isparser = is_lpeg(parser)
- local start, stop
- if isparser then
- start = makepattern(handler,"start",patterns.alwaysmatched)
- stop = makepattern(handler,"stop",patterns.alwaysmatched)
- end
- if handler then
- if isparser then
- specification.display = function(content,settings)
- if handler.startdisplay then handler.startdisplay(settings) end
- lpegmatch(start * displayparser * stop,content,1,settings)
- if handler.stopdisplay then handler.stopdisplay(settings) end
- end
- specification.inline = function(content,settings)
- if handler.startinline then handler.startinline(settings) end
- lpegmatch(start * inlineparser * stop,content,1,settings)
- if handler.stopinline then handler.stopinline(settings) end
- end
- specification.direct = function(content,settings)
- lpegmatch(parser,content,1,settings)
- end
- elseif parser then
- specification.display = function(content,settings)
- if handler.startdisplay then handler.startdisplay(settings) end
- parser(content,settings)
- if handler.stopdisplay then handler.stopdisplay(settings) end
- end
- specification.inline = function(content,settings)
- if handler.startinline then handler.startinline(settings) end
- parser(content,settings)
- if handler.stopinline then handler.stopinline(settings) end
- end
- specification.direct = parser
- end
- elseif isparser then
- specification.display = function(content,settings)
- lpegmatch(start * displayparser * stop,content,1,settings)
- end
- specification.inline = function(content,settings)
- lpegmatch(start * inlineparser * stop,content,1,settings)
- end
- specification.direct = function(content,settings)
- lpegmatch(parser,content,1,settings)
- end
- elseif parser then
- specification.display = parser
- specification.inline = parser
- specification.direct = parser
- end
- return specification
-end
-
-local escapepatterns = allocate()
-visualizers.escapepatterns = escapepatterns
-
-local function texmethod(s)
- context.bgroup()
- context(s)
- context.egroup()
-end
-
-local function texcommand(s)
- context[s]()
-end
-
-local function defaultmethod(s,settings)
- lpegmatch(getvisualizer("default"),lower(s),1,settings)
-end
-
--- we can consider using a nested instead
-
-local space_pattern = patterns.space^0
-local name_pattern = R("az","AZ")^1
-
--- the hack is needed in order to retain newlines when an escape happens at the
--- at the begin of a line; it also ensures proper line numbering; a bit messy
-
-local function hack(pattern)
- return Cs(pattern * Cc(signal))
-end
-
-local split_processor = typesetters.processors.split
-local apply_processor = typesetters.processors.apply
-
--- todo: { before = b, after = a, processor = p }, ...
-
-function visualizers.registerescapepattern(name,befores,afters,normalmethod,escapemethod,processors)
- local escapepattern = escapepatterns[name]
- if not escapepattern then
- if type(befores) ~= "table" then befores = { befores } end
- if type(afters) ~= "table" then afters = { afters } end
- if type(processors) ~= "table" then processors = { processors } end
- for i=1,#befores do
- local before = befores[i]
- local after = afters[i]
- local processor = processors[i]
- if trace_visualize then
- report_visualizers("registering escape pattern, name %a, index %a, before %a, after %a, processor %a",
- name,i,before,after,processor or "default")
- end
- before = P(before) * space_pattern
- after = space_pattern * P(after)
- local action
- if processor then
- action = function(s) apply_processor(processor,s) end
- else
- action = escapemethod or texmethod
- end
- local ep = (before / "") * ((1 - after)^0 / action) * (after / "")
- if escapepattern then
- escapepattern = escapepattern + ep
- else
- escapepattern = ep
- end
- end
- escapepattern = (
- escapepattern
- + hack((1 - escapepattern)^1) / (normalmethod or defaultmethod)
- )^0
- escapepatterns[name] = escapepattern
- end
- return escapepattern
-end
-
-function visualizers.registerescapeline(name,befores,normalmethod,escapemethod,processors)
- local escapepattern = escapepatterns[name]
- if not escapepattern then
- if type(befores) ~= "table" then befores = { befores } end
- if type(processors) ~= "table" then processors = { processors } end
- for i=1,#befores do
- local before = befores[i]
- local processor = processors[i]
- if trace_visualize then
- report_visualizers("registering escape line pattern, name %a, before %a, after <<newline>>",name,before)
- end
- before = P(before) * space_pattern
- after = space_pattern * P("\n")
- local action
- if processor then
- action = function(s) apply_processor(processor,s) end
- else
- action = escapemethod or texmethod
- end
- local ep = (before / "") * ((1 - after)^0 / action) * (space_pattern / "")
- if escapepattern then
- escapepattern = escapepattern + ep
- else
- escapepattern = ep
- end
- end
- escapepattern = (
- escapepattern
- + hack((1 - escapepattern)^1) / (normalmethod or defaultmethod)
- )^0
- escapepatterns[name] = escapepattern
- end
- return escapepattern
-end
-
-function visualizers.registerescapecommand(name,token,normalmethod,escapecommand,processor)
- local escapepattern = escapepatterns[name]
- if not escapepattern then
- if trace_visualize then
- report_visualizers("registering escape token, name %a, token %a",name,token)
- end
- token = P(token)
- local notoken = hack((1 - token)^1)
- local cstoken = name_pattern * space_pattern
- escapepattern = (
- (token / "")
- * (cstoken / (escapecommand or texcommand))
- + (notoken / (normalmethod or defaultmethod))
- )^0
- escapepatterns[name] = escapepattern
- end
- return escapepattern
-end
-
-local escapedvisualizers = { }
-
-local function visualize(content,settings) -- maybe also method in settings
- if content and content ~= "" then
- local method = lower(settings.method or "default")
- local m
- local e = settings.escape
- if e and e ~= "" then
- local newname = format("%s : %s",method,e)
- local newspec = specifications[newname]
- if newspec then
- m = newspec
- else
- local starts, stops, processors = { }, { }, { }
- if e == v_yes then
- starts[1] = "/BTEX"
- stops [1] = "/ETEX"
- else
- local s = settings_to_array(e,true)
- for i=1,#s do
- local si = s[i]
- local processor, pattern = split_processor(si)
- si = processor and pattern or si
- local start, stop = match(si,"^(.-),(.-)$")
- if start then
- local n = #starts + 1
- starts[n] = start
- stops [n] = stop or ""
- processors[n] = processor
- end
- end
- end
- local oldvisualizer = specifications[method] or specifications.default
- local oldparser = oldvisualizer.direct
- local newparser
- if starts[1] and stops[1] ~= "" then
- newparser = visualizers.registerescapepattern(newname,starts,stops,oldparser,nil,processors)
- elseif starts[1] then
- newparser = visualizers.registerescapeline(newname,starts,oldparser,nil,processors)
- else -- for old times sake: /em
- newparser = visualizers.registerescapecommand(newname,e,oldparser,nil,processors)
- end
- m = visualizers.register(newname, {
- parser = newparser,
- handler = oldvisualizer.handler,
- })
- end
- else
- m = specifications[method] or specifications.default
- end
- local nature = settings.nature or "display"
- local n = m and m[nature]
- if n then
- if trace_visualize then
- report_visualizers("visualize using method %a and nature %a",method,nature)
- end
- n(content,settings)
- else
- if trace_visualize then
- report_visualizers("visualize using method %a",method)
- end
- fallback(content,1,settings)
- end
- end
-end
-
-visualizers.visualize = visualize
-visualizers.getvisualizer = getvisualizer
-
-local fallbacks = { } table.setmetatableindex(fallbacks,function(t,k) local v = { nature = k } t[k] = v return v end)
-
-local function checkedsettings(settings,nature)
- if not settings then
- -- let's avoid dummy tables as much as possible
- return fallbacks[nature]
- else
- if not settings.nature then
- settings.nature = nature
- end
- return settings
- end
-end
-
-function visualizers.visualizestring(content,settings)
- visualize(content,checkedsettings(settings,"inline"))
-end
-
-function visualizers.visualizefile(name,settings)
- visualize(resolvers.loadtexfile(name),checkedsettings(settings,"display"))
-end
-
-function visualizers.visualizebuffer(name,settings)
- visualize(buffers.getcontent(name),checkedsettings(settings,"display"))
-end
-
--- --
-
-local space = C(patterns.space) * CargOne / f_space
-local newline = C(patterns.newline) * CargOne / f_newline
-local emptyline = C(patterns.emptyline) * CargOne / f_emptyline
-local beginline = C(patterns.beginline) * CargOne / f_beginline
-local anything = C(patterns.somecontent) * CargOne / f_default
-
------ verbosed = (space + newline * (emptyline^0) * beginline + anything)^0
-local verbosed = (space + newline * (emptyline^0) * beginline + emptyline + newline + anything)^0
-
-local function write(s,settings) -- bad name
- lpegmatch(verbosed,s,1,settings or false)
-end
-
-visualizers.write = write
-visualizers.writenewline = f_newline
-visualizers.writeemptyline = f_emptyline
-visualizers.writespace = f_space
-visualizers.writedefault = f_default
-
-function visualizers.writeargument(...)
- context("{") -- If we didn't have tracing then we could
- write(...) -- use a faster print to tex variant for the
- context("}") -- { } tokens as they always have ctxcatcodes.
-end
-
--- helpers
-
-local function realign(lines,strip) -- "yes", <number>
- local n
- if strip == v_yes then
- n = math.huge
- for i=1, #lines do
- local spaces = find(lines[i],"%S")
- if not spaces then
- -- empty line
- elseif spaces == 0 then
- n = 0
- break
- elseif spaces < n then
- n = spaces
- end
- end
- n = n - 1
- else
- n = tonumber(strip)
- end
- if n and n > 0 then
- local copy = { }
- for i=1,#lines do
- copy[i] = sub(lines[i],n+1)
- end
- return copy
- end
- return lines
-end
-
-local function getstrip(lines,first,last)
- local first, last = first or 1, last or #lines
- for i=first,last do
- local li = lines[i]
- if #li == 0 or find(li,"^%s*$") then
- first = first + 1
- else
- break
- end
- end
- for i=last,first,-1 do
- local li = lines[i]
- if #li == 0 or find(li,"^%s*$") then
- last = last - 1
- else
- break
- end
- end
- return first, last, last - first + 1
-end
-
-local function getrange(lines,first,last,range) -- 1,3 1,+3 fromhere,tothere
- local noflines = #lines
- local first, last = first or 1, last or noflines
- if last < 0 then
- last = noflines + last
- end
- local range = settings.range
- local what = settings_to_array(range)
- local r_first, r_last = what[1], what[2]
- local f, l = tonumber(r_first), tonumber(r_last)
- if r_first then
- if f then
- if f > first then
- first = f
- end
- else
- for i=first,last do
- if find(lines[i],r_first) then
- first = i + 1
- break
- end
- end
- end
- end
- if r_last then
- if l then
- if l < 0 then
- l = noflines + l
- end
- if find(r_last,"^[%+]") then -- 1,+3
- l = first + l
- end
- if l < last then
- last = l
- end
- else
- for i=first,last do
- if find(lines[i],r_last) then
- last = i - 1
- break
- end
- end
- end
- end
- return first, last
-end
-
-local tablength = 7
-
-local function dotabs(content,settings)
- local tab = settings.tab
- tab = tab and (tab == v_yes and tablength or tonumber(tab))
- if tab then
- return tabtospace(content,tab)
- else
- return content
- end
-end
-
-local function filter(lines,settings) -- todo: inline or display in settings
- local strip = settings.strip
- if strip and strip ~= "" then
- lines = realign(lines,strip)
- end
- local line, n = 0, 0
- local first, last, m = getstrip(lines)
- if range then
- first, last = getrange(lines,first,last,range)
- first, last = getstrip(lines,first,last)
- end
- -- \r is \endlinechar but \n would is more generic so this choice is debatable
- local content = concat(lines,(settings.nature == "inline" and " ") or "\n",first,last)
- return content, m
-end
-
-local getlines = buffers.getlines
-
--- interface
-
-function commands.doifelsevisualizer(name)
- commands.doifelse(specifications[lower(name)])
-end
-
-commands.loadvisualizer = visualizers.load
-
--- local decodecomment = resolvers.macros.decodecomment -- experiment
-
-function commands.typebuffer(settings)
- local lines = getlines(settings.name)
- if lines then
- local content, m = filter(lines,settings)
- if content and content ~= "" then
- -- content = decodecomment(content)
- content = dotabs(content,settings)
- visualize(content,checkedsettings(settings,"display"))
- end
- end
-end
-
-function commands.processbuffer(settings)
- local lines = getlines(settings.name)
- if lines then
- local content, m = filter(lines,settings)
- if content and content ~= "" then
- content = dotabs(content,settings)
- visualize(content,checkedsettings(settings,"direct"))
- end
- end
-end
-
--- not really buffers but it's closely related
-
--- A string.gsub(str,"(\\.-) +$","%1") is faster than an lpeg when there is a
--- match but slower when there is no match. But anyway, we need a more clever
--- parser so we use lpeg.
---
--- [[\text ]] [[\text{}]] [[\text \text ]] [[\text \\ \text ]]
-
------ strip = Cs((P(" ")^1 * P(-1)/"" + 1)^0)
-local strip = Cs((P("\\") * ((1-S("\\ "))^1) * (P(" ")/"") + 1)^0) --
-
-function commands.typestring(settings)
- local content = settings.data
- if content and content ~= "" then
- content = #content > 1 and lpegmatch(strip,content) or content -- can be an option, but needed in e.g. tabulate
- -- content = decodecomment(content)
- -- content = dotabs(content,settings)
- visualize(content,checkedsettings(settings,"inline"))
- end
-end
-
-function commands.typefile(settings)
- local filename = settings.name
- local foundname = resolvers.findtexfile(filename)
- if foundname and foundname ~= "" then
- local str = resolvers.loadtexfile(foundname)
- if str and str ~= "" then
- local regime = settings.regime
- if regime and regime ~= "" then
- str = regimes.translate(str,regime)
- end
- if str and str~= "" then
- -- content = decodecomment(content)
- local lines = splitlines(str)
- local content, m = filter(lines,settings)
- if content and content ~= "" then
- content = dotabs(content,settings)
- visualize(content,checkedsettings(settings,"display"))
- end
- end
- end
- end
-end
+if not modules then modules = { } end modules ['buff-ver'] = {
+ version = 1.001,
+ comment = "companion to buff-ver.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- The default visualizers have reserved names starting with buff-imp-*. Users are
+-- supposed to use different names for their own variants.
+--
+-- todo: skip=auto
+
+local type, next, rawset, rawget, setmetatable, getmetatable = type, next, rawset, rawget, setmetatable, getmetatable
+local format, lower, upper,match, find, sub = string.format, string.lower, string.upper, string.match, string.find, string.sub
+local splitlines = string.splitlines
+local concat = table.concat
+local C, P, R, S, V, Carg, Cc, Cs = lpeg.C, lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.Carg, lpeg.Cc, lpeg.Cs
+local patterns, lpegmatch, is_lpeg = lpeg.patterns, lpeg.match, lpeg.is_lpeg
+
+local context, commands = context, commands
+
+local trace_visualize = false trackers.register("buffers.visualize", function(v) trace_visualize = v end)
+local report_visualizers = logs.reporter("buffers","visualizers")
+
+local allocate = utilities.storage.allocate
+
+visualizers = visualizers or { }
+local specifications = allocate()
+visualizers.specifications = specifications
+
+local tabtospace = utilities.strings.tabtospace
+local variables = interfaces.variables
+local settings_to_array = utilities.parsers.settings_to_array
+local variables = interfaces.variables
+local findfile = resolvers.findfile
+local addsuffix = file.addsuffix
+
+local v_auto = variables.auto
+local v_yes = variables.yes
+
+-- beware, all macros have an argument:
+
+local doinlineverbatimnewline = context.doinlineverbatimnewline
+local doinlineverbatimbeginline = context.doinlineverbatimbeginline
+local doinlineverbatimemptyline = context.doinlineverbatimemptyline
+local doinlineverbatimstart = context.doinlineverbatimstart
+local doinlineverbatimstop = context.doinlineverbatimstop
+
+local dodisplayverbatimnewline = context.dodisplayverbatimnewline
+local dodisplayverbatimbeginline = context.dodisplayverbatimbeginline
+local dodisplayverbatimemptyline = context.dodisplayverbatimemptyline
+local dodisplayverbatimstart = context.dodisplayverbatimstart
+local dodisplayverbatimstop = context.dodisplayverbatimstop
+
+local verbatim = context.verbatim
+local doverbatimspace = context.doverbatimspace
+
+local CargOne = Carg(1)
+
+local function f_emptyline(s,settings)
+ if settings and settings.nature == "inline" then
+ doinlineverbatimemptyline()
+ else
+ dodisplayverbatimemptyline()
+ end
+end
+
+local function f_beginline(s,settings)
+ if settings and settings.nature == "inline" then
+ doinlineverbatimbeginline()
+ else
+ dodisplayverbatimbeginline()
+ end
+end
+
+local function f_newline(s,settings)
+ if settings and settings.nature == "inline" then
+ doinlineverbatimnewline()
+ else
+ dodisplayverbatimnewline()
+ end
+end
+
+local function f_start(s,settings)
+ if settings and settings.nature == "inline" then
+ doinlineverbatimstart()
+ else
+ dodisplayverbatimstart()
+ end
+end
+
+local function f_stop(s,settings)
+ if settings and settings.nature == "inline" then
+ doinlineverbatimstop()
+ else
+ dodisplayverbatimstop()
+ end
+end
+
+local function f_default(s) -- (s,settings)
+ verbatim(s)
+end
+
+local function f_space() -- (s,settings)
+ doverbatimspace()
+end
+
+local function f_signal() -- (s,settings)
+ -- we use these for special purposes
+end
+
+local signal = "\000"
+
+visualizers.signal = signal
+visualizers.signalpattern = P(signal)
+
+local functions = { __index = {
+ emptyline = f_emptyline,
+ newline = f_newline,
+ default = f_default,
+ beginline = f_beginline,
+ space = f_space,
+ start = f_start,
+ stop = f_stop,
+ signal = f_signal,
+ }
+}
+
+local handlers = { }
+
+function visualizers.newhandler(name,data)
+ local tname, tdata = type(name), type(data)
+ if tname == "table" then -- (data)
+ setmetatable(name,getmetatable(name) or functions)
+ return name
+ elseif tname == "string" then
+ if tdata == "string" then -- ("name","parent")
+ local result = { }
+ setmetatable(result,getmetatable(handlers[data]) or functions)
+ handlers[name] = result
+ return result
+ elseif tdata == "table" then -- ("name",data)
+ setmetatable(data,getmetatable(data) or functions)
+ handlers[name] = data
+ return data
+ else -- ("name")
+ local result = { }
+ setmetatable(result,functions)
+ handlers[name] = result
+ return result
+ end
+ else -- ()
+ local result = { }
+ setmetatable(result,functions)
+ return result
+ end
+end
+
+function visualizers.newgrammar(name,t)
+ name = lower(name)
+ t = t or { }
+ local g = visualizers.specifications[name]
+ g = g and g.grammar
+ if g then
+ if trace_visualize then
+ report_visualizers("cloning grammar %a",name)
+ end
+ for k,v in next, g do
+ if not t[k] then
+ t[k] = v
+ end
+ if is_lpeg(v) then
+ t[name..":"..k] = v
+ end
+ end
+ end
+ return t
+end
+
+local function getvisualizer(method,nature)
+ method = lower(method)
+ local m = specifications[method] or specifications.default
+ if nature then
+ if trace_visualize then
+ report_visualizers("getting visualizer %a with nature %a",method,nature)
+ end
+ return m and (m[nature] or m.parser) or nil
+ else
+ if trace_visualize then
+ report_visualizers("getting visualizer %a",method)
+ end
+ return m and m.parser or nil
+ end
+end
+
+local fallback = context.verbatim
+
+local function makepattern(visualizer,replacement,pattern)
+ if not pattern then
+ report_visualizers("error in visualizer %a",replacement)
+ return patterns.alwaystrue
+ else
+ if type(visualizer) == "table" and type(replacement) == "string" then
+ replacement = visualizer[replacement] or fallback
+ else
+ replacement = fallback
+ end
+ return (C(pattern) * CargOne) / replacement
+ end
+end
+
+local function makenested(handler,how,start,stop)
+ local b, e, f = P(start), P(stop), how
+ if type(how) == "string" then
+ f = function(s) getvisualizer(how,"direct")(s) end
+ end
+ return makepattern(handler,"name",b)
+ * ((1-e)^1/f)
+ * makepattern(handler,"name",e)
+end
+
+visualizers.pattern = makepattern
+visualizers.makepattern = makepattern
+visualizers.makenested = makenested
+
+function visualizers.load(name)
+ name = lower(name)
+ if rawget(specifications,name) == nil then
+ name = lower(name)
+ local texname = findfile(format("buff-imp-%s.mkiv",name))
+ local luaname = findfile(format("buff-imp-%s.lua" ,name))
+ if texname == "" or luaname == "" then
+ -- assume a user specific file
+ luaname = findfile(addsuffix(name,"mkiv"))
+ texname = findfile(addsuffix(name,"lua" ))
+ end
+ if texname == "" or luaname == "" then
+ if trace_visualize then
+ report_visualizers("unknown visualizer %a",name)
+ end
+ else
+ if trace_visualize then
+ report_visualizers("loading visualizer %a",name)
+ end
+ lua.registercode(luaname)
+ context.input(texname)
+ end
+ if rawget(specifications,name) == nil then
+ rawset(specifications,name,false)
+ end
+ end
+end
+
+function visualizers.register(name,specification)
+ name = lower(name)
+ if trace_visualize then
+ report_visualizers("registering visualizer %a",name)
+ end
+ specifications[name] = specification
+ local parser, handler = specification.parser, specification.handler
+ local displayparser = specification.display or parser
+ local inlineparser = specification.inline or parser
+ local isparser = is_lpeg(parser)
+ local start, stop
+ if isparser then
+ start = makepattern(handler,"start",patterns.alwaysmatched)
+ stop = makepattern(handler,"stop",patterns.alwaysmatched)
+ end
+ if handler then
+ if isparser then
+ specification.display = function(content,settings)
+ if handler.startdisplay then handler.startdisplay(settings) end
+ lpegmatch(start * displayparser * stop,content,1,settings)
+ if handler.stopdisplay then handler.stopdisplay(settings) end
+ end
+ specification.inline = function(content,settings)
+ if handler.startinline then handler.startinline(settings) end
+ lpegmatch(start * inlineparser * stop,content,1,settings)
+ if handler.stopinline then handler.stopinline(settings) end
+ end
+ specification.direct = function(content,settings)
+ lpegmatch(parser,content,1,settings)
+ end
+ elseif parser then
+ specification.display = function(content,settings)
+ if handler.startdisplay then handler.startdisplay(settings) end
+ parser(content,settings)
+ if handler.stopdisplay then handler.stopdisplay(settings) end
+ end
+ specification.inline = function(content,settings)
+ if handler.startinline then handler.startinline(settings) end
+ parser(content,settings)
+ if handler.stopinline then handler.stopinline(settings) end
+ end
+ specification.direct = parser
+ end
+ elseif isparser then
+ specification.display = function(content,settings)
+ lpegmatch(start * displayparser * stop,content,1,settings)
+ end
+ specification.inline = function(content,settings)
+ lpegmatch(start * inlineparser * stop,content,1,settings)
+ end
+ specification.direct = function(content,settings)
+ lpegmatch(parser,content,1,settings)
+ end
+ elseif parser then
+ specification.display = parser
+ specification.inline = parser
+ specification.direct = parser
+ end
+ return specification
+end
+
+local escapepatterns = allocate()
+visualizers.escapepatterns = escapepatterns
+
+local function texmethod(s)
+ context.bgroup()
+ context(s)
+ context.egroup()
+end
+
+local function texcommand(s)
+ context[s]()
+end
+
+local function defaultmethod(s,settings)
+ lpegmatch(getvisualizer("default"),lower(s),1,settings)
+end
+
+-- we can consider using a nested instead
+
+local space_pattern = patterns.space^0
+local name_pattern = R("az","AZ")^1
+
+-- the hack is needed in order to retain newlines when an escape happens at the
+-- at the begin of a line; it also ensures proper line numbering; a bit messy
+
+local function hack(pattern)
+ return Cs(pattern * Cc(signal))
+end
+
+local split_processor = typesetters.processors.split
+local apply_processor = typesetters.processors.apply
+
+-- todo: { before = b, after = a, processor = p }, ...
+
+function visualizers.registerescapepattern(name,befores,afters,normalmethod,escapemethod,processors)
+ local escapepattern = escapepatterns[name]
+ if not escapepattern then
+ if type(befores) ~= "table" then befores = { befores } end
+ if type(afters) ~= "table" then afters = { afters } end
+ if type(processors) ~= "table" then processors = { processors } end
+ for i=1,#befores do
+ local before = befores[i]
+ local after = afters[i]
+ local processor = processors[i]
+ if trace_visualize then
+ report_visualizers("registering escape pattern, name %a, index %a, before %a, after %a, processor %a",
+ name,i,before,after,processor or "default")
+ end
+ before = P(before) * space_pattern
+ after = space_pattern * P(after)
+ local action
+ if processor then
+ action = function(s) apply_processor(processor,s) end
+ else
+ action = escapemethod or texmethod
+ end
+ local ep = (before / "") * ((1 - after)^0 / action) * (after / "")
+ if escapepattern then
+ escapepattern = escapepattern + ep
+ else
+ escapepattern = ep
+ end
+ end
+ escapepattern = (
+ escapepattern
+ + hack((1 - escapepattern)^1) / (normalmethod or defaultmethod)
+ )^0
+ escapepatterns[name] = escapepattern
+ end
+ return escapepattern
+end
+
+function visualizers.registerescapeline(name,befores,normalmethod,escapemethod,processors)
+ local escapepattern = escapepatterns[name]
+ if not escapepattern then
+ if type(befores) ~= "table" then befores = { befores } end
+ if type(processors) ~= "table" then processors = { processors } end
+ for i=1,#befores do
+ local before = befores[i]
+ local processor = processors[i]
+ if trace_visualize then
+ report_visualizers("registering escape line pattern, name %a, before %a, after <<newline>>",name,before)
+ end
+ before = P(before) * space_pattern
+ after = space_pattern * P("\n")
+ local action
+ if processor then
+ action = function(s) apply_processor(processor,s) end
+ else
+ action = escapemethod or texmethod
+ end
+ local ep = (before / "") * ((1 - after)^0 / action) * (space_pattern / "")
+ if escapepattern then
+ escapepattern = escapepattern + ep
+ else
+ escapepattern = ep
+ end
+ end
+ escapepattern = (
+ escapepattern
+ + hack((1 - escapepattern)^1) / (normalmethod or defaultmethod)
+ )^0
+ escapepatterns[name] = escapepattern
+ end
+ return escapepattern
+end
+
+function visualizers.registerescapecommand(name,token,normalmethod,escapecommand,processor)
+ local escapepattern = escapepatterns[name]
+ if not escapepattern then
+ if trace_visualize then
+ report_visualizers("registering escape token, name %a, token %a",name,token)
+ end
+ token = P(token)
+ local notoken = hack((1 - token)^1)
+ local cstoken = name_pattern * space_pattern
+ escapepattern = (
+ (token / "")
+ * (cstoken / (escapecommand or texcommand))
+ + (notoken / (normalmethod or defaultmethod))
+ )^0
+ escapepatterns[name] = escapepattern
+ end
+ return escapepattern
+end
+
+local escapedvisualizers = { }
+
+local function visualize(content,settings) -- maybe also method in settings
+ if content and content ~= "" then
+ local method = lower(settings.method or "default")
+ local m
+ local e = settings.escape
+ if e and e ~= "" then
+ local newname = format("%s : %s",method,e)
+ local newspec = specifications[newname]
+ if newspec then
+ m = newspec
+ else
+ local starts, stops, processors = { }, { }, { }
+ if e == v_yes then
+ starts[1] = "/BTEX"
+ stops [1] = "/ETEX"
+ else
+ local s = settings_to_array(e,true)
+ for i=1,#s do
+ local si = s[i]
+ local processor, pattern = split_processor(si)
+ si = processor and pattern or si
+ local start, stop = match(si,"^(.-),(.-)$")
+ if start then
+ local n = #starts + 1
+ starts[n] = start
+ stops [n] = stop or ""
+ processors[n] = processor
+ end
+ end
+ end
+ local oldvisualizer = specifications[method] or specifications.default
+ local oldparser = oldvisualizer.direct
+ local newparser
+ if starts[1] and stops[1] ~= "" then
+ newparser = visualizers.registerescapepattern(newname,starts,stops,oldparser,nil,processors)
+ elseif starts[1] then
+ newparser = visualizers.registerescapeline(newname,starts,oldparser,nil,processors)
+ else -- for old times sake: /em
+ newparser = visualizers.registerescapecommand(newname,e,oldparser,nil,processors)
+ end
+ m = visualizers.register(newname, {
+ parser = newparser,
+ handler = oldvisualizer.handler,
+ })
+ end
+ else
+ m = specifications[method] or specifications.default
+ end
+ local nature = settings.nature or "display"
+ local n = m and m[nature]
+ if n then
+ if trace_visualize then
+ report_visualizers("visualize using method %a and nature %a",method,nature)
+ end
+ n(content,settings)
+ else
+ if trace_visualize then
+ report_visualizers("visualize using method %a",method)
+ end
+ fallback(content,1,settings)
+ end
+ end
+end
+
+visualizers.visualize = visualize
+visualizers.getvisualizer = getvisualizer
+
+local fallbacks = { } table.setmetatableindex(fallbacks,function(t,k) local v = { nature = k } t[k] = v return v end)
+
+local function checkedsettings(settings,nature)
+ if not settings then
+ -- let's avoid dummy tables as much as possible
+ return fallbacks[nature]
+ else
+ if not settings.nature then
+ settings.nature = nature
+ end
+ return settings
+ end
+end
+
+function visualizers.visualizestring(content,settings)
+ visualize(content,checkedsettings(settings,"inline"))
+end
+
+function visualizers.visualizefile(name,settings)
+ visualize(resolvers.loadtexfile(name),checkedsettings(settings,"display"))
+end
+
+function visualizers.visualizebuffer(name,settings)
+ visualize(buffers.getcontent(name),checkedsettings(settings,"display"))
+end
+
+-- --
+
+local space = C(patterns.space) * CargOne / f_space
+local newline = C(patterns.newline) * CargOne / f_newline
+local emptyline = C(patterns.emptyline) * CargOne / f_emptyline
+local beginline = C(patterns.beginline) * CargOne / f_beginline
+local anything = C(patterns.somecontent) * CargOne / f_default
+
+----- verbosed = (space + newline * (emptyline^0) * beginline + anything)^0
+local verbosed = (space + newline * (emptyline^0) * beginline + emptyline + newline + anything)^0
+
+local function write(s,settings) -- bad name
+ lpegmatch(verbosed,s,1,settings or false)
+end
+
+visualizers.write = write
+visualizers.writenewline = f_newline
+visualizers.writeemptyline = f_emptyline
+visualizers.writespace = f_space
+visualizers.writedefault = f_default
+
+function visualizers.writeargument(...)
+ context("{") -- If we didn't have tracing then we could
+ write(...) -- use a faster print to tex variant for the
+ context("}") -- { } tokens as they always have ctxcatcodes.
+end
+
+-- helpers
+
+local function realign(lines,strip) -- "yes", <number>
+ local n
+ if strip == v_yes then
+ n = math.huge
+ for i=1, #lines do
+ local spaces = find(lines[i],"%S")
+ if not spaces then
+ -- empty line
+ elseif spaces == 0 then
+ n = 0
+ break
+ elseif spaces < n then
+ n = spaces
+ end
+ end
+ n = n - 1
+ else
+ n = tonumber(strip)
+ end
+ if n and n > 0 then
+ local copy = { }
+ for i=1,#lines do
+ copy[i] = sub(lines[i],n+1)
+ end
+ return copy
+ end
+ return lines
+end
+
+local function getstrip(lines,first,last)
+ local first, last = first or 1, last or #lines
+ for i=first,last do
+ local li = lines[i]
+ if #li == 0 or find(li,"^%s*$") then
+ first = first + 1
+ else
+ break
+ end
+ end
+ for i=last,first,-1 do
+ local li = lines[i]
+ if #li == 0 or find(li,"^%s*$") then
+ last = last - 1
+ else
+ break
+ end
+ end
+ return first, last, last - first + 1
+end
+
+local function getrange(lines,first,last,range) -- 1,3 1,+3 fromhere,tothere
+ local noflines = #lines
+ local first, last = first or 1, last or noflines
+ if last < 0 then
+ last = noflines + last
+ end
+ local range = settings.range
+ local what = settings_to_array(range)
+ local r_first, r_last = what[1], what[2]
+ local f, l = tonumber(r_first), tonumber(r_last)
+ if r_first then
+ if f then
+ if f > first then
+ first = f
+ end
+ else
+ for i=first,last do
+ if find(lines[i],r_first) then
+ first = i + 1
+ break
+ end
+ end
+ end
+ end
+ if r_last then
+ if l then
+ if l < 0 then
+ l = noflines + l
+ end
+ if find(r_last,"^[%+]") then -- 1,+3
+ l = first + l
+ end
+ if l < last then
+ last = l
+ end
+ else
+ for i=first,last do
+ if find(lines[i],r_last) then
+ last = i - 1
+ break
+ end
+ end
+ end
+ end
+ return first, last
+end
+
+local tablength = 7
+
+local function dotabs(content,settings)
+ local tab = settings.tab
+ tab = tab and (tab == v_yes and tablength or tonumber(tab))
+ if tab then
+ return tabtospace(content,tab)
+ else
+ return content
+ end
+end
+
+local function filter(lines,settings) -- todo: inline or display in settings
+ local strip = settings.strip
+ if strip and strip ~= "" then
+ lines = realign(lines,strip)
+ end
+ local line, n = 0, 0
+ local first, last, m = getstrip(lines)
+ if range then
+ first, last = getrange(lines,first,last,range)
+ first, last = getstrip(lines,first,last)
+ end
+ -- \r is \endlinechar but \n would is more generic so this choice is debatable
+ local content = concat(lines,(settings.nature == "inline" and " ") or "\n",first,last)
+ return content, m
+end
+
+local getlines = buffers.getlines
+
+-- interface
+
+function commands.doifelsevisualizer(name)
+ commands.doifelse(specifications[lower(name)])
+end
+
+commands.loadvisualizer = visualizers.load
+
+-- local decodecomment = resolvers.macros.decodecomment -- experiment
+
+function commands.typebuffer(settings)
+ local lines = getlines(settings.name)
+ if lines then
+ local content, m = filter(lines,settings)
+ if content and content ~= "" then
+ -- content = decodecomment(content)
+ content = dotabs(content,settings)
+ visualize(content,checkedsettings(settings,"display"))
+ end
+ end
+end
+
+function commands.processbuffer(settings)
+ local lines = getlines(settings.name)
+ if lines then
+ local content, m = filter(lines,settings)
+ if content and content ~= "" then
+ content = dotabs(content,settings)
+ visualize(content,checkedsettings(settings,"direct"))
+ end
+ end
+end
+
+-- not really buffers but it's closely related
+
+-- A string.gsub(str,"(\\.-) +$","%1") is faster than an lpeg when there is a
+-- match but slower when there is no match. But anyway, we need a more clever
+-- parser so we use lpeg.
+--
+-- [[\text ]] [[\text{}]] [[\text \text ]] [[\text \\ \text ]]
+
+----- strip = Cs((P(" ")^1 * P(-1)/"" + 1)^0)
+local strip = Cs((P("\\") * ((1-S("\\ "))^1) * (P(" ")/"") + 1)^0) --
+
+function commands.typestring(settings)
+ local content = settings.data
+ if content and content ~= "" then
+ content = #content > 1 and lpegmatch(strip,content) or content -- can be an option, but needed in e.g. tabulate
+ -- content = decodecomment(content)
+ -- content = dotabs(content,settings)
+ visualize(content,checkedsettings(settings,"inline"))
+ end
+end
+
+function commands.typefile(settings)
+ local filename = settings.name
+ local foundname = resolvers.findtexfile(filename)
+ if foundname and foundname ~= "" then
+ local str = resolvers.loadtexfile(foundname)
+ if str and str ~= "" then
+ local regime = settings.regime
+ if regime and regime ~= "" then
+ str = regimes.translate(str,regime)
+ end
+ if str and str~= "" then
+ -- content = decodecomment(content)
+ local lines = splitlines(str)
+ local content, m = filter(lines,settings)
+ if content and content ~= "" then
+ content = dotabs(content,settings)
+ visualize(content,checkedsettings(settings,"display"))
+ end
+ end
+ end
+ end
+end
diff --git a/tex/context/base/catc-ini.lua b/tex/context/base/catc-ini.lua
index 61f28d789..d4f9b65af 100644
--- a/tex/context/base/catc-ini.lua
+++ b/tex/context/base/catc-ini.lua
@@ -1,41 +1,41 @@
-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
-
--- this only happens at initime
-
-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
-
--- this only happens at runtime
-
-for k, v in next, numbers do
- tex[k] = v -- downward compatible
-end
-
--- nasty
-
-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)
+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
+
+-- this only happens at initime
+
+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
+
+-- this only happens at runtime
+
+for k, v in next, numbers do
+ tex[k] = v -- downward compatible
+end
+
+-- nasty
+
+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/tex/context/base/char-cjk.lua b/tex/context/base/char-cjk.lua
index f4b3373a2..3d7de1423 100644
--- a/tex/context/base/char-cjk.lua
+++ b/tex/context/base/char-cjk.lua
@@ -1,365 +1,365 @@
-if not modules then modules = { } end modules ['char-cjk'] = {
- 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 setmetatable = setmetatable
-local insert = table.insert
-local floor = math.floor
-local format = string.format
-local utfchar = utf.char
-
-local ranges = characters.ranges
-local allocate = utilities.storage.allocate
-
--- Hangul Syllable
-
--- The following conversion is taken from unicode.org/reports/tr15/tr15-23.html#Hangul
--- but adapted to our needs.
-
--- local SBase = 0xAC00
---
--- local LBase, LCount = 0x1100, 19
--- local VBase, VCount = 0x1161, 21
--- local TBase, TCount = 0x11A7, 28
---
--- local NCount = VCount * TCount
--- local SCount = LCount * NCount
---
--- local function decomposed(unicode)
--- local SIndex = unicode - SBase
--- if SIndex >= 0 and SIndex < SCount then
--- local lead_consonant = LBase + floor( SIndex / NCount)
--- local medial_vowel = VBase + floor((SIndex % NCount) / TCount)
--- local tail_consonant = TBase + SIndex % TCount
--- if tail_consonant ~= TBase then
--- return lead_consonant, medial_vowel, tail_consonant
--- else
--- return lead_consonant, medial_vowel
--- end
--- end
--- end
---
--- Lua will optimize the inline constants so the next variant is
--- 10% faster. In practice this will go unnoticed, but it's also less
--- code, so let's do it. Pushing the constant section into the
--- function body saves 5%.
-
-local function decomposed(unicode)
- local index = unicode - 0xAC00
- if index >= 0 and index < 19 * 21 * 28 then
- local lead_consonant = 0x1100 + floor( index / (21 * 28))
- local medial_vowel = 0x1161 + floor((index % (21 * 28)) / 28)
- local tail_consonant = 0x11A7 + index % 28
- if tail_consonant ~= 0x11A7 then
- return lead_consonant, medial_vowel, tail_consonant
- else
- return lead_consonant, medial_vowel
- end
- end
-end
-
-local lead_consonants = { [0] =
- "G", "GG", "N", "D", "DD", "R", "M", "B", "BB",
- "S", "SS", "", "J", "JJ", "C", "K", "T", "P", "H"
-}
-
-local medial_vowels = { [0] =
- "A", "AE", "YA", "YAE", "EO", "E", "YEO", "YE", "O",
- "WA", "WAE", "OE", "YO", "U", "WEO", "WE", "WI",
- "YU", "EU", "YI", "I"
-}
-
-local tail_consonants = { [0] =
- "", "G", "GG", "GS", "N", "NJ", "NH", "D", "L", "LG", "LM",
- "LB", "LS", "LT", "LP", "LH", "M", "B", "BS",
- "S", "SS", "NG", "J", "C", "K", "T", "P", "H"
-}
-
--- local function description(unicode)
--- local index = unicode - 0xAC00
--- if index >= 0 and index < 19 * 21 * 28 then
--- local lead_consonant = floor( index / NCount)
--- local medial_vowel = floor((index % NCount) / TCount)
--- local tail_consonant = index % TCount
--- return format(
--- "HANGUL SYLLABLE %s%s%s",
--- lead_consonants[lead_consonant],
--- medial_vowels [medial_vowel ],
--- tail_consonants[tail_consonant]
--- )
--- end
--- end
-
-local function description(unicode)
- local index = unicode - 0xAC00
- if index >= 0 and index < 19 * 21 * 28 then
- local lead_consonant = floor( index / (21 * 28))
- local medial_vowel = floor((index % (21 * 28)) / 28)
- local tail_consonant = index % 28
- return format(
- "HANGUL SYLLABLE %s%s%s",
- lead_consonants[lead_consonant],
- medial_vowels [medial_vowel ],
- tail_consonants[tail_consonant]
- )
- end
-end
-
--- so far
-
--- We have a [lead consonant,medial vowel,tail consonant] where the last one
--- is optional. For sort ranges we need the first one but some are collapsed.
--- Beware, we map to modern so the font should support it.
-
-local function leadconsonant(unicode)
- return
- -- unicode < 0xAC00 and nil -- original
- -- unicode > 0xD7AF and nil or -- original
- unicode >= 0xD558 and 0x314E or -- 하 => ㅎ
- unicode >= 0xD30C and 0x314D or -- 파 => ㅍ
- unicode >= 0xD0C0 and 0x314C or -- 타 => ㅌ
- unicode >= 0xCE74 and 0x314B or -- 카 => ㅋ
- unicode >= 0xCC28 and 0x314A or -- 차 => ㅊ
- unicode >= 0xC790 and 0x3148 or -- 자 => ㅈ
- unicode >= 0xC544 and 0x3147 or -- 아 => ㅇ
- unicode >= 0xC0AC and 0x3145 or -- 사 => ㅅ
- unicode >= 0xBC14 and 0x3142 or -- 바 => ㅂ
- unicode >= 0xB9C8 and 0x3141 or -- 마 => ㅁ
- unicode >= 0xB77C and 0x3139 or -- 라 => ㄹ
- unicode >= 0xB2E4 and 0x3137 or -- 다 => ㄷ
- unicode >= 0xB098 and 0x3134 or -- 나 => ㄴ
- unicode >= 0xAC00 and 0x3131 or -- 가 => ㄱ
- nil -- can't happen
-end
-
-local remapped = { -- this might be merged into char-def.lua
- [0x1100] = 0x3131, -- G
- [0x1101] = 0x3132, -- GG
- [0x1102] = 0x3134, -- N
- [0x1103] = 0x3137, -- D
- [0x1104] = 0x3138, -- DD
- [0x1105] = 0x3139, -- R
- -- [0X111A] = 0x3140, -- LH used for last sound
- [0x1106] = 0x3141, -- M
- [0x1107] = 0x3142, -- B
- [0x1108] = 0x3143, -- BB
- -- [0x1121] = 0x3144, -- BS used for last sound
- [0x1109] = 0x3145, -- S
- [0x110A] = 0x3146, -- SS
- [0x110B] = 0x3147, -- (IEUNG) no sound but has form
- [0x110C] = 0x3148, -- J
- [0x110D] = 0x3149, -- JJ
- [0x110E] = 0x314A, -- C
- [0x110F] = 0x314B, -- K
- [0x1110] = 0x314C, -- T
- [0x1111] = 0x314D, -- P
- [0x1112] = 0x314E, -- H
-
- [0x1161] = 0x314F, -- A
- [0x1162] = 0x3150, -- AE
- [0x1163] = 0x3151, -- YA
- [0x1164] = 0x3152, -- YAE
- [0x1165] = 0x3153, -- EO
- [0x1166] = 0x3154, -- E
- [0x1167] = 0x3155, -- YEO
- [0x1168] = 0x3156, -- YE
- [0x1169] = 0x3157, -- O
- [0x116A] = 0x3158, -- WA
- [0x116B] = 0x3159, -- WAE
- [0x116C] = 0x315A, -- OE
- [0x116D] = 0x315B, -- YO
- [0x116E] = 0x315C, -- U
- [0x116F] = 0x315D, -- WEO
- [0x1170] = 0x315E, -- WE
- [0x1171] = 0x315F, -- WI
- [0x1172] = 0x3160, -- YU
- [0x1173] = 0x3161, -- EU
- [0x1174] = 0x3162, -- YI
- [0x1175] = 0x3163, -- I
-
- [0x11A8] = 0x3131, -- G
- [0x11A9] = 0x3132, -- GG
- [0x11AA] = 0x3133, -- GS
- [0x11AB] = 0x3134, -- N
- [0x11AC] = 0x3135, -- NJ
- [0x11AD] = 0x3136, -- NH
- [0x11AE] = 0x3137, -- D
- [0x11AF] = 0x3139, -- L
- [0x11B0] = 0x313A, -- LG
- [0x11B1] = 0x313B, -- LM
- [0x11B2] = 0x313C, -- LB
- [0x11B3] = 0x313D, -- LS
- [0x11B4] = 0x313E, -- LT
- [0x11B5] = 0x313F, -- LP
- [0x11B6] = 0x3140, -- LH
- [0x11B7] = 0x3141, -- M
- [0x11B8] = 0x3142, -- B
- [0x11B9] = 0x3144, -- BS
- [0x11BA] = 0x3145, -- S
- [0x11BB] = 0x3146, -- SS
- [0x11BC] = 0x3147, -- NG
- [0x11BD] = 0x3148, -- J
- [0x11BE] = 0x314A, -- C
- [0x11BF] = 0x314B, -- K
- [0x11C0] = 0x314C, -- T
- [0x11C1] = 0x314D, -- P
- [0x11C2] = 0x314E, -- H
-}
-
-characters.hangul = allocate {
- decomposed = decomposed,
- description = description,
- leadconsonant = leadconsonant,
- remapped = remapped,
-}
-
--- so far
-
-local hangul_syllable_basetable = {
- category = "lo",
- cjkwd = "w",
- description = "<Hangul Syllable>",
- direction = "l",
- linebreak = "h2",
-}
-
-local hangul_syllable_metatable = {
- __index = function(t,k)
- local u = t.unicodeslot
- if k == "fscode" or k == "leadconsonant" then
- return leadconsonant(u)
- elseif k == "decomposed" then
- return { decomposed(u) }
- elseif k == "specials" then
- return { "char", decomposed(u) }
- elseif k == "description" then
- return description(u)
- else
- return hangul_syllable_basetable[k]
- end
- end
-}
-
-function characters.remap_hangul_syllabe(t)
- local tt = type(t)
- if tt == "number" then
- return remapped[t] or t
- elseif tt == "table" then
- local r = { }
- for i=1,#t do
- local ti = t[i]
- r[i] = remapped[ti] or ti
- end
- return r
- else
- return t
- end
-end
-
-local hangul_syllable_extender = function(k,v)
- local t = {
- unicodeslot = k,
- }
- setmetatable(t,hangul_syllable_metatable)
- return t
-end
-
-local hangul_syllable_range = {
- first = 0xAC00,
- last = 0xD7A3,
- extender = hangul_syllable_extender,
-}
-
-setmetatable(hangul_syllable_range, hangul_syllable_metatable)
-
--- CJK Ideograph
-
-local cjk_ideograph_metatable = {
- __index = {
- category = "lo",
- cjkwd = "w",
- description = "<CJK Ideograph>",
- direction = "l",
- linebreak = "id",
- }
-}
-
-local cjk_ideograph_extender = function(k,v)
- local t = {
- -- shcode = shcode,
- unicodeslot = k,
- }
- setmetatable(t,cjk_ideograph_metatable)
- return t
-end
-
-local cjk_ideograph_range = {
- first = 0x4E00,
- last = 0x9FBB,
- extender = cjk_ideograph_extender,
-}
-
--- CJK Ideograph Extension A
-
-local cjk_ideograph_extension_a_metatable = {
- __index = {
- category = "lo",
- cjkwd = "w",
- description = "<CJK Ideograph Extension A>",
- direction = "l",
- linebreak = "id",
- }
-}
-
-local cjk_ideograph_extension_a_extender = function(k,v)
- local t = {
- -- shcode = shcode,
- unicodeslot = k,
- }
- setmetatable(t,cjk_ideograph_extension_a_metatable)
- return t
-end
-
-local cjk_ideograph_extension_a_range = {
- first = 0x3400,
- last = 0x4DB5,
- extender = cjk_ideograph_extension_a_extender,
-}
-
--- CJK Ideograph Extension B
-
-local cjk_ideograph_extension_b_metatable = {
- __index = {
- category = "lo",
- cjkwd = "w",
- description = "<CJK Ideograph Extension B>",
- direction = "l",
- linebreak = "id",
- }
-}
-
-local cjk_ideograph_extension_b_extender = function(k,v)
- local t = {
- -- shcode = shcode,
- unicodeslot = k,
- }
- setmetatable(t,cjk_ideograph_extension_b_metatable)
- return t
-end
-
-local cjk_ideograph_extension_b_range = {
- first = 0x20000,
- last = 0x2A6D6,
- extender = cjk_ideograph_extension_b_extender,
-}
-
--- Ranges
-
-insert(ranges, hangul_syllable_range)
-insert(ranges, cjk_ideograph_range)
-insert(ranges, cjk_ideograph_extension_a_range)
-insert(ranges, cjk_ideograph_extension_b_range)
+if not modules then modules = { } end modules ['char-cjk'] = {
+ 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 setmetatable = setmetatable
+local insert = table.insert
+local floor = math.floor
+local format = string.format
+local utfchar = utf.char
+
+local ranges = characters.ranges
+local allocate = utilities.storage.allocate
+
+-- Hangul Syllable
+
+-- The following conversion is taken from unicode.org/reports/tr15/tr15-23.html#Hangul
+-- but adapted to our needs.
+
+-- local SBase = 0xAC00
+--
+-- local LBase, LCount = 0x1100, 19
+-- local VBase, VCount = 0x1161, 21
+-- local TBase, TCount = 0x11A7, 28
+--
+-- local NCount = VCount * TCount
+-- local SCount = LCount * NCount
+--
+-- local function decomposed(unicode)
+-- local SIndex = unicode - SBase
+-- if SIndex >= 0 and SIndex < SCount then
+-- local lead_consonant = LBase + floor( SIndex / NCount)
+-- local medial_vowel = VBase + floor((SIndex % NCount) / TCount)
+-- local tail_consonant = TBase + SIndex % TCount
+-- if tail_consonant ~= TBase then
+-- return lead_consonant, medial_vowel, tail_consonant
+-- else
+-- return lead_consonant, medial_vowel
+-- end
+-- end
+-- end
+--
+-- Lua will optimize the inline constants so the next variant is
+-- 10% faster. In practice this will go unnoticed, but it's also less
+-- code, so let's do it. Pushing the constant section into the
+-- function body saves 5%.
+
+local function decomposed(unicode)
+ local index = unicode - 0xAC00
+ if index >= 0 and index < 19 * 21 * 28 then
+ local lead_consonant = 0x1100 + floor( index / (21 * 28))
+ local medial_vowel = 0x1161 + floor((index % (21 * 28)) / 28)
+ local tail_consonant = 0x11A7 + index % 28
+ if tail_consonant ~= 0x11A7 then
+ return lead_consonant, medial_vowel, tail_consonant
+ else
+ return lead_consonant, medial_vowel
+ end
+ end
+end
+
+local lead_consonants = { [0] =
+ "G", "GG", "N", "D", "DD", "R", "M", "B", "BB",
+ "S", "SS", "", "J", "JJ", "C", "K", "T", "P", "H"
+}
+
+local medial_vowels = { [0] =
+ "A", "AE", "YA", "YAE", "EO", "E", "YEO", "YE", "O",
+ "WA", "WAE", "OE", "YO", "U", "WEO", "WE", "WI",
+ "YU", "EU", "YI", "I"
+}
+
+local tail_consonants = { [0] =
+ "", "G", "GG", "GS", "N", "NJ", "NH", "D", "L", "LG", "LM",
+ "LB", "LS", "LT", "LP", "LH", "M", "B", "BS",
+ "S", "SS", "NG", "J", "C", "K", "T", "P", "H"
+}
+
+-- local function description(unicode)
+-- local index = unicode - 0xAC00
+-- if index >= 0 and index < 19 * 21 * 28 then
+-- local lead_consonant = floor( index / NCount)
+-- local medial_vowel = floor((index % NCount) / TCount)
+-- local tail_consonant = index % TCount
+-- return format(
+-- "HANGUL SYLLABLE %s%s%s",
+-- lead_consonants[lead_consonant],
+-- medial_vowels [medial_vowel ],
+-- tail_consonants[tail_consonant]
+-- )
+-- end
+-- end
+
+local function description(unicode)
+ local index = unicode - 0xAC00
+ if index >= 0 and index < 19 * 21 * 28 then
+ local lead_consonant = floor( index / (21 * 28))
+ local medial_vowel = floor((index % (21 * 28)) / 28)
+ local tail_consonant = index % 28
+ return format(
+ "HANGUL SYLLABLE %s%s%s",
+ lead_consonants[lead_consonant],
+ medial_vowels [medial_vowel ],
+ tail_consonants[tail_consonant]
+ )
+ end
+end
+
+-- so far
+
+-- We have a [lead consonant,medial vowel,tail consonant] where the last one
+-- is optional. For sort ranges we need the first one but some are collapsed.
+-- Beware, we map to modern so the font should support it.
+
+local function leadconsonant(unicode)
+ return
+ -- unicode < 0xAC00 and nil -- original
+ -- unicode > 0xD7AF and nil or -- original
+ unicode >= 0xD558 and 0x314E or -- 하 => ㅎ
+ unicode >= 0xD30C and 0x314D or -- 파 => ㅍ
+ unicode >= 0xD0C0 and 0x314C or -- 타 => ㅌ
+ unicode >= 0xCE74 and 0x314B or -- 카 => ㅋ
+ unicode >= 0xCC28 and 0x314A or -- 차 => ㅊ
+ unicode >= 0xC790 and 0x3148 or -- 자 => ㅈ
+ unicode >= 0xC544 and 0x3147 or -- 아 => ㅇ
+ unicode >= 0xC0AC and 0x3145 or -- 사 => ㅅ
+ unicode >= 0xBC14 and 0x3142 or -- 바 => ㅂ
+ unicode >= 0xB9C8 and 0x3141 or -- 마 => ㅁ
+ unicode >= 0xB77C and 0x3139 or -- 라 => ㄹ
+ unicode >= 0xB2E4 and 0x3137 or -- 다 => ㄷ
+ unicode >= 0xB098 and 0x3134 or -- 나 => ㄴ
+ unicode >= 0xAC00 and 0x3131 or -- 가 => ㄱ
+ nil -- can't happen
+end
+
+local remapped = { -- this might be merged into char-def.lua
+ [0x1100] = 0x3131, -- G
+ [0x1101] = 0x3132, -- GG
+ [0x1102] = 0x3134, -- N
+ [0x1103] = 0x3137, -- D
+ [0x1104] = 0x3138, -- DD
+ [0x1105] = 0x3139, -- R
+ -- [0X111A] = 0x3140, -- LH used for last sound
+ [0x1106] = 0x3141, -- M
+ [0x1107] = 0x3142, -- B
+ [0x1108] = 0x3143, -- BB
+ -- [0x1121] = 0x3144, -- BS used for last sound
+ [0x1109] = 0x3145, -- S
+ [0x110A] = 0x3146, -- SS
+ [0x110B] = 0x3147, -- (IEUNG) no sound but has form
+ [0x110C] = 0x3148, -- J
+ [0x110D] = 0x3149, -- JJ
+ [0x110E] = 0x314A, -- C
+ [0x110F] = 0x314B, -- K
+ [0x1110] = 0x314C, -- T
+ [0x1111] = 0x314D, -- P
+ [0x1112] = 0x314E, -- H
+
+ [0x1161] = 0x314F, -- A
+ [0x1162] = 0x3150, -- AE
+ [0x1163] = 0x3151, -- YA
+ [0x1164] = 0x3152, -- YAE
+ [0x1165] = 0x3153, -- EO
+ [0x1166] = 0x3154, -- E
+ [0x1167] = 0x3155, -- YEO
+ [0x1168] = 0x3156, -- YE
+ [0x1169] = 0x3157, -- O
+ [0x116A] = 0x3158, -- WA
+ [0x116B] = 0x3159, -- WAE
+ [0x116C] = 0x315A, -- OE
+ [0x116D] = 0x315B, -- YO
+ [0x116E] = 0x315C, -- U
+ [0x116F] = 0x315D, -- WEO
+ [0x1170] = 0x315E, -- WE
+ [0x1171] = 0x315F, -- WI
+ [0x1172] = 0x3160, -- YU
+ [0x1173] = 0x3161, -- EU
+ [0x1174] = 0x3162, -- YI
+ [0x1175] = 0x3163, -- I
+
+ [0x11A8] = 0x3131, -- G
+ [0x11A9] = 0x3132, -- GG
+ [0x11AA] = 0x3133, -- GS
+ [0x11AB] = 0x3134, -- N
+ [0x11AC] = 0x3135, -- NJ
+ [0x11AD] = 0x3136, -- NH
+ [0x11AE] = 0x3137, -- D
+ [0x11AF] = 0x3139, -- L
+ [0x11B0] = 0x313A, -- LG
+ [0x11B1] = 0x313B, -- LM
+ [0x11B2] = 0x313C, -- LB
+ [0x11B3] = 0x313D, -- LS
+ [0x11B4] = 0x313E, -- LT
+ [0x11B5] = 0x313F, -- LP
+ [0x11B6] = 0x3140, -- LH
+ [0x11B7] = 0x3141, -- M
+ [0x11B8] = 0x3142, -- B
+ [0x11B9] = 0x3144, -- BS
+ [0x11BA] = 0x3145, -- S
+ [0x11BB] = 0x3146, -- SS
+ [0x11BC] = 0x3147, -- NG
+ [0x11BD] = 0x3148, -- J
+ [0x11BE] = 0x314A, -- C
+ [0x11BF] = 0x314B, -- K
+ [0x11C0] = 0x314C, -- T
+ [0x11C1] = 0x314D, -- P
+ [0x11C2] = 0x314E, -- H
+}
+
+characters.hangul = allocate {
+ decomposed = decomposed,
+ description = description,
+ leadconsonant = leadconsonant,
+ remapped = remapped,
+}
+
+-- so far
+
+local hangul_syllable_basetable = {
+ category = "lo",
+ cjkwd = "w",
+ description = "<Hangul Syllable>",
+ direction = "l",
+ linebreak = "h2",
+}
+
+local hangul_syllable_metatable = {
+ __index = function(t,k)
+ local u = t.unicodeslot
+ if k == "fscode" or k == "leadconsonant" then
+ return leadconsonant(u)
+ elseif k == "decomposed" then
+ return { decomposed(u) }
+ elseif k == "specials" then
+ return { "char", decomposed(u) }
+ elseif k == "description" then
+ return description(u)
+ else
+ return hangul_syllable_basetable[k]
+ end
+ end
+}
+
+function characters.remap_hangul_syllabe(t)
+ local tt = type(t)
+ if tt == "number" then
+ return remapped[t] or t
+ elseif tt == "table" then
+ local r = { }
+ for i=1,#t do
+ local ti = t[i]
+ r[i] = remapped[ti] or ti
+ end
+ return r
+ else
+ return t
+ end
+end
+
+local hangul_syllable_extender = function(k,v)
+ local t = {
+ unicodeslot = k,
+ }
+ setmetatable(t,hangul_syllable_metatable)
+ return t
+end
+
+local hangul_syllable_range = {
+ first = 0xAC00,
+ last = 0xD7A3,
+ extender = hangul_syllable_extender,
+}
+
+setmetatable(hangul_syllable_range, hangul_syllable_metatable)
+
+-- CJK Ideograph
+
+local cjk_ideograph_metatable = {
+ __index = {
+ category = "lo",
+ cjkwd = "w",
+ description = "<CJK Ideograph>",
+ direction = "l",
+ linebreak = "id",
+ }
+}
+
+local cjk_ideograph_extender = function(k,v)
+ local t = {
+ -- shcode = shcode,
+ unicodeslot = k,
+ }
+ setmetatable(t,cjk_ideograph_metatable)
+ return t
+end
+
+local cjk_ideograph_range = {
+ first = 0x4E00,
+ last = 0x9FBB,
+ extender = cjk_ideograph_extender,
+}
+
+-- CJK Ideograph Extension A
+
+local cjk_ideograph_extension_a_metatable = {
+ __index = {
+ category = "lo",
+ cjkwd = "w",
+ description = "<CJK Ideograph Extension A>",
+ direction = "l",
+ linebreak = "id",
+ }
+}
+
+local cjk_ideograph_extension_a_extender = function(k,v)
+ local t = {
+ -- shcode = shcode,
+ unicodeslot = k,
+ }
+ setmetatable(t,cjk_ideograph_extension_a_metatable)
+ return t
+end
+
+local cjk_ideograph_extension_a_range = {
+ first = 0x3400,
+ last = 0x4DB5,
+ extender = cjk_ideograph_extension_a_extender,
+}
+
+-- CJK Ideograph Extension B
+
+local cjk_ideograph_extension_b_metatable = {
+ __index = {
+ category = "lo",
+ cjkwd = "w",
+ description = "<CJK Ideograph Extension B>",
+ direction = "l",
+ linebreak = "id",
+ }
+}
+
+local cjk_ideograph_extension_b_extender = function(k,v)
+ local t = {
+ -- shcode = shcode,
+ unicodeslot = k,
+ }
+ setmetatable(t,cjk_ideograph_extension_b_metatable)
+ return t
+end
+
+local cjk_ideograph_extension_b_range = {
+ first = 0x20000,
+ last = 0x2A6D6,
+ extender = cjk_ideograph_extension_b_extender,
+}
+
+-- Ranges
+
+insert(ranges, hangul_syllable_range)
+insert(ranges, cjk_ideograph_range)
+insert(ranges, cjk_ideograph_extension_a_range)
+insert(ranges, cjk_ideograph_extension_b_range)
diff --git a/tex/context/base/char-enc.lua b/tex/context/base/char-enc.lua
index 5f3ecd888..048837eec 100644
--- a/tex/context/base/char-enc.lua
+++ b/tex/context/base/char-enc.lua
@@ -1,186 +1,186 @@
-if not modules then modules = { } end modules ['char-enc'] = {
- 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"
- -- dataonly = true,
-}
-
--- Thanks to tex4ht for these mappings.
-
-local allocate, setinitializer = utilities.storage.allocate, utilities.storage.setinitializer
-
-characters = characters or { }
-local characters = characters
-
-characters.synonyms = allocate { -- afm mess
- angle = 0x2220,
- anticlockwise = 0x21BA,
- arrowaxisleft = 0x2190,
- arrowaxisright = 0x2192,
- arrowparrleftright = 0x21C6,
- arrowparrrightleft = 0x21C4,
- arrowtailleft = 0x21A2,
- arrowtailright = 0x21A3,
- arrowtripleleft = 0x21DA,
- arrowtripleright = 0x21DB,
- axisshort = 0x2212,
- because = 0x2235,
- between = 0x226C,
- check = 0x2713,
- circleasteris = 0x229B,
- circleequal = 0x2257,
- circleminus = 0x229D,
- circleR = 0x24C7,
- circlering = 0x229A,
- circleS = 0x24C8,
- clockwise = 0x21BB,
- complement = 0x2201,
- curlyleft = 0x21AB,
- curlyright = 0x21AC,
- dblarrowdwn = 0x21CA,
- dblarrowheadleft = 0x219E,
- dblarrowheadright = 0x21A0,
- dblarrowleft = 0x21C7,
- dblarrowright = 0x21C9,
- dblarrowup = 0x21C8,
- defines = 0x225C,
- diamond = 0x2662,
- diamondsolid = 0x2666,
- difference = 0x224F,
- dotplus = 0x2214,
- downfall = 0x22CE,
- equaldotleftright = 0x2252,
- equaldotrightleft = 0x2253,
- equalorfollows = 0x22DF,
- equalorgreater = 0x22DD,
- equalorless = 0x22DC,
- equalorprecedes = 0x22DE,
- equalsdots = 0x2251,
- followsorcurly = 0x227D,
- followsorequal = 0x227F,
- forces = 0x22A9,
- forcesbar = 0x22AA,
- fork = 0x22D4,
- frown = 0x2322,
- geomequivalent = 0x224E,
- greaterdbleqlless = 0x22Da,
- greaterdblequal = 0x2267,
- greaterlessequal = 0x22DA,
- greaterorapproxeql = 0x227F,
- greaterorequalslant= 0x2265,
- greaterorless = 0x2277,
- greaterorsimilar = 0x2273,
- harpoondownleft = 0x21C3,
- harpoondownright = 0x21C2,
- harpoonleftright = 0x21CC,
- harpoonrightleft = 0x21CB,
- harpoonupleft = 0x21BF,
- harpoonupright = 0x21BE,
- intercal = 0x22BA,
- intersectiondbl = 0x22D2,
- lessdbleqlgreater = 0x22DB,
- lessdblequal = 0x2266,
- lessequalgreater = 0x22DB,
- lessorapproxeql = 0x227E,
- lessorequalslant = 0x2264,
- lessorgreater = 0x2276,
- lessorsimilar = 0x2272,
- maltesecross = 0xFFFD,
- measuredangle = 0x2221,
- muchgreater = 0x22D9,
- muchless = 0x22D8,
- multimap = 0x22B8,
- multiopenleft = 0x22CB,
- multiopenright = 0x22CC,
- nand = 0x22BC,
- orunderscore = 0x22BB,
- perpcorrespond = 0x2259,
- precedesorcurly = 0x227C,
- precedesorequal = 0x227E,
- primereverse = 0x2035,
- proportional = 0x221D,
- revasymptequal = 0x2243,
- revsimilar = 0x223D,
- rightanglene = 0x231D,
- rightanglenw = 0x231C,
- rightanglese = 0x231F,
- rightanglesw = 0x231E,
- ringinequal = 0x2256,
- satisfies = 0x22A8,
- shiftleft = 0x21B0,
- shiftright = 0x21B1,
- smile = 0x2323,
- sphericalangle = 0x2222,
- square = 0x25A1,
- squaredot = 0x22A1,
- squareimage = 0x228F,
- squareminus = 0x229F,
- squaremultiply = 0x22A0,
- squareoriginal = 0x2290,
- squareplus = 0x229E,
- squaresmallsolid = 0x25AA,
- squaresolid = 0x25A0,
- squiggleleftright = 0x21AD,
- squiggleright = 0x21DD,
- star = 0x22C6,
- subsetdbl = 0x22D0,
- subsetdblequal = 0x2286,
- supersetdbl = 0x22D1,
- supersetdblequa = 0x2287,
- therefore = 0x2234,
- triangle = 0x25B5,
- triangledownsld = 0x25BE,
- triangleinv = 0x25BF,
- triangleleft = 0x25C3,
- triangleleftequal = 0x22B4,
- triangleleftsld = 0x25C2,
- triangleright = 0x25B9,
- trianglerightequal = 0x22B5,
- trianglerightsld = 0x25B8,
- trianglesolid = 0x25B4,
- uniondbl = 0x22D3,
- uprise = 0x22CF,
- Yen = 0x00A5,
-}
-
--- if not characters.enccodes then
---
--- local enccodes = { } characters.enccodes = enccodes
---
--- for unicode, data in next, characters.data do
--- local encname = data.adobename or data.contextname
--- if encname then
--- enccodes[encname] = unicode
--- end
--- end
---
--- for name, unicode in next, characters.synonyms do
--- if not enccodes[name] then enccodes[name] = unicode end
--- end
---
---
--- end
---
--- storage.register("characters.enccodes", characters.enccodes, "characters.enccodes")
-
--- As this table is seldom used, we can delay its definition. Beware, this means
--- that table.print would not work on this file unless it is accessed once. This
--- why the serializer does a dummy access.
-
-local enccodes = allocate() characters.enccodes = enccodes
-
-local function initialize()
- for unicode, data in next, characters.data do
- local encname = data.adobename or data.contextname
- if encname then
- enccodes[encname] = unicode
- end
- end
- for name, unicode in next, characters.synonyms do
- if not enccodes[name] then enccodes[name] = unicode end
- end
-end
-
-setinitializer(enccodes,initialize)
+if not modules then modules = { } end modules ['char-enc'] = {
+ 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"
+ -- dataonly = true,
+}
+
+-- Thanks to tex4ht for these mappings.
+
+local allocate, setinitializer = utilities.storage.allocate, utilities.storage.setinitializer
+
+characters = characters or { }
+local characters = characters
+
+characters.synonyms = allocate { -- afm mess
+ angle = 0x2220,
+ anticlockwise = 0x21BA,
+ arrowaxisleft = 0x2190,
+ arrowaxisright = 0x2192,
+ arrowparrleftright = 0x21C6,
+ arrowparrrightleft = 0x21C4,
+ arrowtailleft = 0x21A2,
+ arrowtailright = 0x21A3,
+ arrowtripleleft = 0x21DA,
+ arrowtripleright = 0x21DB,
+ axisshort = 0x2212,
+ because = 0x2235,
+ between = 0x226C,
+ check = 0x2713,
+ circleasteris = 0x229B,
+ circleequal = 0x2257,
+ circleminus = 0x229D,
+ circleR = 0x24C7,
+ circlering = 0x229A,
+ circleS = 0x24C8,
+ clockwise = 0x21BB,
+ complement = 0x2201,
+ curlyleft = 0x21AB,
+ curlyright = 0x21AC,
+ dblarrowdwn = 0x21CA,
+ dblarrowheadleft = 0x219E,
+ dblarrowheadright = 0x21A0,
+ dblarrowleft = 0x21C7,
+ dblarrowright = 0x21C9,
+ dblarrowup = 0x21C8,
+ defines = 0x225C,
+ diamond = 0x2662,
+ diamondsolid = 0x2666,
+ difference = 0x224F,
+ dotplus = 0x2214,
+ downfall = 0x22CE,
+ equaldotleftright = 0x2252,
+ equaldotrightleft = 0x2253,
+ equalorfollows = 0x22DF,
+ equalorgreater = 0x22DD,
+ equalorless = 0x22DC,
+ equalorprecedes = 0x22DE,
+ equalsdots = 0x2251,
+ followsorcurly = 0x227D,
+ followsorequal = 0x227F,
+ forces = 0x22A9,
+ forcesbar = 0x22AA,
+ fork = 0x22D4,
+ frown = 0x2322,
+ geomequivalent = 0x224E,
+ greaterdbleqlless = 0x22Da,
+ greaterdblequal = 0x2267,
+ greaterlessequal = 0x22DA,
+ greaterorapproxeql = 0x227F,
+ greaterorequalslant= 0x2265,
+ greaterorless = 0x2277,
+ greaterorsimilar = 0x2273,
+ harpoondownleft = 0x21C3,
+ harpoondownright = 0x21C2,
+ harpoonleftright = 0x21CC,
+ harpoonrightleft = 0x21CB,
+ harpoonupleft = 0x21BF,
+ harpoonupright = 0x21BE,
+ intercal = 0x22BA,
+ intersectiondbl = 0x22D2,
+ lessdbleqlgreater = 0x22DB,
+ lessdblequal = 0x2266,
+ lessequalgreater = 0x22DB,
+ lessorapproxeql = 0x227E,
+ lessorequalslant = 0x2264,
+ lessorgreater = 0x2276,
+ lessorsimilar = 0x2272,
+ maltesecross = 0xFFFD,
+ measuredangle = 0x2221,
+ muchgreater = 0x22D9,
+ muchless = 0x22D8,
+ multimap = 0x22B8,
+ multiopenleft = 0x22CB,
+ multiopenright = 0x22CC,
+ nand = 0x22BC,
+ orunderscore = 0x22BB,
+ perpcorrespond = 0x2259,
+ precedesorcurly = 0x227C,
+ precedesorequal = 0x227E,
+ primereverse = 0x2035,
+ proportional = 0x221D,
+ revasymptequal = 0x2243,
+ revsimilar = 0x223D,
+ rightanglene = 0x231D,
+ rightanglenw = 0x231C,
+ rightanglese = 0x231F,
+ rightanglesw = 0x231E,
+ ringinequal = 0x2256,
+ satisfies = 0x22A8,
+ shiftleft = 0x21B0,
+ shiftright = 0x21B1,
+ smile = 0x2323,
+ sphericalangle = 0x2222,
+ square = 0x25A1,
+ squaredot = 0x22A1,
+ squareimage = 0x228F,
+ squareminus = 0x229F,
+ squaremultiply = 0x22A0,
+ squareoriginal = 0x2290,
+ squareplus = 0x229E,
+ squaresmallsolid = 0x25AA,
+ squaresolid = 0x25A0,
+ squiggleleftright = 0x21AD,
+ squiggleright = 0x21DD,
+ star = 0x22C6,
+ subsetdbl = 0x22D0,
+ subsetdblequal = 0x2286,
+ supersetdbl = 0x22D1,
+ supersetdblequa = 0x2287,
+ therefore = 0x2234,
+ triangle = 0x25B5,
+ triangledownsld = 0x25BE,
+ triangleinv = 0x25BF,
+ triangleleft = 0x25C3,
+ triangleleftequal = 0x22B4,
+ triangleleftsld = 0x25C2,
+ triangleright = 0x25B9,
+ trianglerightequal = 0x22B5,
+ trianglerightsld = 0x25B8,
+ trianglesolid = 0x25B4,
+ uniondbl = 0x22D3,
+ uprise = 0x22CF,
+ Yen = 0x00A5,
+}
+
+-- if not characters.enccodes then
+--
+-- local enccodes = { } characters.enccodes = enccodes
+--
+-- for unicode, data in next, characters.data do
+-- local encname = data.adobename or data.contextname
+-- if encname then
+-- enccodes[encname] = unicode
+-- end
+-- end
+--
+-- for name, unicode in next, characters.synonyms do
+-- if not enccodes[name] then enccodes[name] = unicode end
+-- end
+--
+--
+-- end
+--
+-- storage.register("characters.enccodes", characters.enccodes, "characters.enccodes")
+
+-- As this table is seldom used, we can delay its definition. Beware, this means
+-- that table.print would not work on this file unless it is accessed once. This
+-- why the serializer does a dummy access.
+
+local enccodes = allocate() characters.enccodes = enccodes
+
+local function initialize()
+ for unicode, data in next, characters.data do
+ local encname = data.adobename or data.contextname
+ if encname then
+ enccodes[encname] = unicode
+ end
+ end
+ for name, unicode in next, characters.synonyms do
+ if not enccodes[name] then enccodes[name] = unicode end
+ end
+end
+
+setinitializer(enccodes,initialize)
diff --git a/tex/context/base/char-ent.lua b/tex/context/base/char-ent.lua
index b642d887f..58ee9472c 100644
--- a/tex/context/base/char-ent.lua
+++ b/tex/context/base/char-ent.lua
@@ -1,2257 +1,2257 @@
-if not modules then modules = { } end modules ['char-ent'] = {
- version = 1.001,
- comment = "companion to math-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "derived from the mathml 2.0 specification",
- dataonly = true,
-}
-
--- http://www.w3.org/2003/entities/2007/w3centities-f.ent
--- http://www.w3.org/2003/entities/2007/htmlmathml-f.ent
-
-local entities = utilities.storage.allocate {
- ["AElig"] = "Æ", -- U+000C6
- ["AMP"] = "&", -- U+00026
- ["Aacgr"] = "Ά", -- U+00386
- ["Aacute"] = "Á", -- U+000C1
- ["Abreve"] = "Ă", -- U+00102
- ["Acirc"] = "Â", -- U+000C2
- ["Acy"] = "А", -- U+00410
- ["Afr"] = "𝔄", -- U+1D504
- ["Agr"] = "Α", -- U+00391
- ["Agrave"] = "À", -- U+000C0
- ["Alpha"] = "Α", -- U+00391
- ["Amacr"] = "Ā", -- U+00100
- ["And"] = "⩓", -- U+02A53
- ["Aogon"] = "Ą", -- U+00104
- ["Aopf"] = "𝔸", -- U+1D538
- ["ApplyFunction"] = "⁡", -- U+02061
- ["Aring"] = "Å", -- U+000C5
- ["Ascr"] = "𝒜", -- U+1D49C
- ["Assign"] = "≔", -- U+02254
- ["Atilde"] = "Ã", -- U+000C3
- ["Auml"] = "Ä", -- U+000C4
- ["Backslash"] = "∖", -- U+02216
- ["Barv"] = "⫧", -- U+02AE7
- ["Barwed"] = "⌆", -- U+02306
- ["Bcy"] = "Б", -- U+00411
- ["Because"] = "∵", -- U+02235
- ["Bernoullis"] = "ℬ", -- U+0212C
- ["Beta"] = "Β", -- U+00392
- ["Bfr"] = "𝔅", -- U+1D505
- ["Bgr"] = "Β", -- U+00392
- ["Bopf"] = "𝔹", -- U+1D539
- ["Breve"] = "˘", -- U+002D8
- ["Bscr"] = "ℬ", -- U+0212C
- ["Bumpeq"] = "≎", -- U+0224E
- ["CHcy"] = "Ч", -- U+00427
- ["COPY"] = "©", -- U+000A9
- ["Cacute"] = "Ć", -- U+00106
- ["Cap"] = "⋒", -- U+022D2
- ["CapitalDifferentialD"] = "ⅅ", -- U+02145
- ["Cayleys"] = "ℭ", -- U+0212D
- ["Ccaron"] = "Č", -- U+0010C
- ["Ccedil"] = "Ç", -- U+000C7
- ["Ccirc"] = "Ĉ", -- U+00108
- ["Cconint"] = "∰", -- U+02230
- ["Cdot"] = "Ċ", -- U+0010A
- ["Cedilla"] = "¸", -- U+000B8
- ["CenterDot"] = "·", -- U+000B7
- ["Cfr"] = "ℭ", -- U+0212D
- ["Chi"] = "Χ", -- U+003A7
- ["CircleDot"] = "⊙", -- U+02299
- ["CircleMinus"] = "⊖", -- U+02296
- ["CirclePlus"] = "⊕", -- U+02295
- ["CircleTimes"] = "⊗", -- U+02297
- ["ClockwiseContourIntegral"] = "∲", -- U+02232
- ["CloseCurlyDoubleQuote"] = "”", -- U+0201D
- ["CloseCurlyQuote"] = "’", -- U+02019
- ["Colon"] = "∷", -- U+02237
- ["Colone"] = "⩴", -- U+02A74
- ["Congruent"] = "≡", -- U+02261
- ["Conint"] = "∯", -- U+0222F
- ["ContourIntegral"] = "∮", -- U+0222E
- ["Copf"] = "ℂ", -- U+02102
- ["Coproduct"] = "∐", -- U+02210
- ["CounterClockwiseContourIntegral"] = "∳", -- U+02233
- ["Cross"] = "⨯", -- U+02A2F
- ["Cscr"] = "𝒞", -- U+1D49E
- ["Cup"] = "⋓", -- U+022D3
- ["CupCap"] = "≍", -- U+0224D
- ["DD"] = "ⅅ", -- U+02145
- ["DDotrahd"] = "⤑", -- U+02911
- ["DJcy"] = "Ђ", -- U+00402
- ["DScy"] = "Ѕ", -- U+00405
- ["DZcy"] = "Џ", -- U+0040F
- ["Dagger"] = "‡", -- U+02021
- ["Darr"] = "↡", -- U+021A1
- ["Dashv"] = "⫤", -- U+02AE4
- ["Dcaron"] = "Ď", -- U+0010E
- ["Dcy"] = "Д", -- U+00414
- ["Del"] = "∇", -- U+02207
- ["Delta"] = "Δ", -- U+00394
- ["Dfr"] = "𝔇", -- U+1D507
- ["Dgr"] = "Δ", -- U+00394
- ["DiacriticalAcute"] = "´", -- U+000B4
- ["DiacriticalDot"] = "˙", -- U+002D9
- ["DiacriticalDoubleAcute"] = "˝", -- U+002DD
- ["DiacriticalGrave"] = "`", -- U+00060
- ["DiacriticalTilde"] = "˜", -- U+002DC
- ["Diamond"] = "⋄", -- U+022C4
- ["DifferentialD"] = "ⅆ", -- U+02146
- ["Dopf"] = "𝔻", -- U+1D53B
- ["Dot"] = "¨", -- U+000A8
- ["DotDot"] = "⃜", -- U+020DC
- ["DotEqual"] = "≐", -- U+02250
- ["DoubleContourIntegral"] = "∯", -- U+0222F
- ["DoubleDot"] = "¨", -- U+000A8
- ["DoubleDownArrow"] = "⇓", -- U+021D3
- ["DoubleLeftArrow"] = "⇐", -- U+021D0
- ["DoubleLeftRightArrow"] = "⇔", -- U+021D4
- ["DoubleLeftTee"] = "⫤", -- U+02AE4
- ["DoubleLongLeftArrow"] = "⟸", -- U+027F8
- ["DoubleLongLeftRightArrow"] = "⟺", -- U+027FA
- ["DoubleLongRightArrow"] = "⟹", -- U+027F9
- ["DoubleRightArrow"] = "⇒", -- U+021D2
- ["DoubleRightTee"] = "⊨", -- U+022A8
- ["DoubleUpArrow"] = "⇑", -- U+021D1
- ["DoubleUpDownArrow"] = "⇕", -- U+021D5
- ["DoubleVerticalBar"] = "∥", -- U+02225
- ["DownArrow"] = "↓", -- U+02193
- ["DownArrowBar"] = "⤓", -- U+02913
- ["DownArrowUpArrow"] = "⇵", -- U+021F5
- ["DownBreve"] = "̑", -- U+00311
- ["DownLeftRightVector"] = "⥐", -- U+02950
- ["DownLeftTeeVector"] = "⥞", -- U+0295E
- ["DownLeftVector"] = "↽", -- U+021BD
- ["DownLeftVectorBar"] = "⥖", -- U+02956
- ["DownRightTeeVector"] = "⥟", -- U+0295F
- ["DownRightVector"] = "⇁", -- U+021C1
- ["DownRightVectorBar"] = "⥗", -- U+02957
- ["DownTee"] = "⊤", -- U+022A4
- ["DownTeeArrow"] = "↧", -- U+021A7
- ["Downarrow"] = "⇓", -- U+021D3
- ["Dscr"] = "𝒟", -- U+1D49F
- ["Dstrok"] = "Đ", -- U+00110
- ["EEacgr"] = "Ή", -- U+00389
- ["EEgr"] = "Η", -- U+00397
- ["ENG"] = "Ŋ", -- U+0014A
- ["ETH"] = "Ð", -- U+000D0
- ["Eacgr"] = "Έ", -- U+00388
- ["Eacute"] = "É", -- U+000C9
- ["Ecaron"] = "Ě", -- U+0011A
- ["Ecirc"] = "Ê", -- U+000CA
- ["Ecy"] = "Э", -- U+0042D
- ["Edot"] = "Ė", -- U+00116
- ["Efr"] = "𝔈", -- U+1D508
- ["Egr"] = "Ε", -- U+00395
- ["Egrave"] = "È", -- U+000C8
- ["Element"] = "∈", -- U+02208
- ["Emacr"] = "Ē", -- U+00112
- ["EmptySmallSquare"] = "◻", -- U+025FB
- ["EmptyVerySmallSquare"] = "▫", -- U+025AB
- ["Eogon"] = "Ę", -- U+00118
- ["Eopf"] = "𝔼", -- U+1D53C
- ["Epsilon"] = "Ε", -- U+00395
- ["Equal"] = "⩵", -- U+02A75
- ["EqualTilde"] = "≂", -- U+02242
- ["Equilibrium"] = "⇌", -- U+021CC
- ["Escr"] = "ℰ", -- U+02130
- ["Esim"] = "⩳", -- U+02A73
- ["Eta"] = "Η", -- U+00397
- ["Euml"] = "Ë", -- U+000CB
- ["Exists"] = "∃", -- U+02203
- ["ExponentialE"] = "ⅇ", -- U+02147
- ["Fcy"] = "Ф", -- U+00424
- ["Ffr"] = "𝔉", -- U+1D509
- ["FilledSmallSquare"] = "◼", -- U+025FC
- ["FilledVerySmallSquare"] = "▪", -- U+025AA
- ["Fopf"] = "𝔽", -- U+1D53D
- ["ForAll"] = "∀", -- U+02200
- ["Fouriertrf"] = "ℱ", -- U+02131
- ["Fscr"] = "ℱ", -- U+02131
- ["GJcy"] = "Ѓ", -- U+00403
- ["GT"] = ">", -- U+0003E
- ["Gamma"] = "Γ", -- U+00393
- ["Gammad"] = "Ϝ", -- U+003DC
- ["Gbreve"] = "Ğ", -- U+0011E
- ["Gcedil"] = "Ģ", -- U+00122
- ["Gcirc"] = "Ĝ", -- U+0011C
- ["Gcy"] = "Г", -- U+00413
- ["Gdot"] = "Ġ", -- U+00120
- ["Gfr"] = "𝔊", -- U+1D50A
- ["Gg"] = "⋙", -- U+022D9
- ["Ggr"] = "Γ", -- U+00393
- ["Gopf"] = "𝔾", -- U+1D53E
- ["GreaterEqual"] = "≥", -- U+02265
- ["GreaterEqualLess"] = "⋛", -- U+022DB
- ["GreaterFullEqual"] = "≧", -- U+02267
- ["GreaterGreater"] = "⪢", -- U+02AA2
- ["GreaterLess"] = "≷", -- U+02277
- ["GreaterSlantEqual"] = "⩾", -- U+02A7E
- ["GreaterTilde"] = "≳", -- U+02273
- ["Gscr"] = "𝒢", -- U+1D4A2
- ["Gt"] = "≫", -- U+0226B
- ["HARDcy"] = "Ъ", -- U+0042A
- ["Hacek"] = "ˇ", -- U+002C7
- ["Hat"] = "^", -- U+0005E
- ["Hcirc"] = "Ĥ", -- U+00124
- ["Hfr"] = "ℌ", -- U+0210C
- ["HilbertSpace"] = "ℋ", -- U+0210B
- ["Hopf"] = "ℍ", -- U+0210D
- ["HorizontalLine"] = "─", -- U+02500
- ["Hscr"] = "ℋ", -- U+0210B
- ["Hstrok"] = "Ħ", -- U+00126
- ["HumpDownHump"] = "≎", -- U+0224E
- ["HumpEqual"] = "≏", -- U+0224F
- ["IEcy"] = "Е", -- U+00415
- ["IJlig"] = "IJ", -- U+00132
- ["IOcy"] = "Ё", -- U+00401
- ["Iacgr"] = "Ί", -- U+0038A
- ["Iacute"] = "Í", -- U+000CD
- ["Icirc"] = "Î", -- U+000CE
- ["Icy"] = "И", -- U+00418
- ["Idigr"] = "Ϊ", -- U+003AA
- ["Idot"] = "İ", -- U+00130
- ["Ifr"] = "ℑ", -- U+02111
- ["Igr"] = "Ι", -- U+00399
- ["Igrave"] = "Ì", -- U+000CC
- ["Im"] = "ℑ", -- U+02111
- ["Imacr"] = "Ī", -- U+0012A
- ["ImaginaryI"] = "ⅈ", -- U+02148
- ["Implies"] = "⇒", -- U+021D2
- ["Int"] = "∬", -- U+0222C
- ["Integral"] = "∫", -- U+0222B
- ["Intersection"] = "⋂", -- U+022C2
- ["InvisibleComma"] = "⁣", -- U+02063
- ["InvisibleTimes"] = "⁢", -- U+02062
- ["Iogon"] = "Į", -- U+0012E
- ["Iopf"] = "𝕀", -- U+1D540
- ["Iota"] = "Ι", -- U+00399
- ["Iscr"] = "ℐ", -- U+02110
- ["Itilde"] = "Ĩ", -- U+00128
- ["Iukcy"] = "І", -- U+00406
- ["Iuml"] = "Ï", -- U+000CF
- ["Jcirc"] = "Ĵ", -- U+00134
- ["Jcy"] = "Й", -- U+00419
- ["Jfr"] = "𝔍", -- U+1D50D
- ["Jopf"] = "𝕁", -- U+1D541
- ["Jscr"] = "𝒥", -- U+1D4A5
- ["Jsercy"] = "Ј", -- U+00408
- ["Jukcy"] = "Є", -- U+00404
- ["KHcy"] = "Х", -- U+00425
- ["KHgr"] = "Χ", -- U+003A7
- ["KJcy"] = "Ќ", -- U+0040C
- ["Kappa"] = "Κ", -- U+0039A
- ["Kcedil"] = "Ķ", -- U+00136
- ["Kcy"] = "К", -- U+0041A
- ["Kfr"] = "𝔎", -- U+1D50E
- ["Kgr"] = "Κ", -- U+0039A
- ["Kopf"] = "𝕂", -- U+1D542
- ["Kscr"] = "𝒦", -- U+1D4A6
- ["LJcy"] = "Љ", -- U+00409
- ["LT"] = "<", -- U+00026
- ["Lacute"] = "Ĺ", -- U+00139
- ["Lambda"] = "Λ", -- U+0039B
- ["Lang"] = "⟪", -- U+027EA
- ["Laplacetrf"] = "ℒ", -- U+02112
- ["Larr"] = "↞", -- U+0219E
- ["Lcaron"] = "Ľ", -- U+0013D
- ["Lcedil"] = "Ļ", -- U+0013B
- ["Lcy"] = "Л", -- U+0041B
- ["LeftAngleBracket"] = "⟨", -- U+027E8
- ["LeftArrow"] = "←", -- U+02190
- ["LeftArrowBar"] = "⇤", -- U+021E4
- ["LeftArrowRightArrow"] = "⇆", -- U+021C6
- ["LeftCeiling"] = "⌈", -- U+02308
- ["LeftDoubleBracket"] = "⟦", -- U+027E6
- ["LeftDownTeeVector"] = "⥡", -- U+02961
- ["LeftDownVector"] = "⇃", -- U+021C3
- ["LeftDownVectorBar"] = "⥙", -- U+02959
- ["LeftFloor"] = "⌊", -- U+0230A
- ["LeftRightArrow"] = "↔", -- U+02194
- ["LeftRightVector"] = "⥎", -- U+0294E
- ["LeftTee"] = "⊣", -- U+022A3
- ["LeftTeeArrow"] = "↤", -- U+021A4
- ["LeftTeeVector"] = "⥚", -- U+0295A
- ["LeftTriangle"] = "⊲", -- U+022B2
- ["LeftTriangleBar"] = "⧏", -- U+029CF
- ["LeftTriangleEqual"] = "⊴", -- U+022B4
- ["LeftUpDownVector"] = "⥑", -- U+02951
- ["LeftUpTeeVector"] = "⥠", -- U+02960
- ["LeftUpVector"] = "↿", -- U+021BF
- ["LeftUpVectorBar"] = "⥘", -- U+02958
- ["LeftVector"] = "↼", -- U+021BC
- ["LeftVectorBar"] = "⥒", -- U+02952
- ["Leftarrow"] = "⇐", -- U+021D0
- ["Leftrightarrow"] = "⇔", -- U+021D4
- ["LessEqualGreater"] = "⋚", -- U+022DA
- ["LessFullEqual"] = "≦", -- U+02266
- ["LessGreater"] = "≶", -- U+02276
- ["LessLess"] = "⪡", -- U+02AA1
- ["LessSlantEqual"] = "⩽", -- U+02A7D
- ["LessTilde"] = "≲", -- U+02272
- ["Lfr"] = "𝔏", -- U+1D50F
- ["Lgr"] = "Λ", -- U+0039B
- ["Ll"] = "⋘", -- U+022D8
- ["Lleftarrow"] = "⇚", -- U+021DA
- ["Lmidot"] = "Ŀ", -- U+0013F
- ["LongLeftArrow"] = "⟵", -- U+027F5
- ["LongLeftRightArrow"] = "⟷", -- U+027F7
- ["LongRightArrow"] = "⟶", -- U+027F6
- ["Longleftarrow"] = "⟸", -- U+027F8
- ["Longleftrightarrow"] = "⟺", -- U+027FA
- ["Longrightarrow"] = "⟹", -- U+027F9
- ["Lopf"] = "𝕃", -- U+1D543
- ["LowerLeftArrow"] = "↙", -- U+02199
- ["LowerRightArrow"] = "↘", -- U+02198
- ["Lscr"] = "ℒ", -- U+02112
- ["Lsh"] = "↰", -- U+021B0
- ["Lstrok"] = "Ł", -- U+00141
- ["Lt"] = "≪", -- U+0226A
- ["Map"] = "⤅", -- U+02905
- ["Mcy"] = "М", -- U+0041C
- ["MediumSpace"] = " ", -- U+0205F
- ["Mellintrf"] = "ℳ", -- U+02133
- ["Mfr"] = "𝔐", -- U+1D510
- ["Mgr"] = "Μ", -- U+0039C
- ["MinusPlus"] = "∓", -- U+02213
- ["Mopf"] = "𝕄", -- U+1D544
- ["Mscr"] = "ℳ", -- U+02133
- ["Mu"] = "Μ", -- U+0039C
- ["NJcy"] = "Њ", -- U+0040A
- ["Nacute"] = "Ń", -- U+00143
- ["Ncaron"] = "Ň", -- U+00147
- ["Ncedil"] = "Ņ", -- U+00145
- ["Ncy"] = "Н", -- U+0041D
- ["NegativeMediumSpace"] = "​", -- U+0200B
- ["NegativeThickSpace"] = "​", -- U+0200B
- ["NegativeThinSpace"] = "​", -- U+0200B
- ["NegativeVeryThinSpace"] = "​", -- U+0200B
- ["NestedGreaterGreater"] = "≫", -- U+0226B
- ["NestedLessLess"] = "≪", -- U+0226A
- ["Nfr"] = "𝔑", -- U+1D511
- ["Ngr"] = "Ν", -- U+0039D
- ["NoBreak"] = "⁠", -- U+02060
- ["NonBreakingSpace"] = " ", -- U+000A0
- ["Nopf"] = "ℕ", -- U+02115
- ["Not"] = "⫬", -- U+02AEC
- ["NotCongruent"] = "≢", -- U+02262
- ["NotCupCap"] = "≭", -- U+0226D
- ["NotDoubleVerticalBar"] = "∦", -- U+02226
- ["NotElement"] = "∉", -- U+02209
- ["NotEqual"] = "≠", -- U+02260
- ["NotEqualTilde"] = "≂̸", -- U+02242 00338
- ["NotExists"] = "∄", -- U+02204
- ["NotGreater"] = "≯", -- U+0226F
- ["NotGreaterEqual"] = "≱", -- U+02271
- ["NotGreaterFullEqual"] = "≧̸", -- U+02267 00338
- ["NotGreaterGreater"] = "≫̸", -- U+0226B 00338
- ["NotGreaterLess"] = "≹", -- U+02279
- ["NotGreaterSlantEqual"] = "⩾̸", -- U+02A7E 00338
- ["NotGreaterTilde"] = "≵", -- U+02275
- ["NotHumpDownHump"] = "≎̸", -- U+0224E 00338
- ["NotHumpEqual"] = "≏̸", -- U+0224F 00338
- ["NotLeftTriangle"] = "⋪", -- U+022EA
- ["NotLeftTriangleBar"] = "⧏̸", -- U+029CF 00338
- ["NotLeftTriangleEqual"] = "⋬", -- U+022EC
- ["NotLess"] = "≮", -- U+0226E
- ["NotLessEqual"] = "≰", -- U+02270
- ["NotLessGreater"] = "≸", -- U+02278
- ["NotLessLess"] = "≪̸", -- U+0226A 00338
- ["NotLessSlantEqual"] = "⩽̸", -- U+02A7D 00338
- ["NotLessTilde"] = "≴", -- U+02274
- ["NotNestedGreaterGreater"] = "⪢̸", -- U+02AA2 00338
- ["NotNestedLessLess"] = "⪡̸", -- U+02AA1 00338
- ["NotPrecedes"] = "⊀", -- U+02280
- ["NotPrecedesEqual"] = "⪯̸", -- U+02AAF 00338
- ["NotPrecedesSlantEqual"] = "⋠", -- U+022E0
- ["NotReverseElement"] = "∌", -- U+0220C
- ["NotRightTriangle"] = "⋫", -- U+022EB
- ["NotRightTriangleBar"] = "⧐̸", -- U+029D0 00338
- ["NotRightTriangleEqual"] = "⋭", -- U+022ED
- ["NotSquareSubset"] = "⊏̸", -- U+0228F 00338
- ["NotSquareSubsetEqual"] = "⋢", -- U+022E2
- ["NotSquareSuperset"] = "⊐̸", -- U+02290 00338
- ["NotSquareSupersetEqual"] = "⋣", -- U+022E3
- ["NotSubset"] = "⊂⃒", -- U+02282 020D2
- ["NotSubsetEqual"] = "⊈", -- U+02288
- ["NotSucceeds"] = "⊁", -- U+02281
- ["NotSucceedsEqual"] = "⪰̸", -- U+02AB0 00338
- ["NotSucceedsSlantEqual"] = "⋡", -- U+022E1
- ["NotSucceedsTilde"] = "≿̸", -- U+0227F 00338
- ["NotSuperset"] = "⊃⃒", -- U+02283 020D2
- ["NotSupersetEqual"] = "⊉", -- U+02289
- ["NotTilde"] = "≁", -- U+02241
- ["NotTildeEqual"] = "≄", -- U+02244
- ["NotTildeFullEqual"] = "≇", -- U+02247
- ["NotTildeTilde"] = "≉", -- U+02249
- ["NotVerticalBar"] = "∤", -- U+02224
- ["Nscr"] = "𝒩", -- U+1D4A9
- ["Ntilde"] = "Ñ", -- U+000D1
- ["Nu"] = "Ν", -- U+0039D
- ["OElig"] = "Œ", -- U+00152
- ["OHacgr"] = "Ώ", -- U+0038F
- ["OHgr"] = "Ω", -- U+003A9
- ["Oacgr"] = "Ό", -- U+0038C
- ["Oacute"] = "Ó", -- U+000D3
- ["Ocirc"] = "Ô", -- U+000D4
- ["Ocy"] = "О", -- U+0041E
- ["Odblac"] = "Ő", -- U+00150
- ["Ofr"] = "𝔒", -- U+1D512
- ["Ogr"] = "Ο", -- U+0039F
- ["Ograve"] = "Ò", -- U+000D2
- ["Omacr"] = "Ō", -- U+0014C
- ["Omega"] = "Ω", -- U+003A9
- ["Omicron"] = "Ο", -- U+0039F
- ["Oopf"] = "𝕆", -- U+1D546
- ["OpenCurlyDoubleQuote"] = "“", -- U+0201C
- ["OpenCurlyQuote"] = "‘", -- U+02018
- ["Or"] = "⩔", -- U+02A54
- ["Oscr"] = "𝒪", -- U+1D4AA
- ["Oslash"] = "Ø", -- U+000D8
- ["Otilde"] = "Õ", -- U+000D5
- ["Otimes"] = "⨷", -- U+02A37
- ["Ouml"] = "Ö", -- U+000D6
- ["OverBar"] = "‾", -- U+0203E
- ["OverBrace"] = "⏞", -- U+023DE
- ["OverBracket"] = "⎴", -- U+023B4
- ["OverParenthesis"] = "⏜", -- U+023DC
- ["PHgr"] = "Φ", -- U+003A6
- ["PSgr"] = "Ψ", -- U+003A8
- ["PartialD"] = "∂", -- U+02202
- ["Pcy"] = "П", -- U+0041F
- ["Pfr"] = "𝔓", -- U+1D513
- ["Pgr"] = "Π", -- U+003A0
- ["Phi"] = "Φ", -- U+003A6
- ["Pi"] = "Π", -- U+003A0
- ["PlusMinus"] = "±", -- U+000B1
- ["Poincareplane"] = "ℌ", -- U+0210C
- ["Popf"] = "ℙ", -- U+02119
- ["Pr"] = "⪻", -- U+02ABB
- ["Precedes"] = "≺", -- U+0227A
- ["PrecedesEqual"] = "⪯", -- U+02AAF
- ["PrecedesSlantEqual"] = "≼", -- U+0227C
- ["PrecedesTilde"] = "≾", -- U+0227E
- ["Prime"] = "″", -- U+02033
- ["Product"] = "∏", -- U+0220F
- ["Proportion"] = "∷", -- U+02237
- ["Proportional"] = "∝", -- U+0221D
- ["Pscr"] = "𝒫", -- U+1D4AB
- ["Psi"] = "Ψ", -- U+003A8
- ["QUOT"] = "\"", -- U+00022
- ["Qfr"] = "𝔔", -- U+1D514
- ["Qopf"] = "ℚ", -- U+0211A
- ["Qscr"] = "𝒬", -- U+1D4AC
- ["RBarr"] = "⤐", -- U+02910
- ["REG"] = "®", -- U+000AE
- ["Racute"] = "Ŕ", -- U+00154
- ["Rang"] = "⟫", -- U+027EB
- ["Rarr"] = "↠", -- U+021A0
- ["Rarrtl"] = "⤖", -- U+02916
- ["Rcaron"] = "Ř", -- U+00158
- ["Rcedil"] = "Ŗ", -- U+00156
- ["Rcy"] = "Р", -- U+00420
- ["Re"] = "ℜ", -- U+0211C
- ["ReverseElement"] = "∋", -- U+0220B
- ["ReverseEquilibrium"] = "⇋", -- U+021CB
- ["ReverseUpEquilibrium"] = "⥯", -- U+0296F
- ["Rfr"] = "ℜ", -- U+0211C
- ["Rgr"] = "Ρ", -- U+003A1
- ["Rho"] = "Ρ", -- U+003A1
- ["RightAngleBracket"] = "⟩", -- U+027E9
- ["RightArrow"] = "→", -- U+02192
- ["RightArrowBar"] = "⇥", -- U+021E5
- ["RightArrowLeftArrow"] = "⇄", -- U+021C4
- ["RightCeiling"] = "⌉", -- U+02309
- ["RightDoubleBracket"] = "⟧", -- U+027E7
- ["RightDownTeeVector"] = "⥝", -- U+0295D
- ["RightDownVector"] = "⇂", -- U+021C2
- ["RightDownVectorBar"] = "⥕", -- U+02955
- ["RightFloor"] = "⌋", -- U+0230B
- ["RightTee"] = "⊢", -- U+022A2
- ["RightTeeArrow"] = "↦", -- U+021A6
- ["RightTeeVector"] = "⥛", -- U+0295B
- ["RightTriangle"] = "⊳", -- U+022B3
- ["RightTriangleBar"] = "⧐", -- U+029D0
- ["RightTriangleEqual"] = "⊵", -- U+022B5
- ["RightUpDownVector"] = "⥏", -- U+0294F
- ["RightUpTeeVector"] = "⥜", -- U+0295C
- ["RightUpVector"] = "↾", -- U+021BE
- ["RightUpVectorBar"] = "⥔", -- U+02954
- ["RightVector"] = "⇀", -- U+021C0
- ["RightVectorBar"] = "⥓", -- U+02953
- ["Rightarrow"] = "⇒", -- U+021D2
- ["Ropf"] = "ℝ", -- U+0211D
- ["RoundImplies"] = "⥰", -- U+02970
- ["Rrightarrow"] = "⇛", -- U+021DB
- ["Rscr"] = "ℛ", -- U+0211B
- ["Rsh"] = "↱", -- U+021B1
- ["RuleDelayed"] = "⧴", -- U+029F4
- ["SHCHcy"] = "Щ", -- U+00429
- ["SHcy"] = "Ш", -- U+00428
- ["SOFTcy"] = "Ь", -- U+0042C
- ["Sacute"] = "Ś", -- U+0015A
- ["Sc"] = "⪼", -- U+02ABC
- ["Scaron"] = "Š", -- U+00160
- ["Scedil"] = "Ş", -- U+0015E
- ["Scirc"] = "Ŝ", -- U+0015C
- ["Scy"] = "С", -- U+00421
- ["Sfr"] = "𝔖", -- U+1D516
- ["Sgr"] = "Σ", -- U+003A3
- ["ShortDownArrow"] = "↓", -- U+02193
- ["ShortLeftArrow"] = "←", -- U+02190
- ["ShortRightArrow"] = "→", -- U+02192
- ["ShortUpArrow"] = "↑", -- U+02191
- ["Sigma"] = "Σ", -- U+003A3
- ["SmallCircle"] = "∘", -- U+02218
- ["Sopf"] = "𝕊", -- U+1D54A
- ["Sqrt"] = "√", -- U+0221A
- ["Square"] = "□", -- U+025A1
- ["SquareIntersection"] = "⊓", -- U+02293
- ["SquareSubset"] = "⊏", -- U+0228F
- ["SquareSubsetEqual"] = "⊑", -- U+02291
- ["SquareSuperset"] = "⊐", -- U+02290
- ["SquareSupersetEqual"] = "⊒", -- U+02292
- ["SquareUnion"] = "⊔", -- U+02294
- ["Sscr"] = "𝒮", -- U+1D4AE
- ["Star"] = "⋆", -- U+022C6
- ["Sub"] = "⋐", -- U+022D0
- ["Subset"] = "⋐", -- U+022D0
- ["SubsetEqual"] = "⊆", -- U+02286
- ["Succeeds"] = "≻", -- U+0227B
- ["SucceedsEqual"] = "⪰", -- U+02AB0
- ["SucceedsSlantEqual"] = "≽", -- U+0227D
- ["SucceedsTilde"] = "≿", -- U+0227F
- ["SuchThat"] = "∋", -- U+0220B
- ["Sum"] = "∑", -- U+02211
- ["Sup"] = "⋑", -- U+022D1
- ["Superset"] = "⊃", -- U+02283
- ["SupersetEqual"] = "⊇", -- U+02287
- ["Supset"] = "⋑", -- U+022D1
- ["THORN"] = "Þ", -- U+000DE
- ["THgr"] = "Θ", -- U+00398
- ["TRADE"] = "™", -- U+02122
- ["TSHcy"] = "Ћ", -- U+0040B
- ["TScy"] = "Ц", -- U+00426
- ["Tab"] = "\9", -- U+00009
- ["Tau"] = "Τ", -- U+003A4
- ["Tcaron"] = "Ť", -- U+00164
- ["Tcedil"] = "Ţ", -- U+00162
- ["Tcy"] = "Т", -- U+00422
- ["Tfr"] = "𝔗", -- U+1D517
- ["Tgr"] = "Τ", -- U+003A4
- ["Therefore"] = "∴", -- U+02234
- ["Theta"] = "Θ", -- U+00398
- ["ThickSpace"] = "  ", -- U+0205F 0200A
- ["ThinSpace"] = " ", -- U+02009
- ["Tilde"] = "∼", -- U+0223C
- ["TildeEqual"] = "≃", -- U+02243
- ["TildeFullEqual"] = "≅", -- U+02245
- ["TildeTilde"] = "≈", -- U+02248
- ["Topf"] = "𝕋", -- U+1D54B
- ["TripleDot"] = "⃛", -- U+020DB
- ["Tscr"] = "𝒯", -- U+1D4AF
- ["Tstrok"] = "Ŧ", -- U+00166
- ["Uacgr"] = "Ύ", -- U+0038E
- ["Uacute"] = "Ú", -- U+000DA
- ["Uarr"] = "↟", -- U+0219F
- ["Uarrocir"] = "⥉", -- U+02949
- ["Ubrcy"] = "Ў", -- U+0040E
- ["Ubreve"] = "Ŭ", -- U+0016C
- ["Ucirc"] = "Û", -- U+000DB
- ["Ucy"] = "У", -- U+00423
- ["Udblac"] = "Ű", -- U+00170
- ["Udigr"] = "Ϋ", -- U+003AB
- ["Ufr"] = "𝔘", -- U+1D518
- ["Ugr"] = "Υ", -- U+003A5
- ["Ugrave"] = "Ù", -- U+000D9
- ["Umacr"] = "Ū", -- U+0016A
- -- ["UnderBar"] = "_", -- U+0005F
- ["UnderBar"] = "‾", -- U+0203E
- ["UnderBrace"] = "⏟", -- U+023DF
- ["UnderBracket"] = "⎵", -- U+023B5
- ["UnderParenthesis"] = "⏝", -- U+023DD
- ["Union"] = "⋃", -- U+022C3
- ["UnionPlus"] = "⊎", -- U+0228E
- ["Uogon"] = "Ų", -- U+00172
- ["Uopf"] = "𝕌", -- U+1D54C
- ["UpArrow"] = "↑", -- U+02191
- ["UpArrowBar"] = "⤒", -- U+02912
- ["UpArrowDownArrow"] = "⇅", -- U+021C5
- ["UpDownArrow"] = "↕", -- U+02195
- ["UpEquilibrium"] = "⥮", -- U+0296E
- ["UpTee"] = "⊥", -- U+022A5
- ["UpTeeArrow"] = "↥", -- U+021A5
- ["Uparrow"] = "⇑", -- U+021D1
- ["Updownarrow"] = "⇕", -- U+021D5
- ["UpperLeftArrow"] = "↖", -- U+02196
- ["UpperRightArrow"] = "↗", -- U+02197
- ["Upsi"] = "ϒ", -- U+003D2
- ["Upsilon"] = "Υ", -- U+003A5
- ["Uring"] = "Ů", -- U+0016E
- ["Uscr"] = "𝒰", -- U+1D4B0
- ["Utilde"] = "Ũ", -- U+00168
- ["Uuml"] = "Ü", -- U+000DC
- ["VDash"] = "⊫", -- U+022AB
- ["Vbar"] = "⫫", -- U+02AEB
- ["Vcy"] = "В", -- U+00412
- ["Vdash"] = "⊩", -- U+022A9
- ["Vdashl"] = "⫦", -- U+02AE6
- ["Vee"] = "⋁", -- U+022C1
- ["Verbar"] = "‖", -- U+02016
- ["Vert"] = "‖", -- U+02016
- ["VerticalBar"] = "∣", -- U+02223
- ["VerticalLine"] = "|", -- U+0007C
- ["VerticalSeparator"] = "❘", -- U+02758
- ["VerticalTilde"] = "≀", -- U+02240
- ["VeryThinSpace"] = " ", -- U+0200A
- ["Vfr"] = "𝔙", -- U+1D519
- ["Vopf"] = "𝕍", -- U+1D54D
- ["Vscr"] = "𝒱", -- U+1D4B1
- ["Vvdash"] = "⊪", -- U+022AA
- ["Wcirc"] = "Ŵ", -- U+00174
- ["Wedge"] = "⋀", -- U+022C0
- ["Wfr"] = "𝔚", -- U+1D51A
- ["Wopf"] = "𝕎", -- U+1D54E
- ["Wscr"] = "𝒲", -- U+1D4B2
- ["Xfr"] = "𝔛", -- U+1D51B
- ["Xgr"] = "Ξ", -- U+0039E
- ["Xi"] = "Ξ", -- U+0039E
- ["Xopf"] = "𝕏", -- U+1D54F
- ["Xscr"] = "𝒳", -- U+1D4B3
- ["YAcy"] = "Я", -- U+0042F
- ["YIcy"] = "Ї", -- U+00407
- ["YUcy"] = "Ю", -- U+0042E
- ["Yacute"] = "Ý", -- U+000DD
- ["Ycirc"] = "Ŷ", -- U+00176
- ["Ycy"] = "Ы", -- U+0042B
- ["Yfr"] = "𝔜", -- U+1D51C
- ["Yopf"] = "𝕐", -- U+1D550
- ["Yscr"] = "𝒴", -- U+1D4B4
- ["Yuml"] = "Ÿ", -- U+00178
- ["ZHcy"] = "Ж", -- U+00416
- ["Zacute"] = "Ź", -- U+00179
- ["Zcaron"] = "Ž", -- U+0017D
- ["Zcy"] = "З", -- U+00417
- ["Zdot"] = "Ż", -- U+0017B
- ["ZeroWidthSpace"] = "​", -- U+0200B
- ["Zeta"] = "Ζ", -- U+00396
- ["Zfr"] = "ℨ", -- U+02128
- ["Zgr"] = "Ζ", -- U+00396
- ["Zopf"] = "ℤ", -- U+02124
- ["Zscr"] = "𝒵", -- U+1D4B5
- ["aacgr"] = "ά", -- U+003AC
- ["aacute"] = "á", -- U+000E1
- ["abreve"] = "ă", -- U+00103
- ["ac"] = "∾", -- U+0223E
- ["acE"] = "∾̳", -- U+0223E 00333
- ["acd"] = "∿", -- U+0223F
- ["acirc"] = "â", -- U+000E2
- ["acute"] = "´", -- U+000B4
- ["acy"] = "а", -- U+00430
- ["aelig"] = "æ", -- U+000E6
- ["af"] = "⁡", -- U+02061
- ["afr"] = "𝔞", -- U+1D51E
- ["agr"] = "α", -- U+003B1
- ["agrave"] = "à", -- U+000E0
- ["alefsym"] = "ℵ", -- U+02135
- ["aleph"] = "ℵ", -- U+02135
- ["alpha"] = "α", -- U+003B1
- ["amacr"] = "ā", -- U+00101
- ["amalg"] = "⨿", -- U+02A3F
- ["amp"] = "&", -- U+00026
- ["and"] = "∧", -- U+02227
- ["andand"] = "⩕", -- U+02A55
- ["andd"] = "⩜", -- U+02A5C
- ["andslope"] = "⩘", -- U+02A58
- ["andv"] = "⩚", -- U+02A5A
- ["ang"] = "∠", -- U+02220
- ["ange"] = "⦤", -- U+029A4
- ["angle"] = "∠", -- U+02220
- ["angmsd"] = "∡", -- U+02221
- ["angmsdaa"] = "⦨", -- U+029A8
- ["angmsdab"] = "⦩", -- U+029A9
- ["angmsdac"] = "⦪", -- U+029AA
- ["angmsdad"] = "⦫", -- U+029AB
- ["angmsdae"] = "⦬", -- U+029AC
- ["angmsdaf"] = "⦭", -- U+029AD
- ["angmsdag"] = "⦮", -- U+029AE
- ["angmsdah"] = "⦯", -- U+029AF
- ["angrt"] = "∟", -- U+0221F
- ["angrtvb"] = "⊾", -- U+022BE
- ["angrtvbd"] = "⦝", -- U+0299D
- ["angsph"] = "∢", -- U+02222
- ["angst"] = "Å", -- U+000C5
- ["angzarr"] = "⍼", -- U+0237C
- ["aogon"] = "ą", -- U+00105
- ["aopf"] = "𝕒", -- U+1D552
- ["ap"] = "≈", -- U+02248
- ["apE"] = "⩰", -- U+02A70
- ["apacir"] = "⩯", -- U+02A6F
- ["ape"] = "≊", -- U+0224A
- ["apid"] = "≋", -- U+0224B
- ["apos"] = "'", -- U+00027
- ["approx"] = "≈", -- U+02248
- ["approxeq"] = "≊", -- U+0224A
- ["aring"] = "å", -- U+000E5
- ["ascr"] = "𝒶", -- U+1D4B6
- ["ast"] = "*", -- U+0002A
- ["asymp"] = "≈", -- U+02248
- ["asympeq"] = "≍", -- U+0224D
- ["atilde"] = "ã", -- U+000E3
- ["auml"] = "ä", -- U+000E4
- ["awconint"] = "∳", -- U+02233
- ["awint"] = "⨑", -- U+02A11
- ["b.Delta"] = "𝚫", -- U+1D6AB
- ["b.Gamma"] = "𝚪", -- U+1D6AA
- ["b.Gammad"] = "𝟊", -- U+1D7CA
- ["b.Lambda"] = "𝚲", -- U+1D6B2
- ["b.Omega"] = "𝛀", -- U+1D6C0
- ["b.Phi"] = "𝚽", -- U+1D6BD
- ["b.Pi"] = "𝚷", -- U+1D6B7
- ["b.Psi"] = "𝚿", -- U+1D6BF
- ["b.Sigma"] = "𝚺", -- U+1D6BA
- ["b.Theta"] = "𝚯", -- U+1D6AF
- ["b.Upsi"] = "𝚼", -- U+1D6BC
- ["b.Xi"] = "𝚵", -- U+1D6B5
- ["b.alpha"] = "𝛂", -- U+1D6C2
- ["b.beta"] = "𝛃", -- U+1D6C3
- ["b.chi"] = "𝛘", -- U+1D6D8
- ["b.delta"] = "𝛅", -- U+1D6C5
- ["b.epsi"] = "𝛆", -- U+1D6C6
- ["b.epsiv"] = "𝛜", -- U+1D6DC
- ["b.eta"] = "𝛈", -- U+1D6C8
- ["b.gamma"] = "𝛄", -- U+1D6C4
- ["b.gammad"] = "𝟋", -- U+1D7CB
- ["b.iota"] = "𝛊", -- U+1D6CA
- ["b.kappa"] = "𝛋", -- U+1D6CB
- ["b.kappav"] = "𝛞", -- U+1D6DE
- ["b.lambda"] = "𝛌", -- U+1D6CC
- ["b.mu"] = "𝛍", -- U+1D6CD
- ["b.nu"] = "𝛎", -- U+1D6CE
- ["b.omega"] = "𝛚", -- U+1D6DA
- ["b.phi"] = "𝛗", -- U+1D6D7
- ["b.phiv"] = "𝛟", -- U+1D6DF
- ["b.pi"] = "𝛑", -- U+1D6D1
- ["b.piv"] = "𝛡", -- U+1D6E1
- ["b.psi"] = "𝛙", -- U+1D6D9
- ["b.rho"] = "𝛒", -- U+1D6D2
- ["b.rhov"] = "𝛠", -- U+1D6E0
- ["b.sigma"] = "𝛔", -- U+1D6D4
- ["b.sigmav"] = "𝛓", -- U+1D6D3
- ["b.tau"] = "𝛕", -- U+1D6D5
- ["b.thetas"] = "𝛉", -- U+1D6C9
- ["b.thetav"] = "𝛝", -- U+1D6DD
- ["b.upsi"] = "𝛖", -- U+1D6D6
- ["b.xi"] = "𝛏", -- U+1D6CF
- ["b.zeta"] = "𝛇", -- U+1D6C7
- ["bNot"] = "⫭", -- U+02AED
- ["backcong"] = "≌", -- U+0224C
- ["backepsilon"] = "϶", -- U+003F6
- ["backprime"] = "‵", -- U+02035
- ["backsim"] = "∽", -- U+0223D
- ["backsimeq"] = "⋍", -- U+022CD
- ["barvee"] = "⊽", -- U+022BD
- ["barwed"] = "⌅", -- U+02305
- ["barwedge"] = "⌅", -- U+02305
- ["bbrk"] = "⎵", -- U+023B5
- ["bbrktbrk"] = "⎶", -- U+023B6
- ["bcong"] = "≌", -- U+0224C
- ["bcy"] = "б", -- U+00431
- ["bdquo"] = "„", -- U+0201E
- ["becaus"] = "∵", -- U+02235
- ["because"] = "∵", -- U+02235
- ["bemptyv"] = "⦰", -- U+029B0
- ["bepsi"] = "϶", -- U+003F6
- ["bernou"] = "ℬ", -- U+0212C
- ["beta"] = "β", -- U+003B2
- ["beth"] = "ℶ", -- U+02136
- ["between"] = "≬", -- U+0226C
- ["bfr"] = "𝔟", -- U+1D51F
- ["bgr"] = "β", -- U+003B2
- ["bigcap"] = "⋂", -- U+022C2
- ["bigcirc"] = "◯", -- U+025EF
- ["bigcup"] = "⋃", -- U+022C3
- ["bigodot"] = "⨀", -- U+02A00
- ["bigoplus"] = "⨁", -- U+02A01
- ["bigotimes"] = "⨂", -- U+02A02
- ["bigsqcup"] = "⨆", -- U+02A06
- ["bigstar"] = "★", -- U+02605
- ["bigtriangledown"] = "▽", -- U+025BD
- ["bigtriangleup"] = "△", -- U+025B3
- ["biguplus"] = "⨄", -- U+02A04
- ["bigvee"] = "⋁", -- U+022C1
- ["bigwedge"] = "⋀", -- U+022C0
- ["bkarow"] = "⤍", -- U+0290D
- ["blacklozenge"] = "⧫", -- U+029EB
- ["blacksquare"] = "▪", -- U+025AA
- ["blacktriangle"] = "▴", -- U+025B4
- ["blacktriangledown"] = "▾", -- U+025BE
- ["blacktriangleleft"] = "◂", -- U+025C2
- ["blacktriangleright"] = "▸", -- U+025B8
- ["blank"] = "␣", -- U+02423
- ["blk12"] = "▒", -- U+02592
- ["blk14"] = "░", -- U+02591
- ["blk34"] = "▓", -- U+02593
- ["block"] = "█", -- U+02588
- ["bne"] = "=⃥", -- U+0003D 020E5
- ["bnequiv"] = "≡⃥", -- U+02261 020E5
- ["bnot"] = "⌐", -- U+02310
- ["bopf"] = "𝕓", -- U+1D553
- ["bot"] = "⊥", -- U+022A5
- ["bottom"] = "⊥", -- U+022A5
- ["bowtie"] = "⋈", -- U+022C8
- ["boxDL"] = "╗", -- U+02557
- ["boxDR"] = "╔", -- U+02554
- ["boxDl"] = "╖", -- U+02556
- ["boxDr"] = "╓", -- U+02553
- ["boxH"] = "═", -- U+02550
- ["boxHD"] = "╦", -- U+02566
- ["boxHU"] = "╩", -- U+02569
- ["boxHd"] = "╤", -- U+02564
- ["boxHu"] = "╧", -- U+02567
- ["boxUL"] = "╝", -- U+0255D
- ["boxUR"] = "╚", -- U+0255A
- ["boxUl"] = "╜", -- U+0255C
- ["boxUr"] = "╙", -- U+02559
- ["boxV"] = "║", -- U+02551
- ["boxVH"] = "╬", -- U+0256C
- ["boxVL"] = "╣", -- U+02563
- ["boxVR"] = "╠", -- U+02560
- ["boxVh"] = "╫", -- U+0256B
- ["boxVl"] = "╢", -- U+02562
- ["boxVr"] = "╟", -- U+0255F
- ["boxbox"] = "⧉", -- U+029C9
- ["boxdL"] = "╕", -- U+02555
- ["boxdR"] = "╒", -- U+02552
- ["boxdl"] = "┐", -- U+02510
- ["boxdr"] = "┌", -- U+0250C
- ["boxh"] = "─", -- U+02500
- ["boxhD"] = "╥", -- U+02565
- ["boxhU"] = "╨", -- U+02568
- ["boxhd"] = "┬", -- U+0252C
- ["boxhu"] = "┴", -- U+02534
- ["boxminus"] = "⊟", -- U+0229F
- ["boxplus"] = "⊞", -- U+0229E
- ["boxtimes"] = "⊠", -- U+022A0
- ["boxuL"] = "╛", -- U+0255B
- ["boxuR"] = "╘", -- U+02558
- ["boxul"] = "┘", -- U+02518
- ["boxur"] = "└", -- U+02514
- ["boxv"] = "│", -- U+02502
- ["boxvH"] = "╪", -- U+0256A
- ["boxvL"] = "╡", -- U+02561
- ["boxvR"] = "╞", -- U+0255E
- ["boxvh"] = "┼", -- U+0253C
- ["boxvl"] = "┤", -- U+02524
- ["boxvr"] = "├", -- U+0251C
- ["bprime"] = "‵", -- U+02035
- ["breve"] = "˘", -- U+002D8
- ["brvbar"] = "¦", -- U+000A6
- ["bscr"] = "𝒷", -- U+1D4B7
- ["bsemi"] = "⁏", -- U+0204F
- ["bsim"] = "∽", -- U+0223D
- ["bsime"] = "⋍", -- U+022CD
- ["bsol"] = "\\", -- U+0005C
- ["bsolb"] = "⧅", -- U+029C5
- ["bsolhsub"] = "⟈", -- U+027C8
- ["bull"] = "•", -- U+02022
- ["bullet"] = "•", -- U+02022
- ["bump"] = "≎", -- U+0224E
- ["bumpE"] = "⪮", -- U+02AAE
- ["bumpe"] = "≏", -- U+0224F
- ["bumpeq"] = "≏", -- U+0224F
- ["cacute"] = "ć", -- U+00107
- ["cap"] = "∩", -- U+02229
- ["capand"] = "⩄", -- U+02A44
- ["capbrcup"] = "⩉", -- U+02A49
- ["capcap"] = "⩋", -- U+02A4B
- ["capcup"] = "⩇", -- U+02A47
- ["capdot"] = "⩀", -- U+02A40
- ["caps"] = "∩︀", -- U+02229 0FE00
- ["caret"] = "⁁", -- U+02041
- ["caron"] = "ˇ", -- U+002C7
- ["ccaps"] = "⩍", -- U+02A4D
- ["ccaron"] = "č", -- U+0010D
- ["ccedil"] = "ç", -- U+000E7
- ["ccirc"] = "ĉ", -- U+00109
- ["ccups"] = "⩌", -- U+02A4C
- ["ccupssm"] = "⩐", -- U+02A50
- ["cdot"] = "ċ", -- U+0010B
- ["cedil"] = "¸", -- U+000B8
- ["cemptyv"] = "⦲", -- U+029B2
- ["cent"] = "¢", -- U+000A2
- ["centerdot"] = "·", -- U+000B7
- ["cfr"] = "𝔠", -- U+1D520
- ["chcy"] = "ч", -- U+00447
- ["check"] = "✓", -- U+02713
- ["checkmark"] = "✓", -- U+02713
- ["chi"] = "χ", -- U+003C7
- ["cir"] = "○", -- U+025CB
- ["cirE"] = "⧃", -- U+029C3
- ["circ"] = "ˆ", -- U+002C6
- ["circeq"] = "≗", -- U+02257
- ["circlearrowleft"] = "↺", -- U+021BA
- ["circlearrowright"] = "↻", -- U+021BB
- ["circledR"] = "®", -- U+000AE
- ["circledS"] = "Ⓢ", -- U+024C8
- ["circledast"] = "⊛", -- U+0229B
- ["circledcirc"] = "⊚", -- U+0229A
- ["circleddash"] = "⊝", -- U+0229D
- ["cire"] = "≗", -- U+02257
- ["cirfnint"] = "⨐", -- U+02A10
- ["cirmid"] = "⫯", -- U+02AEF
- ["cirscir"] = "⧂", -- U+029C2
- ["clubs"] = "♣", -- U+02663
- ["clubsuit"] = "♣", -- U+02663
- ["colon"] = ":", -- U+0003A
- ["colone"] = "≔", -- U+02254
- ["coloneq"] = "≔", -- U+02254
- ["comma"] = ",", -- U+0002C
- ["commat"] = "@", -- U+00040
- ["comp"] = "∁", -- U+02201
- ["compfn"] = "∘", -- U+02218
- ["complement"] = "∁", -- U+02201
- ["complexes"] = "ℂ", -- U+02102
- ["cong"] = "≅", -- U+02245
- ["congdot"] = "⩭", -- U+02A6D
- ["conint"] = "∮", -- U+0222E
- ["copf"] = "𝕔", -- U+1D554
- ["coprod"] = "∐", -- U+02210
- ["copy"] = "©", -- U+000A9
- ["copysr"] = "℗", -- U+02117
- ["crarr"] = "↵", -- U+021B5
- ["cross"] = "✗", -- U+02717
- ["cscr"] = "𝒸", -- U+1D4B8
- ["csub"] = "⫏", -- U+02ACF
- ["csube"] = "⫑", -- U+02AD1
- ["csup"] = "⫐", -- U+02AD0
- ["csupe"] = "⫒", -- U+02AD2
- ["ctdot"] = "⋯", -- U+022EF
- ["cudarrl"] = "⤸", -- U+02938
- ["cudarrr"] = "⤵", -- U+02935
- ["cuepr"] = "⋞", -- U+022DE
- ["cuesc"] = "⋟", -- U+022DF
- ["cularr"] = "↶", -- U+021B6
- ["cularrp"] = "⤽", -- U+0293D
- ["cup"] = "∪", -- U+0222A
- ["cupbrcap"] = "⩈", -- U+02A48
- ["cupcap"] = "⩆", -- U+02A46
- ["cupcup"] = "⩊", -- U+02A4A
- ["cupdot"] = "⊍", -- U+0228D
- ["cupor"] = "⩅", -- U+02A45
- ["cups"] = "∪︀", -- U+0222A 0FE00
- ["curarr"] = "↷", -- U+021B7
- ["curarrm"] = "⤼", -- U+0293C
- ["curlyeqprec"] = "⋞", -- U+022DE
- ["curlyeqsucc"] = "⋟", -- U+022DF
- ["curlyvee"] = "⋎", -- U+022CE
- ["curlywedge"] = "⋏", -- U+022CF
- ["curren"] = "¤", -- U+000A4
- ["curvearrowleft"] = "↶", -- U+021B6
- ["curvearrowright"] = "↷", -- U+021B7
- ["cuvee"] = "⋎", -- U+022CE
- ["cuwed"] = "⋏", -- U+022CF
- ["cwconint"] = "∲", -- U+02232
- ["cwint"] = "∱", -- U+02231
- ["cylcty"] = "⌭", -- U+0232D
- ["dArr"] = "⇓", -- U+021D3
- ["dHar"] = "⥥", -- U+02965
- ["dagger"] = "†", -- U+02020
- ["daleth"] = "ℸ", -- U+02138
- ["darr"] = "↓", -- U+02193
- ["dash"] = "‐", -- U+02010
- ["dashv"] = "⊣", -- U+022A3
- ["dbkarow"] = "⤏", -- U+0290F
- ["dblac"] = "˝", -- U+002DD
- ["dcaron"] = "ď", -- U+0010F
- ["dcy"] = "д", -- U+00434
- ["dd"] = "ⅆ", -- U+02146
- ["ddagger"] = "‡", -- U+02021
- ["ddarr"] = "⇊", -- U+021CA
- ["ddotseq"] = "⩷", -- U+02A77
- ["deg"] = "°", -- U+000B0
- ["delta"] = "δ", -- U+003B4
- ["demptyv"] = "⦱", -- U+029B1
- ["dfisht"] = "⥿", -- U+0297F
- ["dfr"] = "𝔡", -- U+1D521
- ["dgr"] = "δ", -- U+003B4
- ["dharl"] = "⇃", -- U+021C3
- ["dharr"] = "⇂", -- U+021C2
- ["diam"] = "⋄", -- U+022C4
- ["diamond"] = "⋄", -- U+022C4
- ["diamondsuit"] = "♦", -- U+02666
- ["diams"] = "♦", -- U+02666
- ["die"] = "¨", -- U+000A8
- ["digamma"] = "ϝ", -- U+003DD
- ["disin"] = "⋲", -- U+022F2
- ["div"] = "÷", -- U+000F7
- ["divide"] = "÷", -- U+000F7
- ["divideontimes"] = "⋇", -- U+022C7
- ["divonx"] = "⋇", -- U+022C7
- ["djcy"] = "ђ", -- U+00452
- ["dlcorn"] = "⌞", -- U+0231E
- ["dlcrop"] = "⌍", -- U+0230D
- ["dollar"] = "$", -- U+00024
- ["dopf"] = "𝕕", -- U+1D555
- ["dot"] = "˙", -- U+002D9
- ["doteq"] = "≐", -- U+02250
- ["doteqdot"] = "≑", -- U+02251
- ["dotminus"] = "∸", -- U+02238
- ["dotplus"] = "∔", -- U+02214
- ["dotsquare"] = "⊡", -- U+022A1
- ["doublebarwedge"] = "⌆", -- U+02306
- ["downarrow"] = "↓", -- U+02193
- ["downdownarrows"] = "⇊", -- U+021CA
- ["downharpoonleft"] = "⇃", -- U+021C3
- ["downharpoonright"] = "⇂", -- U+021C2
- ["drbkarow"] = "⤐", -- U+02910
- ["drcorn"] = "⌟", -- U+0231F
- ["drcrop"] = "⌌", -- U+0230C
- ["dscr"] = "𝒹", -- U+1D4B9
- ["dscy"] = "ѕ", -- U+00455
- ["dsol"] = "⧶", -- U+029F6
- ["dstrok"] = "đ", -- U+00111
- ["dtdot"] = "⋱", -- U+022F1
- ["dtri"] = "▿", -- U+025BF
- ["dtrif"] = "▾", -- U+025BE
- ["duarr"] = "⇵", -- U+021F5
- ["duhar"] = "⥯", -- U+0296F
- ["dwangle"] = "⦦", -- U+029A6
- ["dzcy"] = "џ", -- U+0045F
- ["dzigrarr"] = "⟿", -- U+027FF
- ["eDDot"] = "⩷", -- U+02A77
- ["eDot"] = "≑", -- U+02251
- ["eacgr"] = "έ", -- U+003AD
- ["eacute"] = "é", -- U+000E9
- ["easter"] = "⩮", -- U+02A6E
- ["ecaron"] = "ě", -- U+0011B
- ["ecir"] = "≖", -- U+02256
- ["ecirc"] = "ê", -- U+000EA
- ["ecolon"] = "≕", -- U+02255
- ["ecy"] = "э", -- U+0044D
- ["edot"] = "ė", -- U+00117
- ["ee"] = "ⅇ", -- U+02147
- ["eeacgr"] = "ή", -- U+003AE
- ["eegr"] = "η", -- U+003B7
- ["efDot"] = "≒", -- U+02252
- ["efr"] = "𝔢", -- U+1D522
- ["eg"] = "⪚", -- U+02A9A
- ["egr"] = "ε", -- U+003B5
- ["egrave"] = "è", -- U+000E8
- ["egs"] = "⪖", -- U+02A96
- ["egsdot"] = "⪘", -- U+02A98
- ["el"] = "⪙", -- U+02A99
- ["elinters"] = "⏧", -- U+023E7
- ["ell"] = "ℓ", -- U+02113
- ["els"] = "⪕", -- U+02A95
- ["elsdot"] = "⪗", -- U+02A97
- ["emacr"] = "ē", -- U+00113
- ["empty"] = "∅", -- U+02205
- ["emptyset"] = "∅", -- U+02205
- ["emptyv"] = "∅", -- U+02205
- ["emsp"] = " ", -- U+02003
- ["emsp13"] = " ", -- U+02004
- ["emsp14"] = " ", -- U+02005
- ["eng"] = "ŋ", -- U+0014B
- ["ensp"] = " ", -- U+02002
- ["eogon"] = "ę", -- U+00119
- ["eopf"] = "𝕖", -- U+1D556
- ["epar"] = "⋕", -- U+022D5
- ["eparsl"] = "⧣", -- U+029E3
- ["eplus"] = "⩱", -- U+02A71
- ["epsi"] = "ε", -- U+003B5
- ["epsilon"] = "ε", -- U+003B5
- ["epsiv"] = "ϵ", -- U+003F5
- ["eqcirc"] = "≖", -- U+02256
- ["eqcolon"] = "≕", -- U+02255
- ["eqsim"] = "≂", -- U+02242
- ["eqslantgtr"] = "⪖", -- U+02A96
- ["eqslantless"] = "⪕", -- U+02A95
- ["equals"] = "=", -- U+0003D
- ["equest"] = "≟", -- U+0225F
- ["equiv"] = "≡", -- U+02261
- ["equivDD"] = "⩸", -- U+02A78
- ["eqvparsl"] = "⧥", -- U+029E5
- ["erDot"] = "≓", -- U+02253
- ["erarr"] = "⥱", -- U+02971
- ["escr"] = "ℯ", -- U+0212F
- ["esdot"] = "≐", -- U+02250
- ["esim"] = "≂", -- U+02242
- ["eta"] = "η", -- U+003B7
- ["eth"] = "ð", -- U+000F0
- ["euml"] = "ë", -- U+000EB
- ["euro"] = "€", -- U+020AC
- ["excl"] = "!", -- U+00021
- ["exist"] = "∃", -- U+02203
- ["expectation"] = "ℰ", -- U+02130
- ["exponentiale"] = "ⅇ", -- U+02147
- ["fallingdotseq"] = "≒", -- U+02252
- ["fcy"] = "ф", -- U+00444
- ["female"] = "♀", -- U+02640
- ["ffilig"] = "ffi", -- U+0FB03
- ["fflig"] = "ff", -- U+0FB00
- ["ffllig"] = "ffl", -- U+0FB04
- ["ffr"] = "𝔣", -- U+1D523
- ["filig"] = "fi", -- U+0FB01
- ["fjlig"] = "fj", -- U+00066 0006A
- ["flat"] = "♭", -- U+0266D
- ["fllig"] = "fl", -- U+0FB02
- ["fltns"] = "▱", -- U+025B1
- ["fnof"] = "ƒ", -- U+00192
- ["fopf"] = "𝕗", -- U+1D557
- ["forall"] = "∀", -- U+02200
- ["fork"] = "⋔", -- U+022D4
- ["forkv"] = "⫙", -- U+02AD9
- ["fpartint"] = "⨍", -- U+02A0D
- ["frac12"] = "½", -- U+000BD
- ["frac13"] = "⅓", -- U+02153
- ["frac14"] = "¼", -- U+000BC
- ["frac15"] = "⅕", -- U+02155
- ["frac16"] = "⅙", -- U+02159
- ["frac18"] = "⅛", -- U+0215B
- ["frac23"] = "⅔", -- U+02154
- ["frac25"] = "⅖", -- U+02156
- ["frac34"] = "¾", -- U+000BE
- ["frac35"] = "⅗", -- U+02157
- ["frac38"] = "⅜", -- U+0215C
- ["frac45"] = "⅘", -- U+02158
- ["frac56"] = "⅚", -- U+0215A
- ["frac58"] = "⅝", -- U+0215D
- ["frac78"] = "⅞", -- U+0215E
- ["frasl"] = "⁄", -- U+02044
- ["frown"] = "⌢", -- U+02322
- ["fscr"] = "𝒻", -- U+1D4BB
- ["gE"] = "≧", -- U+02267
- ["gEl"] = "⪌", -- U+02A8C
- ["gacute"] = "ǵ", -- U+001F5
- ["gamma"] = "γ", -- U+003B3
- ["gammad"] = "ϝ", -- U+003DD
- ["gap"] = "⪆", -- U+02A86
- ["gbreve"] = "ğ", -- U+0011F
- ["gcirc"] = "ĝ", -- U+0011D
- ["gcy"] = "г", -- U+00433
- ["gdot"] = "ġ", -- U+00121
- ["ge"] = "≥", -- U+02265
- ["gel"] = "⋛", -- U+022DB
- ["geq"] = "≥", -- U+02265
- ["geqq"] = "≧", -- U+02267
- ["geqslant"] = "⩾", -- U+02A7E
- ["ges"] = "⩾", -- U+02A7E
- ["gescc"] = "⪩", -- U+02AA9
- ["gesdot"] = "⪀", -- U+02A80
- ["gesdoto"] = "⪂", -- U+02A82
- ["gesdotol"] = "⪄", -- U+02A84
- ["gesl"] = "⋛︀", -- U+022DB 0FE00
- ["gesles"] = "⪔", -- U+02A94
- ["gfr"] = "𝔤", -- U+1D524
- ["gg"] = "≫", -- U+0226B
- ["ggg"] = "⋙", -- U+022D9
- ["ggr"] = "γ", -- U+003B3
- ["gimel"] = "ℷ", -- U+02137
- ["gjcy"] = "ѓ", -- U+00453
- ["gl"] = "≷", -- U+02277
- ["glE"] = "⪒", -- U+02A92
- ["gla"] = "⪥", -- U+02AA5
- ["glj"] = "⪤", -- U+02AA4
- ["gnE"] = "≩", -- U+02269
- ["gnap"] = "⪊", -- U+02A8A
- ["gnapprox"] = "⪊", -- U+02A8A
- ["gne"] = "⪈", -- U+02A88
- ["gneq"] = "⪈", -- U+02A88
- ["gneqq"] = "≩", -- U+02269
- ["gnsim"] = "⋧", -- U+022E7
- ["gopf"] = "𝕘", -- U+1D558
- ["grave"] = "`", -- U+00060
- ["gscr"] = "ℊ", -- U+0210A
- ["gsim"] = "≳", -- U+02273
- ["gsime"] = "⪎", -- U+02A8E
- ["gsiml"] = "⪐", -- U+02A90
- ["gt"] = ">", -- U+0003E
- ["gtcc"] = "⪧", -- U+02AA7
- ["gtcir"] = "⩺", -- U+02A7A
- ["gtdot"] = "⋗", -- U+022D7
- ["gtlPar"] = "⦕", -- U+02995
- ["gtquest"] = "⩼", -- U+02A7C
- ["gtrapprox"] = "⪆", -- U+02A86
- ["gtrarr"] = "⥸", -- U+02978
- ["gtrdot"] = "⋗", -- U+022D7
- ["gtreqless"] = "⋛", -- U+022DB
- ["gtreqqless"] = "⪌", -- U+02A8C
- ["gtrless"] = "≷", -- U+02277
- ["gtrsim"] = "≳", -- U+02273
- ["gvertneqq"] = "≩︀", -- U+02269 0FE00
- ["gvnE"] = "≩︀", -- U+02269 0FE00
- ["hArr"] = "⇔", -- U+021D4
- ["hairsp"] = " ", -- U+0200A
- ["half"] = "½", -- U+000BD
- ["hamilt"] = "ℋ", -- U+0210B
- ["hardcy"] = "ъ", -- U+0044A
- ["harr"] = "↔", -- U+02194
- ["harrcir"] = "⥈", -- U+02948
- ["harrw"] = "↭", -- U+021AD
- ["hbar"] = "ℏ", -- U+0210F
- ["hcirc"] = "ĥ", -- U+00125
- ["hearts"] = "♥", -- U+02665
- ["heartsuit"] = "♥", -- U+02665
- ["hellip"] = "…", -- U+02026
- ["hercon"] = "⊹", -- U+022B9
- ["hfr"] = "𝔥", -- U+1D525
- ["hksearow"] = "⤥", -- U+02925
- ["hkswarow"] = "⤦", -- U+02926
- ["hoarr"] = "⇿", -- U+021FF
- ["homtht"] = "∻", -- U+0223B
- ["hookleftarrow"] = "↩", -- U+021A9
- ["hookrightarrow"] = "↪", -- U+021AA
- ["hopf"] = "𝕙", -- U+1D559
- ["horbar"] = "―", -- U+02015
- ["hscr"] = "𝒽", -- U+1D4BD
- ["hslash"] = "ℏ", -- U+0210F
- ["hstrok"] = "ħ", -- U+00127
- ["hybull"] = "⁃", -- U+02043
- ["hyphen"] = "‐", -- U+02010
- ["iacgr"] = "ί", -- U+003AF
- ["iacute"] = "í", -- U+000ED
- ["ic"] = "⁣", -- U+02063
- ["icirc"] = "î", -- U+000EE
- ["icy"] = "и", -- U+00438
- ["idiagr"] = "ΐ", -- U+00390
- ["idigr"] = "ϊ", -- U+003CA
- ["iecy"] = "е", -- U+00435
- ["iexcl"] = "¡", -- U+000A1
- ["iff"] = "⇔", -- U+021D4
- ["ifr"] = "𝔦", -- U+1D526
- ["igr"] = "ι", -- U+003B9
- ["igrave"] = "ì", -- U+000EC
- ["ii"] = "ⅈ", -- U+02148
- ["iiiint"] = "⨌", -- U+02A0C
- ["iiint"] = "∭", -- U+0222D
- ["iinfin"] = "⧜", -- U+029DC
- ["iiota"] = "℩", -- U+02129
- ["ijlig"] = "ij", -- U+00133
- ["imacr"] = "ī", -- U+0012B
- ["image"] = "ℑ", -- U+02111
- ["imagline"] = "ℐ", -- U+02110
- ["imagpart"] = "ℑ", -- U+02111
- ["imath"] = "ı", -- U+00131
- ["imof"] = "⊷", -- U+022B7
- ["imped"] = "Ƶ", -- U+001B5
- ["in"] = "∈", -- U+02208
- ["incare"] = "℅", -- U+02105
- ["infin"] = "∞", -- U+0221E
- ["infintie"] = "⧝", -- U+029DD
- ["inodot"] = "ı", -- U+00131
- ["int"] = "∫", -- U+0222B
- ["intcal"] = "⊺", -- U+022BA
- ["integers"] = "ℤ", -- U+02124
- ["intercal"] = "⊺", -- U+022BA
- ["intlarhk"] = "⨗", -- U+02A17
- ["intprod"] = "⨼", -- U+02A3C
- ["iocy"] = "ё", -- U+00451
- ["iogon"] = "į", -- U+0012F
- ["iopf"] = "𝕚", -- U+1D55A
- ["iota"] = "ι", -- U+003B9
- ["iprod"] = "⨼", -- U+02A3C
- ["iquest"] = "¿", -- U+000BF
- ["iscr"] = "𝒾", -- U+1D4BE
- ["isin"] = "∈", -- U+02208
- ["isinE"] = "⋹", -- U+022F9
- ["isindot"] = "⋵", -- U+022F5
- ["isins"] = "⋴", -- U+022F4
- ["isinsv"] = "⋳", -- U+022F3
- ["isinv"] = "∈", -- U+02208
- ["it"] = "⁢", -- U+02062
- ["itilde"] = "ĩ", -- U+00129
- ["iukcy"] = "і", -- U+00456
- ["iuml"] = "ï", -- U+000EF
- ["jcirc"] = "ĵ", -- U+00135
- ["jcy"] = "й", -- U+00439
- ["jfr"] = "𝔧", -- U+1D527
- ["jmath"] = "ȷ", -- U+00237
- ["jopf"] = "𝕛", -- U+1D55B
- ["jscr"] = "𝒿", -- U+1D4BF
- ["jsercy"] = "ј", -- U+00458
- ["jukcy"] = "є", -- U+00454
- ["kappa"] = "κ", -- U+003BA
- ["kappav"] = "ϰ", -- U+003F0
- ["kcedil"] = "ķ", -- U+00137
- ["kcy"] = "к", -- U+0043A
- ["kfr"] = "𝔨", -- U+1D528
- ["kgr"] = "κ", -- U+003BA
- ["kgreen"] = "ĸ", -- U+00138
- ["khcy"] = "х", -- U+00445
- ["khgr"] = "χ", -- U+003C7
- ["kjcy"] = "ќ", -- U+0045C
- ["kopf"] = "𝕜", -- U+1D55C
- ["kscr"] = "𝓀", -- U+1D4C0
- ["lAarr"] = "⇚", -- U+021DA
- ["lArr"] = "⇐", -- U+021D0
- ["lAtail"] = "⤛", -- U+0291B
- ["lBarr"] = "⤎", -- U+0290E
- ["lE"] = "≦", -- U+02266
- ["lEg"] = "⪋", -- U+02A8B
- ["lHar"] = "⥢", -- U+02962
- ["lacute"] = "ĺ", -- U+0013A
- ["laemptyv"] = "⦴", -- U+029B4
- ["lagran"] = "ℒ", -- U+02112
- ["lambda"] = "λ", -- U+003BB
- ["lang"] = "⟨", -- U+027E8
- ["langd"] = "⦑", -- U+02991
- ["langle"] = "⟨", -- U+027E8
- ["lap"] = "⪅", -- U+02A85
- ["laquo"] = "«", -- U+000AB
- ["larr"] = "←", -- U+02190
- ["larrb"] = "⇤", -- U+021E4
- ["larrbfs"] = "⤟", -- U+0291F
- ["larrfs"] = "⤝", -- U+0291D
- ["larrhk"] = "↩", -- U+021A9
- ["larrlp"] = "↫", -- U+021AB
- ["larrpl"] = "⤹", -- U+02939
- ["larrsim"] = "⥳", -- U+02973
- ["larrtl"] = "↢", -- U+021A2
- ["lat"] = "⪫", -- U+02AAB
- ["latail"] = "⤙", -- U+02919
- ["late"] = "⪭", -- U+02AAD
- ["lates"] = "⪭︀", -- U+02AAD 0FE00
- ["lbarr"] = "⤌", -- U+0290C
- ["lbbrk"] = "❲", -- U+02772
- ["lbrace"] = "{", -- U+0007B
- ["lbrack"] = "[", -- U+0005B
- ["lbrke"] = "⦋", -- U+0298B
- ["lbrksld"] = "⦏", -- U+0298F
- ["lbrkslu"] = "⦍", -- U+0298D
- ["lcaron"] = "ľ", -- U+0013E
- ["lcedil"] = "ļ", -- U+0013C
- ["lceil"] = "⌈", -- U+02308
- ["lcub"] = "{", -- U+0007B
- ["lcy"] = "л", -- U+0043B
- ["ldca"] = "⤶", -- U+02936
- ["ldquo"] = "“", -- U+0201C
- ["ldquor"] = "„", -- U+0201E
- ["ldrdhar"] = "⥧", -- U+02967
- ["ldrushar"] = "⥋", -- U+0294B
- ["ldsh"] = "↲", -- U+021B2
- ["le"] = "≤", -- U+02264
- ["leftarrow"] = "←", -- U+02190
- ["leftarrowtail"] = "↢", -- U+021A2
- ["leftharpoondown"] = "↽", -- U+021BD
- ["leftharpoonup"] = "↼", -- U+021BC
- ["leftleftarrows"] = "⇇", -- U+021C7
- ["leftrightarrow"] = "↔", -- U+02194
- ["leftrightarrows"] = "⇆", -- U+021C6
- ["leftrightharpoons"] = "⇋", -- U+021CB
- ["leftrightsquigarrow"] = "↭", -- U+021AD
- ["leftthreetimes"] = "⋋", -- U+022CB
- ["leg"] = "⋚", -- U+022DA
- ["leq"] = "≤", -- U+02264
- ["leqq"] = "≦", -- U+02266
- ["leqslant"] = "⩽", -- U+02A7D
- ["les"] = "⩽", -- U+02A7D
- ["lescc"] = "⪨", -- U+02AA8
- ["lesdot"] = "⩿", -- U+02A7F
- ["lesdoto"] = "⪁", -- U+02A81
- ["lesdotor"] = "⪃", -- U+02A83
- ["lesg"] = "⋚︀", -- U+022DA 0FE00
- ["lesges"] = "⪓", -- U+02A93
- ["lessapprox"] = "⪅", -- U+02A85
- ["lessdot"] = "⋖", -- U+022D6
- ["lesseqgtr"] = "⋚", -- U+022DA
- ["lesseqqgtr"] = "⪋", -- U+02A8B
- ["lessgtr"] = "≶", -- U+02276
- ["lesssim"] = "≲", -- U+02272
- ["lfisht"] = "⥼", -- U+0297C
- ["lfloor"] = "⌊", -- U+0230A
- ["lfr"] = "𝔩", -- U+1D529
- ["lg"] = "≶", -- U+02276
- ["lgE"] = "⪑", -- U+02A91
- ["lgr"] = "λ", -- U+003BB
- ["lhard"] = "↽", -- U+021BD
- ["lharu"] = "↼", -- U+021BC
- ["lharul"] = "⥪", -- U+0296A
- ["lhblk"] = "▄", -- U+02584
- ["ljcy"] = "љ", -- U+00459
- ["ll"] = "≪", -- U+0226A
- ["llarr"] = "⇇", -- U+021C7
- ["llcorner"] = "⌞", -- U+0231E
- ["llhard"] = "⥫", -- U+0296B
- ["lltri"] = "◺", -- U+025FA
- ["lmidot"] = "ŀ", -- U+00140
- ["lmoust"] = "⎰", -- U+023B0
- ["lmoustache"] = "⎰", -- U+023B0
- ["lnE"] = "≨", -- U+02268
- ["lnap"] = "⪉", -- U+02A89
- ["lnapprox"] = "⪉", -- U+02A89
- ["lne"] = "⪇", -- U+02A87
- ["lneq"] = "⪇", -- U+02A87
- ["lneqq"] = "≨", -- U+02268
- ["lnsim"] = "⋦", -- U+022E6
- ["loang"] = "⟬", -- U+027EC
- ["loarr"] = "⇽", -- U+021FD
- ["lobrk"] = "⟦", -- U+027E6
- ["longleftarrow"] = "⟵", -- U+027F5
- ["longleftrightarrow"] = "⟷", -- U+027F7
- ["longmapsto"] = "⟼", -- U+027FC
- ["longrightarrow"] = "⟶", -- U+027F6
- ["looparrowleft"] = "↫", -- U+021AB
- ["looparrowright"] = "↬", -- U+021AC
- ["lopar"] = "⦅", -- U+02985
- ["lopf"] = "𝕝", -- U+1D55D
- ["loplus"] = "⨭", -- U+02A2D
- ["lotimes"] = "⨴", -- U+02A34
- ["lowast"] = "∗", -- U+02217
- ["lowbar"] = "_", -- U+0005F
- ["loz"] = "◊", -- U+025CA
- ["lozenge"] = "◊", -- U+025CA
- ["lozf"] = "⧫", -- U+029EB
- ["lpar"] = "(", -- U+00028
- ["lparlt"] = "⦓", -- U+02993
- ["lrarr"] = "⇆", -- U+021C6
- ["lrcorner"] = "⌟", -- U+0231F
- ["lrhar"] = "⇋", -- U+021CB
- ["lrhard"] = "⥭", -- U+0296D
- ["lrm"] = "‎", -- U+0200E
- ["lrtri"] = "⊿", -- U+022BF
- ["lsaquo"] = "‹", -- U+02039
- ["lscr"] = "𝓁", -- U+1D4C1
- ["lsh"] = "↰", -- U+021B0
- ["lsim"] = "≲", -- U+02272
- ["lsime"] = "⪍", -- U+02A8D
- ["lsimg"] = "⪏", -- U+02A8F
- ["lsqb"] = "[", -- U+0005B
- ["lsquo"] = "‘", -- U+02018
- ["lsquor"] = "‚", -- U+0201A
- ["lstrok"] = "ł", -- U+00142
- ["lt"] = "<", -- U+00026
- ["ltcc"] = "⪦", -- U+02AA6
- ["ltcir"] = "⩹", -- U+02A79
- ["ltdot"] = "⋖", -- U+022D6
- ["lthree"] = "⋋", -- U+022CB
- ["ltimes"] = "⋉", -- U+022C9
- ["ltlarr"] = "⥶", -- U+02976
- ["ltquest"] = "⩻", -- U+02A7B
- ["ltrPar"] = "⦖", -- U+02996
- ["ltri"] = "◃", -- U+025C3
- ["ltrie"] = "⊴", -- U+022B4
- ["ltrif"] = "◂", -- U+025C2
- ["lurdshar"] = "⥊", -- U+0294A
- ["luruhar"] = "⥦", -- U+02966
- ["lvertneqq"] = "≨︀", -- U+02268 0FE00
- ["lvnE"] = "≨︀", -- U+02268 0FE00
- ["mDDot"] = "∺", -- U+0223A
- ["macr"] = "¯", -- U+000AF
- ["male"] = "♂", -- U+02642
- ["malt"] = "✠", -- U+02720
- ["maltese"] = "✠", -- U+02720
- ["map"] = "↦", -- U+021A6
- ["mapsto"] = "↦", -- U+021A6
- ["mapstodown"] = "↧", -- U+021A7
- ["mapstoleft"] = "↤", -- U+021A4
- ["mapstoup"] = "↥", -- U+021A5
- ["marker"] = "▮", -- U+025AE
- ["mcomma"] = "⨩", -- U+02A29
- ["mcy"] = "м", -- U+0043C
- ["mdash"] = "—", -- U+02014
- ["measuredangle"] = "∡", -- U+02221
- ["mfr"] = "𝔪", -- U+1D52A
- ["mgr"] = "μ", -- U+003BC
- ["mho"] = "℧", -- U+02127
- ["micro"] = "µ", -- U+000B5
- ["mid"] = "∣", -- U+02223
- ["midast"] = "*", -- U+0002A
- ["midcir"] = "⫰", -- U+02AF0
- ["middot"] = "·", -- U+000B7
- ["minus"] = "−", -- U+02212
- ["minusb"] = "⊟", -- U+0229F
- ["minusd"] = "∸", -- U+02238
- ["minusdu"] = "⨪", -- U+02A2A
- ["mlcp"] = "⫛", -- U+02ADB
- ["mldr"] = "…", -- U+02026
- ["mnplus"] = "∓", -- U+02213
- ["models"] = "⊧", -- U+022A7
- ["mopf"] = "𝕞", -- U+1D55E
- ["mp"] = "∓", -- U+02213
- ["mscr"] = "𝓂", -- U+1D4C2
- ["mstpos"] = "∾", -- U+0223E
- ["mu"] = "μ", -- U+003BC
- ["multimap"] = "⊸", -- U+022B8
- ["mumap"] = "⊸", -- U+022B8
- ["nGg"] = "⋙̸", -- U+022D9 00338
- ["nGt"] = "≫⃒", -- U+0226B 020D2
- ["nGtv"] = "≫̸", -- U+0226B 00338
- ["nLeftarrow"] = "⇍", -- U+021CD
- ["nLeftrightarrow"] = "⇎", -- U+021CE
- ["nLl"] = "⋘̸", -- U+022D8 00338
- ["nLt"] = "≪⃒", -- U+0226A 020D2
- ["nLtv"] = "≪̸", -- U+0226A 00338
- ["nRightarrow"] = "⇏", -- U+021CF
- ["nVDash"] = "⊯", -- U+022AF
- ["nVdash"] = "⊮", -- U+022AE
- ["nabla"] = "∇", -- U+02207
- ["nacute"] = "ń", -- U+00144
- ["nang"] = "∠⃒", -- U+02220 020D2
- ["nap"] = "≉", -- U+02249
- ["napE"] = "⩰̸", -- U+02A70 00338
- ["napid"] = "≋̸", -- U+0224B 00338
- ["napos"] = "ʼn", -- U+00149
- ["napprox"] = "≉", -- U+02249
- ["natur"] = "♮", -- U+0266E
- ["natural"] = "♮", -- U+0266E
- ["naturals"] = "ℕ", -- U+02115
- ["nbsp"] = " ", -- U+000A0
- ["nbump"] = "≎̸", -- U+0224E 00338
- ["nbumpe"] = "≏̸", -- U+0224F 00338
- ["ncap"] = "⩃", -- U+02A43
- ["ncaron"] = "ň", -- U+00148
- ["ncedil"] = "ņ", -- U+00146
- ["ncong"] = "≇", -- U+02247
- ["ncongdot"] = "⩭̸", -- U+02A6D 00338
- ["ncup"] = "⩂", -- U+02A42
- ["ncy"] = "н", -- U+0043D
- ["ndash"] = "–", -- U+02013
- ["ne"] = "≠", -- U+02260
- ["neArr"] = "⇗", -- U+021D7
- ["nearhk"] = "⤤", -- U+02924
- ["nearr"] = "↗", -- U+02197
- ["nearrow"] = "↗", -- U+02197
- ["nedot"] = "≐̸", -- U+02250 00338
- ["nequiv"] = "≢", -- U+02262
- ["nesear"] = "⤨", -- U+02928
- ["nesim"] = "≂̸", -- U+02242 00338
- ["nexist"] = "∄", -- U+02204
- ["nexists"] = "∄", -- U+02204
- ["nfr"] = "𝔫", -- U+1D52B
- ["ngE"] = "≧̸", -- U+02267 00338
- ["nge"] = "≱", -- U+02271
- ["ngeq"] = "≱", -- U+02271
- ["ngeqq"] = "≧̸", -- U+02267 00338
- ["ngeqslant"] = "⩾̸", -- U+02A7E 00338
- ["nges"] = "⩾̸", -- U+02A7E 00338
- ["ngr"] = "ν", -- U+003BD
- ["ngsim"] = "≵", -- U+02275
- ["ngt"] = "≯", -- U+0226F
- ["ngtr"] = "≯", -- U+0226F
- ["nhArr"] = "⇎", -- U+021CE
- ["nharr"] = "↮", -- U+021AE
- ["nhpar"] = "⫲", -- U+02AF2
- ["ni"] = "∋", -- U+0220B
- ["nis"] = "⋼", -- U+022FC
- ["nisd"] = "⋺", -- U+022FA
- ["niv"] = "∋", -- U+0220B
- ["njcy"] = "њ", -- U+0045A
- ["nlArr"] = "⇍", -- U+021CD
- ["nlE"] = "≦̸", -- U+02266 00338
- ["nlarr"] = "↚", -- U+0219A
- ["nldr"] = "‥", -- U+02025
- ["nle"] = "≰", -- U+02270
- ["nleftarrow"] = "↚", -- U+0219A
- ["nleftrightarrow"] = "↮", -- U+021AE
- ["nleq"] = "≰", -- U+02270
- ["nleqq"] = "≦̸", -- U+02266 00338
- ["nleqslant"] = "⩽̸", -- U+02A7D 00338
- ["nles"] = "⩽̸", -- U+02A7D 00338
- ["nless"] = "≮", -- U+0226E
- ["nlsim"] = "≴", -- U+02274
- ["nlt"] = "≮", -- U+0226E
- ["nltri"] = "⋪", -- U+022EA
- ["nltrie"] = "⋬", -- U+022EC
- ["nmid"] = "∤", -- U+02224
- ["nopf"] = "𝕟", -- U+1D55F
- ["not"] = "¬", -- U+000AC
- ["notin"] = "∉", -- U+02209
- ["notinE"] = "⋹̸", -- U+022F9 00338
- ["notindot"] = "⋵̸", -- U+022F5 00338
- ["notinva"] = "∉", -- U+02209
- ["notinvb"] = "⋷", -- U+022F7
- ["notinvc"] = "⋶", -- U+022F6
- ["notni"] = "∌", -- U+0220C
- ["notniva"] = "∌", -- U+0220C
- ["notnivb"] = "⋾", -- U+022FE
- ["notnivc"] = "⋽", -- U+022FD
- ["npar"] = "∦", -- U+02226
- ["nparallel"] = "∦", -- U+02226
- ["nparsl"] = "⫽⃥", -- U+02AFD 020E5
- ["npart"] = "∂̸", -- U+02202 00338
- ["npolint"] = "⨔", -- U+02A14
- ["npr"] = "⊀", -- U+02280
- ["nprcue"] = "⋠", -- U+022E0
- ["npre"] = "⪯̸", -- U+02AAF 00338
- ["nprec"] = "⊀", -- U+02280
- ["npreceq"] = "⪯̸", -- U+02AAF 00338
- ["nrArr"] = "⇏", -- U+021CF
- ["nrarr"] = "↛", -- U+0219B
- ["nrarrc"] = "⤳̸", -- U+02933 00338
- ["nrarrw"] = "↝̸", -- U+0219D 00338
- ["nrightarrow"] = "↛", -- U+0219B
- ["nrtri"] = "⋫", -- U+022EB
- ["nrtrie"] = "⋭", -- U+022ED
- ["nsc"] = "⊁", -- U+02281
- ["nsccue"] = "⋡", -- U+022E1
- ["nsce"] = "⪰̸", -- U+02AB0 00338
- ["nscr"] = "𝓃", -- U+1D4C3
- ["nshortmid"] = "∤", -- U+02224
- ["nshortparallel"] = "∦", -- U+02226
- ["nsim"] = "≁", -- U+02241
- ["nsime"] = "≄", -- U+02244
- ["nsimeq"] = "≄", -- U+02244
- ["nsmid"] = "∤", -- U+02224
- ["nspar"] = "∦", -- U+02226
- ["nsqsube"] = "⋢", -- U+022E2
- ["nsqsupe"] = "⋣", -- U+022E3
- ["nsub"] = "⊄", -- U+02284
- ["nsubE"] = "⫅̸", -- U+02AC5 00338
- ["nsube"] = "⊈", -- U+02288
- ["nsubset"] = "⊂⃒", -- U+02282 020D2
- ["nsubseteq"] = "⊈", -- U+02288
- ["nsubseteqq"] = "⫅̸", -- U+02AC5 00338
- ["nsucc"] = "⊁", -- U+02281
- ["nsucceq"] = "⪰̸", -- U+02AB0 00338
- ["nsup"] = "⊅", -- U+02285
- ["nsupE"] = "⫆̸", -- U+02AC6 00338
- ["nsupe"] = "⊉", -- U+02289
- ["nsupset"] = "⊃⃒", -- U+02283 020D2
- ["nsupseteq"] = "⊉", -- U+02289
- ["nsupseteqq"] = "⫆̸", -- U+02AC6 00338
- ["ntgl"] = "≹", -- U+02279
- ["ntilde"] = "ñ", -- U+000F1
- ["ntlg"] = "≸", -- U+02278
- ["ntriangleleft"] = "⋪", -- U+022EA
- ["ntrianglelefteq"] = "⋬", -- U+022EC
- ["ntriangleright"] = "⋫", -- U+022EB
- ["ntrianglerighteq"] = "⋭", -- U+022ED
- ["nu"] = "ν", -- U+003BD
- ["num"] = "#", -- U+00023
- ["numero"] = "№", -- U+02116
- ["numsp"] = " ", -- U+02007
- ["nvDash"] = "⊭", -- U+022AD
- ["nvHarr"] = "⤄", -- U+02904
- ["nvap"] = "≍⃒", -- U+0224D 020D2
- ["nvdash"] = "⊬", -- U+022AC
- ["nvge"] = "≥⃒", -- U+02265 020D2
- ["nvgt"] = ">⃒", -- U+0003E 020D2
- ["nvinfin"] = "⧞", -- U+029DE
- ["nvlArr"] = "⤂", -- U+02902
- ["nvle"] = "≤⃒", -- U+02264 020D2
- ["nvlt"] = "&⃒", -- U+00026 020D2
- ["nvltrie"] = "⊴⃒", -- U+022B4 020D2
- ["nvrArr"] = "⤃", -- U+02903
- ["nvrtrie"] = "⊵⃒", -- U+022B5 020D2
- ["nvsim"] = "∼⃒", -- U+0223C 020D2
- ["nwArr"] = "⇖", -- U+021D6
- ["nwarhk"] = "⤣", -- U+02923
- ["nwarr"] = "↖", -- U+02196
- ["nwarrow"] = "↖", -- U+02196
- ["nwnear"] = "⤧", -- U+02927
- ["oS"] = "Ⓢ", -- U+024C8
- ["oacgr"] = "ό", -- U+003CC
- ["oacute"] = "ó", -- U+000F3
- ["oast"] = "⊛", -- U+0229B
- ["ocir"] = "⊚", -- U+0229A
- ["ocirc"] = "ô", -- U+000F4
- ["ocy"] = "о", -- U+0043E
- ["odash"] = "⊝", -- U+0229D
- ["odblac"] = "ő", -- U+00151
- ["odiv"] = "⨸", -- U+02A38
- ["odot"] = "⊙", -- U+02299
- ["odsold"] = "⦼", -- U+029BC
- ["oelig"] = "œ", -- U+00153
- ["ofcir"] = "⦿", -- U+029BF
- ["ofr"] = "𝔬", -- U+1D52C
- ["ogon"] = "˛", -- U+002DB
- ["ogr"] = "ο", -- U+003BF
- ["ograve"] = "ò", -- U+000F2
- ["ogt"] = "⧁", -- U+029C1
- ["ohacgr"] = "ώ", -- U+003CE
- ["ohbar"] = "⦵", -- U+029B5
- ["ohgr"] = "ω", -- U+003C9
- ["ohm"] = "Ω", -- U+003A9
- ["oint"] = "∮", -- U+0222E
- ["olarr"] = "↺", -- U+021BA
- ["olcir"] = "⦾", -- U+029BE
- ["olcross"] = "⦻", -- U+029BB
- ["oline"] = "‾", -- U+0203E
- ["olt"] = "⧀", -- U+029C0
- ["omacr"] = "ō", -- U+0014D
- ["omega"] = "ω", -- U+003C9
- ["omicron"] = "ο", -- U+003BF
- ["omid"] = "⦶", -- U+029B6
- ["ominus"] = "⊖", -- U+02296
- ["oopf"] = "𝕠", -- U+1D560
- ["opar"] = "⦷", -- U+029B7
- ["operp"] = "⦹", -- U+029B9
- ["oplus"] = "⊕", -- U+02295
- ["or"] = "∨", -- U+02228
- ["orarr"] = "↻", -- U+021BB
- ["ord"] = "⩝", -- U+02A5D
- ["order"] = "ℴ", -- U+02134
- ["orderof"] = "ℴ", -- U+02134
- ["ordf"] = "ª", -- U+000AA
- ["ordm"] = "º", -- U+000BA
- ["origof"] = "⊶", -- U+022B6
- ["oror"] = "⩖", -- U+02A56
- ["orslope"] = "⩗", -- U+02A57
- ["orv"] = "⩛", -- U+02A5B
- ["oscr"] = "ℴ", -- U+02134
- ["oslash"] = "ø", -- U+000F8
- ["osol"] = "⊘", -- U+02298
- ["otilde"] = "õ", -- U+000F5
- ["otimes"] = "⊗", -- U+02297
- ["otimesas"] = "⨶", -- U+02A36
- ["ouml"] = "ö", -- U+000F6
- ["ovbar"] = "⌽", -- U+0233D
- ["par"] = "∥", -- U+02225
- ["para"] = "¶", -- U+000B6
- ["parallel"] = "∥", -- U+02225
- ["parsim"] = "⫳", -- U+02AF3
- ["parsl"] = "⫽", -- U+02AFD
- ["part"] = "∂", -- U+02202
- ["pcy"] = "п", -- U+0043F
- ["percnt"] = "%", -- U+00025
- ["period"] = ".", -- U+0002E
- ["permil"] = "‰", -- U+02030
- ["perp"] = "⊥", -- U+022A5
- ["pertenk"] = "‱", -- U+02031
- ["pfr"] = "𝔭", -- U+1D52D
- ["pgr"] = "π", -- U+003C0
- ["phgr"] = "φ", -- U+003C6
- ["phi"] = "φ", -- U+003C6
- ["phiv"] = "ϕ", -- U+003D5
- ["phmmat"] = "ℳ", -- U+02133
- ["phone"] = "☎", -- U+0260E
- ["pi"] = "π", -- U+003C0
- ["pitchfork"] = "⋔", -- U+022D4
- ["piv"] = "ϖ", -- U+003D6
- ["planck"] = "ℏ", -- U+0210F
- ["planckh"] = "ℎ", -- U+0210E
- ["plankv"] = "ℏ", -- U+0210F
- ["plus"] = "+", -- U+0002B
- ["plusacir"] = "⨣", -- U+02A23
- ["plusb"] = "⊞", -- U+0229E
- ["pluscir"] = "⨢", -- U+02A22
- ["plusdo"] = "∔", -- U+02214
- ["plusdu"] = "⨥", -- U+02A25
- ["pluse"] = "⩲", -- U+02A72
- ["plusmn"] = "±", -- U+000B1
- ["plussim"] = "⨦", -- U+02A26
- ["plustwo"] = "⨧", -- U+02A27
- ["pm"] = "±", -- U+000B1
- ["pointint"] = "⨕", -- U+02A15
- ["popf"] = "𝕡", -- U+1D561
- ["pound"] = "£", -- U+000A3
- ["pr"] = "≺", -- U+0227A
- ["prE"] = "⪳", -- U+02AB3
- ["prap"] = "⪷", -- U+02AB7
- ["prcue"] = "≼", -- U+0227C
- ["pre"] = "⪯", -- U+02AAF
- ["prec"] = "≺", -- U+0227A
- ["precapprox"] = "⪷", -- U+02AB7
- ["preccurlyeq"] = "≼", -- U+0227C
- ["preceq"] = "⪯", -- U+02AAF
- ["precnapprox"] = "⪹", -- U+02AB9
- ["precneqq"] = "⪵", -- U+02AB5
- ["precnsim"] = "⋨", -- U+022E8
- ["precsim"] = "≾", -- U+0227E
- ["prime"] = "′", -- U+02032
- ["primes"] = "ℙ", -- U+02119
- ["prnE"] = "⪵", -- U+02AB5
- ["prnap"] = "⪹", -- U+02AB9
- ["prnsim"] = "⋨", -- U+022E8
- ["prod"] = "∏", -- U+0220F
- ["profalar"] = "⌮", -- U+0232E
- ["profline"] = "⌒", -- U+02312
- ["profsurf"] = "⌓", -- U+02313
- ["prop"] = "∝", -- U+0221D
- ["propto"] = "∝", -- U+0221D
- ["prsim"] = "≾", -- U+0227E
- ["prurel"] = "⊰", -- U+022B0
- ["pscr"] = "𝓅", -- U+1D4C5
- ["psgr"] = "ψ", -- U+003C8
- ["psi"] = "ψ", -- U+003C8
- ["puncsp"] = " ", -- U+02008
- ["qfr"] = "𝔮", -- U+1D52E
- ["qint"] = "⨌", -- U+02A0C
- ["qopf"] = "𝕢", -- U+1D562
- ["qprime"] = "⁗", -- U+02057
- ["qscr"] = "𝓆", -- U+1D4C6
- ["quaternions"] = "ℍ", -- U+0210D
- ["quatint"] = "⨖", -- U+02A16
- ["quest"] = "?", -- U+0003F
- ["questeq"] = "≟", -- U+0225F
- ["quot"] = "\"", -- U+00022
- ["rAarr"] = "⇛", -- U+021DB
- ["rArr"] = "⇒", -- U+021D2
- ["rAtail"] = "⤜", -- U+0291C
- ["rBarr"] = "⤏", -- U+0290F
- ["rHar"] = "⥤", -- U+02964
- ["race"] = "∽̱", -- U+0223D 00331
- ["racute"] = "ŕ", -- U+00155
- ["radic"] = "√", -- U+0221A
- ["raemptyv"] = "⦳", -- U+029B3
- ["rang"] = "⟩", -- U+027E9
- ["rangd"] = "⦒", -- U+02992
- ["range"] = "⦥", -- U+029A5
- ["rangle"] = "⟩", -- U+027E9
- ["raquo"] = "»", -- U+000BB
- ["rarr"] = "→", -- U+02192
- ["rarrap"] = "⥵", -- U+02975
- ["rarrb"] = "⇥", -- U+021E5
- ["rarrbfs"] = "⤠", -- U+02920
- ["rarrc"] = "⤳", -- U+02933
- ["rarrfs"] = "⤞", -- U+0291E
- ["rarrhk"] = "↪", -- U+021AA
- ["rarrlp"] = "↬", -- U+021AC
- ["rarrpl"] = "⥅", -- U+02945
- ["rarrsim"] = "⥴", -- U+02974
- ["rarrtl"] = "↣", -- U+021A3
- ["rarrw"] = "↝", -- U+0219D
- ["ratail"] = "⤚", -- U+0291A
- ["ratio"] = "∶", -- U+02236
- ["rationals"] = "ℚ", -- U+0211A
- ["rbarr"] = "⤍", -- U+0290D
- ["rbbrk"] = "❳", -- U+02773
- ["rbrace"] = "}", -- U+0007D
- ["rbrack"] = "]", -- U+0005D
- ["rbrke"] = "⦌", -- U+0298C
- ["rbrksld"] = "⦎", -- U+0298E
- ["rbrkslu"] = "⦐", -- U+02990
- ["rcaron"] = "ř", -- U+00159
- ["rcedil"] = "ŗ", -- U+00157
- ["rceil"] = "⌉", -- U+02309
- ["rcub"] = "}", -- U+0007D
- ["rcy"] = "р", -- U+00440
- ["rdca"] = "⤷", -- U+02937
- ["rdldhar"] = "⥩", -- U+02969
- ["rdquo"] = "”", -- U+0201D
- ["rdquor"] = "”", -- U+0201D
- ["rdsh"] = "↳", -- U+021B3
- ["real"] = "ℜ", -- U+0211C
- ["realine"] = "ℛ", -- U+0211B
- ["realpart"] = "ℜ", -- U+0211C
- ["reals"] = "ℝ", -- U+0211D
- ["rect"] = "▭", -- U+025AD
- ["reg"] = "®", -- U+000AE
- ["rfisht"] = "⥽", -- U+0297D
- ["rfloor"] = "⌋", -- U+0230B
- ["rfr"] = "𝔯", -- U+1D52F
- ["rgr"] = "ρ", -- U+003C1
- ["rhard"] = "⇁", -- U+021C1
- ["rharu"] = "⇀", -- U+021C0
- ["rharul"] = "⥬", -- U+0296C
- ["rho"] = "ρ", -- U+003C1
- ["rhov"] = "ϱ", -- U+003F1
- ["rightarrow"] = "→", -- U+02192
- ["rightarrowtail"] = "↣", -- U+021A3
- ["rightharpoondown"] = "⇁", -- U+021C1
- ["rightharpoonup"] = "⇀", -- U+021C0
- ["rightleftarrows"] = "⇄", -- U+021C4
- ["rightleftharpoons"] = "⇌", -- U+021CC
- ["rightrightarrows"] = "⇉", -- U+021C9
- ["rightsquigarrow"] = "↝", -- U+0219D
- ["rightthreetimes"] = "⋌", -- U+022CC
- ["ring"] = "˚", -- U+002DA
- ["risingdotseq"] = "≓", -- U+02253
- ["rlarr"] = "⇄", -- U+021C4
- ["rlhar"] = "⇌", -- U+021CC
- ["rlm"] = "‏", -- U+0200F
- ["rmoust"] = "⎱", -- U+023B1
- ["rmoustache"] = "⎱", -- U+023B1
- ["rnmid"] = "⫮", -- U+02AEE
- ["roang"] = "⟭", -- U+027ED
- ["roarr"] = "⇾", -- U+021FE
- ["robrk"] = "⟧", -- U+027E7
- ["ropar"] = "⦆", -- U+02986
- ["ropf"] = "𝕣", -- U+1D563
- ["roplus"] = "⨮", -- U+02A2E
- ["rotimes"] = "⨵", -- U+02A35
- ["rpar"] = ")", -- U+00029
- ["rpargt"] = "⦔", -- U+02994
- ["rppolint"] = "⨒", -- U+02A12
- ["rrarr"] = "⇉", -- U+021C9
- ["rsaquo"] = "›", -- U+0203A
- ["rscr"] = "𝓇", -- U+1D4C7
- ["rsh"] = "↱", -- U+021B1
- ["rsqb"] = "]", -- U+0005D
- ["rsquo"] = "’", -- U+02019
- ["rsquor"] = "’", -- U+02019
- ["rthree"] = "⋌", -- U+022CC
- ["rtimes"] = "⋊", -- U+022CA
- ["rtri"] = "▹", -- U+025B9
- ["rtrie"] = "⊵", -- U+022B5
- ["rtrif"] = "▸", -- U+025B8
- ["rtriltri"] = "⧎", -- U+029CE
- ["ruluhar"] = "⥨", -- U+02968
- ["rx"] = "℞", -- U+0211E
- ["sacute"] = "ś", -- U+0015B
- ["sbquo"] = "‚", -- U+0201A
- ["sc"] = "≻", -- U+0227B
- ["scE"] = "⪴", -- U+02AB4
- ["scap"] = "⪸", -- U+02AB8
- ["scaron"] = "š", -- U+00161
- ["sccue"] = "≽", -- U+0227D
- ["sce"] = "⪰", -- U+02AB0
- ["scedil"] = "ş", -- U+0015F
- ["scirc"] = "ŝ", -- U+0015D
- ["scnE"] = "⪶", -- U+02AB6
- ["scnap"] = "⪺", -- U+02ABA
- ["scnsim"] = "⋩", -- U+022E9
- ["scpolint"] = "⨓", -- U+02A13
- ["scsim"] = "≿", -- U+0227F
- ["scy"] = "с", -- U+00441
- ["sdot"] = "⋅", -- U+022C5
- ["sdotb"] = "⊡", -- U+022A1
- ["sdote"] = "⩦", -- U+02A66
- ["seArr"] = "⇘", -- U+021D8
- ["searhk"] = "⤥", -- U+02925
- ["searr"] = "↘", -- U+02198
- ["searrow"] = "↘", -- U+02198
- ["sect"] = "§", -- U+000A7
- ["semi"] = ";", -- U+0003B
- ["seswar"] = "⤩", -- U+02929
- ["setminus"] = "∖", -- U+02216
- ["setmn"] = "∖", -- U+02216
- ["sext"] = "✶", -- U+02736
- ["sfgr"] = "ς", -- U+003C2
- ["sfr"] = "𝔰", -- U+1D530
- ["sfrown"] = "⌢", -- U+02322
- ["sgr"] = "σ", -- U+003C3
- ["sharp"] = "♯", -- U+0266F
- ["shchcy"] = "щ", -- U+00449
- ["shcy"] = "ш", -- U+00448
- ["shortmid"] = "∣", -- U+02223
- ["shortparallel"] = "∥", -- U+02225
- ["shy"] = "­", -- U+000AD
- ["sigma"] = "σ", -- U+003C3
- ["sigmaf"] = "ς", -- U+003C2
- ["sigmav"] = "ς", -- U+003C2
- ["sim"] = "∼", -- U+0223C
- ["simdot"] = "⩪", -- U+02A6A
- ["sime"] = "≃", -- U+02243
- ["simeq"] = "≃", -- U+02243
- ["simg"] = "⪞", -- U+02A9E
- ["simgE"] = "⪠", -- U+02AA0
- ["siml"] = "⪝", -- U+02A9D
- ["simlE"] = "⪟", -- U+02A9F
- ["simne"] = "≆", -- U+02246
- ["simplus"] = "⨤", -- U+02A24
- ["simrarr"] = "⥲", -- U+02972
- ["slarr"] = "←", -- U+02190
- ["smallsetminus"] = "∖", -- U+02216
- ["smashp"] = "⨳", -- U+02A33
- ["smeparsl"] = "⧤", -- U+029E4
- ["smid"] = "∣", -- U+02223
- ["smile"] = "⌣", -- U+02323
- ["smt"] = "⪪", -- U+02AAA
- ["smte"] = "⪬", -- U+02AAC
- ["smtes"] = "⪬︀", -- U+02AAC 0FE00
- ["softcy"] = "ь", -- U+0044C
- ["sol"] = "/", -- U+0002F
- ["solb"] = "⧄", -- U+029C4
- ["solbar"] = "⌿", -- U+0233F
- ["sopf"] = "𝕤", -- U+1D564
- ["spades"] = "♠", -- U+02660
- ["spadesuit"] = "♠", -- U+02660
- ["spar"] = "∥", -- U+02225
- ["sqcap"] = "⊓", -- U+02293
- ["sqcaps"] = "⊓︀", -- U+02293 0FE00
- ["sqcup"] = "⊔", -- U+02294
- ["sqcups"] = "⊔︀", -- U+02294 0FE00
- ["sqsub"] = "⊏", -- U+0228F
- ["sqsube"] = "⊑", -- U+02291
- ["sqsubset"] = "⊏", -- U+0228F
- ["sqsubseteq"] = "⊑", -- U+02291
- ["sqsup"] = "⊐", -- U+02290
- ["sqsupe"] = "⊒", -- U+02292
- ["sqsupset"] = "⊐", -- U+02290
- ["sqsupseteq"] = "⊒", -- U+02292
- ["squ"] = "□", -- U+025A1
- ["square"] = "□", -- U+025A1
- ["squarf"] = "▪", -- U+025AA
- ["squf"] = "▪", -- U+025AA
- ["srarr"] = "→", -- U+02192
- ["sscr"] = "𝓈", -- U+1D4C8
- ["ssetmn"] = "∖", -- U+02216
- ["ssmile"] = "⌣", -- U+02323
- ["sstarf"] = "⋆", -- U+022C6
- ["star"] = "☆", -- U+02606
- ["starf"] = "★", -- U+02605
- ["straightepsilon"] = "ϵ", -- U+003F5
- ["straightphi"] = "ϕ", -- U+003D5
- ["strns"] = "¯", -- U+000AF
- ["sub"] = "⊂", -- U+02282
- ["subE"] = "⫅", -- U+02AC5
- ["subdot"] = "⪽", -- U+02ABD
- ["sube"] = "⊆", -- U+02286
- ["subedot"] = "⫃", -- U+02AC3
- ["submult"] = "⫁", -- U+02AC1
- ["subnE"] = "⫋", -- U+02ACB
- ["subne"] = "⊊", -- U+0228A
- ["subplus"] = "⪿", -- U+02ABF
- ["subrarr"] = "⥹", -- U+02979
- ["subset"] = "⊂", -- U+02282
- ["subseteq"] = "⊆", -- U+02286
- ["subseteqq"] = "⫅", -- U+02AC5
- ["subsetneq"] = "⊊", -- U+0228A
- ["subsetneqq"] = "⫋", -- U+02ACB
- ["subsim"] = "⫇", -- U+02AC7
- ["subsub"] = "⫕", -- U+02AD5
- ["subsup"] = "⫓", -- U+02AD3
- ["succ"] = "≻", -- U+0227B
- ["succapprox"] = "⪸", -- U+02AB8
- ["succcurlyeq"] = "≽", -- U+0227D
- ["succeq"] = "⪰", -- U+02AB0
- ["succnapprox"] = "⪺", -- U+02ABA
- ["succneqq"] = "⪶", -- U+02AB6
- ["succnsim"] = "⋩", -- U+022E9
- ["succsim"] = "≿", -- U+0227F
- ["sum"] = "∑", -- U+02211
- ["sung"] = "♪", -- U+0266A
- ["sup"] = "⊃", -- U+02283
- ["sup1"] = "¹", -- U+000B9
- ["sup2"] = "²", -- U+000B2
- ["sup3"] = "³", -- U+000B3
- ["supE"] = "⫆", -- U+02AC6
- ["supdot"] = "⪾", -- U+02ABE
- ["supdsub"] = "⫘", -- U+02AD8
- ["supe"] = "⊇", -- U+02287
- ["supedot"] = "⫄", -- U+02AC4
- ["suphsol"] = "⟉", -- U+027C9
- ["suphsub"] = "⫗", -- U+02AD7
- ["suplarr"] = "⥻", -- U+0297B
- ["supmult"] = "⫂", -- U+02AC2
- ["supnE"] = "⫌", -- U+02ACC
- ["supne"] = "⊋", -- U+0228B
- ["supplus"] = "⫀", -- U+02AC0
- ["supset"] = "⊃", -- U+02283
- ["supseteq"] = "⊇", -- U+02287
- ["supseteqq"] = "⫆", -- U+02AC6
- ["supsetneq"] = "⊋", -- U+0228B
- ["supsetneqq"] = "⫌", -- U+02ACC
- ["supsim"] = "⫈", -- U+02AC8
- ["supsub"] = "⫔", -- U+02AD4
- ["supsup"] = "⫖", -- U+02AD6
- ["swArr"] = "⇙", -- U+021D9
- ["swarhk"] = "⤦", -- U+02926
- ["swarr"] = "↙", -- U+02199
- ["swarrow"] = "↙", -- U+02199
- ["swnwar"] = "⤪", -- U+0292A
- ["szlig"] = "ß", -- U+000DF
- ["target"] = "⌖", -- U+02316
- ["tau"] = "τ", -- U+003C4
- ["tbrk"] = "⎴", -- U+023B4
- ["tcaron"] = "ť", -- U+00165
- ["tcedil"] = "ţ", -- U+00163
- ["tcy"] = "т", -- U+00442
- ["tdot"] = "⃛", -- U+020DB
- ["telrec"] = "⌕", -- U+02315
- ["tfr"] = "𝔱", -- U+1D531
- ["tgr"] = "τ", -- U+003C4
- ["there4"] = "∴", -- U+02234
- ["therefore"] = "∴", -- U+02234
- ["theta"] = "θ", -- U+003B8
- ["thetasym"] = "ϑ", -- U+003D1
- ["thetav"] = "ϑ", -- U+003D1
- ["thgr"] = "θ", -- U+003B8
- ["thickapprox"] = "≈", -- U+02248
- ["thicksim"] = "∼", -- U+0223C
- ["thinsp"] = " ", -- U+02009
- ["thkap"] = "≈", -- U+02248
- ["thksim"] = "∼", -- U+0223C
- ["thorn"] = "þ", -- U+000FE
- ["tilde"] = "˜", -- U+002DC
- ["times"] = "×", -- U+000D7
- ["timesb"] = "⊠", -- U+022A0
- ["timesbar"] = "⨱", -- U+02A31
- ["timesd"] = "⨰", -- U+02A30
- ["tint"] = "∭", -- U+0222D
- ["toea"] = "⤨", -- U+02928
- ["top"] = "⊤", -- U+022A4
- ["topbot"] = "⌶", -- U+02336
- ["topcir"] = "⫱", -- U+02AF1
- ["topf"] = "𝕥", -- U+1D565
- ["topfork"] = "⫚", -- U+02ADA
- ["tosa"] = "⤩", -- U+02929
- ["tprime"] = "‴", -- U+02034
- ["trade"] = "™", -- U+02122
- ["triangle"] = "▵", -- U+025B5
- ["triangledown"] = "▿", -- U+025BF
- ["triangleleft"] = "◃", -- U+025C3
- ["trianglelefteq"] = "⊴", -- U+022B4
- ["triangleq"] = "≜", -- U+0225C
- ["triangleright"] = "▹", -- U+025B9
- ["trianglerighteq"] = "⊵", -- U+022B5
- ["tridot"] = "◬", -- U+025EC
- ["trie"] = "≜", -- U+0225C
- ["triminus"] = "⨺", -- U+02A3A
- ["triplus"] = "⨹", -- U+02A39
- ["trisb"] = "⧍", -- U+029CD
- ["tritime"] = "⨻", -- U+02A3B
- ["trpezium"] = "⏢", -- U+023E2
- ["tscr"] = "𝓉", -- U+1D4C9
- ["tscy"] = "ц", -- U+00446
- ["tshcy"] = "ћ", -- U+0045B
- ["tstrok"] = "ŧ", -- U+00167
- ["twixt"] = "≬", -- U+0226C
- ["twoheadleftarrow"] = "↞", -- U+0219E
- ["twoheadrightarrow"] = "↠", -- U+021A0
- ["uArr"] = "⇑", -- U+021D1
- ["uHar"] = "⥣", -- U+02963
- ["uacgr"] = "ύ", -- U+003CD
- ["uacute"] = "ú", -- U+000FA
- ["uarr"] = "↑", -- U+02191
- ["ubrcy"] = "ў", -- U+0045E
- ["ubreve"] = "ŭ", -- U+0016D
- ["ucirc"] = "û", -- U+000FB
- ["ucy"] = "у", -- U+00443
- ["udarr"] = "⇅", -- U+021C5
- ["udblac"] = "ű", -- U+00171
- ["udhar"] = "⥮", -- U+0296E
- ["udiagr"] = "ΰ", -- U+003B0
- ["udigr"] = "ϋ", -- U+003CB
- ["ufisht"] = "⥾", -- U+0297E
- ["ufr"] = "𝔲", -- U+1D532
- ["ugr"] = "υ", -- U+003C5
- ["ugrave"] = "ù", -- U+000F9
- ["uharl"] = "↿", -- U+021BF
- ["uharr"] = "↾", -- U+021BE
- ["uhblk"] = "▀", -- U+02580
- ["ulcorn"] = "⌜", -- U+0231C
- ["ulcorner"] = "⌜", -- U+0231C
- ["ulcrop"] = "⌏", -- U+0230F
- ["ultri"] = "◸", -- U+025F8
- ["umacr"] = "ū", -- U+0016B
- ["uml"] = "¨", -- U+000A8
- ["uogon"] = "ų", -- U+00173
- ["uopf"] = "𝕦", -- U+1D566
- ["uparrow"] = "↑", -- U+02191
- ["updownarrow"] = "↕", -- U+02195
- ["upharpoonleft"] = "↿", -- U+021BF
- ["upharpoonright"] = "↾", -- U+021BE
- ["uplus"] = "⊎", -- U+0228E
- ["upsi"] = "υ", -- U+003C5
- ["upsih"] = "ϒ", -- U+003D2
- ["upsilon"] = "υ", -- U+003C5
- ["upuparrows"] = "⇈", -- U+021C8
- ["urcorn"] = "⌝", -- U+0231D
- ["urcorner"] = "⌝", -- U+0231D
- ["urcrop"] = "⌎", -- U+0230E
- ["uring"] = "ů", -- U+0016F
- ["urtri"] = "◹", -- U+025F9
- ["uscr"] = "𝓊", -- U+1D4CA
- ["utdot"] = "⋰", -- U+022F0
- ["utilde"] = "ũ", -- U+00169
- ["utri"] = "▵", -- U+025B5
- ["utrif"] = "▴", -- U+025B4
- ["uuarr"] = "⇈", -- U+021C8
- ["uuml"] = "ü", -- U+000FC
- ["uwangle"] = "⦧", -- U+029A7
- ["vArr"] = "⇕", -- U+021D5
- ["vBar"] = "⫨", -- U+02AE8
- ["vBarv"] = "⫩", -- U+02AE9
- ["vDash"] = "⊨", -- U+022A8
- ["vangrt"] = "⦜", -- U+0299C
- ["varepsilon"] = "ϵ", -- U+003F5
- ["varkappa"] = "ϰ", -- U+003F0
- ["varnothing"] = "∅", -- U+02205
- ["varphi"] = "ϕ", -- U+003D5
- ["varpi"] = "ϖ", -- U+003D6
- ["varpropto"] = "∝", -- U+0221D
- ["varr"] = "↕", -- U+02195
- ["varrho"] = "ϱ", -- U+003F1
- ["varsigma"] = "ς", -- U+003C2
- ["varsubsetneq"] = "⊊︀", -- U+0228A 0FE00
- ["varsubsetneqq"] = "⫋︀", -- U+02ACB 0FE00
- ["varsupsetneq"] = "⊋︀", -- U+0228B 0FE00
- ["varsupsetneqq"] = "⫌︀", -- U+02ACC 0FE00
- ["vartheta"] = "ϑ", -- U+003D1
- ["vartriangleleft"] = "⊲", -- U+022B2
- ["vartriangleright"] = "⊳", -- U+022B3
- ["vcy"] = "в", -- U+00432
- ["vdash"] = "⊢", -- U+022A2
- ["vee"] = "∨", -- U+02228
- ["veebar"] = "⊻", -- U+022BB
- ["veeeq"] = "≚", -- U+0225A
- ["vellip"] = "⋮", -- U+022EE
- ["verbar"] = "|", -- U+0007C
- ["vert"] = "|", -- U+0007C
- ["vfr"] = "𝔳", -- U+1D533
- ["vltri"] = "⊲", -- U+022B2
- ["vnsub"] = "⊂⃒", -- U+02282 020D2
- ["vnsup"] = "⊃⃒", -- U+02283 020D2
- ["vopf"] = "𝕧", -- U+1D567
- ["vprop"] = "∝", -- U+0221D
- ["vrtri"] = "⊳", -- U+022B3
- ["vscr"] = "𝓋", -- U+1D4CB
- ["vsubnE"] = "⫋︀", -- U+02ACB 0FE00
- ["vsubne"] = "⊊︀", -- U+0228A 0FE00
- ["vsupnE"] = "⫌︀", -- U+02ACC 0FE00
- ["vsupne"] = "⊋︀", -- U+0228B 0FE00
- ["vzigzag"] = "⦚", -- U+0299A
- ["wcirc"] = "ŵ", -- U+00175
- ["wedbar"] = "⩟", -- U+02A5F
- ["wedge"] = "∧", -- U+02227
- ["wedgeq"] = "≙", -- U+02259
- ["weierp"] = "℘", -- U+02118
- ["wfr"] = "𝔴", -- U+1D534
- ["wopf"] = "𝕨", -- U+1D568
- ["wp"] = "℘", -- U+02118
- ["wr"] = "≀", -- U+02240
- ["wreath"] = "≀", -- U+02240
- ["wscr"] = "𝓌", -- U+1D4CC
- ["xcap"] = "⋂", -- U+022C2
- ["xcirc"] = "◯", -- U+025EF
- ["xcup"] = "⋃", -- U+022C3
- ["xdtri"] = "▽", -- U+025BD
- ["xfr"] = "𝔵", -- U+1D535
- ["xgr"] = "ξ", -- U+003BE
- ["xhArr"] = "⟺", -- U+027FA
- ["xharr"] = "⟷", -- U+027F7
- ["xi"] = "ξ", -- U+003BE
- ["xlArr"] = "⟸", -- U+027F8
- ["xlarr"] = "⟵", -- U+027F5
- ["xmap"] = "⟼", -- U+027FC
- ["xnis"] = "⋻", -- U+022FB
- ["xodot"] = "⨀", -- U+02A00
- ["xopf"] = "𝕩", -- U+1D569
- ["xoplus"] = "⨁", -- U+02A01
- ["xotime"] = "⨂", -- U+02A02
- ["xrArr"] = "⟹", -- U+027F9
- ["xrarr"] = "⟶", -- U+027F6
- ["xscr"] = "𝓍", -- U+1D4CD
- ["xsqcup"] = "⨆", -- U+02A06
- ["xuplus"] = "⨄", -- U+02A04
- ["xutri"] = "△", -- U+025B3
- ["xvee"] = "⋁", -- U+022C1
- ["xwedge"] = "⋀", -- U+022C0
- ["yacute"] = "ý", -- U+000FD
- ["yacy"] = "я", -- U+0044F
- ["ycirc"] = "ŷ", -- U+00177
- ["ycy"] = "ы", -- U+0044B
- ["yen"] = "¥", -- U+000A5
- ["yfr"] = "𝔶", -- U+1D536
- ["yicy"] = "ї", -- U+00457
- ["yopf"] = "𝕪", -- U+1D56A
- ["yscr"] = "𝓎", -- U+1D4CE
- ["yucy"] = "ю", -- U+0044E
- ["yuml"] = "ÿ", -- U+000FF
- ["zacute"] = "ź", -- U+0017A
- ["zcaron"] = "ž", -- U+0017E
- ["zcy"] = "з", -- U+00437
- ["zdot"] = "ż", -- U+0017C
- ["zeetrf"] = "ℨ", -- U+02128
- ["zeta"] = "ζ", -- U+003B6
- ["zfr"] = "𝔷", -- U+1D537
- ["zgr"] = "ζ", -- U+003B6
- ["zhcy"] = "ж", -- U+00436
- ["zigrarr"] = "⇝", -- U+021DD
- ["zopf"] = "𝕫", -- U+1D56B
- ["zscr"] = "𝓏", -- U+1D4CF
- ["zwj"] = "‍", -- U+0200D
- ["zwnj"] = "‌", -- U+0200C
-}
-
-characters = characters or { }
-characters.entities = entities
-
-entities.plusminus = "±" -- 0x000B1
-entities.minusplus = "∓" -- 0x02213
-entities.cdots = utf.char(0x02026) -- U+02026
+if not modules then modules = { } end modules ['char-ent'] = {
+ version = 1.001,
+ comment = "companion to math-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "derived from the mathml 2.0 specification",
+ dataonly = true,
+}
+
+-- http://www.w3.org/2003/entities/2007/w3centities-f.ent
+-- http://www.w3.org/2003/entities/2007/htmlmathml-f.ent
+
+local entities = utilities.storage.allocate {
+ ["AElig"] = "Æ", -- U+000C6
+ ["AMP"] = "&", -- U+00026
+ ["Aacgr"] = "Ά", -- U+00386
+ ["Aacute"] = "Á", -- U+000C1
+ ["Abreve"] = "Ă", -- U+00102
+ ["Acirc"] = "Â", -- U+000C2
+ ["Acy"] = "А", -- U+00410
+ ["Afr"] = "𝔄", -- U+1D504
+ ["Agr"] = "Α", -- U+00391
+ ["Agrave"] = "À", -- U+000C0
+ ["Alpha"] = "Α", -- U+00391
+ ["Amacr"] = "Ā", -- U+00100
+ ["And"] = "⩓", -- U+02A53
+ ["Aogon"] = "Ą", -- U+00104
+ ["Aopf"] = "𝔸", -- U+1D538
+ ["ApplyFunction"] = "⁡", -- U+02061
+ ["Aring"] = "Å", -- U+000C5
+ ["Ascr"] = "𝒜", -- U+1D49C
+ ["Assign"] = "≔", -- U+02254
+ ["Atilde"] = "Ã", -- U+000C3
+ ["Auml"] = "Ä", -- U+000C4
+ ["Backslash"] = "∖", -- U+02216
+ ["Barv"] = "⫧", -- U+02AE7
+ ["Barwed"] = "⌆", -- U+02306
+ ["Bcy"] = "Б", -- U+00411
+ ["Because"] = "∵", -- U+02235
+ ["Bernoullis"] = "ℬ", -- U+0212C
+ ["Beta"] = "Β", -- U+00392
+ ["Bfr"] = "𝔅", -- U+1D505
+ ["Bgr"] = "Β", -- U+00392
+ ["Bopf"] = "𝔹", -- U+1D539
+ ["Breve"] = "˘", -- U+002D8
+ ["Bscr"] = "ℬ", -- U+0212C
+ ["Bumpeq"] = "≎", -- U+0224E
+ ["CHcy"] = "Ч", -- U+00427
+ ["COPY"] = "©", -- U+000A9
+ ["Cacute"] = "Ć", -- U+00106
+ ["Cap"] = "⋒", -- U+022D2
+ ["CapitalDifferentialD"] = "ⅅ", -- U+02145
+ ["Cayleys"] = "ℭ", -- U+0212D
+ ["Ccaron"] = "Č", -- U+0010C
+ ["Ccedil"] = "Ç", -- U+000C7
+ ["Ccirc"] = "Ĉ", -- U+00108
+ ["Cconint"] = "∰", -- U+02230
+ ["Cdot"] = "Ċ", -- U+0010A
+ ["Cedilla"] = "¸", -- U+000B8
+ ["CenterDot"] = "·", -- U+000B7
+ ["Cfr"] = "ℭ", -- U+0212D
+ ["Chi"] = "Χ", -- U+003A7
+ ["CircleDot"] = "⊙", -- U+02299
+ ["CircleMinus"] = "⊖", -- U+02296
+ ["CirclePlus"] = "⊕", -- U+02295
+ ["CircleTimes"] = "⊗", -- U+02297
+ ["ClockwiseContourIntegral"] = "∲", -- U+02232
+ ["CloseCurlyDoubleQuote"] = "”", -- U+0201D
+ ["CloseCurlyQuote"] = "’", -- U+02019
+ ["Colon"] = "∷", -- U+02237
+ ["Colone"] = "⩴", -- U+02A74
+ ["Congruent"] = "≡", -- U+02261
+ ["Conint"] = "∯", -- U+0222F
+ ["ContourIntegral"] = "∮", -- U+0222E
+ ["Copf"] = "ℂ", -- U+02102
+ ["Coproduct"] = "∐", -- U+02210
+ ["CounterClockwiseContourIntegral"] = "∳", -- U+02233
+ ["Cross"] = "⨯", -- U+02A2F
+ ["Cscr"] = "𝒞", -- U+1D49E
+ ["Cup"] = "⋓", -- U+022D3
+ ["CupCap"] = "≍", -- U+0224D
+ ["DD"] = "ⅅ", -- U+02145
+ ["DDotrahd"] = "⤑", -- U+02911
+ ["DJcy"] = "Ђ", -- U+00402
+ ["DScy"] = "Ѕ", -- U+00405
+ ["DZcy"] = "Џ", -- U+0040F
+ ["Dagger"] = "‡", -- U+02021
+ ["Darr"] = "↡", -- U+021A1
+ ["Dashv"] = "⫤", -- U+02AE4
+ ["Dcaron"] = "Ď", -- U+0010E
+ ["Dcy"] = "Д", -- U+00414
+ ["Del"] = "∇", -- U+02207
+ ["Delta"] = "Δ", -- U+00394
+ ["Dfr"] = "𝔇", -- U+1D507
+ ["Dgr"] = "Δ", -- U+00394
+ ["DiacriticalAcute"] = "´", -- U+000B4
+ ["DiacriticalDot"] = "˙", -- U+002D9
+ ["DiacriticalDoubleAcute"] = "˝", -- U+002DD
+ ["DiacriticalGrave"] = "`", -- U+00060
+ ["DiacriticalTilde"] = "˜", -- U+002DC
+ ["Diamond"] = "⋄", -- U+022C4
+ ["DifferentialD"] = "ⅆ", -- U+02146
+ ["Dopf"] = "𝔻", -- U+1D53B
+ ["Dot"] = "¨", -- U+000A8
+ ["DotDot"] = "⃜", -- U+020DC
+ ["DotEqual"] = "≐", -- U+02250
+ ["DoubleContourIntegral"] = "∯", -- U+0222F
+ ["DoubleDot"] = "¨", -- U+000A8
+ ["DoubleDownArrow"] = "⇓", -- U+021D3
+ ["DoubleLeftArrow"] = "⇐", -- U+021D0
+ ["DoubleLeftRightArrow"] = "⇔", -- U+021D4
+ ["DoubleLeftTee"] = "⫤", -- U+02AE4
+ ["DoubleLongLeftArrow"] = "⟸", -- U+027F8
+ ["DoubleLongLeftRightArrow"] = "⟺", -- U+027FA
+ ["DoubleLongRightArrow"] = "⟹", -- U+027F9
+ ["DoubleRightArrow"] = "⇒", -- U+021D2
+ ["DoubleRightTee"] = "⊨", -- U+022A8
+ ["DoubleUpArrow"] = "⇑", -- U+021D1
+ ["DoubleUpDownArrow"] = "⇕", -- U+021D5
+ ["DoubleVerticalBar"] = "∥", -- U+02225
+ ["DownArrow"] = "↓", -- U+02193
+ ["DownArrowBar"] = "⤓", -- U+02913
+ ["DownArrowUpArrow"] = "⇵", -- U+021F5
+ ["DownBreve"] = "̑", -- U+00311
+ ["DownLeftRightVector"] = "⥐", -- U+02950
+ ["DownLeftTeeVector"] = "⥞", -- U+0295E
+ ["DownLeftVector"] = "↽", -- U+021BD
+ ["DownLeftVectorBar"] = "⥖", -- U+02956
+ ["DownRightTeeVector"] = "⥟", -- U+0295F
+ ["DownRightVector"] = "⇁", -- U+021C1
+ ["DownRightVectorBar"] = "⥗", -- U+02957
+ ["DownTee"] = "⊤", -- U+022A4
+ ["DownTeeArrow"] = "↧", -- U+021A7
+ ["Downarrow"] = "⇓", -- U+021D3
+ ["Dscr"] = "𝒟", -- U+1D49F
+ ["Dstrok"] = "Đ", -- U+00110
+ ["EEacgr"] = "Ή", -- U+00389
+ ["EEgr"] = "Η", -- U+00397
+ ["ENG"] = "Ŋ", -- U+0014A
+ ["ETH"] = "Ð", -- U+000D0
+ ["Eacgr"] = "Έ", -- U+00388
+ ["Eacute"] = "É", -- U+000C9
+ ["Ecaron"] = "Ě", -- U+0011A
+ ["Ecirc"] = "Ê", -- U+000CA
+ ["Ecy"] = "Э", -- U+0042D
+ ["Edot"] = "Ė", -- U+00116
+ ["Efr"] = "𝔈", -- U+1D508
+ ["Egr"] = "Ε", -- U+00395
+ ["Egrave"] = "È", -- U+000C8
+ ["Element"] = "∈", -- U+02208
+ ["Emacr"] = "Ē", -- U+00112
+ ["EmptySmallSquare"] = "◻", -- U+025FB
+ ["EmptyVerySmallSquare"] = "▫", -- U+025AB
+ ["Eogon"] = "Ę", -- U+00118
+ ["Eopf"] = "𝔼", -- U+1D53C
+ ["Epsilon"] = "Ε", -- U+00395
+ ["Equal"] = "⩵", -- U+02A75
+ ["EqualTilde"] = "≂", -- U+02242
+ ["Equilibrium"] = "⇌", -- U+021CC
+ ["Escr"] = "ℰ", -- U+02130
+ ["Esim"] = "⩳", -- U+02A73
+ ["Eta"] = "Η", -- U+00397
+ ["Euml"] = "Ë", -- U+000CB
+ ["Exists"] = "∃", -- U+02203
+ ["ExponentialE"] = "ⅇ", -- U+02147
+ ["Fcy"] = "Ф", -- U+00424
+ ["Ffr"] = "𝔉", -- U+1D509
+ ["FilledSmallSquare"] = "◼", -- U+025FC
+ ["FilledVerySmallSquare"] = "▪", -- U+025AA
+ ["Fopf"] = "𝔽", -- U+1D53D
+ ["ForAll"] = "∀", -- U+02200
+ ["Fouriertrf"] = "ℱ", -- U+02131
+ ["Fscr"] = "ℱ", -- U+02131
+ ["GJcy"] = "Ѓ", -- U+00403
+ ["GT"] = ">", -- U+0003E
+ ["Gamma"] = "Γ", -- U+00393
+ ["Gammad"] = "Ϝ", -- U+003DC
+ ["Gbreve"] = "Ğ", -- U+0011E
+ ["Gcedil"] = "Ģ", -- U+00122
+ ["Gcirc"] = "Ĝ", -- U+0011C
+ ["Gcy"] = "Г", -- U+00413
+ ["Gdot"] = "Ġ", -- U+00120
+ ["Gfr"] = "𝔊", -- U+1D50A
+ ["Gg"] = "⋙", -- U+022D9
+ ["Ggr"] = "Γ", -- U+00393
+ ["Gopf"] = "𝔾", -- U+1D53E
+ ["GreaterEqual"] = "≥", -- U+02265
+ ["GreaterEqualLess"] = "⋛", -- U+022DB
+ ["GreaterFullEqual"] = "≧", -- U+02267
+ ["GreaterGreater"] = "⪢", -- U+02AA2
+ ["GreaterLess"] = "≷", -- U+02277
+ ["GreaterSlantEqual"] = "⩾", -- U+02A7E
+ ["GreaterTilde"] = "≳", -- U+02273
+ ["Gscr"] = "𝒢", -- U+1D4A2
+ ["Gt"] = "≫", -- U+0226B
+ ["HARDcy"] = "Ъ", -- U+0042A
+ ["Hacek"] = "ˇ", -- U+002C7
+ ["Hat"] = "^", -- U+0005E
+ ["Hcirc"] = "Ĥ", -- U+00124
+ ["Hfr"] = "ℌ", -- U+0210C
+ ["HilbertSpace"] = "ℋ", -- U+0210B
+ ["Hopf"] = "ℍ", -- U+0210D
+ ["HorizontalLine"] = "─", -- U+02500
+ ["Hscr"] = "ℋ", -- U+0210B
+ ["Hstrok"] = "Ħ", -- U+00126
+ ["HumpDownHump"] = "≎", -- U+0224E
+ ["HumpEqual"] = "≏", -- U+0224F
+ ["IEcy"] = "Е", -- U+00415
+ ["IJlig"] = "IJ", -- U+00132
+ ["IOcy"] = "Ё", -- U+00401
+ ["Iacgr"] = "Ί", -- U+0038A
+ ["Iacute"] = "Í", -- U+000CD
+ ["Icirc"] = "Î", -- U+000CE
+ ["Icy"] = "И", -- U+00418
+ ["Idigr"] = "Ϊ", -- U+003AA
+ ["Idot"] = "İ", -- U+00130
+ ["Ifr"] = "ℑ", -- U+02111
+ ["Igr"] = "Ι", -- U+00399
+ ["Igrave"] = "Ì", -- U+000CC
+ ["Im"] = "ℑ", -- U+02111
+ ["Imacr"] = "Ī", -- U+0012A
+ ["ImaginaryI"] = "ⅈ", -- U+02148
+ ["Implies"] = "⇒", -- U+021D2
+ ["Int"] = "∬", -- U+0222C
+ ["Integral"] = "∫", -- U+0222B
+ ["Intersection"] = "⋂", -- U+022C2
+ ["InvisibleComma"] = "⁣", -- U+02063
+ ["InvisibleTimes"] = "⁢", -- U+02062
+ ["Iogon"] = "Į", -- U+0012E
+ ["Iopf"] = "𝕀", -- U+1D540
+ ["Iota"] = "Ι", -- U+00399
+ ["Iscr"] = "ℐ", -- U+02110
+ ["Itilde"] = "Ĩ", -- U+00128
+ ["Iukcy"] = "І", -- U+00406
+ ["Iuml"] = "Ï", -- U+000CF
+ ["Jcirc"] = "Ĵ", -- U+00134
+ ["Jcy"] = "Й", -- U+00419
+ ["Jfr"] = "𝔍", -- U+1D50D
+ ["Jopf"] = "𝕁", -- U+1D541
+ ["Jscr"] = "𝒥", -- U+1D4A5
+ ["Jsercy"] = "Ј", -- U+00408
+ ["Jukcy"] = "Є", -- U+00404
+ ["KHcy"] = "Х", -- U+00425
+ ["KHgr"] = "Χ", -- U+003A7
+ ["KJcy"] = "Ќ", -- U+0040C
+ ["Kappa"] = "Κ", -- U+0039A
+ ["Kcedil"] = "Ķ", -- U+00136
+ ["Kcy"] = "К", -- U+0041A
+ ["Kfr"] = "𝔎", -- U+1D50E
+ ["Kgr"] = "Κ", -- U+0039A
+ ["Kopf"] = "𝕂", -- U+1D542
+ ["Kscr"] = "𝒦", -- U+1D4A6
+ ["LJcy"] = "Љ", -- U+00409
+ ["LT"] = "<", -- U+00026
+ ["Lacute"] = "Ĺ", -- U+00139
+ ["Lambda"] = "Λ", -- U+0039B
+ ["Lang"] = "⟪", -- U+027EA
+ ["Laplacetrf"] = "ℒ", -- U+02112
+ ["Larr"] = "↞", -- U+0219E
+ ["Lcaron"] = "Ľ", -- U+0013D
+ ["Lcedil"] = "Ļ", -- U+0013B
+ ["Lcy"] = "Л", -- U+0041B
+ ["LeftAngleBracket"] = "⟨", -- U+027E8
+ ["LeftArrow"] = "←", -- U+02190
+ ["LeftArrowBar"] = "⇤", -- U+021E4
+ ["LeftArrowRightArrow"] = "⇆", -- U+021C6
+ ["LeftCeiling"] = "⌈", -- U+02308
+ ["LeftDoubleBracket"] = "⟦", -- U+027E6
+ ["LeftDownTeeVector"] = "⥡", -- U+02961
+ ["LeftDownVector"] = "⇃", -- U+021C3
+ ["LeftDownVectorBar"] = "⥙", -- U+02959
+ ["LeftFloor"] = "⌊", -- U+0230A
+ ["LeftRightArrow"] = "↔", -- U+02194
+ ["LeftRightVector"] = "⥎", -- U+0294E
+ ["LeftTee"] = "⊣", -- U+022A3
+ ["LeftTeeArrow"] = "↤", -- U+021A4
+ ["LeftTeeVector"] = "⥚", -- U+0295A
+ ["LeftTriangle"] = "⊲", -- U+022B2
+ ["LeftTriangleBar"] = "⧏", -- U+029CF
+ ["LeftTriangleEqual"] = "⊴", -- U+022B4
+ ["LeftUpDownVector"] = "⥑", -- U+02951
+ ["LeftUpTeeVector"] = "⥠", -- U+02960
+ ["LeftUpVector"] = "↿", -- U+021BF
+ ["LeftUpVectorBar"] = "⥘", -- U+02958
+ ["LeftVector"] = "↼", -- U+021BC
+ ["LeftVectorBar"] = "⥒", -- U+02952
+ ["Leftarrow"] = "⇐", -- U+021D0
+ ["Leftrightarrow"] = "⇔", -- U+021D4
+ ["LessEqualGreater"] = "⋚", -- U+022DA
+ ["LessFullEqual"] = "≦", -- U+02266
+ ["LessGreater"] = "≶", -- U+02276
+ ["LessLess"] = "⪡", -- U+02AA1
+ ["LessSlantEqual"] = "⩽", -- U+02A7D
+ ["LessTilde"] = "≲", -- U+02272
+ ["Lfr"] = "𝔏", -- U+1D50F
+ ["Lgr"] = "Λ", -- U+0039B
+ ["Ll"] = "⋘", -- U+022D8
+ ["Lleftarrow"] = "⇚", -- U+021DA
+ ["Lmidot"] = "Ŀ", -- U+0013F
+ ["LongLeftArrow"] = "⟵", -- U+027F5
+ ["LongLeftRightArrow"] = "⟷", -- U+027F7
+ ["LongRightArrow"] = "⟶", -- U+027F6
+ ["Longleftarrow"] = "⟸", -- U+027F8
+ ["Longleftrightarrow"] = "⟺", -- U+027FA
+ ["Longrightarrow"] = "⟹", -- U+027F9
+ ["Lopf"] = "𝕃", -- U+1D543
+ ["LowerLeftArrow"] = "↙", -- U+02199
+ ["LowerRightArrow"] = "↘", -- U+02198
+ ["Lscr"] = "ℒ", -- U+02112
+ ["Lsh"] = "↰", -- U+021B0
+ ["Lstrok"] = "Ł", -- U+00141
+ ["Lt"] = "≪", -- U+0226A
+ ["Map"] = "⤅", -- U+02905
+ ["Mcy"] = "М", -- U+0041C
+ ["MediumSpace"] = " ", -- U+0205F
+ ["Mellintrf"] = "ℳ", -- U+02133
+ ["Mfr"] = "𝔐", -- U+1D510
+ ["Mgr"] = "Μ", -- U+0039C
+ ["MinusPlus"] = "∓", -- U+02213
+ ["Mopf"] = "𝕄", -- U+1D544
+ ["Mscr"] = "ℳ", -- U+02133
+ ["Mu"] = "Μ", -- U+0039C
+ ["NJcy"] = "Њ", -- U+0040A
+ ["Nacute"] = "Ń", -- U+00143
+ ["Ncaron"] = "Ň", -- U+00147
+ ["Ncedil"] = "Ņ", -- U+00145
+ ["Ncy"] = "Н", -- U+0041D
+ ["NegativeMediumSpace"] = "​", -- U+0200B
+ ["NegativeThickSpace"] = "​", -- U+0200B
+ ["NegativeThinSpace"] = "​", -- U+0200B
+ ["NegativeVeryThinSpace"] = "​", -- U+0200B
+ ["NestedGreaterGreater"] = "≫", -- U+0226B
+ ["NestedLessLess"] = "≪", -- U+0226A
+ ["Nfr"] = "𝔑", -- U+1D511
+ ["Ngr"] = "Ν", -- U+0039D
+ ["NoBreak"] = "⁠", -- U+02060
+ ["NonBreakingSpace"] = " ", -- U+000A0
+ ["Nopf"] = "ℕ", -- U+02115
+ ["Not"] = "⫬", -- U+02AEC
+ ["NotCongruent"] = "≢", -- U+02262
+ ["NotCupCap"] = "≭", -- U+0226D
+ ["NotDoubleVerticalBar"] = "∦", -- U+02226
+ ["NotElement"] = "∉", -- U+02209
+ ["NotEqual"] = "≠", -- U+02260
+ ["NotEqualTilde"] = "≂̸", -- U+02242 00338
+ ["NotExists"] = "∄", -- U+02204
+ ["NotGreater"] = "≯", -- U+0226F
+ ["NotGreaterEqual"] = "≱", -- U+02271
+ ["NotGreaterFullEqual"] = "≧̸", -- U+02267 00338
+ ["NotGreaterGreater"] = "≫̸", -- U+0226B 00338
+ ["NotGreaterLess"] = "≹", -- U+02279
+ ["NotGreaterSlantEqual"] = "⩾̸", -- U+02A7E 00338
+ ["NotGreaterTilde"] = "≵", -- U+02275
+ ["NotHumpDownHump"] = "≎̸", -- U+0224E 00338
+ ["NotHumpEqual"] = "≏̸", -- U+0224F 00338
+ ["NotLeftTriangle"] = "⋪", -- U+022EA
+ ["NotLeftTriangleBar"] = "⧏̸", -- U+029CF 00338
+ ["NotLeftTriangleEqual"] = "⋬", -- U+022EC
+ ["NotLess"] = "≮", -- U+0226E
+ ["NotLessEqual"] = "≰", -- U+02270
+ ["NotLessGreater"] = "≸", -- U+02278
+ ["NotLessLess"] = "≪̸", -- U+0226A 00338
+ ["NotLessSlantEqual"] = "⩽̸", -- U+02A7D 00338
+ ["NotLessTilde"] = "≴", -- U+02274
+ ["NotNestedGreaterGreater"] = "⪢̸", -- U+02AA2 00338
+ ["NotNestedLessLess"] = "⪡̸", -- U+02AA1 00338
+ ["NotPrecedes"] = "⊀", -- U+02280
+ ["NotPrecedesEqual"] = "⪯̸", -- U+02AAF 00338
+ ["NotPrecedesSlantEqual"] = "⋠", -- U+022E0
+ ["NotReverseElement"] = "∌", -- U+0220C
+ ["NotRightTriangle"] = "⋫", -- U+022EB
+ ["NotRightTriangleBar"] = "⧐̸", -- U+029D0 00338
+ ["NotRightTriangleEqual"] = "⋭", -- U+022ED
+ ["NotSquareSubset"] = "⊏̸", -- U+0228F 00338
+ ["NotSquareSubsetEqual"] = "⋢", -- U+022E2
+ ["NotSquareSuperset"] = "⊐̸", -- U+02290 00338
+ ["NotSquareSupersetEqual"] = "⋣", -- U+022E3
+ ["NotSubset"] = "⊂⃒", -- U+02282 020D2
+ ["NotSubsetEqual"] = "⊈", -- U+02288
+ ["NotSucceeds"] = "⊁", -- U+02281
+ ["NotSucceedsEqual"] = "⪰̸", -- U+02AB0 00338
+ ["NotSucceedsSlantEqual"] = "⋡", -- U+022E1
+ ["NotSucceedsTilde"] = "≿̸", -- U+0227F 00338
+ ["NotSuperset"] = "⊃⃒", -- U+02283 020D2
+ ["NotSupersetEqual"] = "⊉", -- U+02289
+ ["NotTilde"] = "≁", -- U+02241
+ ["NotTildeEqual"] = "≄", -- U+02244
+ ["NotTildeFullEqual"] = "≇", -- U+02247
+ ["NotTildeTilde"] = "≉", -- U+02249
+ ["NotVerticalBar"] = "∤", -- U+02224
+ ["Nscr"] = "𝒩", -- U+1D4A9
+ ["Ntilde"] = "Ñ", -- U+000D1
+ ["Nu"] = "Ν", -- U+0039D
+ ["OElig"] = "Œ", -- U+00152
+ ["OHacgr"] = "Ώ", -- U+0038F
+ ["OHgr"] = "Ω", -- U+003A9
+ ["Oacgr"] = "Ό", -- U+0038C
+ ["Oacute"] = "Ó", -- U+000D3
+ ["Ocirc"] = "Ô", -- U+000D4
+ ["Ocy"] = "О", -- U+0041E
+ ["Odblac"] = "Ő", -- U+00150
+ ["Ofr"] = "𝔒", -- U+1D512
+ ["Ogr"] = "Ο", -- U+0039F
+ ["Ograve"] = "Ò", -- U+000D2
+ ["Omacr"] = "Ō", -- U+0014C
+ ["Omega"] = "Ω", -- U+003A9
+ ["Omicron"] = "Ο", -- U+0039F
+ ["Oopf"] = "𝕆", -- U+1D546
+ ["OpenCurlyDoubleQuote"] = "“", -- U+0201C
+ ["OpenCurlyQuote"] = "‘", -- U+02018
+ ["Or"] = "⩔", -- U+02A54
+ ["Oscr"] = "𝒪", -- U+1D4AA
+ ["Oslash"] = "Ø", -- U+000D8
+ ["Otilde"] = "Õ", -- U+000D5
+ ["Otimes"] = "⨷", -- U+02A37
+ ["Ouml"] = "Ö", -- U+000D6
+ ["OverBar"] = "‾", -- U+0203E
+ ["OverBrace"] = "⏞", -- U+023DE
+ ["OverBracket"] = "⎴", -- U+023B4
+ ["OverParenthesis"] = "⏜", -- U+023DC
+ ["PHgr"] = "Φ", -- U+003A6
+ ["PSgr"] = "Ψ", -- U+003A8
+ ["PartialD"] = "∂", -- U+02202
+ ["Pcy"] = "П", -- U+0041F
+ ["Pfr"] = "𝔓", -- U+1D513
+ ["Pgr"] = "Π", -- U+003A0
+ ["Phi"] = "Φ", -- U+003A6
+ ["Pi"] = "Π", -- U+003A0
+ ["PlusMinus"] = "±", -- U+000B1
+ ["Poincareplane"] = "ℌ", -- U+0210C
+ ["Popf"] = "ℙ", -- U+02119
+ ["Pr"] = "⪻", -- U+02ABB
+ ["Precedes"] = "≺", -- U+0227A
+ ["PrecedesEqual"] = "⪯", -- U+02AAF
+ ["PrecedesSlantEqual"] = "≼", -- U+0227C
+ ["PrecedesTilde"] = "≾", -- U+0227E
+ ["Prime"] = "″", -- U+02033
+ ["Product"] = "∏", -- U+0220F
+ ["Proportion"] = "∷", -- U+02237
+ ["Proportional"] = "∝", -- U+0221D
+ ["Pscr"] = "𝒫", -- U+1D4AB
+ ["Psi"] = "Ψ", -- U+003A8
+ ["QUOT"] = "\"", -- U+00022
+ ["Qfr"] = "𝔔", -- U+1D514
+ ["Qopf"] = "ℚ", -- U+0211A
+ ["Qscr"] = "𝒬", -- U+1D4AC
+ ["RBarr"] = "⤐", -- U+02910
+ ["REG"] = "®", -- U+000AE
+ ["Racute"] = "Ŕ", -- U+00154
+ ["Rang"] = "⟫", -- U+027EB
+ ["Rarr"] = "↠", -- U+021A0
+ ["Rarrtl"] = "⤖", -- U+02916
+ ["Rcaron"] = "Ř", -- U+00158
+ ["Rcedil"] = "Ŗ", -- U+00156
+ ["Rcy"] = "Р", -- U+00420
+ ["Re"] = "ℜ", -- U+0211C
+ ["ReverseElement"] = "∋", -- U+0220B
+ ["ReverseEquilibrium"] = "⇋", -- U+021CB
+ ["ReverseUpEquilibrium"] = "⥯", -- U+0296F
+ ["Rfr"] = "ℜ", -- U+0211C
+ ["Rgr"] = "Ρ", -- U+003A1
+ ["Rho"] = "Ρ", -- U+003A1
+ ["RightAngleBracket"] = "⟩", -- U+027E9
+ ["RightArrow"] = "→", -- U+02192
+ ["RightArrowBar"] = "⇥", -- U+021E5
+ ["RightArrowLeftArrow"] = "⇄", -- U+021C4
+ ["RightCeiling"] = "⌉", -- U+02309
+ ["RightDoubleBracket"] = "⟧", -- U+027E7
+ ["RightDownTeeVector"] = "⥝", -- U+0295D
+ ["RightDownVector"] = "⇂", -- U+021C2
+ ["RightDownVectorBar"] = "⥕", -- U+02955
+ ["RightFloor"] = "⌋", -- U+0230B
+ ["RightTee"] = "⊢", -- U+022A2
+ ["RightTeeArrow"] = "↦", -- U+021A6
+ ["RightTeeVector"] = "⥛", -- U+0295B
+ ["RightTriangle"] = "⊳", -- U+022B3
+ ["RightTriangleBar"] = "⧐", -- U+029D0
+ ["RightTriangleEqual"] = "⊵", -- U+022B5
+ ["RightUpDownVector"] = "⥏", -- U+0294F
+ ["RightUpTeeVector"] = "⥜", -- U+0295C
+ ["RightUpVector"] = "↾", -- U+021BE
+ ["RightUpVectorBar"] = "⥔", -- U+02954
+ ["RightVector"] = "⇀", -- U+021C0
+ ["RightVectorBar"] = "⥓", -- U+02953
+ ["Rightarrow"] = "⇒", -- U+021D2
+ ["Ropf"] = "ℝ", -- U+0211D
+ ["RoundImplies"] = "⥰", -- U+02970
+ ["Rrightarrow"] = "⇛", -- U+021DB
+ ["Rscr"] = "ℛ", -- U+0211B
+ ["Rsh"] = "↱", -- U+021B1
+ ["RuleDelayed"] = "⧴", -- U+029F4
+ ["SHCHcy"] = "Щ", -- U+00429
+ ["SHcy"] = "Ш", -- U+00428
+ ["SOFTcy"] = "Ь", -- U+0042C
+ ["Sacute"] = "Ś", -- U+0015A
+ ["Sc"] = "⪼", -- U+02ABC
+ ["Scaron"] = "Š", -- U+00160
+ ["Scedil"] = "Ş", -- U+0015E
+ ["Scirc"] = "Ŝ", -- U+0015C
+ ["Scy"] = "С", -- U+00421
+ ["Sfr"] = "𝔖", -- U+1D516
+ ["Sgr"] = "Σ", -- U+003A3
+ ["ShortDownArrow"] = "↓", -- U+02193
+ ["ShortLeftArrow"] = "←", -- U+02190
+ ["ShortRightArrow"] = "→", -- U+02192
+ ["ShortUpArrow"] = "↑", -- U+02191
+ ["Sigma"] = "Σ", -- U+003A3
+ ["SmallCircle"] = "∘", -- U+02218
+ ["Sopf"] = "𝕊", -- U+1D54A
+ ["Sqrt"] = "√", -- U+0221A
+ ["Square"] = "□", -- U+025A1
+ ["SquareIntersection"] = "⊓", -- U+02293
+ ["SquareSubset"] = "⊏", -- U+0228F
+ ["SquareSubsetEqual"] = "⊑", -- U+02291
+ ["SquareSuperset"] = "⊐", -- U+02290
+ ["SquareSupersetEqual"] = "⊒", -- U+02292
+ ["SquareUnion"] = "⊔", -- U+02294
+ ["Sscr"] = "𝒮", -- U+1D4AE
+ ["Star"] = "⋆", -- U+022C6
+ ["Sub"] = "⋐", -- U+022D0
+ ["Subset"] = "⋐", -- U+022D0
+ ["SubsetEqual"] = "⊆", -- U+02286
+ ["Succeeds"] = "≻", -- U+0227B
+ ["SucceedsEqual"] = "⪰", -- U+02AB0
+ ["SucceedsSlantEqual"] = "≽", -- U+0227D
+ ["SucceedsTilde"] = "≿", -- U+0227F
+ ["SuchThat"] = "∋", -- U+0220B
+ ["Sum"] = "∑", -- U+02211
+ ["Sup"] = "⋑", -- U+022D1
+ ["Superset"] = "⊃", -- U+02283
+ ["SupersetEqual"] = "⊇", -- U+02287
+ ["Supset"] = "⋑", -- U+022D1
+ ["THORN"] = "Þ", -- U+000DE
+ ["THgr"] = "Θ", -- U+00398
+ ["TRADE"] = "™", -- U+02122
+ ["TSHcy"] = "Ћ", -- U+0040B
+ ["TScy"] = "Ц", -- U+00426
+ ["Tab"] = "\9", -- U+00009
+ ["Tau"] = "Τ", -- U+003A4
+ ["Tcaron"] = "Ť", -- U+00164
+ ["Tcedil"] = "Ţ", -- U+00162
+ ["Tcy"] = "Т", -- U+00422
+ ["Tfr"] = "𝔗", -- U+1D517
+ ["Tgr"] = "Τ", -- U+003A4
+ ["Therefore"] = "∴", -- U+02234
+ ["Theta"] = "Θ", -- U+00398
+ ["ThickSpace"] = "  ", -- U+0205F 0200A
+ ["ThinSpace"] = " ", -- U+02009
+ ["Tilde"] = "∼", -- U+0223C
+ ["TildeEqual"] = "≃", -- U+02243
+ ["TildeFullEqual"] = "≅", -- U+02245
+ ["TildeTilde"] = "≈", -- U+02248
+ ["Topf"] = "𝕋", -- U+1D54B
+ ["TripleDot"] = "⃛", -- U+020DB
+ ["Tscr"] = "𝒯", -- U+1D4AF
+ ["Tstrok"] = "Ŧ", -- U+00166
+ ["Uacgr"] = "Ύ", -- U+0038E
+ ["Uacute"] = "Ú", -- U+000DA
+ ["Uarr"] = "↟", -- U+0219F
+ ["Uarrocir"] = "⥉", -- U+02949
+ ["Ubrcy"] = "Ў", -- U+0040E
+ ["Ubreve"] = "Ŭ", -- U+0016C
+ ["Ucirc"] = "Û", -- U+000DB
+ ["Ucy"] = "У", -- U+00423
+ ["Udblac"] = "Ű", -- U+00170
+ ["Udigr"] = "Ϋ", -- U+003AB
+ ["Ufr"] = "𝔘", -- U+1D518
+ ["Ugr"] = "Υ", -- U+003A5
+ ["Ugrave"] = "Ù", -- U+000D9
+ ["Umacr"] = "Ū", -- U+0016A
+ -- ["UnderBar"] = "_", -- U+0005F
+ ["UnderBar"] = "‾", -- U+0203E
+ ["UnderBrace"] = "⏟", -- U+023DF
+ ["UnderBracket"] = "⎵", -- U+023B5
+ ["UnderParenthesis"] = "⏝", -- U+023DD
+ ["Union"] = "⋃", -- U+022C3
+ ["UnionPlus"] = "⊎", -- U+0228E
+ ["Uogon"] = "Ų", -- U+00172
+ ["Uopf"] = "𝕌", -- U+1D54C
+ ["UpArrow"] = "↑", -- U+02191
+ ["UpArrowBar"] = "⤒", -- U+02912
+ ["UpArrowDownArrow"] = "⇅", -- U+021C5
+ ["UpDownArrow"] = "↕", -- U+02195
+ ["UpEquilibrium"] = "⥮", -- U+0296E
+ ["UpTee"] = "⊥", -- U+022A5
+ ["UpTeeArrow"] = "↥", -- U+021A5
+ ["Uparrow"] = "⇑", -- U+021D1
+ ["Updownarrow"] = "⇕", -- U+021D5
+ ["UpperLeftArrow"] = "↖", -- U+02196
+ ["UpperRightArrow"] = "↗", -- U+02197
+ ["Upsi"] = "ϒ", -- U+003D2
+ ["Upsilon"] = "Υ", -- U+003A5
+ ["Uring"] = "Ů", -- U+0016E
+ ["Uscr"] = "𝒰", -- U+1D4B0
+ ["Utilde"] = "Ũ", -- U+00168
+ ["Uuml"] = "Ü", -- U+000DC
+ ["VDash"] = "⊫", -- U+022AB
+ ["Vbar"] = "⫫", -- U+02AEB
+ ["Vcy"] = "В", -- U+00412
+ ["Vdash"] = "⊩", -- U+022A9
+ ["Vdashl"] = "⫦", -- U+02AE6
+ ["Vee"] = "⋁", -- U+022C1
+ ["Verbar"] = "‖", -- U+02016
+ ["Vert"] = "‖", -- U+02016
+ ["VerticalBar"] = "∣", -- U+02223
+ ["VerticalLine"] = "|", -- U+0007C
+ ["VerticalSeparator"] = "❘", -- U+02758
+ ["VerticalTilde"] = "≀", -- U+02240
+ ["VeryThinSpace"] = " ", -- U+0200A
+ ["Vfr"] = "𝔙", -- U+1D519
+ ["Vopf"] = "𝕍", -- U+1D54D
+ ["Vscr"] = "𝒱", -- U+1D4B1
+ ["Vvdash"] = "⊪", -- U+022AA
+ ["Wcirc"] = "Ŵ", -- U+00174
+ ["Wedge"] = "⋀", -- U+022C0
+ ["Wfr"] = "𝔚", -- U+1D51A
+ ["Wopf"] = "𝕎", -- U+1D54E
+ ["Wscr"] = "𝒲", -- U+1D4B2
+ ["Xfr"] = "𝔛", -- U+1D51B
+ ["Xgr"] = "Ξ", -- U+0039E
+ ["Xi"] = "Ξ", -- U+0039E
+ ["Xopf"] = "𝕏", -- U+1D54F
+ ["Xscr"] = "𝒳", -- U+1D4B3
+ ["YAcy"] = "Я", -- U+0042F
+ ["YIcy"] = "Ї", -- U+00407
+ ["YUcy"] = "Ю", -- U+0042E
+ ["Yacute"] = "Ý", -- U+000DD
+ ["Ycirc"] = "Ŷ", -- U+00176
+ ["Ycy"] = "Ы", -- U+0042B
+ ["Yfr"] = "𝔜", -- U+1D51C
+ ["Yopf"] = "𝕐", -- U+1D550
+ ["Yscr"] = "𝒴", -- U+1D4B4
+ ["Yuml"] = "Ÿ", -- U+00178
+ ["ZHcy"] = "Ж", -- U+00416
+ ["Zacute"] = "Ź", -- U+00179
+ ["Zcaron"] = "Ž", -- U+0017D
+ ["Zcy"] = "З", -- U+00417
+ ["Zdot"] = "Ż", -- U+0017B
+ ["ZeroWidthSpace"] = "​", -- U+0200B
+ ["Zeta"] = "Ζ", -- U+00396
+ ["Zfr"] = "ℨ", -- U+02128
+ ["Zgr"] = "Ζ", -- U+00396
+ ["Zopf"] = "ℤ", -- U+02124
+ ["Zscr"] = "𝒵", -- U+1D4B5
+ ["aacgr"] = "ά", -- U+003AC
+ ["aacute"] = "á", -- U+000E1
+ ["abreve"] = "ă", -- U+00103
+ ["ac"] = "∾", -- U+0223E
+ ["acE"] = "∾̳", -- U+0223E 00333
+ ["acd"] = "∿", -- U+0223F
+ ["acirc"] = "â", -- U+000E2
+ ["acute"] = "´", -- U+000B4
+ ["acy"] = "а", -- U+00430
+ ["aelig"] = "æ", -- U+000E6
+ ["af"] = "⁡", -- U+02061
+ ["afr"] = "𝔞", -- U+1D51E
+ ["agr"] = "α", -- U+003B1
+ ["agrave"] = "à", -- U+000E0
+ ["alefsym"] = "ℵ", -- U+02135
+ ["aleph"] = "ℵ", -- U+02135
+ ["alpha"] = "α", -- U+003B1
+ ["amacr"] = "ā", -- U+00101
+ ["amalg"] = "⨿", -- U+02A3F
+ ["amp"] = "&", -- U+00026
+ ["and"] = "∧", -- U+02227
+ ["andand"] = "⩕", -- U+02A55
+ ["andd"] = "⩜", -- U+02A5C
+ ["andslope"] = "⩘", -- U+02A58
+ ["andv"] = "⩚", -- U+02A5A
+ ["ang"] = "∠", -- U+02220
+ ["ange"] = "⦤", -- U+029A4
+ ["angle"] = "∠", -- U+02220
+ ["angmsd"] = "∡", -- U+02221
+ ["angmsdaa"] = "⦨", -- U+029A8
+ ["angmsdab"] = "⦩", -- U+029A9
+ ["angmsdac"] = "⦪", -- U+029AA
+ ["angmsdad"] = "⦫", -- U+029AB
+ ["angmsdae"] = "⦬", -- U+029AC
+ ["angmsdaf"] = "⦭", -- U+029AD
+ ["angmsdag"] = "⦮", -- U+029AE
+ ["angmsdah"] = "⦯", -- U+029AF
+ ["angrt"] = "∟", -- U+0221F
+ ["angrtvb"] = "⊾", -- U+022BE
+ ["angrtvbd"] = "⦝", -- U+0299D
+ ["angsph"] = "∢", -- U+02222
+ ["angst"] = "Å", -- U+000C5
+ ["angzarr"] = "⍼", -- U+0237C
+ ["aogon"] = "ą", -- U+00105
+ ["aopf"] = "𝕒", -- U+1D552
+ ["ap"] = "≈", -- U+02248
+ ["apE"] = "⩰", -- U+02A70
+ ["apacir"] = "⩯", -- U+02A6F
+ ["ape"] = "≊", -- U+0224A
+ ["apid"] = "≋", -- U+0224B
+ ["apos"] = "'", -- U+00027
+ ["approx"] = "≈", -- U+02248
+ ["approxeq"] = "≊", -- U+0224A
+ ["aring"] = "å", -- U+000E5
+ ["ascr"] = "𝒶", -- U+1D4B6
+ ["ast"] = "*", -- U+0002A
+ ["asymp"] = "≈", -- U+02248
+ ["asympeq"] = "≍", -- U+0224D
+ ["atilde"] = "ã", -- U+000E3
+ ["auml"] = "ä", -- U+000E4
+ ["awconint"] = "∳", -- U+02233
+ ["awint"] = "⨑", -- U+02A11
+ ["b.Delta"] = "𝚫", -- U+1D6AB
+ ["b.Gamma"] = "𝚪", -- U+1D6AA
+ ["b.Gammad"] = "𝟊", -- U+1D7CA
+ ["b.Lambda"] = "𝚲", -- U+1D6B2
+ ["b.Omega"] = "𝛀", -- U+1D6C0
+ ["b.Phi"] = "𝚽", -- U+1D6BD
+ ["b.Pi"] = "𝚷", -- U+1D6B7
+ ["b.Psi"] = "𝚿", -- U+1D6BF
+ ["b.Sigma"] = "𝚺", -- U+1D6BA
+ ["b.Theta"] = "𝚯", -- U+1D6AF
+ ["b.Upsi"] = "𝚼", -- U+1D6BC
+ ["b.Xi"] = "𝚵", -- U+1D6B5
+ ["b.alpha"] = "𝛂", -- U+1D6C2
+ ["b.beta"] = "𝛃", -- U+1D6C3
+ ["b.chi"] = "𝛘", -- U+1D6D8
+ ["b.delta"] = "𝛅", -- U+1D6C5
+ ["b.epsi"] = "𝛆", -- U+1D6C6
+ ["b.epsiv"] = "𝛜", -- U+1D6DC
+ ["b.eta"] = "𝛈", -- U+1D6C8
+ ["b.gamma"] = "𝛄", -- U+1D6C4
+ ["b.gammad"] = "𝟋", -- U+1D7CB
+ ["b.iota"] = "𝛊", -- U+1D6CA
+ ["b.kappa"] = "𝛋", -- U+1D6CB
+ ["b.kappav"] = "𝛞", -- U+1D6DE
+ ["b.lambda"] = "𝛌", -- U+1D6CC
+ ["b.mu"] = "𝛍", -- U+1D6CD
+ ["b.nu"] = "𝛎", -- U+1D6CE
+ ["b.omega"] = "𝛚", -- U+1D6DA
+ ["b.phi"] = "𝛗", -- U+1D6D7
+ ["b.phiv"] = "𝛟", -- U+1D6DF
+ ["b.pi"] = "𝛑", -- U+1D6D1
+ ["b.piv"] = "𝛡", -- U+1D6E1
+ ["b.psi"] = "𝛙", -- U+1D6D9
+ ["b.rho"] = "𝛒", -- U+1D6D2
+ ["b.rhov"] = "𝛠", -- U+1D6E0
+ ["b.sigma"] = "𝛔", -- U+1D6D4
+ ["b.sigmav"] = "𝛓", -- U+1D6D3
+ ["b.tau"] = "𝛕", -- U+1D6D5
+ ["b.thetas"] = "𝛉", -- U+1D6C9
+ ["b.thetav"] = "𝛝", -- U+1D6DD
+ ["b.upsi"] = "𝛖", -- U+1D6D6
+ ["b.xi"] = "𝛏", -- U+1D6CF
+ ["b.zeta"] = "𝛇", -- U+1D6C7
+ ["bNot"] = "⫭", -- U+02AED
+ ["backcong"] = "≌", -- U+0224C
+ ["backepsilon"] = "϶", -- U+003F6
+ ["backprime"] = "‵", -- U+02035
+ ["backsim"] = "∽", -- U+0223D
+ ["backsimeq"] = "⋍", -- U+022CD
+ ["barvee"] = "⊽", -- U+022BD
+ ["barwed"] = "⌅", -- U+02305
+ ["barwedge"] = "⌅", -- U+02305
+ ["bbrk"] = "⎵", -- U+023B5
+ ["bbrktbrk"] = "⎶", -- U+023B6
+ ["bcong"] = "≌", -- U+0224C
+ ["bcy"] = "б", -- U+00431
+ ["bdquo"] = "„", -- U+0201E
+ ["becaus"] = "∵", -- U+02235
+ ["because"] = "∵", -- U+02235
+ ["bemptyv"] = "⦰", -- U+029B0
+ ["bepsi"] = "϶", -- U+003F6
+ ["bernou"] = "ℬ", -- U+0212C
+ ["beta"] = "β", -- U+003B2
+ ["beth"] = "ℶ", -- U+02136
+ ["between"] = "≬", -- U+0226C
+ ["bfr"] = "𝔟", -- U+1D51F
+ ["bgr"] = "β", -- U+003B2
+ ["bigcap"] = "⋂", -- U+022C2
+ ["bigcirc"] = "◯", -- U+025EF
+ ["bigcup"] = "⋃", -- U+022C3
+ ["bigodot"] = "⨀", -- U+02A00
+ ["bigoplus"] = "⨁", -- U+02A01
+ ["bigotimes"] = "⨂", -- U+02A02
+ ["bigsqcup"] = "⨆", -- U+02A06
+ ["bigstar"] = "★", -- U+02605
+ ["bigtriangledown"] = "▽", -- U+025BD
+ ["bigtriangleup"] = "△", -- U+025B3
+ ["biguplus"] = "⨄", -- U+02A04
+ ["bigvee"] = "⋁", -- U+022C1
+ ["bigwedge"] = "⋀", -- U+022C0
+ ["bkarow"] = "⤍", -- U+0290D
+ ["blacklozenge"] = "⧫", -- U+029EB
+ ["blacksquare"] = "▪", -- U+025AA
+ ["blacktriangle"] = "▴", -- U+025B4
+ ["blacktriangledown"] = "▾", -- U+025BE
+ ["blacktriangleleft"] = "◂", -- U+025C2
+ ["blacktriangleright"] = "▸", -- U+025B8
+ ["blank"] = "␣", -- U+02423
+ ["blk12"] = "▒", -- U+02592
+ ["blk14"] = "░", -- U+02591
+ ["blk34"] = "▓", -- U+02593
+ ["block"] = "█", -- U+02588
+ ["bne"] = "=⃥", -- U+0003D 020E5
+ ["bnequiv"] = "≡⃥", -- U+02261 020E5
+ ["bnot"] = "⌐", -- U+02310
+ ["bopf"] = "𝕓", -- U+1D553
+ ["bot"] = "⊥", -- U+022A5
+ ["bottom"] = "⊥", -- U+022A5
+ ["bowtie"] = "⋈", -- U+022C8
+ ["boxDL"] = "╗", -- U+02557
+ ["boxDR"] = "╔", -- U+02554
+ ["boxDl"] = "╖", -- U+02556
+ ["boxDr"] = "╓", -- U+02553
+ ["boxH"] = "═", -- U+02550
+ ["boxHD"] = "╦", -- U+02566
+ ["boxHU"] = "╩", -- U+02569
+ ["boxHd"] = "╤", -- U+02564
+ ["boxHu"] = "╧", -- U+02567
+ ["boxUL"] = "╝", -- U+0255D
+ ["boxUR"] = "╚", -- U+0255A
+ ["boxUl"] = "╜", -- U+0255C
+ ["boxUr"] = "╙", -- U+02559
+ ["boxV"] = "║", -- U+02551
+ ["boxVH"] = "╬", -- U+0256C
+ ["boxVL"] = "╣", -- U+02563
+ ["boxVR"] = "╠", -- U+02560
+ ["boxVh"] = "╫", -- U+0256B
+ ["boxVl"] = "╢", -- U+02562
+ ["boxVr"] = "╟", -- U+0255F
+ ["boxbox"] = "⧉", -- U+029C9
+ ["boxdL"] = "╕", -- U+02555
+ ["boxdR"] = "╒", -- U+02552
+ ["boxdl"] = "┐", -- U+02510
+ ["boxdr"] = "┌", -- U+0250C
+ ["boxh"] = "─", -- U+02500
+ ["boxhD"] = "╥", -- U+02565
+ ["boxhU"] = "╨", -- U+02568
+ ["boxhd"] = "┬", -- U+0252C
+ ["boxhu"] = "┴", -- U+02534
+ ["boxminus"] = "⊟", -- U+0229F
+ ["boxplus"] = "⊞", -- U+0229E
+ ["boxtimes"] = "⊠", -- U+022A0
+ ["boxuL"] = "╛", -- U+0255B
+ ["boxuR"] = "╘", -- U+02558
+ ["boxul"] = "┘", -- U+02518
+ ["boxur"] = "└", -- U+02514
+ ["boxv"] = "│", -- U+02502
+ ["boxvH"] = "╪", -- U+0256A
+ ["boxvL"] = "╡", -- U+02561
+ ["boxvR"] = "╞", -- U+0255E
+ ["boxvh"] = "┼", -- U+0253C
+ ["boxvl"] = "┤", -- U+02524
+ ["boxvr"] = "├", -- U+0251C
+ ["bprime"] = "‵", -- U+02035
+ ["breve"] = "˘", -- U+002D8
+ ["brvbar"] = "¦", -- U+000A6
+ ["bscr"] = "𝒷", -- U+1D4B7
+ ["bsemi"] = "⁏", -- U+0204F
+ ["bsim"] = "∽", -- U+0223D
+ ["bsime"] = "⋍", -- U+022CD
+ ["bsol"] = "\\", -- U+0005C
+ ["bsolb"] = "⧅", -- U+029C5
+ ["bsolhsub"] = "⟈", -- U+027C8
+ ["bull"] = "•", -- U+02022
+ ["bullet"] = "•", -- U+02022
+ ["bump"] = "≎", -- U+0224E
+ ["bumpE"] = "⪮", -- U+02AAE
+ ["bumpe"] = "≏", -- U+0224F
+ ["bumpeq"] = "≏", -- U+0224F
+ ["cacute"] = "ć", -- U+00107
+ ["cap"] = "∩", -- U+02229
+ ["capand"] = "⩄", -- U+02A44
+ ["capbrcup"] = "⩉", -- U+02A49
+ ["capcap"] = "⩋", -- U+02A4B
+ ["capcup"] = "⩇", -- U+02A47
+ ["capdot"] = "⩀", -- U+02A40
+ ["caps"] = "∩︀", -- U+02229 0FE00
+ ["caret"] = "⁁", -- U+02041
+ ["caron"] = "ˇ", -- U+002C7
+ ["ccaps"] = "⩍", -- U+02A4D
+ ["ccaron"] = "č", -- U+0010D
+ ["ccedil"] = "ç", -- U+000E7
+ ["ccirc"] = "ĉ", -- U+00109
+ ["ccups"] = "⩌", -- U+02A4C
+ ["ccupssm"] = "⩐", -- U+02A50
+ ["cdot"] = "ċ", -- U+0010B
+ ["cedil"] = "¸", -- U+000B8
+ ["cemptyv"] = "⦲", -- U+029B2
+ ["cent"] = "¢", -- U+000A2
+ ["centerdot"] = "·", -- U+000B7
+ ["cfr"] = "𝔠", -- U+1D520
+ ["chcy"] = "ч", -- U+00447
+ ["check"] = "✓", -- U+02713
+ ["checkmark"] = "✓", -- U+02713
+ ["chi"] = "χ", -- U+003C7
+ ["cir"] = "○", -- U+025CB
+ ["cirE"] = "⧃", -- U+029C3
+ ["circ"] = "ˆ", -- U+002C6
+ ["circeq"] = "≗", -- U+02257
+ ["circlearrowleft"] = "↺", -- U+021BA
+ ["circlearrowright"] = "↻", -- U+021BB
+ ["circledR"] = "®", -- U+000AE
+ ["circledS"] = "Ⓢ", -- U+024C8
+ ["circledast"] = "⊛", -- U+0229B
+ ["circledcirc"] = "⊚", -- U+0229A
+ ["circleddash"] = "⊝", -- U+0229D
+ ["cire"] = "≗", -- U+02257
+ ["cirfnint"] = "⨐", -- U+02A10
+ ["cirmid"] = "⫯", -- U+02AEF
+ ["cirscir"] = "⧂", -- U+029C2
+ ["clubs"] = "♣", -- U+02663
+ ["clubsuit"] = "♣", -- U+02663
+ ["colon"] = ":", -- U+0003A
+ ["colone"] = "≔", -- U+02254
+ ["coloneq"] = "≔", -- U+02254
+ ["comma"] = ",", -- U+0002C
+ ["commat"] = "@", -- U+00040
+ ["comp"] = "∁", -- U+02201
+ ["compfn"] = "∘", -- U+02218
+ ["complement"] = "∁", -- U+02201
+ ["complexes"] = "ℂ", -- U+02102
+ ["cong"] = "≅", -- U+02245
+ ["congdot"] = "⩭", -- U+02A6D
+ ["conint"] = "∮", -- U+0222E
+ ["copf"] = "𝕔", -- U+1D554
+ ["coprod"] = "∐", -- U+02210
+ ["copy"] = "©", -- U+000A9
+ ["copysr"] = "℗", -- U+02117
+ ["crarr"] = "↵", -- U+021B5
+ ["cross"] = "✗", -- U+02717
+ ["cscr"] = "𝒸", -- U+1D4B8
+ ["csub"] = "⫏", -- U+02ACF
+ ["csube"] = "⫑", -- U+02AD1
+ ["csup"] = "⫐", -- U+02AD0
+ ["csupe"] = "⫒", -- U+02AD2
+ ["ctdot"] = "⋯", -- U+022EF
+ ["cudarrl"] = "⤸", -- U+02938
+ ["cudarrr"] = "⤵", -- U+02935
+ ["cuepr"] = "⋞", -- U+022DE
+ ["cuesc"] = "⋟", -- U+022DF
+ ["cularr"] = "↶", -- U+021B6
+ ["cularrp"] = "⤽", -- U+0293D
+ ["cup"] = "∪", -- U+0222A
+ ["cupbrcap"] = "⩈", -- U+02A48
+ ["cupcap"] = "⩆", -- U+02A46
+ ["cupcup"] = "⩊", -- U+02A4A
+ ["cupdot"] = "⊍", -- U+0228D
+ ["cupor"] = "⩅", -- U+02A45
+ ["cups"] = "∪︀", -- U+0222A 0FE00
+ ["curarr"] = "↷", -- U+021B7
+ ["curarrm"] = "⤼", -- U+0293C
+ ["curlyeqprec"] = "⋞", -- U+022DE
+ ["curlyeqsucc"] = "⋟", -- U+022DF
+ ["curlyvee"] = "⋎", -- U+022CE
+ ["curlywedge"] = "⋏", -- U+022CF
+ ["curren"] = "¤", -- U+000A4
+ ["curvearrowleft"] = "↶", -- U+021B6
+ ["curvearrowright"] = "↷", -- U+021B7
+ ["cuvee"] = "⋎", -- U+022CE
+ ["cuwed"] = "⋏", -- U+022CF
+ ["cwconint"] = "∲", -- U+02232
+ ["cwint"] = "∱", -- U+02231
+ ["cylcty"] = "⌭", -- U+0232D
+ ["dArr"] = "⇓", -- U+021D3
+ ["dHar"] = "⥥", -- U+02965
+ ["dagger"] = "†", -- U+02020
+ ["daleth"] = "ℸ", -- U+02138
+ ["darr"] = "↓", -- U+02193
+ ["dash"] = "‐", -- U+02010
+ ["dashv"] = "⊣", -- U+022A3
+ ["dbkarow"] = "⤏", -- U+0290F
+ ["dblac"] = "˝", -- U+002DD
+ ["dcaron"] = "ď", -- U+0010F
+ ["dcy"] = "д", -- U+00434
+ ["dd"] = "ⅆ", -- U+02146
+ ["ddagger"] = "‡", -- U+02021
+ ["ddarr"] = "⇊", -- U+021CA
+ ["ddotseq"] = "⩷", -- U+02A77
+ ["deg"] = "°", -- U+000B0
+ ["delta"] = "δ", -- U+003B4
+ ["demptyv"] = "⦱", -- U+029B1
+ ["dfisht"] = "⥿", -- U+0297F
+ ["dfr"] = "𝔡", -- U+1D521
+ ["dgr"] = "δ", -- U+003B4
+ ["dharl"] = "⇃", -- U+021C3
+ ["dharr"] = "⇂", -- U+021C2
+ ["diam"] = "⋄", -- U+022C4
+ ["diamond"] = "⋄", -- U+022C4
+ ["diamondsuit"] = "♦", -- U+02666
+ ["diams"] = "♦", -- U+02666
+ ["die"] = "¨", -- U+000A8
+ ["digamma"] = "ϝ", -- U+003DD
+ ["disin"] = "⋲", -- U+022F2
+ ["div"] = "÷", -- U+000F7
+ ["divide"] = "÷", -- U+000F7
+ ["divideontimes"] = "⋇", -- U+022C7
+ ["divonx"] = "⋇", -- U+022C7
+ ["djcy"] = "ђ", -- U+00452
+ ["dlcorn"] = "⌞", -- U+0231E
+ ["dlcrop"] = "⌍", -- U+0230D
+ ["dollar"] = "$", -- U+00024
+ ["dopf"] = "𝕕", -- U+1D555
+ ["dot"] = "˙", -- U+002D9
+ ["doteq"] = "≐", -- U+02250
+ ["doteqdot"] = "≑", -- U+02251
+ ["dotminus"] = "∸", -- U+02238
+ ["dotplus"] = "∔", -- U+02214
+ ["dotsquare"] = "⊡", -- U+022A1
+ ["doublebarwedge"] = "⌆", -- U+02306
+ ["downarrow"] = "↓", -- U+02193
+ ["downdownarrows"] = "⇊", -- U+021CA
+ ["downharpoonleft"] = "⇃", -- U+021C3
+ ["downharpoonright"] = "⇂", -- U+021C2
+ ["drbkarow"] = "⤐", -- U+02910
+ ["drcorn"] = "⌟", -- U+0231F
+ ["drcrop"] = "⌌", -- U+0230C
+ ["dscr"] = "𝒹", -- U+1D4B9
+ ["dscy"] = "ѕ", -- U+00455
+ ["dsol"] = "⧶", -- U+029F6
+ ["dstrok"] = "đ", -- U+00111
+ ["dtdot"] = "⋱", -- U+022F1
+ ["dtri"] = "▿", -- U+025BF
+ ["dtrif"] = "▾", -- U+025BE
+ ["duarr"] = "⇵", -- U+021F5
+ ["duhar"] = "⥯", -- U+0296F
+ ["dwangle"] = "⦦", -- U+029A6
+ ["dzcy"] = "џ", -- U+0045F
+ ["dzigrarr"] = "⟿", -- U+027FF
+ ["eDDot"] = "⩷", -- U+02A77
+ ["eDot"] = "≑", -- U+02251
+ ["eacgr"] = "έ", -- U+003AD
+ ["eacute"] = "é", -- U+000E9
+ ["easter"] = "⩮", -- U+02A6E
+ ["ecaron"] = "ě", -- U+0011B
+ ["ecir"] = "≖", -- U+02256
+ ["ecirc"] = "ê", -- U+000EA
+ ["ecolon"] = "≕", -- U+02255
+ ["ecy"] = "э", -- U+0044D
+ ["edot"] = "ė", -- U+00117
+ ["ee"] = "ⅇ", -- U+02147
+ ["eeacgr"] = "ή", -- U+003AE
+ ["eegr"] = "η", -- U+003B7
+ ["efDot"] = "≒", -- U+02252
+ ["efr"] = "𝔢", -- U+1D522
+ ["eg"] = "⪚", -- U+02A9A
+ ["egr"] = "ε", -- U+003B5
+ ["egrave"] = "è", -- U+000E8
+ ["egs"] = "⪖", -- U+02A96
+ ["egsdot"] = "⪘", -- U+02A98
+ ["el"] = "⪙", -- U+02A99
+ ["elinters"] = "⏧", -- U+023E7
+ ["ell"] = "ℓ", -- U+02113
+ ["els"] = "⪕", -- U+02A95
+ ["elsdot"] = "⪗", -- U+02A97
+ ["emacr"] = "ē", -- U+00113
+ ["empty"] = "∅", -- U+02205
+ ["emptyset"] = "∅", -- U+02205
+ ["emptyv"] = "∅", -- U+02205
+ ["emsp"] = " ", -- U+02003
+ ["emsp13"] = " ", -- U+02004
+ ["emsp14"] = " ", -- U+02005
+ ["eng"] = "ŋ", -- U+0014B
+ ["ensp"] = " ", -- U+02002
+ ["eogon"] = "ę", -- U+00119
+ ["eopf"] = "𝕖", -- U+1D556
+ ["epar"] = "⋕", -- U+022D5
+ ["eparsl"] = "⧣", -- U+029E3
+ ["eplus"] = "⩱", -- U+02A71
+ ["epsi"] = "ε", -- U+003B5
+ ["epsilon"] = "ε", -- U+003B5
+ ["epsiv"] = "ϵ", -- U+003F5
+ ["eqcirc"] = "≖", -- U+02256
+ ["eqcolon"] = "≕", -- U+02255
+ ["eqsim"] = "≂", -- U+02242
+ ["eqslantgtr"] = "⪖", -- U+02A96
+ ["eqslantless"] = "⪕", -- U+02A95
+ ["equals"] = "=", -- U+0003D
+ ["equest"] = "≟", -- U+0225F
+ ["equiv"] = "≡", -- U+02261
+ ["equivDD"] = "⩸", -- U+02A78
+ ["eqvparsl"] = "⧥", -- U+029E5
+ ["erDot"] = "≓", -- U+02253
+ ["erarr"] = "⥱", -- U+02971
+ ["escr"] = "ℯ", -- U+0212F
+ ["esdot"] = "≐", -- U+02250
+ ["esim"] = "≂", -- U+02242
+ ["eta"] = "η", -- U+003B7
+ ["eth"] = "ð", -- U+000F0
+ ["euml"] = "ë", -- U+000EB
+ ["euro"] = "€", -- U+020AC
+ ["excl"] = "!", -- U+00021
+ ["exist"] = "∃", -- U+02203
+ ["expectation"] = "ℰ", -- U+02130
+ ["exponentiale"] = "ⅇ", -- U+02147
+ ["fallingdotseq"] = "≒", -- U+02252
+ ["fcy"] = "ф", -- U+00444
+ ["female"] = "♀", -- U+02640
+ ["ffilig"] = "ffi", -- U+0FB03
+ ["fflig"] = "ff", -- U+0FB00
+ ["ffllig"] = "ffl", -- U+0FB04
+ ["ffr"] = "𝔣", -- U+1D523
+ ["filig"] = "fi", -- U+0FB01
+ ["fjlig"] = "fj", -- U+00066 0006A
+ ["flat"] = "♭", -- U+0266D
+ ["fllig"] = "fl", -- U+0FB02
+ ["fltns"] = "▱", -- U+025B1
+ ["fnof"] = "ƒ", -- U+00192
+ ["fopf"] = "𝕗", -- U+1D557
+ ["forall"] = "∀", -- U+02200
+ ["fork"] = "⋔", -- U+022D4
+ ["forkv"] = "⫙", -- U+02AD9
+ ["fpartint"] = "⨍", -- U+02A0D
+ ["frac12"] = "½", -- U+000BD
+ ["frac13"] = "⅓", -- U+02153
+ ["frac14"] = "¼", -- U+000BC
+ ["frac15"] = "⅕", -- U+02155
+ ["frac16"] = "⅙", -- U+02159
+ ["frac18"] = "⅛", -- U+0215B
+ ["frac23"] = "⅔", -- U+02154
+ ["frac25"] = "⅖", -- U+02156
+ ["frac34"] = "¾", -- U+000BE
+ ["frac35"] = "⅗", -- U+02157
+ ["frac38"] = "⅜", -- U+0215C
+ ["frac45"] = "⅘", -- U+02158
+ ["frac56"] = "⅚", -- U+0215A
+ ["frac58"] = "⅝", -- U+0215D
+ ["frac78"] = "⅞", -- U+0215E
+ ["frasl"] = "⁄", -- U+02044
+ ["frown"] = "⌢", -- U+02322
+ ["fscr"] = "𝒻", -- U+1D4BB
+ ["gE"] = "≧", -- U+02267
+ ["gEl"] = "⪌", -- U+02A8C
+ ["gacute"] = "ǵ", -- U+001F5
+ ["gamma"] = "γ", -- U+003B3
+ ["gammad"] = "ϝ", -- U+003DD
+ ["gap"] = "⪆", -- U+02A86
+ ["gbreve"] = "ğ", -- U+0011F
+ ["gcirc"] = "ĝ", -- U+0011D
+ ["gcy"] = "г", -- U+00433
+ ["gdot"] = "ġ", -- U+00121
+ ["ge"] = "≥", -- U+02265
+ ["gel"] = "⋛", -- U+022DB
+ ["geq"] = "≥", -- U+02265
+ ["geqq"] = "≧", -- U+02267
+ ["geqslant"] = "⩾", -- U+02A7E
+ ["ges"] = "⩾", -- U+02A7E
+ ["gescc"] = "⪩", -- U+02AA9
+ ["gesdot"] = "⪀", -- U+02A80
+ ["gesdoto"] = "⪂", -- U+02A82
+ ["gesdotol"] = "⪄", -- U+02A84
+ ["gesl"] = "⋛︀", -- U+022DB 0FE00
+ ["gesles"] = "⪔", -- U+02A94
+ ["gfr"] = "𝔤", -- U+1D524
+ ["gg"] = "≫", -- U+0226B
+ ["ggg"] = "⋙", -- U+022D9
+ ["ggr"] = "γ", -- U+003B3
+ ["gimel"] = "ℷ", -- U+02137
+ ["gjcy"] = "ѓ", -- U+00453
+ ["gl"] = "≷", -- U+02277
+ ["glE"] = "⪒", -- U+02A92
+ ["gla"] = "⪥", -- U+02AA5
+ ["glj"] = "⪤", -- U+02AA4
+ ["gnE"] = "≩", -- U+02269
+ ["gnap"] = "⪊", -- U+02A8A
+ ["gnapprox"] = "⪊", -- U+02A8A
+ ["gne"] = "⪈", -- U+02A88
+ ["gneq"] = "⪈", -- U+02A88
+ ["gneqq"] = "≩", -- U+02269
+ ["gnsim"] = "⋧", -- U+022E7
+ ["gopf"] = "𝕘", -- U+1D558
+ ["grave"] = "`", -- U+00060
+ ["gscr"] = "ℊ", -- U+0210A
+ ["gsim"] = "≳", -- U+02273
+ ["gsime"] = "⪎", -- U+02A8E
+ ["gsiml"] = "⪐", -- U+02A90
+ ["gt"] = ">", -- U+0003E
+ ["gtcc"] = "⪧", -- U+02AA7
+ ["gtcir"] = "⩺", -- U+02A7A
+ ["gtdot"] = "⋗", -- U+022D7
+ ["gtlPar"] = "⦕", -- U+02995
+ ["gtquest"] = "⩼", -- U+02A7C
+ ["gtrapprox"] = "⪆", -- U+02A86
+ ["gtrarr"] = "⥸", -- U+02978
+ ["gtrdot"] = "⋗", -- U+022D7
+ ["gtreqless"] = "⋛", -- U+022DB
+ ["gtreqqless"] = "⪌", -- U+02A8C
+ ["gtrless"] = "≷", -- U+02277
+ ["gtrsim"] = "≳", -- U+02273
+ ["gvertneqq"] = "≩︀", -- U+02269 0FE00
+ ["gvnE"] = "≩︀", -- U+02269 0FE00
+ ["hArr"] = "⇔", -- U+021D4
+ ["hairsp"] = " ", -- U+0200A
+ ["half"] = "½", -- U+000BD
+ ["hamilt"] = "ℋ", -- U+0210B
+ ["hardcy"] = "ъ", -- U+0044A
+ ["harr"] = "↔", -- U+02194
+ ["harrcir"] = "⥈", -- U+02948
+ ["harrw"] = "↭", -- U+021AD
+ ["hbar"] = "ℏ", -- U+0210F
+ ["hcirc"] = "ĥ", -- U+00125
+ ["hearts"] = "♥", -- U+02665
+ ["heartsuit"] = "♥", -- U+02665
+ ["hellip"] = "…", -- U+02026
+ ["hercon"] = "⊹", -- U+022B9
+ ["hfr"] = "𝔥", -- U+1D525
+ ["hksearow"] = "⤥", -- U+02925
+ ["hkswarow"] = "⤦", -- U+02926
+ ["hoarr"] = "⇿", -- U+021FF
+ ["homtht"] = "∻", -- U+0223B
+ ["hookleftarrow"] = "↩", -- U+021A9
+ ["hookrightarrow"] = "↪", -- U+021AA
+ ["hopf"] = "𝕙", -- U+1D559
+ ["horbar"] = "―", -- U+02015
+ ["hscr"] = "𝒽", -- U+1D4BD
+ ["hslash"] = "ℏ", -- U+0210F
+ ["hstrok"] = "ħ", -- U+00127
+ ["hybull"] = "⁃", -- U+02043
+ ["hyphen"] = "‐", -- U+02010
+ ["iacgr"] = "ί", -- U+003AF
+ ["iacute"] = "í", -- U+000ED
+ ["ic"] = "⁣", -- U+02063
+ ["icirc"] = "î", -- U+000EE
+ ["icy"] = "и", -- U+00438
+ ["idiagr"] = "ΐ", -- U+00390
+ ["idigr"] = "ϊ", -- U+003CA
+ ["iecy"] = "е", -- U+00435
+ ["iexcl"] = "¡", -- U+000A1
+ ["iff"] = "⇔", -- U+021D4
+ ["ifr"] = "𝔦", -- U+1D526
+ ["igr"] = "ι", -- U+003B9
+ ["igrave"] = "ì", -- U+000EC
+ ["ii"] = "ⅈ", -- U+02148
+ ["iiiint"] = "⨌", -- U+02A0C
+ ["iiint"] = "∭", -- U+0222D
+ ["iinfin"] = "⧜", -- U+029DC
+ ["iiota"] = "℩", -- U+02129
+ ["ijlig"] = "ij", -- U+00133
+ ["imacr"] = "ī", -- U+0012B
+ ["image"] = "ℑ", -- U+02111
+ ["imagline"] = "ℐ", -- U+02110
+ ["imagpart"] = "ℑ", -- U+02111
+ ["imath"] = "ı", -- U+00131
+ ["imof"] = "⊷", -- U+022B7
+ ["imped"] = "Ƶ", -- U+001B5
+ ["in"] = "∈", -- U+02208
+ ["incare"] = "℅", -- U+02105
+ ["infin"] = "∞", -- U+0221E
+ ["infintie"] = "⧝", -- U+029DD
+ ["inodot"] = "ı", -- U+00131
+ ["int"] = "∫", -- U+0222B
+ ["intcal"] = "⊺", -- U+022BA
+ ["integers"] = "ℤ", -- U+02124
+ ["intercal"] = "⊺", -- U+022BA
+ ["intlarhk"] = "⨗", -- U+02A17
+ ["intprod"] = "⨼", -- U+02A3C
+ ["iocy"] = "ё", -- U+00451
+ ["iogon"] = "į", -- U+0012F
+ ["iopf"] = "𝕚", -- U+1D55A
+ ["iota"] = "ι", -- U+003B9
+ ["iprod"] = "⨼", -- U+02A3C
+ ["iquest"] = "¿", -- U+000BF
+ ["iscr"] = "𝒾", -- U+1D4BE
+ ["isin"] = "∈", -- U+02208
+ ["isinE"] = "⋹", -- U+022F9
+ ["isindot"] = "⋵", -- U+022F5
+ ["isins"] = "⋴", -- U+022F4
+ ["isinsv"] = "⋳", -- U+022F3
+ ["isinv"] = "∈", -- U+02208
+ ["it"] = "⁢", -- U+02062
+ ["itilde"] = "ĩ", -- U+00129
+ ["iukcy"] = "і", -- U+00456
+ ["iuml"] = "ï", -- U+000EF
+ ["jcirc"] = "ĵ", -- U+00135
+ ["jcy"] = "й", -- U+00439
+ ["jfr"] = "𝔧", -- U+1D527
+ ["jmath"] = "ȷ", -- U+00237
+ ["jopf"] = "𝕛", -- U+1D55B
+ ["jscr"] = "𝒿", -- U+1D4BF
+ ["jsercy"] = "ј", -- U+00458
+ ["jukcy"] = "є", -- U+00454
+ ["kappa"] = "κ", -- U+003BA
+ ["kappav"] = "ϰ", -- U+003F0
+ ["kcedil"] = "ķ", -- U+00137
+ ["kcy"] = "к", -- U+0043A
+ ["kfr"] = "𝔨", -- U+1D528
+ ["kgr"] = "κ", -- U+003BA
+ ["kgreen"] = "ĸ", -- U+00138
+ ["khcy"] = "х", -- U+00445
+ ["khgr"] = "χ", -- U+003C7
+ ["kjcy"] = "ќ", -- U+0045C
+ ["kopf"] = "𝕜", -- U+1D55C
+ ["kscr"] = "𝓀", -- U+1D4C0
+ ["lAarr"] = "⇚", -- U+021DA
+ ["lArr"] = "⇐", -- U+021D0
+ ["lAtail"] = "⤛", -- U+0291B
+ ["lBarr"] = "⤎", -- U+0290E
+ ["lE"] = "≦", -- U+02266
+ ["lEg"] = "⪋", -- U+02A8B
+ ["lHar"] = "⥢", -- U+02962
+ ["lacute"] = "ĺ", -- U+0013A
+ ["laemptyv"] = "⦴", -- U+029B4
+ ["lagran"] = "ℒ", -- U+02112
+ ["lambda"] = "λ", -- U+003BB
+ ["lang"] = "⟨", -- U+027E8
+ ["langd"] = "⦑", -- U+02991
+ ["langle"] = "⟨", -- U+027E8
+ ["lap"] = "⪅", -- U+02A85
+ ["laquo"] = "«", -- U+000AB
+ ["larr"] = "←", -- U+02190
+ ["larrb"] = "⇤", -- U+021E4
+ ["larrbfs"] = "⤟", -- U+0291F
+ ["larrfs"] = "⤝", -- U+0291D
+ ["larrhk"] = "↩", -- U+021A9
+ ["larrlp"] = "↫", -- U+021AB
+ ["larrpl"] = "⤹", -- U+02939
+ ["larrsim"] = "⥳", -- U+02973
+ ["larrtl"] = "↢", -- U+021A2
+ ["lat"] = "⪫", -- U+02AAB
+ ["latail"] = "⤙", -- U+02919
+ ["late"] = "⪭", -- U+02AAD
+ ["lates"] = "⪭︀", -- U+02AAD 0FE00
+ ["lbarr"] = "⤌", -- U+0290C
+ ["lbbrk"] = "❲", -- U+02772
+ ["lbrace"] = "{", -- U+0007B
+ ["lbrack"] = "[", -- U+0005B
+ ["lbrke"] = "⦋", -- U+0298B
+ ["lbrksld"] = "⦏", -- U+0298F
+ ["lbrkslu"] = "⦍", -- U+0298D
+ ["lcaron"] = "ľ", -- U+0013E
+ ["lcedil"] = "ļ", -- U+0013C
+ ["lceil"] = "⌈", -- U+02308
+ ["lcub"] = "{", -- U+0007B
+ ["lcy"] = "л", -- U+0043B
+ ["ldca"] = "⤶", -- U+02936
+ ["ldquo"] = "“", -- U+0201C
+ ["ldquor"] = "„", -- U+0201E
+ ["ldrdhar"] = "⥧", -- U+02967
+ ["ldrushar"] = "⥋", -- U+0294B
+ ["ldsh"] = "↲", -- U+021B2
+ ["le"] = "≤", -- U+02264
+ ["leftarrow"] = "←", -- U+02190
+ ["leftarrowtail"] = "↢", -- U+021A2
+ ["leftharpoondown"] = "↽", -- U+021BD
+ ["leftharpoonup"] = "↼", -- U+021BC
+ ["leftleftarrows"] = "⇇", -- U+021C7
+ ["leftrightarrow"] = "↔", -- U+02194
+ ["leftrightarrows"] = "⇆", -- U+021C6
+ ["leftrightharpoons"] = "⇋", -- U+021CB
+ ["leftrightsquigarrow"] = "↭", -- U+021AD
+ ["leftthreetimes"] = "⋋", -- U+022CB
+ ["leg"] = "⋚", -- U+022DA
+ ["leq"] = "≤", -- U+02264
+ ["leqq"] = "≦", -- U+02266
+ ["leqslant"] = "⩽", -- U+02A7D
+ ["les"] = "⩽", -- U+02A7D
+ ["lescc"] = "⪨", -- U+02AA8
+ ["lesdot"] = "⩿", -- U+02A7F
+ ["lesdoto"] = "⪁", -- U+02A81
+ ["lesdotor"] = "⪃", -- U+02A83
+ ["lesg"] = "⋚︀", -- U+022DA 0FE00
+ ["lesges"] = "⪓", -- U+02A93
+ ["lessapprox"] = "⪅", -- U+02A85
+ ["lessdot"] = "⋖", -- U+022D6
+ ["lesseqgtr"] = "⋚", -- U+022DA
+ ["lesseqqgtr"] = "⪋", -- U+02A8B
+ ["lessgtr"] = "≶", -- U+02276
+ ["lesssim"] = "≲", -- U+02272
+ ["lfisht"] = "⥼", -- U+0297C
+ ["lfloor"] = "⌊", -- U+0230A
+ ["lfr"] = "𝔩", -- U+1D529
+ ["lg"] = "≶", -- U+02276
+ ["lgE"] = "⪑", -- U+02A91
+ ["lgr"] = "λ", -- U+003BB
+ ["lhard"] = "↽", -- U+021BD
+ ["lharu"] = "↼", -- U+021BC
+ ["lharul"] = "⥪", -- U+0296A
+ ["lhblk"] = "▄", -- U+02584
+ ["ljcy"] = "љ", -- U+00459
+ ["ll"] = "≪", -- U+0226A
+ ["llarr"] = "⇇", -- U+021C7
+ ["llcorner"] = "⌞", -- U+0231E
+ ["llhard"] = "⥫", -- U+0296B
+ ["lltri"] = "◺", -- U+025FA
+ ["lmidot"] = "ŀ", -- U+00140
+ ["lmoust"] = "⎰", -- U+023B0
+ ["lmoustache"] = "⎰", -- U+023B0
+ ["lnE"] = "≨", -- U+02268
+ ["lnap"] = "⪉", -- U+02A89
+ ["lnapprox"] = "⪉", -- U+02A89
+ ["lne"] = "⪇", -- U+02A87
+ ["lneq"] = "⪇", -- U+02A87
+ ["lneqq"] = "≨", -- U+02268
+ ["lnsim"] = "⋦", -- U+022E6
+ ["loang"] = "⟬", -- U+027EC
+ ["loarr"] = "⇽", -- U+021FD
+ ["lobrk"] = "⟦", -- U+027E6
+ ["longleftarrow"] = "⟵", -- U+027F5
+ ["longleftrightarrow"] = "⟷", -- U+027F7
+ ["longmapsto"] = "⟼", -- U+027FC
+ ["longrightarrow"] = "⟶", -- U+027F6
+ ["looparrowleft"] = "↫", -- U+021AB
+ ["looparrowright"] = "↬", -- U+021AC
+ ["lopar"] = "⦅", -- U+02985
+ ["lopf"] = "𝕝", -- U+1D55D
+ ["loplus"] = "⨭", -- U+02A2D
+ ["lotimes"] = "⨴", -- U+02A34
+ ["lowast"] = "∗", -- U+02217
+ ["lowbar"] = "_", -- U+0005F
+ ["loz"] = "◊", -- U+025CA
+ ["lozenge"] = "◊", -- U+025CA
+ ["lozf"] = "⧫", -- U+029EB
+ ["lpar"] = "(", -- U+00028
+ ["lparlt"] = "⦓", -- U+02993
+ ["lrarr"] = "⇆", -- U+021C6
+ ["lrcorner"] = "⌟", -- U+0231F
+ ["lrhar"] = "⇋", -- U+021CB
+ ["lrhard"] = "⥭", -- U+0296D
+ ["lrm"] = "‎", -- U+0200E
+ ["lrtri"] = "⊿", -- U+022BF
+ ["lsaquo"] = "‹", -- U+02039
+ ["lscr"] = "𝓁", -- U+1D4C1
+ ["lsh"] = "↰", -- U+021B0
+ ["lsim"] = "≲", -- U+02272
+ ["lsime"] = "⪍", -- U+02A8D
+ ["lsimg"] = "⪏", -- U+02A8F
+ ["lsqb"] = "[", -- U+0005B
+ ["lsquo"] = "‘", -- U+02018
+ ["lsquor"] = "‚", -- U+0201A
+ ["lstrok"] = "ł", -- U+00142
+ ["lt"] = "<", -- U+00026
+ ["ltcc"] = "⪦", -- U+02AA6
+ ["ltcir"] = "⩹", -- U+02A79
+ ["ltdot"] = "⋖", -- U+022D6
+ ["lthree"] = "⋋", -- U+022CB
+ ["ltimes"] = "⋉", -- U+022C9
+ ["ltlarr"] = "⥶", -- U+02976
+ ["ltquest"] = "⩻", -- U+02A7B
+ ["ltrPar"] = "⦖", -- U+02996
+ ["ltri"] = "◃", -- U+025C3
+ ["ltrie"] = "⊴", -- U+022B4
+ ["ltrif"] = "◂", -- U+025C2
+ ["lurdshar"] = "⥊", -- U+0294A
+ ["luruhar"] = "⥦", -- U+02966
+ ["lvertneqq"] = "≨︀", -- U+02268 0FE00
+ ["lvnE"] = "≨︀", -- U+02268 0FE00
+ ["mDDot"] = "∺", -- U+0223A
+ ["macr"] = "¯", -- U+000AF
+ ["male"] = "♂", -- U+02642
+ ["malt"] = "✠", -- U+02720
+ ["maltese"] = "✠", -- U+02720
+ ["map"] = "↦", -- U+021A6
+ ["mapsto"] = "↦", -- U+021A6
+ ["mapstodown"] = "↧", -- U+021A7
+ ["mapstoleft"] = "↤", -- U+021A4
+ ["mapstoup"] = "↥", -- U+021A5
+ ["marker"] = "▮", -- U+025AE
+ ["mcomma"] = "⨩", -- U+02A29
+ ["mcy"] = "м", -- U+0043C
+ ["mdash"] = "—", -- U+02014
+ ["measuredangle"] = "∡", -- U+02221
+ ["mfr"] = "𝔪", -- U+1D52A
+ ["mgr"] = "μ", -- U+003BC
+ ["mho"] = "℧", -- U+02127
+ ["micro"] = "µ", -- U+000B5
+ ["mid"] = "∣", -- U+02223
+ ["midast"] = "*", -- U+0002A
+ ["midcir"] = "⫰", -- U+02AF0
+ ["middot"] = "·", -- U+000B7
+ ["minus"] = "−", -- U+02212
+ ["minusb"] = "⊟", -- U+0229F
+ ["minusd"] = "∸", -- U+02238
+ ["minusdu"] = "⨪", -- U+02A2A
+ ["mlcp"] = "⫛", -- U+02ADB
+ ["mldr"] = "…", -- U+02026
+ ["mnplus"] = "∓", -- U+02213
+ ["models"] = "⊧", -- U+022A7
+ ["mopf"] = "𝕞", -- U+1D55E
+ ["mp"] = "∓", -- U+02213
+ ["mscr"] = "𝓂", -- U+1D4C2
+ ["mstpos"] = "∾", -- U+0223E
+ ["mu"] = "μ", -- U+003BC
+ ["multimap"] = "⊸", -- U+022B8
+ ["mumap"] = "⊸", -- U+022B8
+ ["nGg"] = "⋙̸", -- U+022D9 00338
+ ["nGt"] = "≫⃒", -- U+0226B 020D2
+ ["nGtv"] = "≫̸", -- U+0226B 00338
+ ["nLeftarrow"] = "⇍", -- U+021CD
+ ["nLeftrightarrow"] = "⇎", -- U+021CE
+ ["nLl"] = "⋘̸", -- U+022D8 00338
+ ["nLt"] = "≪⃒", -- U+0226A 020D2
+ ["nLtv"] = "≪̸", -- U+0226A 00338
+ ["nRightarrow"] = "⇏", -- U+021CF
+ ["nVDash"] = "⊯", -- U+022AF
+ ["nVdash"] = "⊮", -- U+022AE
+ ["nabla"] = "∇", -- U+02207
+ ["nacute"] = "ń", -- U+00144
+ ["nang"] = "∠⃒", -- U+02220 020D2
+ ["nap"] = "≉", -- U+02249
+ ["napE"] = "⩰̸", -- U+02A70 00338
+ ["napid"] = "≋̸", -- U+0224B 00338
+ ["napos"] = "ʼn", -- U+00149
+ ["napprox"] = "≉", -- U+02249
+ ["natur"] = "♮", -- U+0266E
+ ["natural"] = "♮", -- U+0266E
+ ["naturals"] = "ℕ", -- U+02115
+ ["nbsp"] = " ", -- U+000A0
+ ["nbump"] = "≎̸", -- U+0224E 00338
+ ["nbumpe"] = "≏̸", -- U+0224F 00338
+ ["ncap"] = "⩃", -- U+02A43
+ ["ncaron"] = "ň", -- U+00148
+ ["ncedil"] = "ņ", -- U+00146
+ ["ncong"] = "≇", -- U+02247
+ ["ncongdot"] = "⩭̸", -- U+02A6D 00338
+ ["ncup"] = "⩂", -- U+02A42
+ ["ncy"] = "н", -- U+0043D
+ ["ndash"] = "–", -- U+02013
+ ["ne"] = "≠", -- U+02260
+ ["neArr"] = "⇗", -- U+021D7
+ ["nearhk"] = "⤤", -- U+02924
+ ["nearr"] = "↗", -- U+02197
+ ["nearrow"] = "↗", -- U+02197
+ ["nedot"] = "≐̸", -- U+02250 00338
+ ["nequiv"] = "≢", -- U+02262
+ ["nesear"] = "⤨", -- U+02928
+ ["nesim"] = "≂̸", -- U+02242 00338
+ ["nexist"] = "∄", -- U+02204
+ ["nexists"] = "∄", -- U+02204
+ ["nfr"] = "𝔫", -- U+1D52B
+ ["ngE"] = "≧̸", -- U+02267 00338
+ ["nge"] = "≱", -- U+02271
+ ["ngeq"] = "≱", -- U+02271
+ ["ngeqq"] = "≧̸", -- U+02267 00338
+ ["ngeqslant"] = "⩾̸", -- U+02A7E 00338
+ ["nges"] = "⩾̸", -- U+02A7E 00338
+ ["ngr"] = "ν", -- U+003BD
+ ["ngsim"] = "≵", -- U+02275
+ ["ngt"] = "≯", -- U+0226F
+ ["ngtr"] = "≯", -- U+0226F
+ ["nhArr"] = "⇎", -- U+021CE
+ ["nharr"] = "↮", -- U+021AE
+ ["nhpar"] = "⫲", -- U+02AF2
+ ["ni"] = "∋", -- U+0220B
+ ["nis"] = "⋼", -- U+022FC
+ ["nisd"] = "⋺", -- U+022FA
+ ["niv"] = "∋", -- U+0220B
+ ["njcy"] = "њ", -- U+0045A
+ ["nlArr"] = "⇍", -- U+021CD
+ ["nlE"] = "≦̸", -- U+02266 00338
+ ["nlarr"] = "↚", -- U+0219A
+ ["nldr"] = "‥", -- U+02025
+ ["nle"] = "≰", -- U+02270
+ ["nleftarrow"] = "↚", -- U+0219A
+ ["nleftrightarrow"] = "↮", -- U+021AE
+ ["nleq"] = "≰", -- U+02270
+ ["nleqq"] = "≦̸", -- U+02266 00338
+ ["nleqslant"] = "⩽̸", -- U+02A7D 00338
+ ["nles"] = "⩽̸", -- U+02A7D 00338
+ ["nless"] = "≮", -- U+0226E
+ ["nlsim"] = "≴", -- U+02274
+ ["nlt"] = "≮", -- U+0226E
+ ["nltri"] = "⋪", -- U+022EA
+ ["nltrie"] = "⋬", -- U+022EC
+ ["nmid"] = "∤", -- U+02224
+ ["nopf"] = "𝕟", -- U+1D55F
+ ["not"] = "¬", -- U+000AC
+ ["notin"] = "∉", -- U+02209
+ ["notinE"] = "⋹̸", -- U+022F9 00338
+ ["notindot"] = "⋵̸", -- U+022F5 00338
+ ["notinva"] = "∉", -- U+02209
+ ["notinvb"] = "⋷", -- U+022F7
+ ["notinvc"] = "⋶", -- U+022F6
+ ["notni"] = "∌", -- U+0220C
+ ["notniva"] = "∌", -- U+0220C
+ ["notnivb"] = "⋾", -- U+022FE
+ ["notnivc"] = "⋽", -- U+022FD
+ ["npar"] = "∦", -- U+02226
+ ["nparallel"] = "∦", -- U+02226
+ ["nparsl"] = "⫽⃥", -- U+02AFD 020E5
+ ["npart"] = "∂̸", -- U+02202 00338
+ ["npolint"] = "⨔", -- U+02A14
+ ["npr"] = "⊀", -- U+02280
+ ["nprcue"] = "⋠", -- U+022E0
+ ["npre"] = "⪯̸", -- U+02AAF 00338
+ ["nprec"] = "⊀", -- U+02280
+ ["npreceq"] = "⪯̸", -- U+02AAF 00338
+ ["nrArr"] = "⇏", -- U+021CF
+ ["nrarr"] = "↛", -- U+0219B
+ ["nrarrc"] = "⤳̸", -- U+02933 00338
+ ["nrarrw"] = "↝̸", -- U+0219D 00338
+ ["nrightarrow"] = "↛", -- U+0219B
+ ["nrtri"] = "⋫", -- U+022EB
+ ["nrtrie"] = "⋭", -- U+022ED
+ ["nsc"] = "⊁", -- U+02281
+ ["nsccue"] = "⋡", -- U+022E1
+ ["nsce"] = "⪰̸", -- U+02AB0 00338
+ ["nscr"] = "𝓃", -- U+1D4C3
+ ["nshortmid"] = "∤", -- U+02224
+ ["nshortparallel"] = "∦", -- U+02226
+ ["nsim"] = "≁", -- U+02241
+ ["nsime"] = "≄", -- U+02244
+ ["nsimeq"] = "≄", -- U+02244
+ ["nsmid"] = "∤", -- U+02224
+ ["nspar"] = "∦", -- U+02226
+ ["nsqsube"] = "⋢", -- U+022E2
+ ["nsqsupe"] = "⋣", -- U+022E3
+ ["nsub"] = "⊄", -- U+02284
+ ["nsubE"] = "⫅̸", -- U+02AC5 00338
+ ["nsube"] = "⊈", -- U+02288
+ ["nsubset"] = "⊂⃒", -- U+02282 020D2
+ ["nsubseteq"] = "⊈", -- U+02288
+ ["nsubseteqq"] = "⫅̸", -- U+02AC5 00338
+ ["nsucc"] = "⊁", -- U+02281
+ ["nsucceq"] = "⪰̸", -- U+02AB0 00338
+ ["nsup"] = "⊅", -- U+02285
+ ["nsupE"] = "⫆̸", -- U+02AC6 00338
+ ["nsupe"] = "⊉", -- U+02289
+ ["nsupset"] = "⊃⃒", -- U+02283 020D2
+ ["nsupseteq"] = "⊉", -- U+02289
+ ["nsupseteqq"] = "⫆̸", -- U+02AC6 00338
+ ["ntgl"] = "≹", -- U+02279
+ ["ntilde"] = "ñ", -- U+000F1
+ ["ntlg"] = "≸", -- U+02278
+ ["ntriangleleft"] = "⋪", -- U+022EA
+ ["ntrianglelefteq"] = "⋬", -- U+022EC
+ ["ntriangleright"] = "⋫", -- U+022EB
+ ["ntrianglerighteq"] = "⋭", -- U+022ED
+ ["nu"] = "ν", -- U+003BD
+ ["num"] = "#", -- U+00023
+ ["numero"] = "№", -- U+02116
+ ["numsp"] = " ", -- U+02007
+ ["nvDash"] = "⊭", -- U+022AD
+ ["nvHarr"] = "⤄", -- U+02904
+ ["nvap"] = "≍⃒", -- U+0224D 020D2
+ ["nvdash"] = "⊬", -- U+022AC
+ ["nvge"] = "≥⃒", -- U+02265 020D2
+ ["nvgt"] = ">⃒", -- U+0003E 020D2
+ ["nvinfin"] = "⧞", -- U+029DE
+ ["nvlArr"] = "⤂", -- U+02902
+ ["nvle"] = "≤⃒", -- U+02264 020D2
+ ["nvlt"] = "&⃒", -- U+00026 020D2
+ ["nvltrie"] = "⊴⃒", -- U+022B4 020D2
+ ["nvrArr"] = "⤃", -- U+02903
+ ["nvrtrie"] = "⊵⃒", -- U+022B5 020D2
+ ["nvsim"] = "∼⃒", -- U+0223C 020D2
+ ["nwArr"] = "⇖", -- U+021D6
+ ["nwarhk"] = "⤣", -- U+02923
+ ["nwarr"] = "↖", -- U+02196
+ ["nwarrow"] = "↖", -- U+02196
+ ["nwnear"] = "⤧", -- U+02927
+ ["oS"] = "Ⓢ", -- U+024C8
+ ["oacgr"] = "ό", -- U+003CC
+ ["oacute"] = "ó", -- U+000F3
+ ["oast"] = "⊛", -- U+0229B
+ ["ocir"] = "⊚", -- U+0229A
+ ["ocirc"] = "ô", -- U+000F4
+ ["ocy"] = "о", -- U+0043E
+ ["odash"] = "⊝", -- U+0229D
+ ["odblac"] = "ő", -- U+00151
+ ["odiv"] = "⨸", -- U+02A38
+ ["odot"] = "⊙", -- U+02299
+ ["odsold"] = "⦼", -- U+029BC
+ ["oelig"] = "œ", -- U+00153
+ ["ofcir"] = "⦿", -- U+029BF
+ ["ofr"] = "𝔬", -- U+1D52C
+ ["ogon"] = "˛", -- U+002DB
+ ["ogr"] = "ο", -- U+003BF
+ ["ograve"] = "ò", -- U+000F2
+ ["ogt"] = "⧁", -- U+029C1
+ ["ohacgr"] = "ώ", -- U+003CE
+ ["ohbar"] = "⦵", -- U+029B5
+ ["ohgr"] = "ω", -- U+003C9
+ ["ohm"] = "Ω", -- U+003A9
+ ["oint"] = "∮", -- U+0222E
+ ["olarr"] = "↺", -- U+021BA
+ ["olcir"] = "⦾", -- U+029BE
+ ["olcross"] = "⦻", -- U+029BB
+ ["oline"] = "‾", -- U+0203E
+ ["olt"] = "⧀", -- U+029C0
+ ["omacr"] = "ō", -- U+0014D
+ ["omega"] = "ω", -- U+003C9
+ ["omicron"] = "ο", -- U+003BF
+ ["omid"] = "⦶", -- U+029B6
+ ["ominus"] = "⊖", -- U+02296
+ ["oopf"] = "𝕠", -- U+1D560
+ ["opar"] = "⦷", -- U+029B7
+ ["operp"] = "⦹", -- U+029B9
+ ["oplus"] = "⊕", -- U+02295
+ ["or"] = "∨", -- U+02228
+ ["orarr"] = "↻", -- U+021BB
+ ["ord"] = "⩝", -- U+02A5D
+ ["order"] = "ℴ", -- U+02134
+ ["orderof"] = "ℴ", -- U+02134
+ ["ordf"] = "ª", -- U+000AA
+ ["ordm"] = "º", -- U+000BA
+ ["origof"] = "⊶", -- U+022B6
+ ["oror"] = "⩖", -- U+02A56
+ ["orslope"] = "⩗", -- U+02A57
+ ["orv"] = "⩛", -- U+02A5B
+ ["oscr"] = "ℴ", -- U+02134
+ ["oslash"] = "ø", -- U+000F8
+ ["osol"] = "⊘", -- U+02298
+ ["otilde"] = "õ", -- U+000F5
+ ["otimes"] = "⊗", -- U+02297
+ ["otimesas"] = "⨶", -- U+02A36
+ ["ouml"] = "ö", -- U+000F6
+ ["ovbar"] = "⌽", -- U+0233D
+ ["par"] = "∥", -- U+02225
+ ["para"] = "¶", -- U+000B6
+ ["parallel"] = "∥", -- U+02225
+ ["parsim"] = "⫳", -- U+02AF3
+ ["parsl"] = "⫽", -- U+02AFD
+ ["part"] = "∂", -- U+02202
+ ["pcy"] = "п", -- U+0043F
+ ["percnt"] = "%", -- U+00025
+ ["period"] = ".", -- U+0002E
+ ["permil"] = "‰", -- U+02030
+ ["perp"] = "⊥", -- U+022A5
+ ["pertenk"] = "‱", -- U+02031
+ ["pfr"] = "𝔭", -- U+1D52D
+ ["pgr"] = "π", -- U+003C0
+ ["phgr"] = "φ", -- U+003C6
+ ["phi"] = "φ", -- U+003C6
+ ["phiv"] = "ϕ", -- U+003D5
+ ["phmmat"] = "ℳ", -- U+02133
+ ["phone"] = "☎", -- U+0260E
+ ["pi"] = "π", -- U+003C0
+ ["pitchfork"] = "⋔", -- U+022D4
+ ["piv"] = "ϖ", -- U+003D6
+ ["planck"] = "ℏ", -- U+0210F
+ ["planckh"] = "ℎ", -- U+0210E
+ ["plankv"] = "ℏ", -- U+0210F
+ ["plus"] = "+", -- U+0002B
+ ["plusacir"] = "⨣", -- U+02A23
+ ["plusb"] = "⊞", -- U+0229E
+ ["pluscir"] = "⨢", -- U+02A22
+ ["plusdo"] = "∔", -- U+02214
+ ["plusdu"] = "⨥", -- U+02A25
+ ["pluse"] = "⩲", -- U+02A72
+ ["plusmn"] = "±", -- U+000B1
+ ["plussim"] = "⨦", -- U+02A26
+ ["plustwo"] = "⨧", -- U+02A27
+ ["pm"] = "±", -- U+000B1
+ ["pointint"] = "⨕", -- U+02A15
+ ["popf"] = "𝕡", -- U+1D561
+ ["pound"] = "£", -- U+000A3
+ ["pr"] = "≺", -- U+0227A
+ ["prE"] = "⪳", -- U+02AB3
+ ["prap"] = "⪷", -- U+02AB7
+ ["prcue"] = "≼", -- U+0227C
+ ["pre"] = "⪯", -- U+02AAF
+ ["prec"] = "≺", -- U+0227A
+ ["precapprox"] = "⪷", -- U+02AB7
+ ["preccurlyeq"] = "≼", -- U+0227C
+ ["preceq"] = "⪯", -- U+02AAF
+ ["precnapprox"] = "⪹", -- U+02AB9
+ ["precneqq"] = "⪵", -- U+02AB5
+ ["precnsim"] = "⋨", -- U+022E8
+ ["precsim"] = "≾", -- U+0227E
+ ["prime"] = "′", -- U+02032
+ ["primes"] = "ℙ", -- U+02119
+ ["prnE"] = "⪵", -- U+02AB5
+ ["prnap"] = "⪹", -- U+02AB9
+ ["prnsim"] = "⋨", -- U+022E8
+ ["prod"] = "∏", -- U+0220F
+ ["profalar"] = "⌮", -- U+0232E
+ ["profline"] = "⌒", -- U+02312
+ ["profsurf"] = "⌓", -- U+02313
+ ["prop"] = "∝", -- U+0221D
+ ["propto"] = "∝", -- U+0221D
+ ["prsim"] = "≾", -- U+0227E
+ ["prurel"] = "⊰", -- U+022B0
+ ["pscr"] = "𝓅", -- U+1D4C5
+ ["psgr"] = "ψ", -- U+003C8
+ ["psi"] = "ψ", -- U+003C8
+ ["puncsp"] = " ", -- U+02008
+ ["qfr"] = "𝔮", -- U+1D52E
+ ["qint"] = "⨌", -- U+02A0C
+ ["qopf"] = "𝕢", -- U+1D562
+ ["qprime"] = "⁗", -- U+02057
+ ["qscr"] = "𝓆", -- U+1D4C6
+ ["quaternions"] = "ℍ", -- U+0210D
+ ["quatint"] = "⨖", -- U+02A16
+ ["quest"] = "?", -- U+0003F
+ ["questeq"] = "≟", -- U+0225F
+ ["quot"] = "\"", -- U+00022
+ ["rAarr"] = "⇛", -- U+021DB
+ ["rArr"] = "⇒", -- U+021D2
+ ["rAtail"] = "⤜", -- U+0291C
+ ["rBarr"] = "⤏", -- U+0290F
+ ["rHar"] = "⥤", -- U+02964
+ ["race"] = "∽̱", -- U+0223D 00331
+ ["racute"] = "ŕ", -- U+00155
+ ["radic"] = "√", -- U+0221A
+ ["raemptyv"] = "⦳", -- U+029B3
+ ["rang"] = "⟩", -- U+027E9
+ ["rangd"] = "⦒", -- U+02992
+ ["range"] = "⦥", -- U+029A5
+ ["rangle"] = "⟩", -- U+027E9
+ ["raquo"] = "»", -- U+000BB
+ ["rarr"] = "→", -- U+02192
+ ["rarrap"] = "⥵", -- U+02975
+ ["rarrb"] = "⇥", -- U+021E5
+ ["rarrbfs"] = "⤠", -- U+02920
+ ["rarrc"] = "⤳", -- U+02933
+ ["rarrfs"] = "⤞", -- U+0291E
+ ["rarrhk"] = "↪", -- U+021AA
+ ["rarrlp"] = "↬", -- U+021AC
+ ["rarrpl"] = "⥅", -- U+02945
+ ["rarrsim"] = "⥴", -- U+02974
+ ["rarrtl"] = "↣", -- U+021A3
+ ["rarrw"] = "↝", -- U+0219D
+ ["ratail"] = "⤚", -- U+0291A
+ ["ratio"] = "∶", -- U+02236
+ ["rationals"] = "ℚ", -- U+0211A
+ ["rbarr"] = "⤍", -- U+0290D
+ ["rbbrk"] = "❳", -- U+02773
+ ["rbrace"] = "}", -- U+0007D
+ ["rbrack"] = "]", -- U+0005D
+ ["rbrke"] = "⦌", -- U+0298C
+ ["rbrksld"] = "⦎", -- U+0298E
+ ["rbrkslu"] = "⦐", -- U+02990
+ ["rcaron"] = "ř", -- U+00159
+ ["rcedil"] = "ŗ", -- U+00157
+ ["rceil"] = "⌉", -- U+02309
+ ["rcub"] = "}", -- U+0007D
+ ["rcy"] = "р", -- U+00440
+ ["rdca"] = "⤷", -- U+02937
+ ["rdldhar"] = "⥩", -- U+02969
+ ["rdquo"] = "”", -- U+0201D
+ ["rdquor"] = "”", -- U+0201D
+ ["rdsh"] = "↳", -- U+021B3
+ ["real"] = "ℜ", -- U+0211C
+ ["realine"] = "ℛ", -- U+0211B
+ ["realpart"] = "ℜ", -- U+0211C
+ ["reals"] = "ℝ", -- U+0211D
+ ["rect"] = "▭", -- U+025AD
+ ["reg"] = "®", -- U+000AE
+ ["rfisht"] = "⥽", -- U+0297D
+ ["rfloor"] = "⌋", -- U+0230B
+ ["rfr"] = "𝔯", -- U+1D52F
+ ["rgr"] = "ρ", -- U+003C1
+ ["rhard"] = "⇁", -- U+021C1
+ ["rharu"] = "⇀", -- U+021C0
+ ["rharul"] = "⥬", -- U+0296C
+ ["rho"] = "ρ", -- U+003C1
+ ["rhov"] = "ϱ", -- U+003F1
+ ["rightarrow"] = "→", -- U+02192
+ ["rightarrowtail"] = "↣", -- U+021A3
+ ["rightharpoondown"] = "⇁", -- U+021C1
+ ["rightharpoonup"] = "⇀", -- U+021C0
+ ["rightleftarrows"] = "⇄", -- U+021C4
+ ["rightleftharpoons"] = "⇌", -- U+021CC
+ ["rightrightarrows"] = "⇉", -- U+021C9
+ ["rightsquigarrow"] = "↝", -- U+0219D
+ ["rightthreetimes"] = "⋌", -- U+022CC
+ ["ring"] = "˚", -- U+002DA
+ ["risingdotseq"] = "≓", -- U+02253
+ ["rlarr"] = "⇄", -- U+021C4
+ ["rlhar"] = "⇌", -- U+021CC
+ ["rlm"] = "‏", -- U+0200F
+ ["rmoust"] = "⎱", -- U+023B1
+ ["rmoustache"] = "⎱", -- U+023B1
+ ["rnmid"] = "⫮", -- U+02AEE
+ ["roang"] = "⟭", -- U+027ED
+ ["roarr"] = "⇾", -- U+021FE
+ ["robrk"] = "⟧", -- U+027E7
+ ["ropar"] = "⦆", -- U+02986
+ ["ropf"] = "𝕣", -- U+1D563
+ ["roplus"] = "⨮", -- U+02A2E
+ ["rotimes"] = "⨵", -- U+02A35
+ ["rpar"] = ")", -- U+00029
+ ["rpargt"] = "⦔", -- U+02994
+ ["rppolint"] = "⨒", -- U+02A12
+ ["rrarr"] = "⇉", -- U+021C9
+ ["rsaquo"] = "›", -- U+0203A
+ ["rscr"] = "𝓇", -- U+1D4C7
+ ["rsh"] = "↱", -- U+021B1
+ ["rsqb"] = "]", -- U+0005D
+ ["rsquo"] = "’", -- U+02019
+ ["rsquor"] = "’", -- U+02019
+ ["rthree"] = "⋌", -- U+022CC
+ ["rtimes"] = "⋊", -- U+022CA
+ ["rtri"] = "▹", -- U+025B9
+ ["rtrie"] = "⊵", -- U+022B5
+ ["rtrif"] = "▸", -- U+025B8
+ ["rtriltri"] = "⧎", -- U+029CE
+ ["ruluhar"] = "⥨", -- U+02968
+ ["rx"] = "℞", -- U+0211E
+ ["sacute"] = "ś", -- U+0015B
+ ["sbquo"] = "‚", -- U+0201A
+ ["sc"] = "≻", -- U+0227B
+ ["scE"] = "⪴", -- U+02AB4
+ ["scap"] = "⪸", -- U+02AB8
+ ["scaron"] = "š", -- U+00161
+ ["sccue"] = "≽", -- U+0227D
+ ["sce"] = "⪰", -- U+02AB0
+ ["scedil"] = "ş", -- U+0015F
+ ["scirc"] = "ŝ", -- U+0015D
+ ["scnE"] = "⪶", -- U+02AB6
+ ["scnap"] = "⪺", -- U+02ABA
+ ["scnsim"] = "⋩", -- U+022E9
+ ["scpolint"] = "⨓", -- U+02A13
+ ["scsim"] = "≿", -- U+0227F
+ ["scy"] = "с", -- U+00441
+ ["sdot"] = "⋅", -- U+022C5
+ ["sdotb"] = "⊡", -- U+022A1
+ ["sdote"] = "⩦", -- U+02A66
+ ["seArr"] = "⇘", -- U+021D8
+ ["searhk"] = "⤥", -- U+02925
+ ["searr"] = "↘", -- U+02198
+ ["searrow"] = "↘", -- U+02198
+ ["sect"] = "§", -- U+000A7
+ ["semi"] = ";", -- U+0003B
+ ["seswar"] = "⤩", -- U+02929
+ ["setminus"] = "∖", -- U+02216
+ ["setmn"] = "∖", -- U+02216
+ ["sext"] = "✶", -- U+02736
+ ["sfgr"] = "ς", -- U+003C2
+ ["sfr"] = "𝔰", -- U+1D530
+ ["sfrown"] = "⌢", -- U+02322
+ ["sgr"] = "σ", -- U+003C3
+ ["sharp"] = "♯", -- U+0266F
+ ["shchcy"] = "щ", -- U+00449
+ ["shcy"] = "ш", -- U+00448
+ ["shortmid"] = "∣", -- U+02223
+ ["shortparallel"] = "∥", -- U+02225
+ ["shy"] = "­", -- U+000AD
+ ["sigma"] = "σ", -- U+003C3
+ ["sigmaf"] = "ς", -- U+003C2
+ ["sigmav"] = "ς", -- U+003C2
+ ["sim"] = "∼", -- U+0223C
+ ["simdot"] = "⩪", -- U+02A6A
+ ["sime"] = "≃", -- U+02243
+ ["simeq"] = "≃", -- U+02243
+ ["simg"] = "⪞", -- U+02A9E
+ ["simgE"] = "⪠", -- U+02AA0
+ ["siml"] = "⪝", -- U+02A9D
+ ["simlE"] = "⪟", -- U+02A9F
+ ["simne"] = "≆", -- U+02246
+ ["simplus"] = "⨤", -- U+02A24
+ ["simrarr"] = "⥲", -- U+02972
+ ["slarr"] = "←", -- U+02190
+ ["smallsetminus"] = "∖", -- U+02216
+ ["smashp"] = "⨳", -- U+02A33
+ ["smeparsl"] = "⧤", -- U+029E4
+ ["smid"] = "∣", -- U+02223
+ ["smile"] = "⌣", -- U+02323
+ ["smt"] = "⪪", -- U+02AAA
+ ["smte"] = "⪬", -- U+02AAC
+ ["smtes"] = "⪬︀", -- U+02AAC 0FE00
+ ["softcy"] = "ь", -- U+0044C
+ ["sol"] = "/", -- U+0002F
+ ["solb"] = "⧄", -- U+029C4
+ ["solbar"] = "⌿", -- U+0233F
+ ["sopf"] = "𝕤", -- U+1D564
+ ["spades"] = "♠", -- U+02660
+ ["spadesuit"] = "♠", -- U+02660
+ ["spar"] = "∥", -- U+02225
+ ["sqcap"] = "⊓", -- U+02293
+ ["sqcaps"] = "⊓︀", -- U+02293 0FE00
+ ["sqcup"] = "⊔", -- U+02294
+ ["sqcups"] = "⊔︀", -- U+02294 0FE00
+ ["sqsub"] = "⊏", -- U+0228F
+ ["sqsube"] = "⊑", -- U+02291
+ ["sqsubset"] = "⊏", -- U+0228F
+ ["sqsubseteq"] = "⊑", -- U+02291
+ ["sqsup"] = "⊐", -- U+02290
+ ["sqsupe"] = "⊒", -- U+02292
+ ["sqsupset"] = "⊐", -- U+02290
+ ["sqsupseteq"] = "⊒", -- U+02292
+ ["squ"] = "□", -- U+025A1
+ ["square"] = "□", -- U+025A1
+ ["squarf"] = "▪", -- U+025AA
+ ["squf"] = "▪", -- U+025AA
+ ["srarr"] = "→", -- U+02192
+ ["sscr"] = "𝓈", -- U+1D4C8
+ ["ssetmn"] = "∖", -- U+02216
+ ["ssmile"] = "⌣", -- U+02323
+ ["sstarf"] = "⋆", -- U+022C6
+ ["star"] = "☆", -- U+02606
+ ["starf"] = "★", -- U+02605
+ ["straightepsilon"] = "ϵ", -- U+003F5
+ ["straightphi"] = "ϕ", -- U+003D5
+ ["strns"] = "¯", -- U+000AF
+ ["sub"] = "⊂", -- U+02282
+ ["subE"] = "⫅", -- U+02AC5
+ ["subdot"] = "⪽", -- U+02ABD
+ ["sube"] = "⊆", -- U+02286
+ ["subedot"] = "⫃", -- U+02AC3
+ ["submult"] = "⫁", -- U+02AC1
+ ["subnE"] = "⫋", -- U+02ACB
+ ["subne"] = "⊊", -- U+0228A
+ ["subplus"] = "⪿", -- U+02ABF
+ ["subrarr"] = "⥹", -- U+02979
+ ["subset"] = "⊂", -- U+02282
+ ["subseteq"] = "⊆", -- U+02286
+ ["subseteqq"] = "⫅", -- U+02AC5
+ ["subsetneq"] = "⊊", -- U+0228A
+ ["subsetneqq"] = "⫋", -- U+02ACB
+ ["subsim"] = "⫇", -- U+02AC7
+ ["subsub"] = "⫕", -- U+02AD5
+ ["subsup"] = "⫓", -- U+02AD3
+ ["succ"] = "≻", -- U+0227B
+ ["succapprox"] = "⪸", -- U+02AB8
+ ["succcurlyeq"] = "≽", -- U+0227D
+ ["succeq"] = "⪰", -- U+02AB0
+ ["succnapprox"] = "⪺", -- U+02ABA
+ ["succneqq"] = "⪶", -- U+02AB6
+ ["succnsim"] = "⋩", -- U+022E9
+ ["succsim"] = "≿", -- U+0227F
+ ["sum"] = "∑", -- U+02211
+ ["sung"] = "♪", -- U+0266A
+ ["sup"] = "⊃", -- U+02283
+ ["sup1"] = "¹", -- U+000B9
+ ["sup2"] = "²", -- U+000B2
+ ["sup3"] = "³", -- U+000B3
+ ["supE"] = "⫆", -- U+02AC6
+ ["supdot"] = "⪾", -- U+02ABE
+ ["supdsub"] = "⫘", -- U+02AD8
+ ["supe"] = "⊇", -- U+02287
+ ["supedot"] = "⫄", -- U+02AC4
+ ["suphsol"] = "⟉", -- U+027C9
+ ["suphsub"] = "⫗", -- U+02AD7
+ ["suplarr"] = "⥻", -- U+0297B
+ ["supmult"] = "⫂", -- U+02AC2
+ ["supnE"] = "⫌", -- U+02ACC
+ ["supne"] = "⊋", -- U+0228B
+ ["supplus"] = "⫀", -- U+02AC0
+ ["supset"] = "⊃", -- U+02283
+ ["supseteq"] = "⊇", -- U+02287
+ ["supseteqq"] = "⫆", -- U+02AC6
+ ["supsetneq"] = "⊋", -- U+0228B
+ ["supsetneqq"] = "⫌", -- U+02ACC
+ ["supsim"] = "⫈", -- U+02AC8
+ ["supsub"] = "⫔", -- U+02AD4
+ ["supsup"] = "⫖", -- U+02AD6
+ ["swArr"] = "⇙", -- U+021D9
+ ["swarhk"] = "⤦", -- U+02926
+ ["swarr"] = "↙", -- U+02199
+ ["swarrow"] = "↙", -- U+02199
+ ["swnwar"] = "⤪", -- U+0292A
+ ["szlig"] = "ß", -- U+000DF
+ ["target"] = "⌖", -- U+02316
+ ["tau"] = "τ", -- U+003C4
+ ["tbrk"] = "⎴", -- U+023B4
+ ["tcaron"] = "ť", -- U+00165
+ ["tcedil"] = "ţ", -- U+00163
+ ["tcy"] = "т", -- U+00442
+ ["tdot"] = "⃛", -- U+020DB
+ ["telrec"] = "⌕", -- U+02315
+ ["tfr"] = "𝔱", -- U+1D531
+ ["tgr"] = "τ", -- U+003C4
+ ["there4"] = "∴", -- U+02234
+ ["therefore"] = "∴", -- U+02234
+ ["theta"] = "θ", -- U+003B8
+ ["thetasym"] = "ϑ", -- U+003D1
+ ["thetav"] = "ϑ", -- U+003D1
+ ["thgr"] = "θ", -- U+003B8
+ ["thickapprox"] = "≈", -- U+02248
+ ["thicksim"] = "∼", -- U+0223C
+ ["thinsp"] = " ", -- U+02009
+ ["thkap"] = "≈", -- U+02248
+ ["thksim"] = "∼", -- U+0223C
+ ["thorn"] = "þ", -- U+000FE
+ ["tilde"] = "˜", -- U+002DC
+ ["times"] = "×", -- U+000D7
+ ["timesb"] = "⊠", -- U+022A0
+ ["timesbar"] = "⨱", -- U+02A31
+ ["timesd"] = "⨰", -- U+02A30
+ ["tint"] = "∭", -- U+0222D
+ ["toea"] = "⤨", -- U+02928
+ ["top"] = "⊤", -- U+022A4
+ ["topbot"] = "⌶", -- U+02336
+ ["topcir"] = "⫱", -- U+02AF1
+ ["topf"] = "𝕥", -- U+1D565
+ ["topfork"] = "⫚", -- U+02ADA
+ ["tosa"] = "⤩", -- U+02929
+ ["tprime"] = "‴", -- U+02034
+ ["trade"] = "™", -- U+02122
+ ["triangle"] = "▵", -- U+025B5
+ ["triangledown"] = "▿", -- U+025BF
+ ["triangleleft"] = "◃", -- U+025C3
+ ["trianglelefteq"] = "⊴", -- U+022B4
+ ["triangleq"] = "≜", -- U+0225C
+ ["triangleright"] = "▹", -- U+025B9
+ ["trianglerighteq"] = "⊵", -- U+022B5
+ ["tridot"] = "◬", -- U+025EC
+ ["trie"] = "≜", -- U+0225C
+ ["triminus"] = "⨺", -- U+02A3A
+ ["triplus"] = "⨹", -- U+02A39
+ ["trisb"] = "⧍", -- U+029CD
+ ["tritime"] = "⨻", -- U+02A3B
+ ["trpezium"] = "⏢", -- U+023E2
+ ["tscr"] = "𝓉", -- U+1D4C9
+ ["tscy"] = "ц", -- U+00446
+ ["tshcy"] = "ћ", -- U+0045B
+ ["tstrok"] = "ŧ", -- U+00167
+ ["twixt"] = "≬", -- U+0226C
+ ["twoheadleftarrow"] = "↞", -- U+0219E
+ ["twoheadrightarrow"] = "↠", -- U+021A0
+ ["uArr"] = "⇑", -- U+021D1
+ ["uHar"] = "⥣", -- U+02963
+ ["uacgr"] = "ύ", -- U+003CD
+ ["uacute"] = "ú", -- U+000FA
+ ["uarr"] = "↑", -- U+02191
+ ["ubrcy"] = "ў", -- U+0045E
+ ["ubreve"] = "ŭ", -- U+0016D
+ ["ucirc"] = "û", -- U+000FB
+ ["ucy"] = "у", -- U+00443
+ ["udarr"] = "⇅", -- U+021C5
+ ["udblac"] = "ű", -- U+00171
+ ["udhar"] = "⥮", -- U+0296E
+ ["udiagr"] = "ΰ", -- U+003B0
+ ["udigr"] = "ϋ", -- U+003CB
+ ["ufisht"] = "⥾", -- U+0297E
+ ["ufr"] = "𝔲", -- U+1D532
+ ["ugr"] = "υ", -- U+003C5
+ ["ugrave"] = "ù", -- U+000F9
+ ["uharl"] = "↿", -- U+021BF
+ ["uharr"] = "↾", -- U+021BE
+ ["uhblk"] = "▀", -- U+02580
+ ["ulcorn"] = "⌜", -- U+0231C
+ ["ulcorner"] = "⌜", -- U+0231C
+ ["ulcrop"] = "⌏", -- U+0230F
+ ["ultri"] = "◸", -- U+025F8
+ ["umacr"] = "ū", -- U+0016B
+ ["uml"] = "¨", -- U+000A8
+ ["uogon"] = "ų", -- U+00173
+ ["uopf"] = "𝕦", -- U+1D566
+ ["uparrow"] = "↑", -- U+02191
+ ["updownarrow"] = "↕", -- U+02195
+ ["upharpoonleft"] = "↿", -- U+021BF
+ ["upharpoonright"] = "↾", -- U+021BE
+ ["uplus"] = "⊎", -- U+0228E
+ ["upsi"] = "υ", -- U+003C5
+ ["upsih"] = "ϒ", -- U+003D2
+ ["upsilon"] = "υ", -- U+003C5
+ ["upuparrows"] = "⇈", -- U+021C8
+ ["urcorn"] = "⌝", -- U+0231D
+ ["urcorner"] = "⌝", -- U+0231D
+ ["urcrop"] = "⌎", -- U+0230E
+ ["uring"] = "ů", -- U+0016F
+ ["urtri"] = "◹", -- U+025F9
+ ["uscr"] = "𝓊", -- U+1D4CA
+ ["utdot"] = "⋰", -- U+022F0
+ ["utilde"] = "ũ", -- U+00169
+ ["utri"] = "▵", -- U+025B5
+ ["utrif"] = "▴", -- U+025B4
+ ["uuarr"] = "⇈", -- U+021C8
+ ["uuml"] = "ü", -- U+000FC
+ ["uwangle"] = "⦧", -- U+029A7
+ ["vArr"] = "⇕", -- U+021D5
+ ["vBar"] = "⫨", -- U+02AE8
+ ["vBarv"] = "⫩", -- U+02AE9
+ ["vDash"] = "⊨", -- U+022A8
+ ["vangrt"] = "⦜", -- U+0299C
+ ["varepsilon"] = "ϵ", -- U+003F5
+ ["varkappa"] = "ϰ", -- U+003F0
+ ["varnothing"] = "∅", -- U+02205
+ ["varphi"] = "ϕ", -- U+003D5
+ ["varpi"] = "ϖ", -- U+003D6
+ ["varpropto"] = "∝", -- U+0221D
+ ["varr"] = "↕", -- U+02195
+ ["varrho"] = "ϱ", -- U+003F1
+ ["varsigma"] = "ς", -- U+003C2
+ ["varsubsetneq"] = "⊊︀", -- U+0228A 0FE00
+ ["varsubsetneqq"] = "⫋︀", -- U+02ACB 0FE00
+ ["varsupsetneq"] = "⊋︀", -- U+0228B 0FE00
+ ["varsupsetneqq"] = "⫌︀", -- U+02ACC 0FE00
+ ["vartheta"] = "ϑ", -- U+003D1
+ ["vartriangleleft"] = "⊲", -- U+022B2
+ ["vartriangleright"] = "⊳", -- U+022B3
+ ["vcy"] = "в", -- U+00432
+ ["vdash"] = "⊢", -- U+022A2
+ ["vee"] = "∨", -- U+02228
+ ["veebar"] = "⊻", -- U+022BB
+ ["veeeq"] = "≚", -- U+0225A
+ ["vellip"] = "⋮", -- U+022EE
+ ["verbar"] = "|", -- U+0007C
+ ["vert"] = "|", -- U+0007C
+ ["vfr"] = "𝔳", -- U+1D533
+ ["vltri"] = "⊲", -- U+022B2
+ ["vnsub"] = "⊂⃒", -- U+02282 020D2
+ ["vnsup"] = "⊃⃒", -- U+02283 020D2
+ ["vopf"] = "𝕧", -- U+1D567
+ ["vprop"] = "∝", -- U+0221D
+ ["vrtri"] = "⊳", -- U+022B3
+ ["vscr"] = "𝓋", -- U+1D4CB
+ ["vsubnE"] = "⫋︀", -- U+02ACB 0FE00
+ ["vsubne"] = "⊊︀", -- U+0228A 0FE00
+ ["vsupnE"] = "⫌︀", -- U+02ACC 0FE00
+ ["vsupne"] = "⊋︀", -- U+0228B 0FE00
+ ["vzigzag"] = "⦚", -- U+0299A
+ ["wcirc"] = "ŵ", -- U+00175
+ ["wedbar"] = "⩟", -- U+02A5F
+ ["wedge"] = "∧", -- U+02227
+ ["wedgeq"] = "≙", -- U+02259
+ ["weierp"] = "℘", -- U+02118
+ ["wfr"] = "𝔴", -- U+1D534
+ ["wopf"] = "𝕨", -- U+1D568
+ ["wp"] = "℘", -- U+02118
+ ["wr"] = "≀", -- U+02240
+ ["wreath"] = "≀", -- U+02240
+ ["wscr"] = "𝓌", -- U+1D4CC
+ ["xcap"] = "⋂", -- U+022C2
+ ["xcirc"] = "◯", -- U+025EF
+ ["xcup"] = "⋃", -- U+022C3
+ ["xdtri"] = "▽", -- U+025BD
+ ["xfr"] = "𝔵", -- U+1D535
+ ["xgr"] = "ξ", -- U+003BE
+ ["xhArr"] = "⟺", -- U+027FA
+ ["xharr"] = "⟷", -- U+027F7
+ ["xi"] = "ξ", -- U+003BE
+ ["xlArr"] = "⟸", -- U+027F8
+ ["xlarr"] = "⟵", -- U+027F5
+ ["xmap"] = "⟼", -- U+027FC
+ ["xnis"] = "⋻", -- U+022FB
+ ["xodot"] = "⨀", -- U+02A00
+ ["xopf"] = "𝕩", -- U+1D569
+ ["xoplus"] = "⨁", -- U+02A01
+ ["xotime"] = "⨂", -- U+02A02
+ ["xrArr"] = "⟹", -- U+027F9
+ ["xrarr"] = "⟶", -- U+027F6
+ ["xscr"] = "𝓍", -- U+1D4CD
+ ["xsqcup"] = "⨆", -- U+02A06
+ ["xuplus"] = "⨄", -- U+02A04
+ ["xutri"] = "△", -- U+025B3
+ ["xvee"] = "⋁", -- U+022C1
+ ["xwedge"] = "⋀", -- U+022C0
+ ["yacute"] = "ý", -- U+000FD
+ ["yacy"] = "я", -- U+0044F
+ ["ycirc"] = "ŷ", -- U+00177
+ ["ycy"] = "ы", -- U+0044B
+ ["yen"] = "¥", -- U+000A5
+ ["yfr"] = "𝔶", -- U+1D536
+ ["yicy"] = "ї", -- U+00457
+ ["yopf"] = "𝕪", -- U+1D56A
+ ["yscr"] = "𝓎", -- U+1D4CE
+ ["yucy"] = "ю", -- U+0044E
+ ["yuml"] = "ÿ", -- U+000FF
+ ["zacute"] = "ź", -- U+0017A
+ ["zcaron"] = "ž", -- U+0017E
+ ["zcy"] = "з", -- U+00437
+ ["zdot"] = "ż", -- U+0017C
+ ["zeetrf"] = "ℨ", -- U+02128
+ ["zeta"] = "ζ", -- U+003B6
+ ["zfr"] = "𝔷", -- U+1D537
+ ["zgr"] = "ζ", -- U+003B6
+ ["zhcy"] = "ж", -- U+00436
+ ["zigrarr"] = "⇝", -- U+021DD
+ ["zopf"] = "𝕫", -- U+1D56B
+ ["zscr"] = "𝓏", -- U+1D4CF
+ ["zwj"] = "‍", -- U+0200D
+ ["zwnj"] = "‌", -- U+0200C
+}
+
+characters = characters or { }
+characters.entities = entities
+
+entities.plusminus = "±" -- 0x000B1
+entities.minusplus = "∓" -- 0x02213
+entities.cdots = utf.char(0x02026) -- U+02026
diff --git a/tex/context/base/char-ini.lua b/tex/context/base/char-ini.lua
index c5e4da8c4..b75f5eda7 100644
--- a/tex/context/base/char-ini.lua
+++ b/tex/context/base/char-ini.lua
@@ -1,1158 +1,1158 @@
-if not modules then modules = { } end modules ['char-ini'] = {
- 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"
-}
-
--- todo: make two files, one for format generation, one for format use
-
--- we can remove the tag range starting at 0xE0000 (special applications)
-
-local utfchar, utfbyte, utfvalues, ustring = utf.char, utf.byte, utf.values, utf.ustring
-local concat, unpack, tohash = table.concat, table.unpack, table.tohash
-local next, tonumber, type, rawget, rawset = next, tonumber, type, rawget, rawset
-local format, lower, gsub, match, gmatch = string.format, string.lower, string.gsub, string.match, string.match, string.gmatch
-local P, R, Cs, lpegmatch, patterns = lpeg.P, lpeg.R, lpeg.Cs, lpeg.match, lpeg.patterns
-
-local utf8byte = patterns.utf8byte
-local utf8char = patterns.utf8char
-
-local allocate = utilities.storage.allocate
-local mark = utilities.storage.mark
-
-local setmetatableindex = table.setmetatableindex
-
-local trace_defining = false trackers.register("characters.defining", function(v) characters_defining = v end)
-
-local report_defining = logs.reporter("characters")
-
---[[ldx--
-<p>This module implements some methods and creates additional datastructured
-from the big character table that we use for all kind of purposes:
-<type>char-def.lua</type>.</p>
-
-<p>We assume that at this point <type>characters.data</type> is already
-loaded!</p>
---ldx]]--
-
-characters = characters or { }
-local characters = characters
-local data = characters.data
-
-if data then
- mark(data) -- why does this fail
-else
- report_defining("fatal error: 'char-def.lua' is not loaded")
- os.exit()
-end
-
---[[ldx--
-<p>This converts a string (if given) into a number.</p>
---ldx]]--
-
-local pattern = (P("0x") + P("U+")) * ((R("09","AF")^1 * P(-1)) / function(s) return tonumber(s,16) end)
-
-patterns.chartonumber = pattern
-
-local function chartonumber(k)
- if type(k) == "string" then
- local u = lpegmatch(pattern,k)
- if u then
- return utfbyte(u)
- else
- return utfbyte(k) or 0
- end
- else
- return k or 0
- end
-end
-
-local function charfromnumber(k)
- if type(k) == "number" then
- return utfchar(k) or ""
- else
- local u = lpegmatch(pattern,k)
- if u then
- return utfchar(u)
- else
- return k
- end
- end
-end
-
---~ print(chartonumber(97), chartonumber("a"), chartonumber("0x61"), chartonumber("U+61"))
-
-characters.tonumber = chartonumber
-characters.fromnumber = charfromnumber
-
-local private = {
- description = "PRIVATE SLOT",
-}
-
-local ranges = allocate()
-characters.ranges = ranges
-
-setmetatableindex(data, function(t,k)
- local tk = type(k)
- if tk == "string" then
- k = lpegmatch(pattern,k) or utfbyte(k)
- if k then
- local v = rawget(t,k)
- if v then
- return v
- else
- tk = "number" -- fall through to range
- end
- else
- return private
- end
- end
- if tk == "number" and k < 0xF0000 then
- for r=1,#ranges do
- local rr = ranges[r]
- if k >= rr.first and k <= rr.last then
- local extender = rr.extender
- if extender then
- local v = extender(k,v)
- t[k] = v
- return v
- end
- end
- end
- end
- return private -- handy for when we loop over characters in fonts and check for a property
-end)
-
-local blocks = allocate {
- ["aegeannumbers"] = { first = 0x10100, last = 0x1013F, description = "Aegean Numbers" },
- ["alchemicalsymbols"] = { first = 0x1F700, last = 0x1F77F, description = "Alchemical Symbols" },
- ["alphabeticpresentationforms"] = { first = 0x0FB00, last = 0x0FB4F, otf="latn", description = "Alphabetic Presentation Forms" },
- ["ancientgreekmusicalnotation"] = { first = 0x1D200, last = 0x1D24F, otf="grek", description = "Ancient Greek Musical Notation" },
- ["ancientgreeknumbers"] = { first = 0x10140, last = 0x1018F, otf="grek", description = "Ancient Greek Numbers" },
- ["ancientsymbols"] = { first = 0x10190, last = 0x101CF, otf="grek", description = "Ancient Symbols" },
- ["arabic"] = { first = 0x00600, last = 0x006FF, otf="arab", description = "Arabic" },
- ["arabicextendeda"] = { first = 0x008A0, last = 0x008FF, description = "Arabic Extended-A" },
- ["arabicmathematicalalphabeticsymbols"] = { first = 0x1EE00, last = 0x1EEFF, description = "Arabic Mathematical Alphabetic Symbols" },
- ["arabicpresentationformsa"] = { first = 0x0FB50, last = 0x0FDFF, otf="arab", description = "Arabic Presentation Forms-A" },
- ["arabicpresentationformsb"] = { first = 0x0FE70, last = 0x0FEFF, otf="arab", description = "Arabic Presentation Forms-B" },
- ["arabicsupplement"] = { first = 0x00750, last = 0x0077F, otf="arab", description = "Arabic Supplement" },
- ["armenian"] = { first = 0x00530, last = 0x0058F, otf="armn", description = "Armenian" },
- ["arrows"] = { first = 0x02190, last = 0x021FF, description = "Arrows" },
- ["avestan"] = { first = 0x10B00, last = 0x10B3F, description = "Avestan" },
- ["balinese"] = { first = 0x01B00, last = 0x01B7F, otf="bali", description = "Balinese" },
- ["bamum"] = { first = 0x0A6A0, last = 0x0A6FF, description = "Bamum" },
- ["bamumsupplement"] = { first = 0x16800, last = 0x16A3F, description = "Bamum Supplement" },
- ["basiclatin"] = { first = 0x00000, last = 0x0007F, otf="latn", description = "Basic Latin" },
- ["batak"] = { first = 0x01BC0, last = 0x01BFF, description = "Batak" },
- ["bengali"] = { first = 0x00980, last = 0x009FF, otf="beng", description = "Bengali" },
- ["blockelements"] = { first = 0x02580, last = 0x0259F, otf="bopo", description = "Block Elements" },
- ["bopomofo"] = { first = 0x03100, last = 0x0312F, otf="bopo", description = "Bopomofo" },
- ["bopomofoextended"] = { first = 0x031A0, last = 0x031BF, otf="bopo", description = "Bopomofo Extended" },
- ["boxdrawing"] = { first = 0x02500, last = 0x0257F, description = "Box Drawing" },
- ["brahmi"] = { first = 0x11000, last = 0x1107F, description = "Brahmi" },
- ["braillepatterns"] = { first = 0x02800, last = 0x028FF, otf="brai", description = "Braille Patterns" },
- ["buginese"] = { first = 0x01A00, last = 0x01A1F, otf="bugi", description = "Buginese" },
- ["buhid"] = { first = 0x01740, last = 0x0175F, otf="buhd", description = "Buhid" },
- ["byzantinemusicalsymbols"] = { first = 0x1D000, last = 0x1D0FF, otf="byzm", description = "Byzantine Musical Symbols" },
- ["commonindicnumberforms"] = { first = 0x0A830, last = 0x0A83F, description = "Common Indic Number Forms" },
- ["carian"] = { first = 0x102A0, last = 0x102DF, description = "Carian" },
- ["cham"] = { first = 0x0AA00, last = 0x0AA5F, description = "Cham" },
- ["cherokee"] = { first = 0x013A0, last = 0x013FF, otf="cher", description = "Cherokee" },
- ["cjkcompatibility"] = { first = 0x03300, last = 0x033FF, otf="hang", description = "CJK Compatibility" },
- ["cjkcompatibilityforms"] = { first = 0x0FE30, last = 0x0FE4F, otf="hang", description = "CJK Compatibility Forms" },
- ["cjkcompatibilityideographs"] = { first = 0x0F900, last = 0x0FAFF, otf="hang", description = "CJK Compatibility Ideographs" },
- ["cjkcompatibilityideographssupplement"] = { first = 0x2F800, last = 0x2FA1F, otf="hang", description = "CJK Compatibility Ideographs Supplement" },
- ["cjkradicalssupplement"] = { first = 0x02E80, last = 0x02EFF, otf="hang", description = "CJK Radicals Supplement" },
- ["cjkstrokes"] = { first = 0x031C0, last = 0x031EF, otf="hang", description = "CJK Strokes" },
- ["cjksymbolsandpunctuation"] = { first = 0x03000, last = 0x0303F, otf="hang", description = "CJK Symbols and Punctuation" },
- ["cjkunifiedideographs"] = { first = 0x04E00, last = 0x09FFF, otf="hang", description = "CJK Unified Ideographs" },
- ["cjkunifiedideographsextensiona"] = { first = 0x03400, last = 0x04DBF, otf="hang", description = "CJK Unified Ideographs Extension A" },
- ["cjkunifiedideographsextensionb"] = { first = 0x20000, last = 0x2A6DF, otf="hang", description = "CJK Unified Ideographs Extension B" },
- ["combiningdiacriticalmarks"] = { first = 0x00300, last = 0x0036F, description = "Combining Diacritical Marks" },
- ["combiningdiacriticalmarksforsymbols"] = { first = 0x020D0, last = 0x020FF, description = "Combining Diacritical Marks for Symbols" },
- ["combiningdiacriticalmarkssupplement"] = { first = 0x01DC0, last = 0x01DFF, description = "Combining Diacritical Marks Supplement" },
- ["combininghalfmarks"] = { first = 0x0FE20, last = 0x0FE2F, description = "Combining Half Marks" },
- ["controlpictures"] = { first = 0x02400, last = 0x0243F, description = "Control Pictures" },
- ["coptic"] = { first = 0x02C80, last = 0x02CFF, otf="copt", description = "Coptic" },
- ["countingrodnumerals"] = { first = 0x1D360, last = 0x1D37F, description = "Counting Rod Numerals" },
- ["cuneiform"] = { first = 0x12000, last = 0x123FF, otf="xsux", description = "Cuneiform" },
- ["cuneiformnumbersandpunctuation"] = { first = 0x12400, last = 0x1247F, otf="xsux", description = "Cuneiform Numbers and Punctuation" },
- ["currencysymbols"] = { first = 0x020A0, last = 0x020CF, description = "Currency Symbols" },
- ["cypriotsyllabary"] = { first = 0x10800, last = 0x1083F, otf="cprt", description = "Cypriot Syllabary" },
- ["cyrillic"] = { first = 0x00400, last = 0x004FF, otf="cyrl", description = "Cyrillic" },
- ["cyrillicextendeda"] = { first = 0x02DE0, last = 0x02DFF, otf="cyrl", description = "Cyrillic Extended-A" },
- ["cyrillicextendedb"] = { first = 0x0A640, last = 0x0A69F, otf="cyrl", description = "Cyrillic Extended-B" },
- ["cyrillicsupplement"] = { first = 0x00500, last = 0x0052F, otf="cyrl", description = "Cyrillic Supplement" },
- ["deseret"] = { first = 0x10400, last = 0x1044F, otf="dsrt", description = "Deseret" },
- ["devanagari"] = { first = 0x00900, last = 0x0097F, otf="deva", description = "Devanagari" },
- ["devanagariextended"] = { first = 0x0A8E0, last = 0x0A8FF, description = "Devanagari Extended" },
- ["dingbats"] = { first = 0x02700, last = 0x027BF, description = "Dingbats" },
- ["dominotiles"] = { first = 0x1F030, last = 0x1F09F, description = "Domino Tiles" },
- ["egyptianhieroglyphs"] = { first = 0x13000, last = 0x1342F, description = "Egyptian Hieroglyphs" },
- ["emoticons"] = { first = 0x1F600, last = 0x1F64F, description = "Emoticons" },
- ["enclosedalphanumericsupplement"] = { first = 0x1F100, last = 0x1F1FF, description = "Enclosed Alphanumeric Supplement" },
- ["enclosedalphanumerics"] = { first = 0x02460, last = 0x024FF, description = "Enclosed Alphanumerics" },
- ["enclosedcjklettersandmonths"] = { first = 0x03200, last = 0x032FF, description = "Enclosed CJK Letters and Months" },
- ["enclosedideographicsupplement"] = { first = 0x1F200, last = 0x1F2FF, description = "Enclosed Ideographic Supplement" },
- ["ethiopic"] = { first = 0x01200, last = 0x0137F, otf="ethi", description = "Ethiopic" },
- ["ethiopicextended"] = { first = 0x02D80, last = 0x02DDF, otf="ethi", description = "Ethiopic Extended" },
- ["ethiopicextendeda"] = { first = 0x0AB00, last = 0x0AB2F, description = "Ethiopic Extended-A" },
- ["ethiopicsupplement"] = { first = 0x01380, last = 0x0139F, otf="ethi", description = "Ethiopic Supplement" },
- ["generalpunctuation"] = { first = 0x02000, last = 0x0206F, description = "General Punctuation" },
- ["geometricshapes"] = { first = 0x025A0, last = 0x025FF, description = "Geometric Shapes" },
- ["georgian"] = { first = 0x010A0, last = 0x010FF, otf="geor", description = "Georgian" },
- ["georgiansupplement"] = { first = 0x02D00, last = 0x02D2F, otf="geor", description = "Georgian Supplement" },
- ["glagolitic"] = { first = 0x02C00, last = 0x02C5F, otf="glag", description = "Glagolitic" },
- ["gothic"] = { first = 0x10330, last = 0x1034F, otf="goth", description = "Gothic" },
- ["greekandcoptic"] = { first = 0x00370, last = 0x003FF, otf="grek", description = "Greek and Coptic" },
- ["greekextended"] = { first = 0x01F00, last = 0x01FFF, otf="grek", description = "Greek Extended" },
- ["gujarati"] = { first = 0x00A80, last = 0x00AFF, otf="gujr", description = "Gujarati" },
- ["gurmukhi"] = { first = 0x00A00, last = 0x00A7F, otf="guru", description = "Gurmukhi" },
- ["halfwidthandfullwidthforms"] = { first = 0x0FF00, last = 0x0FFEF, description = "Halfwidth and Fullwidth Forms" },
- ["hangulcompatibilityjamo"] = { first = 0x03130, last = 0x0318F, otf="jamo", description = "Hangul Compatibility Jamo" },
- ["hanguljamo"] = { first = 0x01100, last = 0x011FF, otf="jamo", description = "Hangul Jamo" },
- ["hanguljamoextendeda"] = { first = 0x0A960, last = 0x0A97F, description = "Hangul Jamo Extended-A" },
- ["hanguljamoextendedb"] = { first = 0x0D7B0, last = 0x0D7FF, description = "Hangul Jamo Extended-B" },
- ["hangulsyllables"] = { first = 0x0AC00, last = 0x0D7AF, otf="hang", description = "Hangul Syllables" },
- ["hanunoo"] = { first = 0x01720, last = 0x0173F, otf="hano", description = "Hanunoo" },
- ["hebrew"] = { first = 0x00590, last = 0x005FF, otf="hebr", description = "Hebrew" },
- ["highprivateusesurrogates"] = { first = 0x0DB80, last = 0x0DBFF, description = "High Private Use Surrogates" },
- ["highsurrogates"] = { first = 0x0D800, last = 0x0DB7F, description = "High Surrogates" },
- ["hiragana"] = { first = 0x03040, last = 0x0309F, otf="kana", description = "Hiragana" },
- ["ideographicdescriptioncharacters"] = { first = 0x02FF0, last = 0x02FFF, description = "Ideographic Description Characters" },
- ["imperialaramaic"] = { first = 0x10840, last = 0x1085F, description = "Imperial Aramaic" },
- ["inscriptionalpahlavi"] = { first = 0x10B60, last = 0x10B7F, description = "Inscriptional Pahlavi" },
- ["inscriptionalparthian"] = { first = 0x10B40, last = 0x10B5F, description = "Inscriptional Parthian" },
- ["ipaextensions"] = { first = 0x00250, last = 0x002AF, description = "IPA Extensions" },
- ["javanese"] = { first = 0x0A980, last = 0x0A9DF, description = "Javanese" },
- ["kaithi"] = { first = 0x11080, last = 0x110CF, description = "Kaithi" },
- ["kanasupplement"] = { first = 0x1B000, last = 0x1B0FF, description = "Kana Supplement" },
- ["kanbun"] = { first = 0x03190, last = 0x0319F, description = "Kanbun" },
- ["kangxiradicals"] = { first = 0x02F00, last = 0x02FDF, description = "Kangxi Radicals" },
- ["kannada"] = { first = 0x00C80, last = 0x00CFF, otf="knda", description = "Kannada" },
- ["katakana"] = { first = 0x030A0, last = 0x030FF, otf="kana", description = "Katakana" },
- ["katakanaphoneticextensions"] = { first = 0x031F0, last = 0x031FF, otf="kana", description = "Katakana Phonetic Extensions" },
- ["kayahli"] = { first = 0x0A900, last = 0x0A92F, description = "Kayah Li" },
- ["kharoshthi"] = { first = 0x10A00, last = 0x10A5F, otf="khar", description = "Kharoshthi" },
- ["khmer"] = { first = 0x01780, last = 0x017FF, otf="khmr", description = "Khmer" },
- ["khmersymbols"] = { first = 0x019E0, last = 0x019FF, otf="khmr", description = "Khmer Symbols" },
- ["lao"] = { first = 0x00E80, last = 0x00EFF, otf="lao", description = "Lao" },
- ["latinextendeda"] = { first = 0x00100, last = 0x0017F, otf="latn", description = "Latin Extended-A" },
- ["latinextendedadditional"] = { first = 0x01E00, last = 0x01EFF, otf="latn", description = "Latin Extended Additional" },
- ["latinextendedb"] = { first = 0x00180, last = 0x0024F, otf="latn", description = "Latin Extended-B" },
- ["latinextendedc"] = { first = 0x02C60, last = 0x02C7F, otf="latn", description = "Latin Extended-C" },
- ["latinextendedd"] = { first = 0x0A720, last = 0x0A7FF, otf="latn", description = "Latin Extended-D" },
- ["latinsupplement"] = { first = 0x00080, last = 0x000FF, otf="latn", description = "Latin-1 Supplement" },
- ["lepcha"] = { first = 0x01C00, last = 0x01C4F, description = "Lepcha" },
- ["letterlikesymbols"] = { first = 0x02100, last = 0x0214F, description = "Letterlike Symbols" },
- ["limbu"] = { first = 0x01900, last = 0x0194F, otf="limb", description = "Limbu" },
- ["linearbideograms"] = { first = 0x10080, last = 0x100FF, otf="linb", description = "Linear B Ideograms" },
- ["linearbsyllabary"] = { first = 0x10000, last = 0x1007F, otf="linb", description = "Linear B Syllabary" },
- ["lisu"] = { first = 0x0A4D0, last = 0x0A4FF, description = "Lisu" },
- ["lowsurrogates"] = { first = 0x0DC00, last = 0x0DFFF, description = "Low Surrogates" },
- ["lycian"] = { first = 0x10280, last = 0x1029F, description = "Lycian" },
- ["lydian"] = { first = 0x10920, last = 0x1093F, description = "Lydian" },
- ["mahjongtiles"] = { first = 0x1F000, last = 0x1F02F, description = "Mahjong Tiles" },
- ["malayalam"] = { first = 0x00D00, last = 0x00D7F, otf="mlym", description = "Malayalam" },
- ["mandiac"] = { first = 0x00840, last = 0x0085F, otf="mand", description = "Mandaic" },
- ["mathematicalalphanumericsymbols"] = { first = 0x1D400, last = 0x1D7FF, description = "Mathematical Alphanumeric Symbols" },
- ["mathematicaloperators"] = { first = 0x02200, last = 0x022FF, description = "Mathematical Operators" },
- ["meeteimayek"] = { first = 0x0ABC0, last = 0x0ABFF, description = "Meetei Mayek" },
- ["meeteimayekextensions"] = { first = 0x0AAE0, last = 0x0AAFF, description = "Meetei Mayek Extensions" },
- ["meroiticcursive"] = { first = 0x109A0, last = 0x109FF, description = "Meroitic Cursive" },
- ["meroitichieroglyphs"] = { first = 0x10980, last = 0x1099F, description = "Meroitic Hieroglyphs" },
- ["miao"] = { first = 0x16F00, last = 0x16F9F, description = "Miao" },
- ["miscellaneousmathematicalsymbolsa"] = { first = 0x027C0, last = 0x027EF, description = "Miscellaneous Mathematical Symbols-A" },
- ["miscellaneousmathematicalsymbolsb"] = { first = 0x02980, last = 0x029FF, description = "Miscellaneous Mathematical Symbols-B" },
- ["miscellaneoussymbols"] = { first = 0x02600, last = 0x026FF, description = "Miscellaneous Symbols" },
- ["miscellaneoussymbolsandarrows"] = { first = 0x02B00, last = 0x02BFF, description = "Miscellaneous Symbols and Arrows" },
- ["miscellaneoussymbolsandpictographs"] = { first = 0x1F300, last = 0x1F5FF, description = "Miscellaneous Symbols And Pictographs" },
- ["miscellaneoustechnical"] = { first = 0x02300, last = 0x023FF, description = "Miscellaneous Technical" },
- ["modifiertoneletters"] = { first = 0x0A700, last = 0x0A71F, description = "Modifier Tone Letters" },
- ["mongolian"] = { first = 0x01800, last = 0x018AF, otf="mong", description = "Mongolian" },
- ["musicalsymbols"] = { first = 0x1D100, last = 0x1D1FF, otf="musc", description = "Musical Symbols" },
- ["myanmar"] = { first = 0x01000, last = 0x0109F, otf="mymr", description = "Myanmar" },
- ["myanmarextendeda"] = { first = 0x0AA60, last = 0x0AA7F, description = "Myanmar Extended-A" },
- ["newtailue"] = { first = 0x01980, last = 0x019DF, description = "New Tai Lue" },
- ["nko"] = { first = 0x007C0, last = 0x007FF, otf="nko", description = "NKo" },
- ["numberforms"] = { first = 0x02150, last = 0x0218F, description = "Number Forms" },
- ["ogham"] = { first = 0x01680, last = 0x0169F, otf="ogam", description = "Ogham" },
- ["olchiki"] = { first = 0x01C50, last = 0x01C7F, description = "Ol Chiki" },
- ["olditalic"] = { first = 0x10300, last = 0x1032F, otf="ital", description = "Old Italic" },
- ["oldpersian"] = { first = 0x103A0, last = 0x103DF, otf="xpeo", description = "Old Persian" },
- ["oldsoutharabian"] = { first = 0x10A60, last = 0x10A7F, description = "Old South Arabian" },
- ["odlturkic"] = { first = 0x10C00, last = 0x10C4F, description = "Old Turkic" },
- ["opticalcharacterrecognition"] = { first = 0x02440, last = 0x0245F, description = "Optical Character Recognition" },
- ["oriya"] = { first = 0x00B00, last = 0x00B7F, otf="orya", description = "Oriya" },
- ["osmanya"] = { first = 0x10480, last = 0x104AF, otf="osma", description = "Osmanya" },
- ["phagspa"] = { first = 0x0A840, last = 0x0A87F, otf="phag", description = "Phags-pa" },
- ["phaistosdisc"] = { first = 0x101D0, last = 0x101FF, description = "Phaistos Disc" },
- ["phoenician"] = { first = 0x10900, last = 0x1091F, otf="phnx", description = "Phoenician" },
- ["phoneticextensions"] = { first = 0x01D00, last = 0x01D7F, description = "Phonetic Extensions" },
- ["phoneticextensionssupplement"] = { first = 0x01D80, last = 0x01DBF, description = "Phonetic Extensions Supplement" },
- ["playingcards"] = { first = 0x1F0A0, last = 0x1F0FF, description = "Playing Cards" },
- ["privateusearea"] = { first = 0x0E000, last = 0x0F8FF, description = "Private Use Area" },
- ["rejang"] = { first = 0x0A930, last = 0x0A95F, description = "Rejang" },
- ["ruminumeralsymbols"] = { first = 0x10E60, last = 0x10E7F, description = "Rumi Numeral Symbols" },
- ["runic"] = { first = 0x016A0, last = 0x016FF, otf="runr", description = "Runic" },
- ["samaritan"] = { first = 0x00800, last = 0x0083F, description = "Samaritan" },
- ["saurashtra"] = { first = 0x0A880, last = 0x0A8DF, description = "Saurashtra" },
- ["sharada"] = { first = 0x11180, last = 0x111DF, description = "Sharada" },
- ["shavian"] = { first = 0x10450, last = 0x1047F, otf="shaw", description = "Shavian" },
- ["sinhala"] = { first = 0x00D80, last = 0x00DFF, otf="sinh", description = "Sinhala" },
- ["smallformvariants"] = { first = 0x0FE50, last = 0x0FE6F, description = "Small Form Variants" },
- ["sorasompeng"] = { first = 0x110D0, last = 0x110FF, description = "Sora Sompeng" },
- ["spacingmodifierletters"] = { first = 0x002B0, last = 0x002FF, description = "Spacing Modifier Letters" },
- ["specials"] = { first = 0x0FFF0, last = 0x0FFFF, description = "Specials" },
- ["sundanese"] = { first = 0x01B80, last = 0x01BBF, description = "Sundanese" },
- ["sundanesesupplement"] = { first = 0x01CC0, last = 0x01CCF, description = "Sundanese Supplement" },
- ["superscriptsandsubscripts"] = { first = 0x02070, last = 0x0209F, description = "Superscripts and Subscripts" },
- ["supplementalarrowsa"] = { first = 0x027F0, last = 0x027FF, description = "Supplemental Arrows-A" },
- ["supplementalarrowsb"] = { first = 0x02900, last = 0x0297F, description = "Supplemental Arrows-B" },
- ["supplementalmathematicaloperators"] = { first = 0x02A00, last = 0x02AFF, description = "Supplemental Mathematical Operators" },
- ["supplementalpunctuation"] = { first = 0x02E00, last = 0x02E7F, description = "Supplemental Punctuation" },
- ["supplementaryprivateuseareaa"] = { first = 0xF0000, last = 0xFFFFF, description = "Supplementary Private Use Area-A" },
- ["supplementaryprivateuseareab"] = { first = 0x100000,last = 0x10FFFF, description = "Supplementary Private Use Area-B" },
- ["sylotinagri"] = { first = 0x0A800, last = 0x0A82F, otf="sylo", description = "Syloti Nagri" },
- ["syriac"] = { first = 0x00700, last = 0x0074F, otf="syrc", description = "Syriac" },
- ["tagalog"] = { first = 0x01700, last = 0x0171F, otf="tglg", description = "Tagalog" },
- ["tagbanwa"] = { first = 0x01760, last = 0x0177F, otf="tagb", description = "Tagbanwa" },
- ["tags"] = { first = 0xE0000, last = 0xE007F, description = "Tags" },
- ["taile"] = { first = 0x01950, last = 0x0197F, otf="tale", description = "Tai Le" },
- ["taitham"] = { first = 0x01A20, last = 0x01AAF, description = "Tai Tham" },
- ["taiviet"] = { first = 0x0AA80, last = 0x0AADF, description = "Tai Viet" },
- ["taixuanjingsymbols"] = { first = 0x1D300, last = 0x1D35F, description = "Tai Xuan Jing Symbols" },
- ["takri"] = { first = 0x11680, last = 0x116CF, description = "Takri" },
- ["tamil"] = { first = 0x00B80, last = 0x00BFF, otf="taml", description = "Tamil" },
- ["telugu"] = { first = 0x00C00, last = 0x00C7F, otf="telu", description = "Telugu" },
- ["thaana"] = { first = 0x00780, last = 0x007BF, otf="thaa", description = "Thaana" },
- ["thai"] = { first = 0x00E00, last = 0x00E7F, otf="thai", description = "Thai" },
- ["tibetan"] = { first = 0x00F00, last = 0x00FFF, otf="tibt", description = "Tibetan" },
- ["tifinagh"] = { first = 0x02D30, last = 0x02D7F, otf="tfng", description = "Tifinagh" },
- ["transportandmapsymbols"] = { first = 0x1F680, last = 0x1F6FF, description = "Transport And Map Symbols" },
- ["ugaritic"] = { first = 0x10380, last = 0x1039F, otf="ugar", description = "Ugaritic" },
- ["unifiedcanadianaboriginalsyllabics"] = { first = 0x01400, last = 0x0167F, otf="cans", description = "Unified Canadian Aboriginal Syllabics" },
- ["unifiedcanadianaboriginalsyllabicsextended"] = { first = 0x018B0, last = 0x018FF, description = "Unified Canadian Aboriginal Syllabics Extended" },
- ["vai"] = { first = 0x0A500, last = 0x0A63F, description = "Vai" },
- ["variationselectors"] = { first = 0x0FE00, last = 0x0FE0F, description = "Variation Selectors" },
- ["variationselectorssupplement"] = { first = 0xE0100, last = 0xE01EF, description = "Variation Selectors Supplement" },
- ["vedicextensions"] = { first = 0x01CD0, last = 0x01CFF, description = "Vedic Extensions" },
- ["verticalforms"] = { first = 0x0FE10, last = 0x0FE1F, description = "Vertical Forms" },
- ["yijinghexagramsymbols"] = { first = 0x04DC0, last = 0x04DFF, otf="yi", description = "Yijing Hexagram Symbols" },
- ["yiradicals"] = { first = 0x0A490, last = 0x0A4CF, otf="yi", description = "Yi Radicals" },
- ["yisyllables"] = { first = 0x0A000, last = 0x0A48F, otf="yi", description = "Yi Syllables" },
-}
-
-characters.blocks = blocks
-
-function characters.blockrange(name)
- local b = blocks[name]
- if b then
- return b.first, b.last
- else
- return 0, 0
- end
-end
-
-setmetatableindex(blocks, function(t,k) -- we could use an intermediate table if called often
- return k and rawget(t,lower(gsub(k,"[^a-zA-Z]","")))
-end)
-
-local otfscripts = utilities.storage.allocate()
-characters.otfscripts = otfscripts
-
-setmetatableindex(otfscripts,function(t,unicode)
- for k, v in next, blocks do
- local first, last = v.first, v.last
- if unicode >= first and unicode <= last then
- local script = v.otf or "dflt"
- for u=first,last do
- t[u] = script
- end
- return script
- end
- end
- -- pretty slow when we're here
- t[unicode] = "dflt"
- return "dflt"
-end)
-
-function characters.getrange(name) -- used in font fallback definitions (name or range)
- local range = blocks[name]
- if range then
- return range.first, range.last, range.description
- end
- name = gsub(name,'"',"0x") -- goodie: tex hex notation
- local start, stop = match(name,"^(.-)[%-%:](.-)$")
- if start and stop then
- start, stop = tonumber(start,16) or tonumber(start), tonumber(stop,16) or tonumber(stop)
- if start and stop then
- return start, stop, nil
- end
- end
- local slot = tonumber(name,16) or tonumber(name)
- return slot, slot, nil
-end
-
-local categorytags = allocate {
- lu = "Letter Uppercase",
- ll = "Letter Lowercase",
- lt = "Letter Titlecase",
- lm = "Letter Modifier",
- lo = "Letter Other",
- mn = "Mark Nonspacing",
- mc = "Mark Spacing Combining",
- me = "Mark Enclosing",
- nd = "Number Decimal Digit",
- nl = "Number Letter",
- no = "Number Other",
- pc = "Punctuation Connector",
- pd = "Punctuation Dash",
- ps = "Punctuation Open",
- pe = "Punctuation Close",
- pi = "Punctuation Initial Quote",
- pf = "Punctuation Final Quote",
- po = "Punctuation Other",
- sm = "Symbol Math",
- sc = "Symbol Currency",
- sk = "Symbol Modifier",
- so = "Symbol Other",
- zs = "Separator Space",
- zl = "Separator Line",
- zp = "Separator Paragraph",
- cc = "Other Control",
- cf = "Other Format",
- cs = "Other Surrogate",
- co = "Other Private Use",
- cn = "Other Not Assigned",
-}
-
-characters.categorytags = categorytags
-
---~ special : cf (softhyphen) zs (emspace)
---~ characters: ll lm lo lt lu mn nl no pc pd pe pf pi po ps sc sk sm so
-
-local is_character = allocate ( tohash {
- "lu","ll","lt","lm","lo",
- "nd","nl","no",
- "mn",
- "nl","no",
- "pc","pd","ps","pe","pi","pf","po",
- "sm","sc","sk","so"
-} )
-
-local is_letter = allocate ( tohash {
- "ll","lm","lo","lt","lu"
-} )
-
-local is_command = allocate ( tohash {
- "cf","zs"
-} )
-
-local is_spacing = allocate ( tohash {
- "zs", "zl","zp",
-} )
-
-local is_mark = allocate ( tohash {
- "mn", "ms",
-} )
-
--- to be redone: store checked characters
-
-characters.is_character = is_character
-characters.is_letter = is_letter
-characters.is_command = is_command
-characters.is_spacing = is_spacing
-characters.is_mark = is_mark
-
-local mt = { -- yes or no ?
- __index = function(t,k)
- if type(k) == "number" then
- local c = data[k].category
- return c and rawget(t,c)
- else
- -- avoid auto conversion in data.characters lookups
- end
- end
-}
-
-setmetatableindex(characters.is_character, mt)
-setmetatableindex(characters.is_letter, mt)
-setmetatableindex(characters.is_command, mt)
-setmetatableindex(characters.is_spacing, mt)
-
--- linebreak: todo: hash
---
--- normative : BK CR LF CM SG GL CB SP ZW NL WJ JL JV JT H2 H3
--- informative : XX OP CL QU NS EX SY IS PR PO NU AL ID IN HY BB BA SA AI B2 new:CP
-
--- east asian width:
---
--- N A H W F Na
-
-characters.bidi = allocate {
- l = "Left-to-Right",
- lre = "Left-to-Right Embedding",
- lro = "Left-to-Right Override",
- r = "Right-to-Left",
- al = "Right-to-Left Arabic",
- rle = "Right-to-Left Embedding",
- rlo = "Right-to-Left Override",
- pdf = "Pop Directional Format",
- en = "European Number",
- es = "European Number Separator",
- et = "European Number Terminator",
- an = "Arabic Number",
- cs = "Common Number Separator",
- nsm = "Non-Spacing Mark",
- bn = "Boundary Neutral",
- b = "Paragraph Separator",
- s = "Segment Separator",
- ws = "Whitespace",
- on = "Other Neutrals",
-}
-
---[[ldx--
-<p>At this point we assume that the big data table is loaded. From this
-table we derive a few more.</p>
---ldx]]--
-
-if not characters.fallbacks then
-
- characters.fallbacks = { } -- not than many
-
- local fallbacks = characters.fallbacks
-
- for k, d in next, data do
- local specials = d.specials
- if specials and specials[1] == "compat" and specials[2] == 0x0020 then
- local s = specials[3]
- if s then
- fallbacks[k] = s
- fallbacks[s] = k
- end
- end
- end
-
-end
-
-if storage then
- storage.register("characters/fallbacks", characters.fallbacks, "characters.fallbacks") -- accents and such
-end
-
-characters.directions = { }
-
-setmetatableindex(characters.directions,function(t,k)
- local d = data[k]
- if d then
- local v = d.direction
- if v then
- t[k] = v
- return v
- end
- end
- t[k] = false -- maybe 'l'
- return v
-end)
-
---[[ldx--
-<p>Next comes a whole series of helper methods. These are (will be) part
-of the official <l n='api'/>.</p>
---ldx]]--
-
--- we could make them virtual: characters.contextnames[n]
-
-function characters.contextname(n) return data[n].contextname or "" end
-function characters.adobename (n) return data[n].adobename or "" end
-function characters.description(n) return data[n].description or "" end
--------- characters.category (n) return data[n].category or "" end
-
-function characters.category(n,verbose)
- local c = data[n].category
- if not c then
- return ""
- elseif verbose then
- return categorytags[c]
- else
- return c
- end
-end
-
--- -- some day we will make a table .. not that many calls to utfchar
---
--- local utfchar = utf.char
--- local utfbyte = utf.byte
--- local utfbytes = { }
--- local utfchars = { }
---
--- table.setmetatableindex(utfbytes,function(t,k) local v = utfchar(k) t[k] = v return v end)
--- table.setmetatableindex(utfchars,function(t,k) local v = utfbyte(k) t[k] = v return v end)
-
-local function toutfstring(s)
- if type(s) == "table" then
- return utfchar(unpack(s)) -- concat { utfchar( unpack(s) ) }
- else
- return utfchar(s)
- end
-end
-
-utf.tostring = toutfstring
-
-local categories = allocate() characters.categories = categories -- lazy table
-
-setmetatableindex(categories, function(t,u) if u then local c = data[u] c = c and c.category or u t[u] = c return c end end)
-
-local lccodes = allocate() characters.lccodes = lccodes -- lazy table
-local uccodes = allocate() characters.uccodes = uccodes -- lazy table
-local shcodes = allocate() characters.shcodes = shcodes -- lazy table
-local fscodes = allocate() characters.fscodes = fscodes -- lazy table
-
-setmetatableindex(lccodes, function(t,u) if u then local c = data[u] c = c and c.lccode or (type(u) == "string" and utfbyte(u)) or u t[u] = c return c end end)
-setmetatableindex(uccodes, function(t,u) if u then local c = data[u] c = c and c.uccode or (type(u) == "string" and utfbyte(u)) or u t[u] = c return c end end)
-setmetatableindex(shcodes, function(t,u) if u then local c = data[u] c = c and c.shcode or (type(u) == "string" and utfbyte(u)) or u t[u] = c return c end end)
-setmetatableindex(fscodes, function(t,u) if u then local c = data[u] c = c and c.fscode or (type(u) == "string" and utfbyte(u)) or u t[u] = c return c end end)
-
-local lcchars = allocate() characters.lcchars = lcchars -- lazy table
-local ucchars = allocate() characters.ucchars = ucchars -- lazy table
-local shchars = allocate() characters.shchars = shchars -- lazy table
-local fschars = allocate() characters.fschars = fschars -- lazy table
-
-setmetatableindex(lcchars, function(t,u) if u then local c = data[u] c = c and c.lccode c = c and toutfstring(c) or (type(u) == "number" and utfchar(u)) or u t[u] = c return c end end)
-setmetatableindex(ucchars, function(t,u) if u then local c = data[u] c = c and c.uccode c = c and toutfstring(c) or (type(u) == "number" and utfchar(u)) or u t[u] = c return c end end)
-setmetatableindex(shchars, function(t,u) if u then local c = data[u] c = c and c.shcode c = c and toutfstring(c) or (type(u) == "number" and utfchar(u)) or u t[u] = c return c end end)
-setmetatableindex(fschars, function(t,u) if u then local c = data[u] c = c and c.fscode c = c and toutfstring(c) or (type(u) == "number" and utfchar(u)) or u t[u] = c return c end end)
-
-local decomposed = allocate() characters.decomposed = decomposed -- lazy table
-local specials = allocate() characters.specials = specials -- lazy table
-
-setmetatableindex(decomposed, function(t,u) -- either a table or false
- if u then
- local c = data[u]
- local s = c and c.decomposed or false -- could fall back to specials
- t[u] = s
- return s
- end
-end)
-
-setmetatableindex(specials, function(t,u) -- either a table or false
- if u then
- local c = data[u]
- local s = c and c.specials or false
- t[u] = s
- return s
- end
-end)
-
-local specialchars = allocate() characters.specialchars = specialchars -- lazy table
-local descriptions = allocate() characters.descriptions = descriptions -- lazy table
-
-setmetatableindex(specialchars, function(t,u)
- if u then
- local c = data[u]
- local s = c and c.specials
- if s then
- local tt, ttn = { }, 0
- for i=2,#s do
- local si = s[i]
- local c = data[si]
- if is_letter[c.category] then
- ttn = ttn + 1
- tt[ttn] = utfchar(si)
- end
- end
- c = concat(tt)
- t[u] = c
- return c
- else
- if type(u) == "number" then
- u = utfchar(u)
- end
- t[u] = u
- return u
- end
- end
-end)
-
-setmetatableindex(descriptions, function(t,k)
- -- 0.05 - 0.10 sec
- for u, c in next, data do
- local d = c.description
- if d then
- d = gsub(d," ","")
- d = lower(d)
- t[d] = u
- end
- end
- local d = rawget(t,k)
- if not d then
- t[k] = k
- end
- return d
-end)
-
-function characters.unicodechar(asked)
- local n = tonumber(asked)
- if n then
- return n
- elseif type(asked) == "string" then
- return descriptions[asked] or descriptions[gsub(asked," ","")]
- end
-end
-
--- function characters.lower(str)
--- local new, n = { }, 0
--- for u in utfvalues(str) do
--- n = n + 1
--- new[n] = lcchars[u]
--- end
--- return concat(new)
--- end
---
--- function characters.upper(str)
--- local new, n = { }, 0
--- for u in utfvalues(str) do
--- n = n + 1
--- new[n] = ucchars[u]
--- end
--- return concat(new)
--- end
---
--- function characters.shaped(str)
--- local new, n = { }, 0
--- for u in utfvalues(str) do
--- n = n + 1
--- new[n] = shchars[u]
--- end
--- return concat(new)
--- end
-
------ tolower = Cs((utf8byte/lcchars)^0)
------ toupper = Cs((utf8byte/ucchars)^0)
------ toshape = Cs((utf8byte/shchars)^0)
-
-local tolower = Cs((utf8char/lcchars)^0)
-local toupper = Cs((utf8char/ucchars)^0)
-local toshape = Cs((utf8char/shchars)^0)
-
-patterns.tolower = tolower
-patterns.toupper = toupper
-patterns.toshape = toshape
-
-function characters.lower (str) return lpegmatch(tolower,str) end
-function characters.upper (str) return lpegmatch(toupper,str) end
-function characters.shaped(str) return lpegmatch(toshape,str) end
-
-function characters.lettered(str,spacing)
- local new, n = { }, 0
- if spacing then
- local done = false
- for u in utfvalues(str) do
- local c = data[u].category
- if is_letter[c] then
- if done and n > 1 then
- n = n + 1
- new[n] = " "
- done = false
- end
- n = n + 1
- new[n] = utfchar(u)
- elseif spacing and is_spacing[c] then
- done = true
- end
- end
- else
- for u in utfvalues(str) do
- if is_letter[data[u].category] then
- n = n + 1
- new[n] = utfchar(u)
- end
- end
- end
- return concat(new)
-end
-
---[[ldx--
-<p>Requesting lower and uppercase codes:</p>
---ldx]]--
-
-function characters.uccode(n) return uccodes[n] end -- obsolete
-function characters.lccode(n) return lccodes[n] end -- obsolete
-
-function characters.safechar(n)
- local c = data[n]
- if c and c.contextname then
- return "\\" .. c.contextname
- else
- return utfchar(n)
- end
-end
-
-function characters.shape(n)
- local shcode = shcodes[n]
- if not shcode then
- return n, nil
- elseif type(shcode) == "table" then
- return shcode[1], shcode[#shcode]
- else
- return shcode, nil
- end
-end
-
--- -- some day we might go this route, but it does not really save that much
--- -- so not now (we can generate a lot using mtx-unicode that operates on the
--- -- database)
---
--- -- category cjkwd direction linebreak
---
--- -- adobename comment contextcommand contextname description fallback lccode
--- -- mathclass mathfiller mathname mathspec mathstretch mathsymbol mirror
--- -- range shcode specials uccode uccodes unicodeslot
---
--- local data = {
--- ['one']={
--- common = {
--- category="cc",
--- direction="bn",
--- linebreak="cm",
--- },
--- vector = {
--- [0x0000] = {
--- description="NULL",
--- group='one',
--- unicodeslot=0x0000,
--- },
--- {
--- description="START OF HEADING",
--- group='one',
--- unicodeslot=0x0001,
--- },
--- }
--- }
--- }
---
--- local chardata, groupdata = { }, { }
---
--- for group, gdata in next, data do
--- local common, vector = { __index = gdata.common }, gdata.vector
--- for character, cdata in next, vector do
--- chardata[character] = cdata
--- setmetatable(cdata,common)
--- end
--- groupdata[group] = gdata
--- end
-
---~ characters.data, characters.groups = chardata, groupdata
-
---~ [0xF0000]={
---~ category="co",
---~ cjkwd="a",
---~ description="<Plane 0x000F Private Use, First>",
---~ direction="l",
---~ unicodeslot=0xF0000,
---~ },
---~ [0xFFFFD]={
---~ category="co",
---~ cjkwd="a",
---~ description="<Plane 0x000F Private Use, Last>",
---~ direction="l",
---~ unicodeslot=0xFFFFD,
---~ },
---~ [0x100000]={
---~ category="co",
---~ cjkwd="a",
---~ description="<Plane 0x0010 Private Use, First>",
---~ direction="l",
---~ unicodeslot=0x100000,
---~ },
---~ [0x10FFFD]={
---~ category="co",
---~ cjkwd="a",
---~ description="<Plane 0x0010 Private Use, Last>",
---~ direction="l",
---~ unicodeslot=0x10FFFD,
---~ },
-
-if not characters.superscripts then
-
- local superscripts = allocate() characters.superscripts = superscripts
- local subscripts = allocate() characters.subscripts = subscripts
-
- -- skipping U+02120 (service mark) U+02122 (trademark)
-
- for k, v in next, data do
- local specials = v.specials
- if specials then
- local what = specials[1]
- if what == "super" then
- if #specials == 2 then
- superscripts[k] = specials[2]
- else
- report_defining("ignoring %s %a, char %c, description %a","superscript",ustring(k),k,v.description)
- end
- elseif what == "sub" then
- if #specials == 2 then
- subscripts[k] = specials[2]
- else
- report_defining("ignoring %s %a, char %c, description %a","subscript",ustring(k),k,v.description)
- end
- end
- end
- end
-
- -- print(table.serialize(superscripts, "superscripts", { hexify = true }))
- -- print(table.serialize(subscripts, "subscripts", { hexify = true }))
-
- if storage then
- storage.register("characters/superscripts", superscripts, "characters.superscripts")
- storage.register("characters/subscripts", subscripts, "characters.subscripts")
- end
-
-end
-
--- for the moment only a few
-
-local tracedchars = utilities.strings.tracers
-
-tracedchars[0x00] = "[signal]"
-tracedchars[0x20] = "[space]"
-
--- the following code will move to char-tex.lua
-
--- tex
-
-if not tex or not context or not commands then return characters end
-
-local tex = tex
-local texsetlccode = tex.setlccode
-local texsetuccode = tex.setuccode
-local texsetsfcode = tex.setsfcode
-local texsetcatcode = tex.setcatcode
-
-local contextsprint = context.sprint
-local ctxcatcodes = catcodes.numbers.ctxcatcodes
-
---[[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]]--
-
-function commands.makeactive(n,name) --
- contextsprint(ctxcatcodes,format("\\catcode%s=13\\unexpanded\\def %s{\\%s}",n,utfchar(n),name))
- -- context("\\catcode%s=13\\unexpanded\\def %s{\\%s}",n,utfchar(n),name)
-end
-
-function commands.utfchar(c,n)
- if n then
- -- contextsprint(c,charfromnumber(n))
- contextsprint(c,utfchar(n))
- else
- -- contextsprint(charfromnumber(c))
- contextsprint(utfchar(c))
- end
-end
-
-function commands.safechar(n)
- local c = data[n]
- if c and c.contextname then
- contextsprint("\\" .. c.contextname) -- context[c.contextname]()
- else
- contextsprint(utfchar(n))
- end
-end
-
-tex.uprint = commands.utfchar
-
-local forbidden = tohash { -- at least now
- 0x00A0,
- 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x200B, 0x200C, 0x200D,
- 0x202F,
- 0x205F,
- -- 0xFEFF,
-}
-
-function characters.define(tobelettered, tobeactivated) -- catcodetables
-
- if trace_defining then
- report_defining("defining active character commands")
- end
-
- local activated, a = { }, 0
-
- for u, chr in next, data do -- these will be commands
- local fallback = chr.fallback
- if fallback then
- contextsprint("{\\catcode",u,"=13\\unexpanded\\gdef ",utfchar(u),"{\\checkedchar{",u,"}{",fallback,"}}}")
- a = a + 1
- activated[a] = u
- else
- local contextname = chr.contextname
- if contextname then
- local category = chr.category
- if is_character[category] then
- if chr.unicodeslot < 128 then
- if is_letter[category] then
- contextsprint(ctxcatcodes,format("\\def\\%s{%s}",contextname,utfchar(u))) -- has no s
- else
- contextsprint(ctxcatcodes,format("\\chardef\\%s=%s",contextname,u)) -- has no s
- end
- else
- contextsprint(ctxcatcodes,format("\\def\\%s{%s}",contextname,utfchar(u))) -- has no s
- end
- elseif is_command[category] and not forbidden[u] then
- contextsprint("{\\catcode",u,"=13\\unexpanded\\gdef ",utfchar(u),"{\\"..contextname,"}}")
- a = a + 1
- activated[a] = u
- end
- end
- end
- end
-
- if tobelettered then -- shared
- local saved = tex.catcodetable
- for i=1,#tobelettered do
- tex.catcodetable = tobelettered[i]
- if trace_defining then
- report_defining("defining letters (global, shared)")
- end
- for u, chr in next, data do
- if not chr.fallback and is_letter[chr.category] and u >= 128 and u <= 65536 then
- texsetcatcode(u,11)
- end
- local range = chr.range
- if range then
- for i=1,range.first,range.last do
- texsetcatcode(i,11)
- end
- end
- end
- texsetcatcode(0x200C,11) -- non-joiner
- texsetcatcode(0x200D,11) -- joiner
- end
- tex.catcodetable = saved
- end
-
- local nofactivated = #tobeactivated
- if tobeactivated and nofactivated > 0 then
- for i=1,nofactivated do
- local u = activated[i]
- if u then
- report_defining("character %U is active in set %a, containing %a",u,data[u].description,tobeactivated)
- end
- end
- local saved = tex.catcodetable
- for i=1,#tobeactivated do
- local vector = tobeactivated[i]
- if trace_defining then
- report_defining("defining %a active characters in vector %a",nofactivated,vector)
- end
- tex.catcodetable = vector
- for i=1,nofactivated do
- local u = activated[i]
- if u then
- texsetcatcode(u,13)
- end
- end
- end
- tex.catcodetable = saved
- end
-
-end
-
---[[ldx--
-<p>Setting the lccodes is also done in a loop over the data table.</p>
---ldx]]--
-
-local sfmode = "unset" -- unset, traditional, normal
-
-function characters.setcodes()
- if trace_defining then
- report_defining("defining lc and uc codes")
- end
- local traditional = sfstate == "traditional" or sfstate == "unset"
- for code, chr in next, data do
- local cc = chr.category
- if is_letter[cc] then
- local range = chr.range
- if range then
- for i=range.first,range.last do
- texsetcatcode(i,11) -- letter
- texsetlccode(i,i,i) -- self self
- end
- else
- local lc, uc = chr.lccode, chr.uccode
- if not lc then
- chr.lccode, lc = code, code
- elseif type(lc) == "table" then
- lc = code
- end
- if not uc then
- chr.uccode, uc = code, code
- elseif type(uc) == "table" then
- uc = code
- end
- texsetcatcode(code,11) -- letter
- texsetlccode(code,lc,uc)
- if traditional and cc == "lu" then
- texsetsfcode(code,999)
- end
- end
- elseif is_mark[cc] then
- texsetlccode(code,code,code) -- for hyphenation
- end
- end
- if traditional then
- sfstate = "traditional"
- end
-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 code, chr in next, data do
- if chr.category == "lu" then
- texsetsfcode(code,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)
-
--- xml
-
-characters.activeoffset = 0x10000 -- there will be remapped in that byte range
-
-function commands.remapentity(chr,slot)
- contextsprint(format("{\\catcode%s=13\\xdef%s{\\string%s}}",slot,utfchar(slot),chr))
-end
-
--- xml.entities = xml.entities or { }
---
--- storage.register("xml/entities",xml.entities,"xml.entities") -- this will move to lxml
---
--- function characters.setmkiventities()
--- local entities = xml.entities
--- entities.lt = "<"
--- entities.amp = "&"
--- entities.gt = ">"
--- end
---
--- function characters.setmkiientities()
--- local entities = xml.entities
--- entities.lt = utfchar(characters.activeoffset + utfbyte("<"))
--- entities.amp = utfchar(characters.activeoffset + utfbyte("&"))
--- entities.gt = utfchar(characters.activeoffset + utfbyte(">"))
--- end
-
+if not modules then modules = { } end modules ['char-ini'] = {
+ 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"
+}
+
+-- todo: make two files, one for format generation, one for format use
+
+-- we can remove the tag range starting at 0xE0000 (special applications)
+
+local utfchar, utfbyte, utfvalues, ustring = utf.char, utf.byte, utf.values, utf.ustring
+local concat, unpack, tohash = table.concat, table.unpack, table.tohash
+local next, tonumber, type, rawget, rawset = next, tonumber, type, rawget, rawset
+local format, lower, gsub, match, gmatch = string.format, string.lower, string.gsub, string.match, string.match, string.gmatch
+local P, R, Cs, lpegmatch, patterns = lpeg.P, lpeg.R, lpeg.Cs, lpeg.match, lpeg.patterns
+
+local utf8byte = patterns.utf8byte
+local utf8char = patterns.utf8char
+
+local allocate = utilities.storage.allocate
+local mark = utilities.storage.mark
+
+local setmetatableindex = table.setmetatableindex
+
+local trace_defining = false trackers.register("characters.defining", function(v) characters_defining = v end)
+
+local report_defining = logs.reporter("characters")
+
+--[[ldx--
+<p>This module implements some methods and creates additional datastructured
+from the big character table that we use for all kind of purposes:
+<type>char-def.lua</type>.</p>
+
+<p>We assume that at this point <type>characters.data</type> is already
+loaded!</p>
+--ldx]]--
+
+characters = characters or { }
+local characters = characters
+local data = characters.data
+
+if data then
+ mark(data) -- why does this fail
+else
+ report_defining("fatal error: 'char-def.lua' is not loaded")
+ os.exit()
+end
+
+--[[ldx--
+<p>This converts a string (if given) into a number.</p>
+--ldx]]--
+
+local pattern = (P("0x") + P("U+")) * ((R("09","AF")^1 * P(-1)) / function(s) return tonumber(s,16) end)
+
+patterns.chartonumber = pattern
+
+local function chartonumber(k)
+ if type(k) == "string" then
+ local u = lpegmatch(pattern,k)
+ if u then
+ return utfbyte(u)
+ else
+ return utfbyte(k) or 0
+ end
+ else
+ return k or 0
+ end
+end
+
+local function charfromnumber(k)
+ if type(k) == "number" then
+ return utfchar(k) or ""
+ else
+ local u = lpegmatch(pattern,k)
+ if u then
+ return utfchar(u)
+ else
+ return k
+ end
+ end
+end
+
+--~ print(chartonumber(97), chartonumber("a"), chartonumber("0x61"), chartonumber("U+61"))
+
+characters.tonumber = chartonumber
+characters.fromnumber = charfromnumber
+
+local private = {
+ description = "PRIVATE SLOT",
+}
+
+local ranges = allocate()
+characters.ranges = ranges
+
+setmetatableindex(data, function(t,k)
+ local tk = type(k)
+ if tk == "string" then
+ k = lpegmatch(pattern,k) or utfbyte(k)
+ if k then
+ local v = rawget(t,k)
+ if v then
+ return v
+ else
+ tk = "number" -- fall through to range
+ end
+ else
+ return private
+ end
+ end
+ if tk == "number" and k < 0xF0000 then
+ for r=1,#ranges do
+ local rr = ranges[r]
+ if k >= rr.first and k <= rr.last then
+ local extender = rr.extender
+ if extender then
+ local v = extender(k,v)
+ t[k] = v
+ return v
+ end
+ end
+ end
+ end
+ return private -- handy for when we loop over characters in fonts and check for a property
+end)
+
+local blocks = allocate {
+ ["aegeannumbers"] = { first = 0x10100, last = 0x1013F, description = "Aegean Numbers" },
+ ["alchemicalsymbols"] = { first = 0x1F700, last = 0x1F77F, description = "Alchemical Symbols" },
+ ["alphabeticpresentationforms"] = { first = 0x0FB00, last = 0x0FB4F, otf="latn", description = "Alphabetic Presentation Forms" },
+ ["ancientgreekmusicalnotation"] = { first = 0x1D200, last = 0x1D24F, otf="grek", description = "Ancient Greek Musical Notation" },
+ ["ancientgreeknumbers"] = { first = 0x10140, last = 0x1018F, otf="grek", description = "Ancient Greek Numbers" },
+ ["ancientsymbols"] = { first = 0x10190, last = 0x101CF, otf="grek", description = "Ancient Symbols" },
+ ["arabic"] = { first = 0x00600, last = 0x006FF, otf="arab", description = "Arabic" },
+ ["arabicextendeda"] = { first = 0x008A0, last = 0x008FF, description = "Arabic Extended-A" },
+ ["arabicmathematicalalphabeticsymbols"] = { first = 0x1EE00, last = 0x1EEFF, description = "Arabic Mathematical Alphabetic Symbols" },
+ ["arabicpresentationformsa"] = { first = 0x0FB50, last = 0x0FDFF, otf="arab", description = "Arabic Presentation Forms-A" },
+ ["arabicpresentationformsb"] = { first = 0x0FE70, last = 0x0FEFF, otf="arab", description = "Arabic Presentation Forms-B" },
+ ["arabicsupplement"] = { first = 0x00750, last = 0x0077F, otf="arab", description = "Arabic Supplement" },
+ ["armenian"] = { first = 0x00530, last = 0x0058F, otf="armn", description = "Armenian" },
+ ["arrows"] = { first = 0x02190, last = 0x021FF, description = "Arrows" },
+ ["avestan"] = { first = 0x10B00, last = 0x10B3F, description = "Avestan" },
+ ["balinese"] = { first = 0x01B00, last = 0x01B7F, otf="bali", description = "Balinese" },
+ ["bamum"] = { first = 0x0A6A0, last = 0x0A6FF, description = "Bamum" },
+ ["bamumsupplement"] = { first = 0x16800, last = 0x16A3F, description = "Bamum Supplement" },
+ ["basiclatin"] = { first = 0x00000, last = 0x0007F, otf="latn", description = "Basic Latin" },
+ ["batak"] = { first = 0x01BC0, last = 0x01BFF, description = "Batak" },
+ ["bengali"] = { first = 0x00980, last = 0x009FF, otf="beng", description = "Bengali" },
+ ["blockelements"] = { first = 0x02580, last = 0x0259F, otf="bopo", description = "Block Elements" },
+ ["bopomofo"] = { first = 0x03100, last = 0x0312F, otf="bopo", description = "Bopomofo" },
+ ["bopomofoextended"] = { first = 0x031A0, last = 0x031BF, otf="bopo", description = "Bopomofo Extended" },
+ ["boxdrawing"] = { first = 0x02500, last = 0x0257F, description = "Box Drawing" },
+ ["brahmi"] = { first = 0x11000, last = 0x1107F, description = "Brahmi" },
+ ["braillepatterns"] = { first = 0x02800, last = 0x028FF, otf="brai", description = "Braille Patterns" },
+ ["buginese"] = { first = 0x01A00, last = 0x01A1F, otf="bugi", description = "Buginese" },
+ ["buhid"] = { first = 0x01740, last = 0x0175F, otf="buhd", description = "Buhid" },
+ ["byzantinemusicalsymbols"] = { first = 0x1D000, last = 0x1D0FF, otf="byzm", description = "Byzantine Musical Symbols" },
+ ["commonindicnumberforms"] = { first = 0x0A830, last = 0x0A83F, description = "Common Indic Number Forms" },
+ ["carian"] = { first = 0x102A0, last = 0x102DF, description = "Carian" },
+ ["cham"] = { first = 0x0AA00, last = 0x0AA5F, description = "Cham" },
+ ["cherokee"] = { first = 0x013A0, last = 0x013FF, otf="cher", description = "Cherokee" },
+ ["cjkcompatibility"] = { first = 0x03300, last = 0x033FF, otf="hang", description = "CJK Compatibility" },
+ ["cjkcompatibilityforms"] = { first = 0x0FE30, last = 0x0FE4F, otf="hang", description = "CJK Compatibility Forms" },
+ ["cjkcompatibilityideographs"] = { first = 0x0F900, last = 0x0FAFF, otf="hang", description = "CJK Compatibility Ideographs" },
+ ["cjkcompatibilityideographssupplement"] = { first = 0x2F800, last = 0x2FA1F, otf="hang", description = "CJK Compatibility Ideographs Supplement" },
+ ["cjkradicalssupplement"] = { first = 0x02E80, last = 0x02EFF, otf="hang", description = "CJK Radicals Supplement" },
+ ["cjkstrokes"] = { first = 0x031C0, last = 0x031EF, otf="hang", description = "CJK Strokes" },
+ ["cjksymbolsandpunctuation"] = { first = 0x03000, last = 0x0303F, otf="hang", description = "CJK Symbols and Punctuation" },
+ ["cjkunifiedideographs"] = { first = 0x04E00, last = 0x09FFF, otf="hang", description = "CJK Unified Ideographs" },
+ ["cjkunifiedideographsextensiona"] = { first = 0x03400, last = 0x04DBF, otf="hang", description = "CJK Unified Ideographs Extension A" },
+ ["cjkunifiedideographsextensionb"] = { first = 0x20000, last = 0x2A6DF, otf="hang", description = "CJK Unified Ideographs Extension B" },
+ ["combiningdiacriticalmarks"] = { first = 0x00300, last = 0x0036F, description = "Combining Diacritical Marks" },
+ ["combiningdiacriticalmarksforsymbols"] = { first = 0x020D0, last = 0x020FF, description = "Combining Diacritical Marks for Symbols" },
+ ["combiningdiacriticalmarkssupplement"] = { first = 0x01DC0, last = 0x01DFF, description = "Combining Diacritical Marks Supplement" },
+ ["combininghalfmarks"] = { first = 0x0FE20, last = 0x0FE2F, description = "Combining Half Marks" },
+ ["controlpictures"] = { first = 0x02400, last = 0x0243F, description = "Control Pictures" },
+ ["coptic"] = { first = 0x02C80, last = 0x02CFF, otf="copt", description = "Coptic" },
+ ["countingrodnumerals"] = { first = 0x1D360, last = 0x1D37F, description = "Counting Rod Numerals" },
+ ["cuneiform"] = { first = 0x12000, last = 0x123FF, otf="xsux", description = "Cuneiform" },
+ ["cuneiformnumbersandpunctuation"] = { first = 0x12400, last = 0x1247F, otf="xsux", description = "Cuneiform Numbers and Punctuation" },
+ ["currencysymbols"] = { first = 0x020A0, last = 0x020CF, description = "Currency Symbols" },
+ ["cypriotsyllabary"] = { first = 0x10800, last = 0x1083F, otf="cprt", description = "Cypriot Syllabary" },
+ ["cyrillic"] = { first = 0x00400, last = 0x004FF, otf="cyrl", description = "Cyrillic" },
+ ["cyrillicextendeda"] = { first = 0x02DE0, last = 0x02DFF, otf="cyrl", description = "Cyrillic Extended-A" },
+ ["cyrillicextendedb"] = { first = 0x0A640, last = 0x0A69F, otf="cyrl", description = "Cyrillic Extended-B" },
+ ["cyrillicsupplement"] = { first = 0x00500, last = 0x0052F, otf="cyrl", description = "Cyrillic Supplement" },
+ ["deseret"] = { first = 0x10400, last = 0x1044F, otf="dsrt", description = "Deseret" },
+ ["devanagari"] = { first = 0x00900, last = 0x0097F, otf="deva", description = "Devanagari" },
+ ["devanagariextended"] = { first = 0x0A8E0, last = 0x0A8FF, description = "Devanagari Extended" },
+ ["dingbats"] = { first = 0x02700, last = 0x027BF, description = "Dingbats" },
+ ["dominotiles"] = { first = 0x1F030, last = 0x1F09F, description = "Domino Tiles" },
+ ["egyptianhieroglyphs"] = { first = 0x13000, last = 0x1342F, description = "Egyptian Hieroglyphs" },
+ ["emoticons"] = { first = 0x1F600, last = 0x1F64F, description = "Emoticons" },
+ ["enclosedalphanumericsupplement"] = { first = 0x1F100, last = 0x1F1FF, description = "Enclosed Alphanumeric Supplement" },
+ ["enclosedalphanumerics"] = { first = 0x02460, last = 0x024FF, description = "Enclosed Alphanumerics" },
+ ["enclosedcjklettersandmonths"] = { first = 0x03200, last = 0x032FF, description = "Enclosed CJK Letters and Months" },
+ ["enclosedideographicsupplement"] = { first = 0x1F200, last = 0x1F2FF, description = "Enclosed Ideographic Supplement" },
+ ["ethiopic"] = { first = 0x01200, last = 0x0137F, otf="ethi", description = "Ethiopic" },
+ ["ethiopicextended"] = { first = 0x02D80, last = 0x02DDF, otf="ethi", description = "Ethiopic Extended" },
+ ["ethiopicextendeda"] = { first = 0x0AB00, last = 0x0AB2F, description = "Ethiopic Extended-A" },
+ ["ethiopicsupplement"] = { first = 0x01380, last = 0x0139F, otf="ethi", description = "Ethiopic Supplement" },
+ ["generalpunctuation"] = { first = 0x02000, last = 0x0206F, description = "General Punctuation" },
+ ["geometricshapes"] = { first = 0x025A0, last = 0x025FF, description = "Geometric Shapes" },
+ ["georgian"] = { first = 0x010A0, last = 0x010FF, otf="geor", description = "Georgian" },
+ ["georgiansupplement"] = { first = 0x02D00, last = 0x02D2F, otf="geor", description = "Georgian Supplement" },
+ ["glagolitic"] = { first = 0x02C00, last = 0x02C5F, otf="glag", description = "Glagolitic" },
+ ["gothic"] = { first = 0x10330, last = 0x1034F, otf="goth", description = "Gothic" },
+ ["greekandcoptic"] = { first = 0x00370, last = 0x003FF, otf="grek", description = "Greek and Coptic" },
+ ["greekextended"] = { first = 0x01F00, last = 0x01FFF, otf="grek", description = "Greek Extended" },
+ ["gujarati"] = { first = 0x00A80, last = 0x00AFF, otf="gujr", description = "Gujarati" },
+ ["gurmukhi"] = { first = 0x00A00, last = 0x00A7F, otf="guru", description = "Gurmukhi" },
+ ["halfwidthandfullwidthforms"] = { first = 0x0FF00, last = 0x0FFEF, description = "Halfwidth and Fullwidth Forms" },
+ ["hangulcompatibilityjamo"] = { first = 0x03130, last = 0x0318F, otf="jamo", description = "Hangul Compatibility Jamo" },
+ ["hanguljamo"] = { first = 0x01100, last = 0x011FF, otf="jamo", description = "Hangul Jamo" },
+ ["hanguljamoextendeda"] = { first = 0x0A960, last = 0x0A97F, description = "Hangul Jamo Extended-A" },
+ ["hanguljamoextendedb"] = { first = 0x0D7B0, last = 0x0D7FF, description = "Hangul Jamo Extended-B" },
+ ["hangulsyllables"] = { first = 0x0AC00, last = 0x0D7AF, otf="hang", description = "Hangul Syllables" },
+ ["hanunoo"] = { first = 0x01720, last = 0x0173F, otf="hano", description = "Hanunoo" },
+ ["hebrew"] = { first = 0x00590, last = 0x005FF, otf="hebr", description = "Hebrew" },
+ ["highprivateusesurrogates"] = { first = 0x0DB80, last = 0x0DBFF, description = "High Private Use Surrogates" },
+ ["highsurrogates"] = { first = 0x0D800, last = 0x0DB7F, description = "High Surrogates" },
+ ["hiragana"] = { first = 0x03040, last = 0x0309F, otf="kana", description = "Hiragana" },
+ ["ideographicdescriptioncharacters"] = { first = 0x02FF0, last = 0x02FFF, description = "Ideographic Description Characters" },
+ ["imperialaramaic"] = { first = 0x10840, last = 0x1085F, description = "Imperial Aramaic" },
+ ["inscriptionalpahlavi"] = { first = 0x10B60, last = 0x10B7F, description = "Inscriptional Pahlavi" },
+ ["inscriptionalparthian"] = { first = 0x10B40, last = 0x10B5F, description = "Inscriptional Parthian" },
+ ["ipaextensions"] = { first = 0x00250, last = 0x002AF, description = "IPA Extensions" },
+ ["javanese"] = { first = 0x0A980, last = 0x0A9DF, description = "Javanese" },
+ ["kaithi"] = { first = 0x11080, last = 0x110CF, description = "Kaithi" },
+ ["kanasupplement"] = { first = 0x1B000, last = 0x1B0FF, description = "Kana Supplement" },
+ ["kanbun"] = { first = 0x03190, last = 0x0319F, description = "Kanbun" },
+ ["kangxiradicals"] = { first = 0x02F00, last = 0x02FDF, description = "Kangxi Radicals" },
+ ["kannada"] = { first = 0x00C80, last = 0x00CFF, otf="knda", description = "Kannada" },
+ ["katakana"] = { first = 0x030A0, last = 0x030FF, otf="kana", description = "Katakana" },
+ ["katakanaphoneticextensions"] = { first = 0x031F0, last = 0x031FF, otf="kana", description = "Katakana Phonetic Extensions" },
+ ["kayahli"] = { first = 0x0A900, last = 0x0A92F, description = "Kayah Li" },
+ ["kharoshthi"] = { first = 0x10A00, last = 0x10A5F, otf="khar", description = "Kharoshthi" },
+ ["khmer"] = { first = 0x01780, last = 0x017FF, otf="khmr", description = "Khmer" },
+ ["khmersymbols"] = { first = 0x019E0, last = 0x019FF, otf="khmr", description = "Khmer Symbols" },
+ ["lao"] = { first = 0x00E80, last = 0x00EFF, otf="lao", description = "Lao" },
+ ["latinextendeda"] = { first = 0x00100, last = 0x0017F, otf="latn", description = "Latin Extended-A" },
+ ["latinextendedadditional"] = { first = 0x01E00, last = 0x01EFF, otf="latn", description = "Latin Extended Additional" },
+ ["latinextendedb"] = { first = 0x00180, last = 0x0024F, otf="latn", description = "Latin Extended-B" },
+ ["latinextendedc"] = { first = 0x02C60, last = 0x02C7F, otf="latn", description = "Latin Extended-C" },
+ ["latinextendedd"] = { first = 0x0A720, last = 0x0A7FF, otf="latn", description = "Latin Extended-D" },
+ ["latinsupplement"] = { first = 0x00080, last = 0x000FF, otf="latn", description = "Latin-1 Supplement" },
+ ["lepcha"] = { first = 0x01C00, last = 0x01C4F, description = "Lepcha" },
+ ["letterlikesymbols"] = { first = 0x02100, last = 0x0214F, description = "Letterlike Symbols" },
+ ["limbu"] = { first = 0x01900, last = 0x0194F, otf="limb", description = "Limbu" },
+ ["linearbideograms"] = { first = 0x10080, last = 0x100FF, otf="linb", description = "Linear B Ideograms" },
+ ["linearbsyllabary"] = { first = 0x10000, last = 0x1007F, otf="linb", description = "Linear B Syllabary" },
+ ["lisu"] = { first = 0x0A4D0, last = 0x0A4FF, description = "Lisu" },
+ ["lowsurrogates"] = { first = 0x0DC00, last = 0x0DFFF, description = "Low Surrogates" },
+ ["lycian"] = { first = 0x10280, last = 0x1029F, description = "Lycian" },
+ ["lydian"] = { first = 0x10920, last = 0x1093F, description = "Lydian" },
+ ["mahjongtiles"] = { first = 0x1F000, last = 0x1F02F, description = "Mahjong Tiles" },
+ ["malayalam"] = { first = 0x00D00, last = 0x00D7F, otf="mlym", description = "Malayalam" },
+ ["mandiac"] = { first = 0x00840, last = 0x0085F, otf="mand", description = "Mandaic" },
+ ["mathematicalalphanumericsymbols"] = { first = 0x1D400, last = 0x1D7FF, description = "Mathematical Alphanumeric Symbols" },
+ ["mathematicaloperators"] = { first = 0x02200, last = 0x022FF, description = "Mathematical Operators" },
+ ["meeteimayek"] = { first = 0x0ABC0, last = 0x0ABFF, description = "Meetei Mayek" },
+ ["meeteimayekextensions"] = { first = 0x0AAE0, last = 0x0AAFF, description = "Meetei Mayek Extensions" },
+ ["meroiticcursive"] = { first = 0x109A0, last = 0x109FF, description = "Meroitic Cursive" },
+ ["meroitichieroglyphs"] = { first = 0x10980, last = 0x1099F, description = "Meroitic Hieroglyphs" },
+ ["miao"] = { first = 0x16F00, last = 0x16F9F, description = "Miao" },
+ ["miscellaneousmathematicalsymbolsa"] = { first = 0x027C0, last = 0x027EF, description = "Miscellaneous Mathematical Symbols-A" },
+ ["miscellaneousmathematicalsymbolsb"] = { first = 0x02980, last = 0x029FF, description = "Miscellaneous Mathematical Symbols-B" },
+ ["miscellaneoussymbols"] = { first = 0x02600, last = 0x026FF, description = "Miscellaneous Symbols" },
+ ["miscellaneoussymbolsandarrows"] = { first = 0x02B00, last = 0x02BFF, description = "Miscellaneous Symbols and Arrows" },
+ ["miscellaneoussymbolsandpictographs"] = { first = 0x1F300, last = 0x1F5FF, description = "Miscellaneous Symbols And Pictographs" },
+ ["miscellaneoustechnical"] = { first = 0x02300, last = 0x023FF, description = "Miscellaneous Technical" },
+ ["modifiertoneletters"] = { first = 0x0A700, last = 0x0A71F, description = "Modifier Tone Letters" },
+ ["mongolian"] = { first = 0x01800, last = 0x018AF, otf="mong", description = "Mongolian" },
+ ["musicalsymbols"] = { first = 0x1D100, last = 0x1D1FF, otf="musc", description = "Musical Symbols" },
+ ["myanmar"] = { first = 0x01000, last = 0x0109F, otf="mymr", description = "Myanmar" },
+ ["myanmarextendeda"] = { first = 0x0AA60, last = 0x0AA7F, description = "Myanmar Extended-A" },
+ ["newtailue"] = { first = 0x01980, last = 0x019DF, description = "New Tai Lue" },
+ ["nko"] = { first = 0x007C0, last = 0x007FF, otf="nko", description = "NKo" },
+ ["numberforms"] = { first = 0x02150, last = 0x0218F, description = "Number Forms" },
+ ["ogham"] = { first = 0x01680, last = 0x0169F, otf="ogam", description = "Ogham" },
+ ["olchiki"] = { first = 0x01C50, last = 0x01C7F, description = "Ol Chiki" },
+ ["olditalic"] = { first = 0x10300, last = 0x1032F, otf="ital", description = "Old Italic" },
+ ["oldpersian"] = { first = 0x103A0, last = 0x103DF, otf="xpeo", description = "Old Persian" },
+ ["oldsoutharabian"] = { first = 0x10A60, last = 0x10A7F, description = "Old South Arabian" },
+ ["odlturkic"] = { first = 0x10C00, last = 0x10C4F, description = "Old Turkic" },
+ ["opticalcharacterrecognition"] = { first = 0x02440, last = 0x0245F, description = "Optical Character Recognition" },
+ ["oriya"] = { first = 0x00B00, last = 0x00B7F, otf="orya", description = "Oriya" },
+ ["osmanya"] = { first = 0x10480, last = 0x104AF, otf="osma", description = "Osmanya" },
+ ["phagspa"] = { first = 0x0A840, last = 0x0A87F, otf="phag", description = "Phags-pa" },
+ ["phaistosdisc"] = { first = 0x101D0, last = 0x101FF, description = "Phaistos Disc" },
+ ["phoenician"] = { first = 0x10900, last = 0x1091F, otf="phnx", description = "Phoenician" },
+ ["phoneticextensions"] = { first = 0x01D00, last = 0x01D7F, description = "Phonetic Extensions" },
+ ["phoneticextensionssupplement"] = { first = 0x01D80, last = 0x01DBF, description = "Phonetic Extensions Supplement" },
+ ["playingcards"] = { first = 0x1F0A0, last = 0x1F0FF, description = "Playing Cards" },
+ ["privateusearea"] = { first = 0x0E000, last = 0x0F8FF, description = "Private Use Area" },
+ ["rejang"] = { first = 0x0A930, last = 0x0A95F, description = "Rejang" },
+ ["ruminumeralsymbols"] = { first = 0x10E60, last = 0x10E7F, description = "Rumi Numeral Symbols" },
+ ["runic"] = { first = 0x016A0, last = 0x016FF, otf="runr", description = "Runic" },
+ ["samaritan"] = { first = 0x00800, last = 0x0083F, description = "Samaritan" },
+ ["saurashtra"] = { first = 0x0A880, last = 0x0A8DF, description = "Saurashtra" },
+ ["sharada"] = { first = 0x11180, last = 0x111DF, description = "Sharada" },
+ ["shavian"] = { first = 0x10450, last = 0x1047F, otf="shaw", description = "Shavian" },
+ ["sinhala"] = { first = 0x00D80, last = 0x00DFF, otf="sinh", description = "Sinhala" },
+ ["smallformvariants"] = { first = 0x0FE50, last = 0x0FE6F, description = "Small Form Variants" },
+ ["sorasompeng"] = { first = 0x110D0, last = 0x110FF, description = "Sora Sompeng" },
+ ["spacingmodifierletters"] = { first = 0x002B0, last = 0x002FF, description = "Spacing Modifier Letters" },
+ ["specials"] = { first = 0x0FFF0, last = 0x0FFFF, description = "Specials" },
+ ["sundanese"] = { first = 0x01B80, last = 0x01BBF, description = "Sundanese" },
+ ["sundanesesupplement"] = { first = 0x01CC0, last = 0x01CCF, description = "Sundanese Supplement" },
+ ["superscriptsandsubscripts"] = { first = 0x02070, last = 0x0209F, description = "Superscripts and Subscripts" },
+ ["supplementalarrowsa"] = { first = 0x027F0, last = 0x027FF, description = "Supplemental Arrows-A" },
+ ["supplementalarrowsb"] = { first = 0x02900, last = 0x0297F, description = "Supplemental Arrows-B" },
+ ["supplementalmathematicaloperators"] = { first = 0x02A00, last = 0x02AFF, description = "Supplemental Mathematical Operators" },
+ ["supplementalpunctuation"] = { first = 0x02E00, last = 0x02E7F, description = "Supplemental Punctuation" },
+ ["supplementaryprivateuseareaa"] = { first = 0xF0000, last = 0xFFFFF, description = "Supplementary Private Use Area-A" },
+ ["supplementaryprivateuseareab"] = { first = 0x100000,last = 0x10FFFF, description = "Supplementary Private Use Area-B" },
+ ["sylotinagri"] = { first = 0x0A800, last = 0x0A82F, otf="sylo", description = "Syloti Nagri" },
+ ["syriac"] = { first = 0x00700, last = 0x0074F, otf="syrc", description = "Syriac" },
+ ["tagalog"] = { first = 0x01700, last = 0x0171F, otf="tglg", description = "Tagalog" },
+ ["tagbanwa"] = { first = 0x01760, last = 0x0177F, otf="tagb", description = "Tagbanwa" },
+ ["tags"] = { first = 0xE0000, last = 0xE007F, description = "Tags" },
+ ["taile"] = { first = 0x01950, last = 0x0197F, otf="tale", description = "Tai Le" },
+ ["taitham"] = { first = 0x01A20, last = 0x01AAF, description = "Tai Tham" },
+ ["taiviet"] = { first = 0x0AA80, last = 0x0AADF, description = "Tai Viet" },
+ ["taixuanjingsymbols"] = { first = 0x1D300, last = 0x1D35F, description = "Tai Xuan Jing Symbols" },
+ ["takri"] = { first = 0x11680, last = 0x116CF, description = "Takri" },
+ ["tamil"] = { first = 0x00B80, last = 0x00BFF, otf="taml", description = "Tamil" },
+ ["telugu"] = { first = 0x00C00, last = 0x00C7F, otf="telu", description = "Telugu" },
+ ["thaana"] = { first = 0x00780, last = 0x007BF, otf="thaa", description = "Thaana" },
+ ["thai"] = { first = 0x00E00, last = 0x00E7F, otf="thai", description = "Thai" },
+ ["tibetan"] = { first = 0x00F00, last = 0x00FFF, otf="tibt", description = "Tibetan" },
+ ["tifinagh"] = { first = 0x02D30, last = 0x02D7F, otf="tfng", description = "Tifinagh" },
+ ["transportandmapsymbols"] = { first = 0x1F680, last = 0x1F6FF, description = "Transport And Map Symbols" },
+ ["ugaritic"] = { first = 0x10380, last = 0x1039F, otf="ugar", description = "Ugaritic" },
+ ["unifiedcanadianaboriginalsyllabics"] = { first = 0x01400, last = 0x0167F, otf="cans", description = "Unified Canadian Aboriginal Syllabics" },
+ ["unifiedcanadianaboriginalsyllabicsextended"] = { first = 0x018B0, last = 0x018FF, description = "Unified Canadian Aboriginal Syllabics Extended" },
+ ["vai"] = { first = 0x0A500, last = 0x0A63F, description = "Vai" },
+ ["variationselectors"] = { first = 0x0FE00, last = 0x0FE0F, description = "Variation Selectors" },
+ ["variationselectorssupplement"] = { first = 0xE0100, last = 0xE01EF, description = "Variation Selectors Supplement" },
+ ["vedicextensions"] = { first = 0x01CD0, last = 0x01CFF, description = "Vedic Extensions" },
+ ["verticalforms"] = { first = 0x0FE10, last = 0x0FE1F, description = "Vertical Forms" },
+ ["yijinghexagramsymbols"] = { first = 0x04DC0, last = 0x04DFF, otf="yi", description = "Yijing Hexagram Symbols" },
+ ["yiradicals"] = { first = 0x0A490, last = 0x0A4CF, otf="yi", description = "Yi Radicals" },
+ ["yisyllables"] = { first = 0x0A000, last = 0x0A48F, otf="yi", description = "Yi Syllables" },
+}
+
+characters.blocks = blocks
+
+function characters.blockrange(name)
+ local b = blocks[name]
+ if b then
+ return b.first, b.last
+ else
+ return 0, 0
+ end
+end
+
+setmetatableindex(blocks, function(t,k) -- we could use an intermediate table if called often
+ return k and rawget(t,lower(gsub(k,"[^a-zA-Z]","")))
+end)
+
+local otfscripts = utilities.storage.allocate()
+characters.otfscripts = otfscripts
+
+setmetatableindex(otfscripts,function(t,unicode)
+ for k, v in next, blocks do
+ local first, last = v.first, v.last
+ if unicode >= first and unicode <= last then
+ local script = v.otf or "dflt"
+ for u=first,last do
+ t[u] = script
+ end
+ return script
+ end
+ end
+ -- pretty slow when we're here
+ t[unicode] = "dflt"
+ return "dflt"
+end)
+
+function characters.getrange(name) -- used in font fallback definitions (name or range)
+ local range = blocks[name]
+ if range then
+ return range.first, range.last, range.description
+ end
+ name = gsub(name,'"',"0x") -- goodie: tex hex notation
+ local start, stop = match(name,"^(.-)[%-%:](.-)$")
+ if start and stop then
+ start, stop = tonumber(start,16) or tonumber(start), tonumber(stop,16) or tonumber(stop)
+ if start and stop then
+ return start, stop, nil
+ end
+ end
+ local slot = tonumber(name,16) or tonumber(name)
+ return slot, slot, nil
+end
+
+local categorytags = allocate {
+ lu = "Letter Uppercase",
+ ll = "Letter Lowercase",
+ lt = "Letter Titlecase",
+ lm = "Letter Modifier",
+ lo = "Letter Other",
+ mn = "Mark Nonspacing",
+ mc = "Mark Spacing Combining",
+ me = "Mark Enclosing",
+ nd = "Number Decimal Digit",
+ nl = "Number Letter",
+ no = "Number Other",
+ pc = "Punctuation Connector",
+ pd = "Punctuation Dash",
+ ps = "Punctuation Open",
+ pe = "Punctuation Close",
+ pi = "Punctuation Initial Quote",
+ pf = "Punctuation Final Quote",
+ po = "Punctuation Other",
+ sm = "Symbol Math",
+ sc = "Symbol Currency",
+ sk = "Symbol Modifier",
+ so = "Symbol Other",
+ zs = "Separator Space",
+ zl = "Separator Line",
+ zp = "Separator Paragraph",
+ cc = "Other Control",
+ cf = "Other Format",
+ cs = "Other Surrogate",
+ co = "Other Private Use",
+ cn = "Other Not Assigned",
+}
+
+characters.categorytags = categorytags
+
+--~ special : cf (softhyphen) zs (emspace)
+--~ characters: ll lm lo lt lu mn nl no pc pd pe pf pi po ps sc sk sm so
+
+local is_character = allocate ( tohash {
+ "lu","ll","lt","lm","lo",
+ "nd","nl","no",
+ "mn",
+ "nl","no",
+ "pc","pd","ps","pe","pi","pf","po",
+ "sm","sc","sk","so"
+} )
+
+local is_letter = allocate ( tohash {
+ "ll","lm","lo","lt","lu"
+} )
+
+local is_command = allocate ( tohash {
+ "cf","zs"
+} )
+
+local is_spacing = allocate ( tohash {
+ "zs", "zl","zp",
+} )
+
+local is_mark = allocate ( tohash {
+ "mn", "ms",
+} )
+
+-- to be redone: store checked characters
+
+characters.is_character = is_character
+characters.is_letter = is_letter
+characters.is_command = is_command
+characters.is_spacing = is_spacing
+characters.is_mark = is_mark
+
+local mt = { -- yes or no ?
+ __index = function(t,k)
+ if type(k) == "number" then
+ local c = data[k].category
+ return c and rawget(t,c)
+ else
+ -- avoid auto conversion in data.characters lookups
+ end
+ end
+}
+
+setmetatableindex(characters.is_character, mt)
+setmetatableindex(characters.is_letter, mt)
+setmetatableindex(characters.is_command, mt)
+setmetatableindex(characters.is_spacing, mt)
+
+-- linebreak: todo: hash
+--
+-- normative : BK CR LF CM SG GL CB SP ZW NL WJ JL JV JT H2 H3
+-- informative : XX OP CL QU NS EX SY IS PR PO NU AL ID IN HY BB BA SA AI B2 new:CP
+
+-- east asian width:
+--
+-- N A H W F Na
+
+characters.bidi = allocate {
+ l = "Left-to-Right",
+ lre = "Left-to-Right Embedding",
+ lro = "Left-to-Right Override",
+ r = "Right-to-Left",
+ al = "Right-to-Left Arabic",
+ rle = "Right-to-Left Embedding",
+ rlo = "Right-to-Left Override",
+ pdf = "Pop Directional Format",
+ en = "European Number",
+ es = "European Number Separator",
+ et = "European Number Terminator",
+ an = "Arabic Number",
+ cs = "Common Number Separator",
+ nsm = "Non-Spacing Mark",
+ bn = "Boundary Neutral",
+ b = "Paragraph Separator",
+ s = "Segment Separator",
+ ws = "Whitespace",
+ on = "Other Neutrals",
+}
+
+--[[ldx--
+<p>At this point we assume that the big data table is loaded. From this
+table we derive a few more.</p>
+--ldx]]--
+
+if not characters.fallbacks then
+
+ characters.fallbacks = { } -- not than many
+
+ local fallbacks = characters.fallbacks
+
+ for k, d in next, data do
+ local specials = d.specials
+ if specials and specials[1] == "compat" and specials[2] == 0x0020 then
+ local s = specials[3]
+ if s then
+ fallbacks[k] = s
+ fallbacks[s] = k
+ end
+ end
+ end
+
+end
+
+if storage then
+ storage.register("characters/fallbacks", characters.fallbacks, "characters.fallbacks") -- accents and such
+end
+
+characters.directions = { }
+
+setmetatableindex(characters.directions,function(t,k)
+ local d = data[k]
+ if d then
+ local v = d.direction
+ if v then
+ t[k] = v
+ return v
+ end
+ end
+ t[k] = false -- maybe 'l'
+ return v
+end)
+
+--[[ldx--
+<p>Next comes a whole series of helper methods. These are (will be) part
+of the official <l n='api'/>.</p>
+--ldx]]--
+
+-- we could make them virtual: characters.contextnames[n]
+
+function characters.contextname(n) return data[n].contextname or "" end
+function characters.adobename (n) return data[n].adobename or "" end
+function characters.description(n) return data[n].description or "" end
+-------- characters.category (n) return data[n].category or "" end
+
+function characters.category(n,verbose)
+ local c = data[n].category
+ if not c then
+ return ""
+ elseif verbose then
+ return categorytags[c]
+ else
+ return c
+ end
+end
+
+-- -- some day we will make a table .. not that many calls to utfchar
+--
+-- local utfchar = utf.char
+-- local utfbyte = utf.byte
+-- local utfbytes = { }
+-- local utfchars = { }
+--
+-- table.setmetatableindex(utfbytes,function(t,k) local v = utfchar(k) t[k] = v return v end)
+-- table.setmetatableindex(utfchars,function(t,k) local v = utfbyte(k) t[k] = v return v end)
+
+local function toutfstring(s)
+ if type(s) == "table" then
+ return utfchar(unpack(s)) -- concat { utfchar( unpack(s) ) }
+ else
+ return utfchar(s)
+ end
+end
+
+utf.tostring = toutfstring
+
+local categories = allocate() characters.categories = categories -- lazy table
+
+setmetatableindex(categories, function(t,u) if u then local c = data[u] c = c and c.category or u t[u] = c return c end end)
+
+local lccodes = allocate() characters.lccodes = lccodes -- lazy table
+local uccodes = allocate() characters.uccodes = uccodes -- lazy table
+local shcodes = allocate() characters.shcodes = shcodes -- lazy table
+local fscodes = allocate() characters.fscodes = fscodes -- lazy table
+
+setmetatableindex(lccodes, function(t,u) if u then local c = data[u] c = c and c.lccode or (type(u) == "string" and utfbyte(u)) or u t[u] = c return c end end)
+setmetatableindex(uccodes, function(t,u) if u then local c = data[u] c = c and c.uccode or (type(u) == "string" and utfbyte(u)) or u t[u] = c return c end end)
+setmetatableindex(shcodes, function(t,u) if u then local c = data[u] c = c and c.shcode or (type(u) == "string" and utfbyte(u)) or u t[u] = c return c end end)
+setmetatableindex(fscodes, function(t,u) if u then local c = data[u] c = c and c.fscode or (type(u) == "string" and utfbyte(u)) or u t[u] = c return c end end)
+
+local lcchars = allocate() characters.lcchars = lcchars -- lazy table
+local ucchars = allocate() characters.ucchars = ucchars -- lazy table
+local shchars = allocate() characters.shchars = shchars -- lazy table
+local fschars = allocate() characters.fschars = fschars -- lazy table
+
+setmetatableindex(lcchars, function(t,u) if u then local c = data[u] c = c and c.lccode c = c and toutfstring(c) or (type(u) == "number" and utfchar(u)) or u t[u] = c return c end end)
+setmetatableindex(ucchars, function(t,u) if u then local c = data[u] c = c and c.uccode c = c and toutfstring(c) or (type(u) == "number" and utfchar(u)) or u t[u] = c return c end end)
+setmetatableindex(shchars, function(t,u) if u then local c = data[u] c = c and c.shcode c = c and toutfstring(c) or (type(u) == "number" and utfchar(u)) or u t[u] = c return c end end)
+setmetatableindex(fschars, function(t,u) if u then local c = data[u] c = c and c.fscode c = c and toutfstring(c) or (type(u) == "number" and utfchar(u)) or u t[u] = c return c end end)
+
+local decomposed = allocate() characters.decomposed = decomposed -- lazy table
+local specials = allocate() characters.specials = specials -- lazy table
+
+setmetatableindex(decomposed, function(t,u) -- either a table or false
+ if u then
+ local c = data[u]
+ local s = c and c.decomposed or false -- could fall back to specials
+ t[u] = s
+ return s
+ end
+end)
+
+setmetatableindex(specials, function(t,u) -- either a table or false
+ if u then
+ local c = data[u]
+ local s = c and c.specials or false
+ t[u] = s
+ return s
+ end
+end)
+
+local specialchars = allocate() characters.specialchars = specialchars -- lazy table
+local descriptions = allocate() characters.descriptions = descriptions -- lazy table
+
+setmetatableindex(specialchars, function(t,u)
+ if u then
+ local c = data[u]
+ local s = c and c.specials
+ if s then
+ local tt, ttn = { }, 0
+ for i=2,#s do
+ local si = s[i]
+ local c = data[si]
+ if is_letter[c.category] then
+ ttn = ttn + 1
+ tt[ttn] = utfchar(si)
+ end
+ end
+ c = concat(tt)
+ t[u] = c
+ return c
+ else
+ if type(u) == "number" then
+ u = utfchar(u)
+ end
+ t[u] = u
+ return u
+ end
+ end
+end)
+
+setmetatableindex(descriptions, function(t,k)
+ -- 0.05 - 0.10 sec
+ for u, c in next, data do
+ local d = c.description
+ if d then
+ d = gsub(d," ","")
+ d = lower(d)
+ t[d] = u
+ end
+ end
+ local d = rawget(t,k)
+ if not d then
+ t[k] = k
+ end
+ return d
+end)
+
+function characters.unicodechar(asked)
+ local n = tonumber(asked)
+ if n then
+ return n
+ elseif type(asked) == "string" then
+ return descriptions[asked] or descriptions[gsub(asked," ","")]
+ end
+end
+
+-- function characters.lower(str)
+-- local new, n = { }, 0
+-- for u in utfvalues(str) do
+-- n = n + 1
+-- new[n] = lcchars[u]
+-- end
+-- return concat(new)
+-- end
+--
+-- function characters.upper(str)
+-- local new, n = { }, 0
+-- for u in utfvalues(str) do
+-- n = n + 1
+-- new[n] = ucchars[u]
+-- end
+-- return concat(new)
+-- end
+--
+-- function characters.shaped(str)
+-- local new, n = { }, 0
+-- for u in utfvalues(str) do
+-- n = n + 1
+-- new[n] = shchars[u]
+-- end
+-- return concat(new)
+-- end
+
+----- tolower = Cs((utf8byte/lcchars)^0)
+----- toupper = Cs((utf8byte/ucchars)^0)
+----- toshape = Cs((utf8byte/shchars)^0)
+
+local tolower = Cs((utf8char/lcchars)^0)
+local toupper = Cs((utf8char/ucchars)^0)
+local toshape = Cs((utf8char/shchars)^0)
+
+patterns.tolower = tolower
+patterns.toupper = toupper
+patterns.toshape = toshape
+
+function characters.lower (str) return lpegmatch(tolower,str) end
+function characters.upper (str) return lpegmatch(toupper,str) end
+function characters.shaped(str) return lpegmatch(toshape,str) end
+
+function characters.lettered(str,spacing)
+ local new, n = { }, 0
+ if spacing then
+ local done = false
+ for u in utfvalues(str) do
+ local c = data[u].category
+ if is_letter[c] then
+ if done and n > 1 then
+ n = n + 1
+ new[n] = " "
+ done = false
+ end
+ n = n + 1
+ new[n] = utfchar(u)
+ elseif spacing and is_spacing[c] then
+ done = true
+ end
+ end
+ else
+ for u in utfvalues(str) do
+ if is_letter[data[u].category] then
+ n = n + 1
+ new[n] = utfchar(u)
+ end
+ end
+ end
+ return concat(new)
+end
+
+--[[ldx--
+<p>Requesting lower and uppercase codes:</p>
+--ldx]]--
+
+function characters.uccode(n) return uccodes[n] end -- obsolete
+function characters.lccode(n) return lccodes[n] end -- obsolete
+
+function characters.safechar(n)
+ local c = data[n]
+ if c and c.contextname then
+ return "\\" .. c.contextname
+ else
+ return utfchar(n)
+ end
+end
+
+function characters.shape(n)
+ local shcode = shcodes[n]
+ if not shcode then
+ return n, nil
+ elseif type(shcode) == "table" then
+ return shcode[1], shcode[#shcode]
+ else
+ return shcode, nil
+ end
+end
+
+-- -- some day we might go this route, but it does not really save that much
+-- -- so not now (we can generate a lot using mtx-unicode that operates on the
+-- -- database)
+--
+-- -- category cjkwd direction linebreak
+--
+-- -- adobename comment contextcommand contextname description fallback lccode
+-- -- mathclass mathfiller mathname mathspec mathstretch mathsymbol mirror
+-- -- range shcode specials uccode uccodes unicodeslot
+--
+-- local data = {
+-- ['one']={
+-- common = {
+-- category="cc",
+-- direction="bn",
+-- linebreak="cm",
+-- },
+-- vector = {
+-- [0x0000] = {
+-- description="NULL",
+-- group='one',
+-- unicodeslot=0x0000,
+-- },
+-- {
+-- description="START OF HEADING",
+-- group='one',
+-- unicodeslot=0x0001,
+-- },
+-- }
+-- }
+-- }
+--
+-- local chardata, groupdata = { }, { }
+--
+-- for group, gdata in next, data do
+-- local common, vector = { __index = gdata.common }, gdata.vector
+-- for character, cdata in next, vector do
+-- chardata[character] = cdata
+-- setmetatable(cdata,common)
+-- end
+-- groupdata[group] = gdata
+-- end
+
+--~ characters.data, characters.groups = chardata, groupdata
+
+--~ [0xF0000]={
+--~ category="co",
+--~ cjkwd="a",
+--~ description="<Plane 0x000F Private Use, First>",
+--~ direction="l",
+--~ unicodeslot=0xF0000,
+--~ },
+--~ [0xFFFFD]={
+--~ category="co",
+--~ cjkwd="a",
+--~ description="<Plane 0x000F Private Use, Last>",
+--~ direction="l",
+--~ unicodeslot=0xFFFFD,
+--~ },
+--~ [0x100000]={
+--~ category="co",
+--~ cjkwd="a",
+--~ description="<Plane 0x0010 Private Use, First>",
+--~ direction="l",
+--~ unicodeslot=0x100000,
+--~ },
+--~ [0x10FFFD]={
+--~ category="co",
+--~ cjkwd="a",
+--~ description="<Plane 0x0010 Private Use, Last>",
+--~ direction="l",
+--~ unicodeslot=0x10FFFD,
+--~ },
+
+if not characters.superscripts then
+
+ local superscripts = allocate() characters.superscripts = superscripts
+ local subscripts = allocate() characters.subscripts = subscripts
+
+ -- skipping U+02120 (service mark) U+02122 (trademark)
+
+ for k, v in next, data do
+ local specials = v.specials
+ if specials then
+ local what = specials[1]
+ if what == "super" then
+ if #specials == 2 then
+ superscripts[k] = specials[2]
+ else
+ report_defining("ignoring %s %a, char %c, description %a","superscript",ustring(k),k,v.description)
+ end
+ elseif what == "sub" then
+ if #specials == 2 then
+ subscripts[k] = specials[2]
+ else
+ report_defining("ignoring %s %a, char %c, description %a","subscript",ustring(k),k,v.description)
+ end
+ end
+ end
+ end
+
+ -- print(table.serialize(superscripts, "superscripts", { hexify = true }))
+ -- print(table.serialize(subscripts, "subscripts", { hexify = true }))
+
+ if storage then
+ storage.register("characters/superscripts", superscripts, "characters.superscripts")
+ storage.register("characters/subscripts", subscripts, "characters.subscripts")
+ end
+
+end
+
+-- for the moment only a few
+
+local tracedchars = utilities.strings.tracers
+
+tracedchars[0x00] = "[signal]"
+tracedchars[0x20] = "[space]"
+
+-- the following code will move to char-tex.lua
+
+-- tex
+
+if not tex or not context or not commands then return characters end
+
+local tex = tex
+local texsetlccode = tex.setlccode
+local texsetuccode = tex.setuccode
+local texsetsfcode = tex.setsfcode
+local texsetcatcode = tex.setcatcode
+
+local contextsprint = context.sprint
+local ctxcatcodes = catcodes.numbers.ctxcatcodes
+
+--[[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]]--
+
+function commands.makeactive(n,name) --
+ contextsprint(ctxcatcodes,format("\\catcode%s=13\\unexpanded\\def %s{\\%s}",n,utfchar(n),name))
+ -- context("\\catcode%s=13\\unexpanded\\def %s{\\%s}",n,utfchar(n),name)
+end
+
+function commands.utfchar(c,n)
+ if n then
+ -- contextsprint(c,charfromnumber(n))
+ contextsprint(c,utfchar(n))
+ else
+ -- contextsprint(charfromnumber(c))
+ contextsprint(utfchar(c))
+ end
+end
+
+function commands.safechar(n)
+ local c = data[n]
+ if c and c.contextname then
+ contextsprint("\\" .. c.contextname) -- context[c.contextname]()
+ else
+ contextsprint(utfchar(n))
+ end
+end
+
+tex.uprint = commands.utfchar
+
+local forbidden = tohash { -- at least now
+ 0x00A0,
+ 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x200B, 0x200C, 0x200D,
+ 0x202F,
+ 0x205F,
+ -- 0xFEFF,
+}
+
+function characters.define(tobelettered, tobeactivated) -- catcodetables
+
+ if trace_defining then
+ report_defining("defining active character commands")
+ end
+
+ local activated, a = { }, 0
+
+ for u, chr in next, data do -- these will be commands
+ local fallback = chr.fallback
+ if fallback then
+ contextsprint("{\\catcode",u,"=13\\unexpanded\\gdef ",utfchar(u),"{\\checkedchar{",u,"}{",fallback,"}}}")
+ a = a + 1
+ activated[a] = u
+ else
+ local contextname = chr.contextname
+ if contextname then
+ local category = chr.category
+ if is_character[category] then
+ if chr.unicodeslot < 128 then
+ if is_letter[category] then
+ contextsprint(ctxcatcodes,format("\\def\\%s{%s}",contextname,utfchar(u))) -- has no s
+ else
+ contextsprint(ctxcatcodes,format("\\chardef\\%s=%s",contextname,u)) -- has no s
+ end
+ else
+ contextsprint(ctxcatcodes,format("\\def\\%s{%s}",contextname,utfchar(u))) -- has no s
+ end
+ elseif is_command[category] and not forbidden[u] then
+ contextsprint("{\\catcode",u,"=13\\unexpanded\\gdef ",utfchar(u),"{\\"..contextname,"}}")
+ a = a + 1
+ activated[a] = u
+ end
+ end
+ end
+ end
+
+ if tobelettered then -- shared
+ local saved = tex.catcodetable
+ for i=1,#tobelettered do
+ tex.catcodetable = tobelettered[i]
+ if trace_defining then
+ report_defining("defining letters (global, shared)")
+ end
+ for u, chr in next, data do
+ if not chr.fallback and is_letter[chr.category] and u >= 128 and u <= 65536 then
+ texsetcatcode(u,11)
+ end
+ local range = chr.range
+ if range then
+ for i=1,range.first,range.last do
+ texsetcatcode(i,11)
+ end
+ end
+ end
+ texsetcatcode(0x200C,11) -- non-joiner
+ texsetcatcode(0x200D,11) -- joiner
+ end
+ tex.catcodetable = saved
+ end
+
+ local nofactivated = #tobeactivated
+ if tobeactivated and nofactivated > 0 then
+ for i=1,nofactivated do
+ local u = activated[i]
+ if u then
+ report_defining("character %U is active in set %a, containing %a",u,data[u].description,tobeactivated)
+ end
+ end
+ local saved = tex.catcodetable
+ for i=1,#tobeactivated do
+ local vector = tobeactivated[i]
+ if trace_defining then
+ report_defining("defining %a active characters in vector %a",nofactivated,vector)
+ end
+ tex.catcodetable = vector
+ for i=1,nofactivated do
+ local u = activated[i]
+ if u then
+ texsetcatcode(u,13)
+ end
+ end
+ end
+ tex.catcodetable = saved
+ end
+
+end
+
+--[[ldx--
+<p>Setting the lccodes is also done in a loop over the data table.</p>
+--ldx]]--
+
+local sfmode = "unset" -- unset, traditional, normal
+
+function characters.setcodes()
+ if trace_defining then
+ report_defining("defining lc and uc codes")
+ end
+ local traditional = sfstate == "traditional" or sfstate == "unset"
+ for code, chr in next, data do
+ local cc = chr.category
+ if is_letter[cc] then
+ local range = chr.range
+ if range then
+ for i=range.first,range.last do
+ texsetcatcode(i,11) -- letter
+ texsetlccode(i,i,i) -- self self
+ end
+ else
+ local lc, uc = chr.lccode, chr.uccode
+ if not lc then
+ chr.lccode, lc = code, code
+ elseif type(lc) == "table" then
+ lc = code
+ end
+ if not uc then
+ chr.uccode, uc = code, code
+ elseif type(uc) == "table" then
+ uc = code
+ end
+ texsetcatcode(code,11) -- letter
+ texsetlccode(code,lc,uc)
+ if traditional and cc == "lu" then
+ texsetsfcode(code,999)
+ end
+ end
+ elseif is_mark[cc] then
+ texsetlccode(code,code,code) -- for hyphenation
+ end
+ end
+ if traditional then
+ sfstate = "traditional"
+ end
+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 code, chr in next, data do
+ if chr.category == "lu" then
+ texsetsfcode(code,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)
+
+-- xml
+
+characters.activeoffset = 0x10000 -- there will be remapped in that byte range
+
+function commands.remapentity(chr,slot)
+ contextsprint(format("{\\catcode%s=13\\xdef%s{\\string%s}}",slot,utfchar(slot),chr))
+end
+
+-- xml.entities = xml.entities or { }
+--
+-- storage.register("xml/entities",xml.entities,"xml.entities") -- this will move to lxml
+--
+-- function characters.setmkiventities()
+-- local entities = xml.entities
+-- entities.lt = "<"
+-- entities.amp = "&"
+-- entities.gt = ">"
+-- end
+--
+-- function characters.setmkiientities()
+-- local entities = xml.entities
+-- entities.lt = utfchar(characters.activeoffset + utfbyte("<"))
+-- entities.amp = utfchar(characters.activeoffset + utfbyte("&"))
+-- entities.gt = utfchar(characters.activeoffset + utfbyte(">"))
+-- end
+
diff --git a/tex/context/base/char-map.lua b/tex/context/base/char-map.lua
index e0e275169..749da5289 100644
--- a/tex/context/base/char-map.lua
+++ b/tex/context/base/char-map.lua
@@ -1,1072 +1,1072 @@
-if not modules then modules = { } end modules ['char-map'] = {
- version = 1.001,
- comment = "companion to char-ini.mkiv",
- author = "Hans Hagen & Arthur Reutenauer",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files",
- dataonly = true,
-}
-
--- not yet used
-
-characters = characters or { }
-
-characters.casemap={
- [0x0049]={
- ["az"]={
- ["not_before_dot"]={
- ["lower"]={ 0x0131 },
- ["title"]={ 0x0049 },
- ["upper"]={ 0x0049 },
- },
- },
- ["lt"]={
- ["more_above"]={
- ["lower"]={ 0x0069, 0x0307 },
- ["title"]={ 0x0049 },
- ["upper"]={ 0x0049 },
- },
- },
- ["tr"]={
- ["not_before_dot"]={
- ["lower"]={ 0x0131 },
- ["title"]={ 0x0049 },
- ["upper"]={ 0x0049 },
- },
- },
- },
- [0x004A]={
- ["lt"]={
- ["more_above"]={
- ["lower"]={ 0x006A, 0x0307 },
- ["title"]={ 0x004A },
- ["upper"]={ 0x004A },
- },
- },
- },
- [0x0069]={
- ["az"]={
- ["all"]={
- ["lower"]={ 0x0069 },
- ["title"]={ 0x0130 },
- ["upper"]={ 0x0130 },
- },
- },
- ["tr"]={
- ["all"]={
- ["lower"]={ 0x0069 },
- ["title"]={ 0x0130 },
- ["upper"]={ 0x0130 },
- },
- },
- },
- [0x00CC]={
- ["lt"]={
- ["all"]={
- ["lower"]={ 0x0069, 0x0307, 0x0300 },
- ["title"]={ 0x00CC },
- ["upper"]={ 0x00CC },
- },
- },
- },
- [0x00CD]={
- ["lt"]={
- ["all"]={
- ["lower"]={ 0x0069, 0x0307, 0x0301 },
- ["title"]={ 0x00CD },
- ["upper"]={ 0x00CD },
- },
- },
- },
- [0x00DF]={
- [""]={
- ["all"]={
- ["lower"]={ 0x00DF },
- ["title"]={ 0x0053, 0x0073 },
- ["upper"]={ 0x0053, 0x0053 },
- },
- },
- },
- [0x0128]={
- ["lt"]={
- ["all"]={
- ["lower"]={ 0x0069, 0x0307, 0x0303 },
- ["title"]={ 0x0128 },
- ["upper"]={ 0x0128 },
- },
- },
- },
- [0x012E]={
- ["lt"]={
- ["more_above"]={
- ["lower"]={ 0x012F, 0x0307 },
- ["title"]={ 0x012E },
- ["upper"]={ 0x012E },
- },
- },
- },
- [0x0130]={
- [""]={
- ["all"]={
- ["lower"]={ 0x0069, 0x0307 },
- ["title"]={ 0x0130 },
- ["upper"]={ 0x0130 },
- },
- },
- ["az"]={
- ["all"]={
- ["lower"]={ 0x0069 },
- ["title"]={ 0x0130 },
- ["upper"]={ 0x0130 },
- },
- },
- ["tr"]={
- ["all"]={
- ["lower"]={ 0x0069 },
- ["title"]={ 0x0130 },
- ["upper"]={ 0x0130 },
- },
- },
- },
- [0x0149]={
- [""]={
- ["all"]={
- ["lower"]={ 0x0149 },
- ["title"]={ 0x02BC, 0x004E },
- ["upper"]={ 0x02BC, 0x004E },
- },
- },
- },
- [0x01F0]={
- [""]={
- ["all"]={
- ["lower"]={ 0x01F0 },
- ["title"]={ 0x004A, 0x030C },
- ["upper"]={ 0x004A, 0x030C },
- },
- },
- },
- [0x0307]={
- ["az"]={
- ["after_i"]={
- ["lower"]={},
- ["title"]={ 0x0307 },
- ["upper"]={ 0x0307 },
- },
- },
- ["lt"]={
- ["after_soft_dotted"]={
- ["lower"]={ 0x0307 },
- ["title"]={},
- ["upper"]={},
- },
- },
- ["tr"]={
- ["after_i"]={
- ["lower"]={},
- ["title"]={ 0x0307 },
- ["upper"]={ 0x0307 },
- },
- },
- },
- [0x0390]={
- [""]={
- ["all"]={
- ["lower"]={ 0x0390 },
- ["title"]={ 0x0399, 0x0308, 0x0301 },
- ["upper"]={ 0x0399, 0x0308, 0x0301 },
- },
- },
- },
- [0x03A3]={
- ["final_sigma"]={
- ["all"]={
- ["lower"]={ 0x03C2 },
- ["title"]={ 0x03A3 },
- ["upper"]={ 0x03A3 },
- },
- },
- },
- [0x03B0]={
- [""]={
- ["all"]={
- ["lower"]={ 0x03B0 },
- ["title"]={ 0x03A5, 0x0308, 0x0301 },
- ["upper"]={ 0x03A5, 0x0308, 0x0301 },
- },
- },
- },
- [0x0587]={
- [""]={
- ["all"]={
- ["lower"]={ 0x0587 },
- ["title"]={ 0x0535, 0x0582 },
- ["upper"]={ 0x0535, 0x0552 },
- },
- },
- },
- [0x1E96]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1E96 },
- ["title"]={ 0x0048, 0x0331 },
- ["upper"]={ 0x0048, 0x0331 },
- },
- },
- },
- [0x1E97]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1E97 },
- ["title"]={ 0x0054, 0x0308 },
- ["upper"]={ 0x0054, 0x0308 },
- },
- },
- },
- [0x1E98]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1E98 },
- ["title"]={ 0x0057, 0x030A },
- ["upper"]={ 0x0057, 0x030A },
- },
- },
- },
- [0x1E99]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1E99 },
- ["title"]={ 0x0059, 0x030A },
- ["upper"]={ 0x0059, 0x030A },
- },
- },
- },
- [0x1E9A]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1E9A },
- ["title"]={ 0x0041, 0x02BE },
- ["upper"]={ 0x0041, 0x02BE },
- },
- },
- },
- [0x1F50]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F50 },
- ["title"]={ 0x03A5, 0x0313 },
- ["upper"]={ 0x03A5, 0x0313 },
- },
- },
- },
- [0x1F52]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F52 },
- ["title"]={ 0x03A5, 0x0313, 0x0300 },
- ["upper"]={ 0x03A5, 0x0313, 0x0300 },
- },
- },
- },
- [0x1F54]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F54 },
- ["title"]={ 0x03A5, 0x0313, 0x0301 },
- ["upper"]={ 0x03A5, 0x0313, 0x0301 },
- },
- },
- },
- [0x1F56]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F56 },
- ["title"]={ 0x03A5, 0x0313, 0x0342 },
- ["upper"]={ 0x03A5, 0x0313, 0x0342 },
- },
- },
- },
- [0x1F80]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F80 },
- ["title"]={ 0x1F88 },
- ["upper"]={ 0x1F08, 0x0399 },
- },
- },
- },
- [0x1F81]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F81 },
- ["title"]={ 0x1F89 },
- ["upper"]={ 0x1F09, 0x0399 },
- },
- },
- },
- [0x1F82]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F82 },
- ["title"]={ 0x1F8A },
- ["upper"]={ 0x1F0A, 0x0399 },
- },
- },
- },
- [0x1F83]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F83 },
- ["title"]={ 0x1F8B },
- ["upper"]={ 0x1F0B, 0x0399 },
- },
- },
- },
- [0x1F84]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F84 },
- ["title"]={ 0x1F8C },
- ["upper"]={ 0x1F0C, 0x0399 },
- },
- },
- },
- [0x1F85]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F85 },
- ["title"]={ 0x1F8D },
- ["upper"]={ 0x1F0D, 0x0399 },
- },
- },
- },
- [0x1F86]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F86 },
- ["title"]={ 0x1F8E },
- ["upper"]={ 0x1F0E, 0x0399 },
- },
- },
- },
- [0x1F87]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F87 },
- ["title"]={ 0x1F8F },
- ["upper"]={ 0x1F0F, 0x0399 },
- },
- },
- },
- [0x1F88]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F80 },
- ["title"]={ 0x1F88 },
- ["upper"]={ 0x1F08, 0x0399 },
- },
- },
- },
- [0x1F89]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F81 },
- ["title"]={ 0x1F89 },
- ["upper"]={ 0x1F09, 0x0399 },
- },
- },
- },
- [0x1F8A]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F82 },
- ["title"]={ 0x1F8A },
- ["upper"]={ 0x1F0A, 0x0399 },
- },
- },
- },
- [0x1F8B]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F83 },
- ["title"]={ 0x1F8B },
- ["upper"]={ 0x1F0B, 0x0399 },
- },
- },
- },
- [0x1F8C]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F84 },
- ["title"]={ 0x1F8C },
- ["upper"]={ 0x1F0C, 0x0399 },
- },
- },
- },
- [0x1F8D]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F85 },
- ["title"]={ 0x1F8D },
- ["upper"]={ 0x1F0D, 0x0399 },
- },
- },
- },
- [0x1F8E]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F86 },
- ["title"]={ 0x1F8E },
- ["upper"]={ 0x1F0E, 0x0399 },
- },
- },
- },
- [0x1F8F]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F87 },
- ["title"]={ 0x1F8F },
- ["upper"]={ 0x1F0F, 0x0399 },
- },
- },
- },
- [0x1F90]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F90 },
- ["title"]={ 0x1F98 },
- ["upper"]={ 0x1F28, 0x0399 },
- },
- },
- },
- [0x1F91]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F91 },
- ["title"]={ 0x1F99 },
- ["upper"]={ 0x1F29, 0x0399 },
- },
- },
- },
- [0x1F92]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F92 },
- ["title"]={ 0x1F9A },
- ["upper"]={ 0x1F2A, 0x0399 },
- },
- },
- },
- [0x1F93]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F93 },
- ["title"]={ 0x1F9B },
- ["upper"]={ 0x1F2B, 0x0399 },
- },
- },
- },
- [0x1F94]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F94 },
- ["title"]={ 0x1F9C },
- ["upper"]={ 0x1F2C, 0x0399 },
- },
- },
- },
- [0x1F95]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F95 },
- ["title"]={ 0x1F9D },
- ["upper"]={ 0x1F2D, 0x0399 },
- },
- },
- },
- [0x1F96]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F96 },
- ["title"]={ 0x1F9E },
- ["upper"]={ 0x1F2E, 0x0399 },
- },
- },
- },
- [0x1F97]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F97 },
- ["title"]={ 0x1F9F },
- ["upper"]={ 0x1F2F, 0x0399 },
- },
- },
- },
- [0x1F98]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F90 },
- ["title"]={ 0x1F98 },
- ["upper"]={ 0x1F28, 0x0399 },
- },
- },
- },
- [0x1F99]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F91 },
- ["title"]={ 0x1F99 },
- ["upper"]={ 0x1F29, 0x0399 },
- },
- },
- },
- [0x1F9A]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F92 },
- ["title"]={ 0x1F9A },
- ["upper"]={ 0x1F2A, 0x0399 },
- },
- },
- },
- [0x1F9B]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F93 },
- ["title"]={ 0x1F9B },
- ["upper"]={ 0x1F2B, 0x0399 },
- },
- },
- },
- [0x1F9C]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F94 },
- ["title"]={ 0x1F9C },
- ["upper"]={ 0x1F2C, 0x0399 },
- },
- },
- },
- [0x1F9D]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F95 },
- ["title"]={ 0x1F9D },
- ["upper"]={ 0x1F2D, 0x0399 },
- },
- },
- },
- [0x1F9E]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F96 },
- ["title"]={ 0x1F9E },
- ["upper"]={ 0x1F2E, 0x0399 },
- },
- },
- },
- [0x1F9F]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1F97 },
- ["title"]={ 0x1F9F },
- ["upper"]={ 0x1F2F, 0x0399 },
- },
- },
- },
- [0x1FA0]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FA0 },
- ["title"]={ 0x1FA8 },
- ["upper"]={ 0x1F68, 0x0399 },
- },
- },
- },
- [0x1FA1]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FA1 },
- ["title"]={ 0x1FA9 },
- ["upper"]={ 0x1F69, 0x0399 },
- },
- },
- },
- [0x1FA2]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FA2 },
- ["title"]={ 0x1FAA },
- ["upper"]={ 0x1F6A, 0x0399 },
- },
- },
- },
- [0x1FA3]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FA3 },
- ["title"]={ 0x1FAB },
- ["upper"]={ 0x1F6B, 0x0399 },
- },
- },
- },
- [0x1FA4]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FA4 },
- ["title"]={ 0x1FAC },
- ["upper"]={ 0x1F6C, 0x0399 },
- },
- },
- },
- [0x1FA5]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FA5 },
- ["title"]={ 0x1FAD },
- ["upper"]={ 0x1F6D, 0x0399 },
- },
- },
- },
- [0x1FA6]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FA6 },
- ["title"]={ 0x1FAE },
- ["upper"]={ 0x1F6E, 0x0399 },
- },
- },
- },
- [0x1FA7]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FA7 },
- ["title"]={ 0x1FAF },
- ["upper"]={ 0x1F6F, 0x0399 },
- },
- },
- },
- [0x1FA8]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FA0 },
- ["title"]={ 0x1FA8 },
- ["upper"]={ 0x1F68, 0x0399 },
- },
- },
- },
- [0x1FA9]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FA1 },
- ["title"]={ 0x1FA9 },
- ["upper"]={ 0x1F69, 0x0399 },
- },
- },
- },
- [0x1FAA]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FA2 },
- ["title"]={ 0x1FAA },
- ["upper"]={ 0x1F6A, 0x0399 },
- },
- },
- },
- [0x1FAB]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FA3 },
- ["title"]={ 0x1FAB },
- ["upper"]={ 0x1F6B, 0x0399 },
- },
- },
- },
- [0x1FAC]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FA4 },
- ["title"]={ 0x1FAC },
- ["upper"]={ 0x1F6C, 0x0399 },
- },
- },
- },
- [0x1FAD]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FA5 },
- ["title"]={ 0x1FAD },
- ["upper"]={ 0x1F6D, 0x0399 },
- },
- },
- },
- [0x1FAE]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FA6 },
- ["title"]={ 0x1FAE },
- ["upper"]={ 0x1F6E, 0x0399 },
- },
- },
- },
- [0x1FAF]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FA7 },
- ["title"]={ 0x1FAF },
- ["upper"]={ 0x1F6F, 0x0399 },
- },
- },
- },
- [0x1FB2]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FB2 },
- ["title"]={ 0x1FBA, 0x0345 },
- ["upper"]={ 0x1FBA, 0x0399 },
- },
- },
- },
- [0x1FB3]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FB3 },
- ["title"]={ 0x1FBC },
- ["upper"]={ 0x0391, 0x0399 },
- },
- },
- },
- [0x1FB4]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FB4 },
- ["title"]={ 0x0386, 0x0345 },
- ["upper"]={ 0x0386, 0x0399 },
- },
- },
- },
- [0x1FB6]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FB6 },
- ["title"]={ 0x0391, 0x0342 },
- ["upper"]={ 0x0391, 0x0342 },
- },
- },
- },
- [0x1FB7]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FB7 },
- ["title"]={ 0x0391, 0x0342, 0x0345 },
- ["upper"]={ 0x0391, 0x0342, 0x0399 },
- },
- },
- },
- [0x1FBC]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FB3 },
- ["title"]={ 0x1FBC },
- ["upper"]={ 0x0391, 0x0399 },
- },
- },
- },
- [0x1FC2]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FC2 },
- ["title"]={ 0x1FCA, 0x0345 },
- ["upper"]={ 0x1FCA, 0x0399 },
- },
- },
- },
- [0x1FC3]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FC3 },
- ["title"]={ 0x1FCC },
- ["upper"]={ 0x0397, 0x0399 },
- },
- },
- },
- [0x1FC4]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FC4 },
- ["title"]={ 0x0389, 0x0345 },
- ["upper"]={ 0x0389, 0x0399 },
- },
- },
- },
- [0x1FC6]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FC6 },
- ["title"]={ 0x0397, 0x0342 },
- ["upper"]={ 0x0397, 0x0342 },
- },
- },
- },
- [0x1FC7]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FC7 },
- ["title"]={ 0x0397, 0x0342, 0x0345 },
- ["upper"]={ 0x0397, 0x0342, 0x0399 },
- },
- },
- },
- [0x1FCC]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FC3 },
- ["title"]={ 0x1FCC },
- ["upper"]={ 0x0397, 0x0399 },
- },
- },
- },
- [0x1FD2]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FD2 },
- ["title"]={ 0x0399, 0x0308, 0x0300 },
- ["upper"]={ 0x0399, 0x0308, 0x0300 },
- },
- },
- },
- [0x1FD3]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FD3 },
- ["title"]={ 0x0399, 0x0308, 0x0301 },
- ["upper"]={ 0x0399, 0x0308, 0x0301 },
- },
- },
- },
- [0x1FD6]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FD6 },
- ["title"]={ 0x0399, 0x0342 },
- ["upper"]={ 0x0399, 0x0342 },
- },
- },
- },
- [0x1FD7]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FD7 },
- ["title"]={ 0x0399, 0x0308, 0x0342 },
- ["upper"]={ 0x0399, 0x0308, 0x0342 },
- },
- },
- },
- [0x1FE2]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FE2 },
- ["title"]={ 0x03A5, 0x0308, 0x0300 },
- ["upper"]={ 0x03A5, 0x0308, 0x0300 },
- },
- },
- },
- [0x1FE3]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FE3 },
- ["title"]={ 0x03A5, 0x0308, 0x0301 },
- ["upper"]={ 0x03A5, 0x0308, 0x0301 },
- },
- },
- },
- [0x1FE4]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FE4 },
- ["title"]={ 0x03A1, 0x0313 },
- ["upper"]={ 0x03A1, 0x0313 },
- },
- },
- },
- [0x1FE6]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FE6 },
- ["title"]={ 0x03A5, 0x0342 },
- ["upper"]={ 0x03A5, 0x0342 },
- },
- },
- },
- [0x1FE7]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FE7 },
- ["title"]={ 0x03A5, 0x0308, 0x0342 },
- ["upper"]={ 0x03A5, 0x0308, 0x0342 },
- },
- },
- },
- [0x1FF2]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FF2 },
- ["title"]={ 0x1FFA, 0x0345 },
- ["upper"]={ 0x1FFA, 0x0399 },
- },
- },
- },
- [0x1FF3]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FF3 },
- ["title"]={ 0x1FFC },
- ["upper"]={ 0x03A9, 0x0399 },
- },
- },
- },
- [0x1FF4]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FF4 },
- ["title"]={ 0x038F, 0x0345 },
- ["upper"]={ 0x038F, 0x0399 },
- },
- },
- },
- [0x1FF6]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FF6 },
- ["title"]={ 0x03A9, 0x0342 },
- ["upper"]={ 0x03A9, 0x0342 },
- },
- },
- },
- [0x1FF7]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FF7 },
- ["title"]={ 0x03A9, 0x0342, 0x0345 },
- ["upper"]={ 0x03A9, 0x0342, 0x0399 },
- },
- },
- },
- [0x1FFC]={
- [""]={
- ["all"]={
- ["lower"]={ 0x1FF3 },
- ["title"]={ 0x1FFC },
- ["upper"]={ 0x03A9, 0x0399 },
- },
- },
- },
- [0xFB00]={
- [""]={
- ["all"]={
- ["lower"]={ 0xFB00 },
- ["title"]={ 0x0046, 0x0066 },
- ["upper"]={ 0x0046, 0x0046 },
- },
- },
- },
- [0xFB01]={
- [""]={
- ["all"]={
- ["lower"]={ 0xFB01 },
- ["title"]={ 0x0046, 0x0069 },
- ["upper"]={ 0x0046, 0x0049 },
- },
- },
- },
- [0xFB02]={
- [""]={
- ["all"]={
- ["lower"]={ 0xFB02 },
- ["title"]={ 0x0046, 0x006C },
- ["upper"]={ 0x0046, 0x004C },
- },
- },
- },
- [0xFB03]={
- [""]={
- ["all"]={
- ["lower"]={ 0xFB03 },
- ["title"]={ 0x0046, 0x0066, 0x0069 },
- ["upper"]={ 0x0046, 0x0046, 0x0049 },
- },
- },
- },
- [0xFB04]={
- [""]={
- ["all"]={
- ["lower"]={ 0xFB04 },
- ["title"]={ 0x0046, 0x0066, 0x006C },
- ["upper"]={ 0x0046, 0x0046, 0x004C },
- },
- },
- },
- [0xFB05]={
- [""]={
- ["all"]={
- ["lower"]={ 0xFB05 },
- ["title"]={ 0x0053, 0x0074 },
- ["upper"]={ 0x0053, 0x0054 },
- },
- },
- },
- [0xFB06]={
- [""]={
- ["all"]={
- ["lower"]={ 0xFB06 },
- ["title"]={ 0x0053, 0x0074 },
- ["upper"]={ 0x0053, 0x0054 },
- },
- },
- },
- [0xFB13]={
- [""]={
- ["all"]={
- ["lower"]={ 0xFB13 },
- ["title"]={ 0x0544, 0x0576 },
- ["upper"]={ 0x0544, 0x0546 },
- },
- },
- },
- [0xFB14]={
- [""]={
- ["all"]={
- ["lower"]={ 0xFB14 },
- ["title"]={ 0x0544, 0x0565 },
- ["upper"]={ 0x0544, 0x0535 },
- },
- },
- },
- [0xFB15]={
- [""]={
- ["all"]={
- ["lower"]={ 0xFB15 },
- ["title"]={ 0x0544, 0x056B },
- ["upper"]={ 0x0544, 0x053B },
- },
- },
- },
- [0xFB16]={
- [""]={
- ["all"]={
- ["lower"]={ 0xFB16 },
- ["title"]={ 0x054E, 0x0576 },
- ["upper"]={ 0x054E, 0x0546 },
- },
- },
- },
- [0xFB17]={
- [""]={
- ["all"]={
- ["lower"]={ 0xFB17 },
- ["title"]={ 0x0544, 0x056D },
- ["upper"]={ 0x0544, 0x053D },
- },
- },
- },
-}
+if not modules then modules = { } end modules ['char-map'] = {
+ version = 1.001,
+ comment = "companion to char-ini.mkiv",
+ author = "Hans Hagen & Arthur Reutenauer",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+ dataonly = true,
+}
+
+-- not yet used
+
+characters = characters or { }
+
+characters.casemap={
+ [0x0049]={
+ ["az"]={
+ ["not_before_dot"]={
+ ["lower"]={ 0x0131 },
+ ["title"]={ 0x0049 },
+ ["upper"]={ 0x0049 },
+ },
+ },
+ ["lt"]={
+ ["more_above"]={
+ ["lower"]={ 0x0069, 0x0307 },
+ ["title"]={ 0x0049 },
+ ["upper"]={ 0x0049 },
+ },
+ },
+ ["tr"]={
+ ["not_before_dot"]={
+ ["lower"]={ 0x0131 },
+ ["title"]={ 0x0049 },
+ ["upper"]={ 0x0049 },
+ },
+ },
+ },
+ [0x004A]={
+ ["lt"]={
+ ["more_above"]={
+ ["lower"]={ 0x006A, 0x0307 },
+ ["title"]={ 0x004A },
+ ["upper"]={ 0x004A },
+ },
+ },
+ },
+ [0x0069]={
+ ["az"]={
+ ["all"]={
+ ["lower"]={ 0x0069 },
+ ["title"]={ 0x0130 },
+ ["upper"]={ 0x0130 },
+ },
+ },
+ ["tr"]={
+ ["all"]={
+ ["lower"]={ 0x0069 },
+ ["title"]={ 0x0130 },
+ ["upper"]={ 0x0130 },
+ },
+ },
+ },
+ [0x00CC]={
+ ["lt"]={
+ ["all"]={
+ ["lower"]={ 0x0069, 0x0307, 0x0300 },
+ ["title"]={ 0x00CC },
+ ["upper"]={ 0x00CC },
+ },
+ },
+ },
+ [0x00CD]={
+ ["lt"]={
+ ["all"]={
+ ["lower"]={ 0x0069, 0x0307, 0x0301 },
+ ["title"]={ 0x00CD },
+ ["upper"]={ 0x00CD },
+ },
+ },
+ },
+ [0x00DF]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x00DF },
+ ["title"]={ 0x0053, 0x0073 },
+ ["upper"]={ 0x0053, 0x0053 },
+ },
+ },
+ },
+ [0x0128]={
+ ["lt"]={
+ ["all"]={
+ ["lower"]={ 0x0069, 0x0307, 0x0303 },
+ ["title"]={ 0x0128 },
+ ["upper"]={ 0x0128 },
+ },
+ },
+ },
+ [0x012E]={
+ ["lt"]={
+ ["more_above"]={
+ ["lower"]={ 0x012F, 0x0307 },
+ ["title"]={ 0x012E },
+ ["upper"]={ 0x012E },
+ },
+ },
+ },
+ [0x0130]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x0069, 0x0307 },
+ ["title"]={ 0x0130 },
+ ["upper"]={ 0x0130 },
+ },
+ },
+ ["az"]={
+ ["all"]={
+ ["lower"]={ 0x0069 },
+ ["title"]={ 0x0130 },
+ ["upper"]={ 0x0130 },
+ },
+ },
+ ["tr"]={
+ ["all"]={
+ ["lower"]={ 0x0069 },
+ ["title"]={ 0x0130 },
+ ["upper"]={ 0x0130 },
+ },
+ },
+ },
+ [0x0149]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x0149 },
+ ["title"]={ 0x02BC, 0x004E },
+ ["upper"]={ 0x02BC, 0x004E },
+ },
+ },
+ },
+ [0x01F0]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x01F0 },
+ ["title"]={ 0x004A, 0x030C },
+ ["upper"]={ 0x004A, 0x030C },
+ },
+ },
+ },
+ [0x0307]={
+ ["az"]={
+ ["after_i"]={
+ ["lower"]={},
+ ["title"]={ 0x0307 },
+ ["upper"]={ 0x0307 },
+ },
+ },
+ ["lt"]={
+ ["after_soft_dotted"]={
+ ["lower"]={ 0x0307 },
+ ["title"]={},
+ ["upper"]={},
+ },
+ },
+ ["tr"]={
+ ["after_i"]={
+ ["lower"]={},
+ ["title"]={ 0x0307 },
+ ["upper"]={ 0x0307 },
+ },
+ },
+ },
+ [0x0390]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x0390 },
+ ["title"]={ 0x0399, 0x0308, 0x0301 },
+ ["upper"]={ 0x0399, 0x0308, 0x0301 },
+ },
+ },
+ },
+ [0x03A3]={
+ ["final_sigma"]={
+ ["all"]={
+ ["lower"]={ 0x03C2 },
+ ["title"]={ 0x03A3 },
+ ["upper"]={ 0x03A3 },
+ },
+ },
+ },
+ [0x03B0]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x03B0 },
+ ["title"]={ 0x03A5, 0x0308, 0x0301 },
+ ["upper"]={ 0x03A5, 0x0308, 0x0301 },
+ },
+ },
+ },
+ [0x0587]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x0587 },
+ ["title"]={ 0x0535, 0x0582 },
+ ["upper"]={ 0x0535, 0x0552 },
+ },
+ },
+ },
+ [0x1E96]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1E96 },
+ ["title"]={ 0x0048, 0x0331 },
+ ["upper"]={ 0x0048, 0x0331 },
+ },
+ },
+ },
+ [0x1E97]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1E97 },
+ ["title"]={ 0x0054, 0x0308 },
+ ["upper"]={ 0x0054, 0x0308 },
+ },
+ },
+ },
+ [0x1E98]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1E98 },
+ ["title"]={ 0x0057, 0x030A },
+ ["upper"]={ 0x0057, 0x030A },
+ },
+ },
+ },
+ [0x1E99]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1E99 },
+ ["title"]={ 0x0059, 0x030A },
+ ["upper"]={ 0x0059, 0x030A },
+ },
+ },
+ },
+ [0x1E9A]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1E9A },
+ ["title"]={ 0x0041, 0x02BE },
+ ["upper"]={ 0x0041, 0x02BE },
+ },
+ },
+ },
+ [0x1F50]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F50 },
+ ["title"]={ 0x03A5, 0x0313 },
+ ["upper"]={ 0x03A5, 0x0313 },
+ },
+ },
+ },
+ [0x1F52]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F52 },
+ ["title"]={ 0x03A5, 0x0313, 0x0300 },
+ ["upper"]={ 0x03A5, 0x0313, 0x0300 },
+ },
+ },
+ },
+ [0x1F54]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F54 },
+ ["title"]={ 0x03A5, 0x0313, 0x0301 },
+ ["upper"]={ 0x03A5, 0x0313, 0x0301 },
+ },
+ },
+ },
+ [0x1F56]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F56 },
+ ["title"]={ 0x03A5, 0x0313, 0x0342 },
+ ["upper"]={ 0x03A5, 0x0313, 0x0342 },
+ },
+ },
+ },
+ [0x1F80]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F80 },
+ ["title"]={ 0x1F88 },
+ ["upper"]={ 0x1F08, 0x0399 },
+ },
+ },
+ },
+ [0x1F81]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F81 },
+ ["title"]={ 0x1F89 },
+ ["upper"]={ 0x1F09, 0x0399 },
+ },
+ },
+ },
+ [0x1F82]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F82 },
+ ["title"]={ 0x1F8A },
+ ["upper"]={ 0x1F0A, 0x0399 },
+ },
+ },
+ },
+ [0x1F83]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F83 },
+ ["title"]={ 0x1F8B },
+ ["upper"]={ 0x1F0B, 0x0399 },
+ },
+ },
+ },
+ [0x1F84]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F84 },
+ ["title"]={ 0x1F8C },
+ ["upper"]={ 0x1F0C, 0x0399 },
+ },
+ },
+ },
+ [0x1F85]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F85 },
+ ["title"]={ 0x1F8D },
+ ["upper"]={ 0x1F0D, 0x0399 },
+ },
+ },
+ },
+ [0x1F86]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F86 },
+ ["title"]={ 0x1F8E },
+ ["upper"]={ 0x1F0E, 0x0399 },
+ },
+ },
+ },
+ [0x1F87]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F87 },
+ ["title"]={ 0x1F8F },
+ ["upper"]={ 0x1F0F, 0x0399 },
+ },
+ },
+ },
+ [0x1F88]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F80 },
+ ["title"]={ 0x1F88 },
+ ["upper"]={ 0x1F08, 0x0399 },
+ },
+ },
+ },
+ [0x1F89]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F81 },
+ ["title"]={ 0x1F89 },
+ ["upper"]={ 0x1F09, 0x0399 },
+ },
+ },
+ },
+ [0x1F8A]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F82 },
+ ["title"]={ 0x1F8A },
+ ["upper"]={ 0x1F0A, 0x0399 },
+ },
+ },
+ },
+ [0x1F8B]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F83 },
+ ["title"]={ 0x1F8B },
+ ["upper"]={ 0x1F0B, 0x0399 },
+ },
+ },
+ },
+ [0x1F8C]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F84 },
+ ["title"]={ 0x1F8C },
+ ["upper"]={ 0x1F0C, 0x0399 },
+ },
+ },
+ },
+ [0x1F8D]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F85 },
+ ["title"]={ 0x1F8D },
+ ["upper"]={ 0x1F0D, 0x0399 },
+ },
+ },
+ },
+ [0x1F8E]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F86 },
+ ["title"]={ 0x1F8E },
+ ["upper"]={ 0x1F0E, 0x0399 },
+ },
+ },
+ },
+ [0x1F8F]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F87 },
+ ["title"]={ 0x1F8F },
+ ["upper"]={ 0x1F0F, 0x0399 },
+ },
+ },
+ },
+ [0x1F90]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F90 },
+ ["title"]={ 0x1F98 },
+ ["upper"]={ 0x1F28, 0x0399 },
+ },
+ },
+ },
+ [0x1F91]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F91 },
+ ["title"]={ 0x1F99 },
+ ["upper"]={ 0x1F29, 0x0399 },
+ },
+ },
+ },
+ [0x1F92]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F92 },
+ ["title"]={ 0x1F9A },
+ ["upper"]={ 0x1F2A, 0x0399 },
+ },
+ },
+ },
+ [0x1F93]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F93 },
+ ["title"]={ 0x1F9B },
+ ["upper"]={ 0x1F2B, 0x0399 },
+ },
+ },
+ },
+ [0x1F94]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F94 },
+ ["title"]={ 0x1F9C },
+ ["upper"]={ 0x1F2C, 0x0399 },
+ },
+ },
+ },
+ [0x1F95]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F95 },
+ ["title"]={ 0x1F9D },
+ ["upper"]={ 0x1F2D, 0x0399 },
+ },
+ },
+ },
+ [0x1F96]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F96 },
+ ["title"]={ 0x1F9E },
+ ["upper"]={ 0x1F2E, 0x0399 },
+ },
+ },
+ },
+ [0x1F97]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F97 },
+ ["title"]={ 0x1F9F },
+ ["upper"]={ 0x1F2F, 0x0399 },
+ },
+ },
+ },
+ [0x1F98]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F90 },
+ ["title"]={ 0x1F98 },
+ ["upper"]={ 0x1F28, 0x0399 },
+ },
+ },
+ },
+ [0x1F99]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F91 },
+ ["title"]={ 0x1F99 },
+ ["upper"]={ 0x1F29, 0x0399 },
+ },
+ },
+ },
+ [0x1F9A]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F92 },
+ ["title"]={ 0x1F9A },
+ ["upper"]={ 0x1F2A, 0x0399 },
+ },
+ },
+ },
+ [0x1F9B]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F93 },
+ ["title"]={ 0x1F9B },
+ ["upper"]={ 0x1F2B, 0x0399 },
+ },
+ },
+ },
+ [0x1F9C]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F94 },
+ ["title"]={ 0x1F9C },
+ ["upper"]={ 0x1F2C, 0x0399 },
+ },
+ },
+ },
+ [0x1F9D]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F95 },
+ ["title"]={ 0x1F9D },
+ ["upper"]={ 0x1F2D, 0x0399 },
+ },
+ },
+ },
+ [0x1F9E]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F96 },
+ ["title"]={ 0x1F9E },
+ ["upper"]={ 0x1F2E, 0x0399 },
+ },
+ },
+ },
+ [0x1F9F]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1F97 },
+ ["title"]={ 0x1F9F },
+ ["upper"]={ 0x1F2F, 0x0399 },
+ },
+ },
+ },
+ [0x1FA0]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FA0 },
+ ["title"]={ 0x1FA8 },
+ ["upper"]={ 0x1F68, 0x0399 },
+ },
+ },
+ },
+ [0x1FA1]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FA1 },
+ ["title"]={ 0x1FA9 },
+ ["upper"]={ 0x1F69, 0x0399 },
+ },
+ },
+ },
+ [0x1FA2]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FA2 },
+ ["title"]={ 0x1FAA },
+ ["upper"]={ 0x1F6A, 0x0399 },
+ },
+ },
+ },
+ [0x1FA3]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FA3 },
+ ["title"]={ 0x1FAB },
+ ["upper"]={ 0x1F6B, 0x0399 },
+ },
+ },
+ },
+ [0x1FA4]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FA4 },
+ ["title"]={ 0x1FAC },
+ ["upper"]={ 0x1F6C, 0x0399 },
+ },
+ },
+ },
+ [0x1FA5]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FA5 },
+ ["title"]={ 0x1FAD },
+ ["upper"]={ 0x1F6D, 0x0399 },
+ },
+ },
+ },
+ [0x1FA6]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FA6 },
+ ["title"]={ 0x1FAE },
+ ["upper"]={ 0x1F6E, 0x0399 },
+ },
+ },
+ },
+ [0x1FA7]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FA7 },
+ ["title"]={ 0x1FAF },
+ ["upper"]={ 0x1F6F, 0x0399 },
+ },
+ },
+ },
+ [0x1FA8]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FA0 },
+ ["title"]={ 0x1FA8 },
+ ["upper"]={ 0x1F68, 0x0399 },
+ },
+ },
+ },
+ [0x1FA9]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FA1 },
+ ["title"]={ 0x1FA9 },
+ ["upper"]={ 0x1F69, 0x0399 },
+ },
+ },
+ },
+ [0x1FAA]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FA2 },
+ ["title"]={ 0x1FAA },
+ ["upper"]={ 0x1F6A, 0x0399 },
+ },
+ },
+ },
+ [0x1FAB]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FA3 },
+ ["title"]={ 0x1FAB },
+ ["upper"]={ 0x1F6B, 0x0399 },
+ },
+ },
+ },
+ [0x1FAC]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FA4 },
+ ["title"]={ 0x1FAC },
+ ["upper"]={ 0x1F6C, 0x0399 },
+ },
+ },
+ },
+ [0x1FAD]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FA5 },
+ ["title"]={ 0x1FAD },
+ ["upper"]={ 0x1F6D, 0x0399 },
+ },
+ },
+ },
+ [0x1FAE]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FA6 },
+ ["title"]={ 0x1FAE },
+ ["upper"]={ 0x1F6E, 0x0399 },
+ },
+ },
+ },
+ [0x1FAF]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FA7 },
+ ["title"]={ 0x1FAF },
+ ["upper"]={ 0x1F6F, 0x0399 },
+ },
+ },
+ },
+ [0x1FB2]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FB2 },
+ ["title"]={ 0x1FBA, 0x0345 },
+ ["upper"]={ 0x1FBA, 0x0399 },
+ },
+ },
+ },
+ [0x1FB3]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FB3 },
+ ["title"]={ 0x1FBC },
+ ["upper"]={ 0x0391, 0x0399 },
+ },
+ },
+ },
+ [0x1FB4]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FB4 },
+ ["title"]={ 0x0386, 0x0345 },
+ ["upper"]={ 0x0386, 0x0399 },
+ },
+ },
+ },
+ [0x1FB6]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FB6 },
+ ["title"]={ 0x0391, 0x0342 },
+ ["upper"]={ 0x0391, 0x0342 },
+ },
+ },
+ },
+ [0x1FB7]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FB7 },
+ ["title"]={ 0x0391, 0x0342, 0x0345 },
+ ["upper"]={ 0x0391, 0x0342, 0x0399 },
+ },
+ },
+ },
+ [0x1FBC]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FB3 },
+ ["title"]={ 0x1FBC },
+ ["upper"]={ 0x0391, 0x0399 },
+ },
+ },
+ },
+ [0x1FC2]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FC2 },
+ ["title"]={ 0x1FCA, 0x0345 },
+ ["upper"]={ 0x1FCA, 0x0399 },
+ },
+ },
+ },
+ [0x1FC3]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FC3 },
+ ["title"]={ 0x1FCC },
+ ["upper"]={ 0x0397, 0x0399 },
+ },
+ },
+ },
+ [0x1FC4]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FC4 },
+ ["title"]={ 0x0389, 0x0345 },
+ ["upper"]={ 0x0389, 0x0399 },
+ },
+ },
+ },
+ [0x1FC6]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FC6 },
+ ["title"]={ 0x0397, 0x0342 },
+ ["upper"]={ 0x0397, 0x0342 },
+ },
+ },
+ },
+ [0x1FC7]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FC7 },
+ ["title"]={ 0x0397, 0x0342, 0x0345 },
+ ["upper"]={ 0x0397, 0x0342, 0x0399 },
+ },
+ },
+ },
+ [0x1FCC]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FC3 },
+ ["title"]={ 0x1FCC },
+ ["upper"]={ 0x0397, 0x0399 },
+ },
+ },
+ },
+ [0x1FD2]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FD2 },
+ ["title"]={ 0x0399, 0x0308, 0x0300 },
+ ["upper"]={ 0x0399, 0x0308, 0x0300 },
+ },
+ },
+ },
+ [0x1FD3]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FD3 },
+ ["title"]={ 0x0399, 0x0308, 0x0301 },
+ ["upper"]={ 0x0399, 0x0308, 0x0301 },
+ },
+ },
+ },
+ [0x1FD6]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FD6 },
+ ["title"]={ 0x0399, 0x0342 },
+ ["upper"]={ 0x0399, 0x0342 },
+ },
+ },
+ },
+ [0x1FD7]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FD7 },
+ ["title"]={ 0x0399, 0x0308, 0x0342 },
+ ["upper"]={ 0x0399, 0x0308, 0x0342 },
+ },
+ },
+ },
+ [0x1FE2]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FE2 },
+ ["title"]={ 0x03A5, 0x0308, 0x0300 },
+ ["upper"]={ 0x03A5, 0x0308, 0x0300 },
+ },
+ },
+ },
+ [0x1FE3]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FE3 },
+ ["title"]={ 0x03A5, 0x0308, 0x0301 },
+ ["upper"]={ 0x03A5, 0x0308, 0x0301 },
+ },
+ },
+ },
+ [0x1FE4]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FE4 },
+ ["title"]={ 0x03A1, 0x0313 },
+ ["upper"]={ 0x03A1, 0x0313 },
+ },
+ },
+ },
+ [0x1FE6]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FE6 },
+ ["title"]={ 0x03A5, 0x0342 },
+ ["upper"]={ 0x03A5, 0x0342 },
+ },
+ },
+ },
+ [0x1FE7]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FE7 },
+ ["title"]={ 0x03A5, 0x0308, 0x0342 },
+ ["upper"]={ 0x03A5, 0x0308, 0x0342 },
+ },
+ },
+ },
+ [0x1FF2]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FF2 },
+ ["title"]={ 0x1FFA, 0x0345 },
+ ["upper"]={ 0x1FFA, 0x0399 },
+ },
+ },
+ },
+ [0x1FF3]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FF3 },
+ ["title"]={ 0x1FFC },
+ ["upper"]={ 0x03A9, 0x0399 },
+ },
+ },
+ },
+ [0x1FF4]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FF4 },
+ ["title"]={ 0x038F, 0x0345 },
+ ["upper"]={ 0x038F, 0x0399 },
+ },
+ },
+ },
+ [0x1FF6]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FF6 },
+ ["title"]={ 0x03A9, 0x0342 },
+ ["upper"]={ 0x03A9, 0x0342 },
+ },
+ },
+ },
+ [0x1FF7]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FF7 },
+ ["title"]={ 0x03A9, 0x0342, 0x0345 },
+ ["upper"]={ 0x03A9, 0x0342, 0x0399 },
+ },
+ },
+ },
+ [0x1FFC]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0x1FF3 },
+ ["title"]={ 0x1FFC },
+ ["upper"]={ 0x03A9, 0x0399 },
+ },
+ },
+ },
+ [0xFB00]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0xFB00 },
+ ["title"]={ 0x0046, 0x0066 },
+ ["upper"]={ 0x0046, 0x0046 },
+ },
+ },
+ },
+ [0xFB01]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0xFB01 },
+ ["title"]={ 0x0046, 0x0069 },
+ ["upper"]={ 0x0046, 0x0049 },
+ },
+ },
+ },
+ [0xFB02]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0xFB02 },
+ ["title"]={ 0x0046, 0x006C },
+ ["upper"]={ 0x0046, 0x004C },
+ },
+ },
+ },
+ [0xFB03]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0xFB03 },
+ ["title"]={ 0x0046, 0x0066, 0x0069 },
+ ["upper"]={ 0x0046, 0x0046, 0x0049 },
+ },
+ },
+ },
+ [0xFB04]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0xFB04 },
+ ["title"]={ 0x0046, 0x0066, 0x006C },
+ ["upper"]={ 0x0046, 0x0046, 0x004C },
+ },
+ },
+ },
+ [0xFB05]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0xFB05 },
+ ["title"]={ 0x0053, 0x0074 },
+ ["upper"]={ 0x0053, 0x0054 },
+ },
+ },
+ },
+ [0xFB06]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0xFB06 },
+ ["title"]={ 0x0053, 0x0074 },
+ ["upper"]={ 0x0053, 0x0054 },
+ },
+ },
+ },
+ [0xFB13]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0xFB13 },
+ ["title"]={ 0x0544, 0x0576 },
+ ["upper"]={ 0x0544, 0x0546 },
+ },
+ },
+ },
+ [0xFB14]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0xFB14 },
+ ["title"]={ 0x0544, 0x0565 },
+ ["upper"]={ 0x0544, 0x0535 },
+ },
+ },
+ },
+ [0xFB15]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0xFB15 },
+ ["title"]={ 0x0544, 0x056B },
+ ["upper"]={ 0x0544, 0x053B },
+ },
+ },
+ },
+ [0xFB16]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0xFB16 },
+ ["title"]={ 0x054E, 0x0576 },
+ ["upper"]={ 0x054E, 0x0546 },
+ },
+ },
+ },
+ [0xFB17]={
+ [""]={
+ ["all"]={
+ ["lower"]={ 0xFB17 },
+ ["title"]={ 0x0544, 0x056D },
+ ["upper"]={ 0x0544, 0x053D },
+ },
+ },
+ },
+}
diff --git a/tex/context/base/char-tex.lua b/tex/context/base/char-tex.lua
index c470eb6c4..91aa387b9 100644
--- a/tex/context/base/char-tex.lua
+++ b/tex/context/base/char-tex.lua
@@ -1,211 +1,211 @@
-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 find = string.find
-local P, C, R, S, Cs, Cc = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cs, lpeg.Cc
-local U, lpegmatch = lpeg.patterns.utf8, lpeg.match
-
-local allocate, mark = utilities.storage.allocate, utilities.storage.mark
-
-characters = characters or { }
-local characters = characters
-characters.tex = characters.tex or { }
-
-local accentmapping = allocate {
- ['"'] = { [""] = "¨",
- A = "Ä", a = "ä",
- E = "Ë", e = "ë",
- I = "Ï", i = "ï", ["ı"] = "ï",
- O = "Ö", o = "ö",
- U = "Ü", u = "ü",
- Y = "Ÿ", y = "ÿ",
- },
- ["'"] = { [""] = "´",
- A = "Á", a = "á",
- C = "Ć", c = "ć",
- E = "É", e = "é",
- 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",
- Z = "Ż", z = "ż",
- },
- ["="] = { [""] = "¯",
- A = "Ā", a = "ā",
- E = "Ē", e = "ē",
- I = "Ī", i = "ī", ["ı"] = "ī",
- O = "Ō", o = "ō",
- U = "Ū", u = "ū",
- },
- ["H"] = { [""] = "˝",
- O = "Ő", o = "ő",
- U = "Ű", u = "ű",
- },
- ["^"] = { [""] = "ˆ",
- A = "Â", a = "â",
- C = "Ĉ", c = "ĉ",
- E = "Ê", e = "ê",
- G = "Ĝ", g = "ĝ",
- H = "Ĥ", h = "ĥ",
- I = "Î", i = "î", ["ı"] = "î",
- J = "Ĵ", j = "ĵ",
- O = "Ô", o = "ô",
- S = "Ŝ", s = "ŝ",
- U = "Û", u = "û",
- W = "Ŵ", w = "ŵ",
- Y = "Ŷ", y = "ŷ",
- },
- ["`"] = { [""] = "`",
- A = "À", a = "à",
- E = "È", e = "è",
- 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 = "ĭ", ["ı"] = "ĭ",
- 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 = "ĩ", ["ı"] = "ĩ",
- N = "Ñ", n = "ñ",
- O = "Õ", o = "õ",
- U = "Ũ", u = "ũ",
- },
-}
-
-characters.tex.accentmapping = accentmapping
-
-local accent_map = allocate { -- incomplete
- ['~'] = "̃" , -- ̃ Ẽ
- ['"'] = "̈" , -- ̈ Ë
- ["`"] = "̀" , -- ̀ È
- ["'"] = "́" , -- ́ É
- ["^"] = "̂" , -- ̂ Ê
- -- ̄ Ē
- -- ̆ Ĕ
- -- ̇ Ė
- -- ̉ Ẻ
- -- ̌ Ě
- -- ̏ Ȅ
- -- ̑ Ȇ
- -- ̣ Ẹ
- -- ̧ Ȩ
- -- ̨ Ę
- -- ̭ Ḙ
- -- ̰ Ḛ
-}
-
-local accents = table.concat(table.keys(accent_map))
-
-local function remap_accents(a,c,braced)
- local m = accent_map[a]
- if m then
- return c .. m
- elseif braced then
- return "\\" .. a .. "{" .. c .. "}"
- else
- return "\\" .. a .. c
- end
-end
-
-local command_map = allocate {
- ["i"] = "ı"
-}
-
-local function remap_commands(c)
- local m = command_map[c]
- if m then
- return m
- else
- return "\\" .. c
- end
-end
-
-local accents = (P('\\') * C(S(accents)) * (P("{") * C(U) * P("}" * Cc(true)) + C(U) * Cc(false))) / remap_accents
-local commands = (P('\\') * C(R("az","AZ")^1)) / remap_commands
-
-local convert_accents = Cs((accents + P(1))^0)
-local convert_commands = Cs((commands + P(1))^0)
-
-local no_l = P("{") / ""
-local no_r = P("}") / ""
-
-local convert_accents_strip = Cs((no_l * accents * no_r + accents + P(1))^0)
-local convert_commands_strip = Cs((no_l * commands * no_r + commands + P(1))^0)
-
-function characters.tex.toutf(str,strip)
- if not find(str,"\\") then -- we can start at the found position
- return str
- elseif strip then
- return lpegmatch(convert_accents_strip,lpegmatch(convert_commands_strip,str))
- else
- return lpegmatch(convert_accents, lpegmatch(convert_commands, str))
- end
-end
-
---~ print(characters.tex.toutf([[\"{e}]]),true)
---~ print(characters.tex.toutf([[{\"{e}}]],true))
-
-function characters.tex.defineaccents()
- for accent, group in next, accentmapping do
- context.dodefineaccentcommand(accent)
- for character, mapping in next, group do
- context.dodefineaccent(accent,character,mapping)
- end
- end
-end
+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 find = string.find
+local P, C, R, S, Cs, Cc = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cs, lpeg.Cc
+local U, lpegmatch = lpeg.patterns.utf8, lpeg.match
+
+local allocate, mark = utilities.storage.allocate, utilities.storage.mark
+
+characters = characters or { }
+local characters = characters
+characters.tex = characters.tex or { }
+
+local accentmapping = allocate {
+ ['"'] = { [""] = "¨",
+ A = "Ä", a = "ä",
+ E = "Ë", e = "ë",
+ I = "Ï", i = "ï", ["ı"] = "ï",
+ O = "Ö", o = "ö",
+ U = "Ü", u = "ü",
+ Y = "Ÿ", y = "ÿ",
+ },
+ ["'"] = { [""] = "´",
+ A = "Á", a = "á",
+ C = "Ć", c = "ć",
+ E = "É", e = "é",
+ 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",
+ Z = "Ż", z = "ż",
+ },
+ ["="] = { [""] = "¯",
+ A = "Ā", a = "ā",
+ E = "Ē", e = "ē",
+ I = "Ī", i = "ī", ["ı"] = "ī",
+ O = "Ō", o = "ō",
+ U = "Ū", u = "ū",
+ },
+ ["H"] = { [""] = "˝",
+ O = "Ő", o = "ő",
+ U = "Ű", u = "ű",
+ },
+ ["^"] = { [""] = "ˆ",
+ A = "Â", a = "â",
+ C = "Ĉ", c = "ĉ",
+ E = "Ê", e = "ê",
+ G = "Ĝ", g = "ĝ",
+ H = "Ĥ", h = "ĥ",
+ I = "Î", i = "î", ["ı"] = "î",
+ J = "Ĵ", j = "ĵ",
+ O = "Ô", o = "ô",
+ S = "Ŝ", s = "ŝ",
+ U = "Û", u = "û",
+ W = "Ŵ", w = "ŵ",
+ Y = "Ŷ", y = "ŷ",
+ },
+ ["`"] = { [""] = "`",
+ A = "À", a = "à",
+ E = "È", e = "è",
+ 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 = "ĭ", ["ı"] = "ĭ",
+ 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 = "ĩ", ["ı"] = "ĩ",
+ N = "Ñ", n = "ñ",
+ O = "Õ", o = "õ",
+ U = "Ũ", u = "ũ",
+ },
+}
+
+characters.tex.accentmapping = accentmapping
+
+local accent_map = allocate { -- incomplete
+ ['~'] = "̃" , -- ̃ Ẽ
+ ['"'] = "̈" , -- ̈ Ë
+ ["`"] = "̀" , -- ̀ È
+ ["'"] = "́" , -- ́ É
+ ["^"] = "̂" , -- ̂ Ê
+ -- ̄ Ē
+ -- ̆ Ĕ
+ -- ̇ Ė
+ -- ̉ Ẻ
+ -- ̌ Ě
+ -- ̏ Ȅ
+ -- ̑ Ȇ
+ -- ̣ Ẹ
+ -- ̧ Ȩ
+ -- ̨ Ę
+ -- ̭ Ḙ
+ -- ̰ Ḛ
+}
+
+local accents = table.concat(table.keys(accent_map))
+
+local function remap_accents(a,c,braced)
+ local m = accent_map[a]
+ if m then
+ return c .. m
+ elseif braced then
+ return "\\" .. a .. "{" .. c .. "}"
+ else
+ return "\\" .. a .. c
+ end
+end
+
+local command_map = allocate {
+ ["i"] = "ı"
+}
+
+local function remap_commands(c)
+ local m = command_map[c]
+ if m then
+ return m
+ else
+ return "\\" .. c
+ end
+end
+
+local accents = (P('\\') * C(S(accents)) * (P("{") * C(U) * P("}" * Cc(true)) + C(U) * Cc(false))) / remap_accents
+local commands = (P('\\') * C(R("az","AZ")^1)) / remap_commands
+
+local convert_accents = Cs((accents + P(1))^0)
+local convert_commands = Cs((commands + P(1))^0)
+
+local no_l = P("{") / ""
+local no_r = P("}") / ""
+
+local convert_accents_strip = Cs((no_l * accents * no_r + accents + P(1))^0)
+local convert_commands_strip = Cs((no_l * commands * no_r + commands + P(1))^0)
+
+function characters.tex.toutf(str,strip)
+ if not find(str,"\\") then -- we can start at the found position
+ return str
+ elseif strip then
+ return lpegmatch(convert_accents_strip,lpegmatch(convert_commands_strip,str))
+ else
+ return lpegmatch(convert_accents, lpegmatch(convert_commands, str))
+ end
+end
+
+--~ print(characters.tex.toutf([[\"{e}]]),true)
+--~ print(characters.tex.toutf([[{\"{e}}]],true))
+
+function characters.tex.defineaccents()
+ for accent, group in next, accentmapping do
+ context.dodefineaccentcommand(accent)
+ for character, mapping in next, group do
+ context.dodefineaccent(accent,character,mapping)
+ end
+ end
+end
diff --git a/tex/context/base/char-utf.lua b/tex/context/base/char-utf.lua
index 424018b62..d0e40e664 100644
--- a/tex/context/base/char-utf.lua
+++ b/tex/context/base/char-utf.lua
@@ -1,553 +1,553 @@
-if not modules then modules = { } end modules ['char-utf'] = {
- version = 1.001,
- comment = "companion to char-utf.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
---[[ldx--
-<p>When a sequence of <l n='utf'/> characters enters the application, it may
-be neccessary to collapse subsequences into their composed variant.</p>
-
-<p>This module implements methods for collapsing and expanding <l n='utf'/>
-sequences. We also provide means to deal with characters that are
-special to <l n='tex'/> as well as 8-bit characters that need to end up
-in special kinds of output (for instance <l n='pdf'/>).</p>
-
-<p>We implement these manipulations as filters. One can run multiple filters
-over a string.</p>
---ldx]]--
-
-local concat, gmatch, gsub, find = table.concat, string.gmatch, string.gsub, string.find
-local utfchar, utfbyte, utfcharacters, utfvalues = utf.char, utf.byte, utf.characters, utf.values
-local allocate = utilities.storage.allocate
-local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
-
-local charfromnumber = characters.fromnumber
-
--- todo: trackers
--- graphemes: basic symbols
-
-characters = characters or { }
-local characters = characters
-
-characters.graphemes = allocate()
-local graphemes = characters.graphemes
-
-characters.combined = allocate()
-local combined = characters.combined
-
-characters.decomposed = allocate()
-local decomposed = characters.decomposed
-
-characters.mathpairs = allocate()
-local mathpairs = characters.mathpairs
-
-characters.filters = allocate()
-local filters = characters.filters
-
-filters.utf = filters.utf or { }
-local utffilters = characters.filters.utf
-
--- is characters.combined cached?
-
---[[ldx--
-<p>It only makes sense to collapse at runtime, since we don't expect
-source code to depend on collapsing.</p>
---ldx]]--
-
--- for the moment, will be entries in char-def.lua
-
-local decomposed = allocate {
- ["IJ"] = "IJ",
- ["ij"] = "ij",
- ["և"] = "եւ",
- ["ff"] = "ff",
- ["fi"] = "fi",
- ["fl"] = "fl",
- ["ffi"] = "ffi",
- ["ffl"] = "ffl",
- ["ſt"] = "ſt",
- ["st"] = "st",
- ["ﬓ"] = "մն",
- ["ﬔ"] = "մե",
- ["ﬕ"] = "մի",
- ["ﬖ"] = "վն",
- ["ﬗ"] = "մխ",
-}
-
-characters.decomposed = decomposed
-
-local function initialize() -- maybe only 'mn'
- local data = characters.data
- for unicode, v in next, data do
- -- using vs and first testing for length is faster (.02->.01 s)
- local vs = v.specials
- local vc = vs and #vs == 3 and vs[1]
- if vc == "char" then
- local one, two = vs[2], vs[3]
- if data[two].category == "mn" then
- local cgf = combined[one]
- if not cgf then
- cgf = { [two] = unicode }
- combined[one] = cgf
- else
- cgf[two] = unicode
- end
- end
- local first, second, combination = utfchar(one), utfchar(two), utfchar(unicode)
- local cgf = graphemes[first]
- if not cgf then
- cgf = { [second] = combination }
- graphemes[first] = cgf
- else
- cgf[second] = combination
- end
- if v.mathclass or v.mathspec then
- local mps = mathpairs[two]
- if not mps then
- mps = { [one] = unicode }
- mathpairs[two] = mps
- else
- mps[one] = unicode -- here unicode
- end
- local mps = mathpairs[second]
- if not mps then
- mps = { [first] = combination }
- mathpairs[second] = mps
- else
- mps[first] = combination
- end
- end
- -- elseif vc == "compat" then
- -- else
- -- local description = v.description
- -- if find(description,"LIGATURE") then
- -- if vs then
- -- local t = { }
- -- for i=2,#vs do
- -- t[#t+1] = utfchar(vs[i])
- -- end
- -- decomposed[utfchar(unicode)] = concat(t)
- -- else
- -- local vs = v.shcode
- -- if vs then
- -- local t = { }
- -- for i=1,#vs do
- -- t[i] = utfchar(vs[i])
- -- end
- -- decomposed[utfchar(unicode)] = concat(t)
- -- end
- -- end
- -- end
- end
- end
- initialize = false
- characters.initialize = function() end -- when used outside tex
-end
-
-characters.initialize = initialize
-
--- utffilters.addgrapheme(utfchar(318),'l','\string~')
--- utffilters.addgrapheme('c','a','b')
-
-function utffilters.addgrapheme(result,first,second) -- can be U+ 0x string or utf or number
- local result = charfromnumber(result)
- local first = charfromnumber(first)
- local second = charfromnumber(second)
- if not graphemes[first] then
- graphemes[first] = { [second] = result }
- else
- graphemes[first][second] = result
- end
-end
-
---[[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.</p>
-
-<p>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.</p>
-
-<p>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
-
-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
-
-local function set(ch)
- 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
-
-private.set = set
-
--- function private.escape (str) return gsub(str,"(.)", escapes) end
--- function private.replace(str) return utfgsub(str,"(.)", low ) end
--- function private.revert (str) return utfgsub(str,"(.)", high ) end
-
-private.escape = utf.remapper(escapes)
-private.replace = utf.remapper(low)
-private.revert = utf.remapper(high)
-
-for ch in gmatch(special,".") do set(ch) end
-
---[[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>
-
-<p>The next variant has lazy token collecting, on a 140 page mk.tex this saves
-about .25 seconds, which is understandable because we have no graphmes and
-not collecting tokens is not only faster but also saves garbage collecting.
-</p>
---ldx]]--
-
--- lpeg variant is not faster
---
--- I might use the combined loop at some point for the filter
--- some day.
-
--- function utffilters.collapse(str) -- not really tested (we could preallocate a table)
--- if str and str ~= "" then
--- local nstr = #str
--- if nstr > 1 then
--- if initialize then -- saves a call
--- initialize()
--- end
--- local tokens, t, first, done, n = { }, 0, false, false, 0
--- for second in utfcharacters(str) do
--- local dec = decomposed[second]
--- if dec then
--- if not done then
--- if n > 0 then
--- for s in utfcharacters(str) do
--- if n == 1 then
--- break
--- else
--- t = t + 1
--- tokens[t] = s
--- n = n - 1
--- end
--- end
--- end
--- done = true
--- elseif first then
--- t = t + 1
--- tokens[t] = first
--- end
--- t = t + 1
--- tokens[t] = dec
--- first = false
--- elseif done then
--- local crs = high[second]
--- if crs then
--- if first then
--- t = t + 1
--- tokens[t] = first
--- end
--- first = crs
--- else
--- local cgf = graphemes[first]
--- if cgf and cgf[second] then
--- first = cgf[second]
--- elseif first then
--- t = t + 1
--- tokens[t] = first
--- first = second
--- else
--- first = second
--- end
--- end
--- else
--- local crs = high[second]
--- if crs then
--- for s in utfcharacters(str) do
--- if n == 1 then
--- break
--- else
--- t = t + 1
--- tokens[t] = s
--- n = n - 1
--- end
--- end
--- if first then
--- t = t + 1
--- tokens[t] = first
--- end
--- first = crs
--- done = true
--- else
--- local cgf = graphemes[first]
--- if cgf and cgf[second] then
--- for s in utfcharacters(str) do
--- if n == 1 then
--- break
--- else
--- t = t + 1
--- tokens[t] = s
--- n = n - 1
--- end
--- end
--- first = cgf[second]
--- done = true
--- else
--- first = second
--- n = n + 1
--- end
--- end
--- end
--- end
--- if done then
--- if first then
--- t = t + 1
--- tokens[t] = first
--- end
--- return concat(tokens) -- seldom called
--- end
--- elseif nstr > 0 then
--- return high[str] or str
--- end
--- end
--- return str
--- end
-
-local skippable = table.tohash { "mkiv", "mkvi" }
-local filesuffix = file.suffix
-
--- we could reuse tokens but it's seldom populated anyway
-
-function utffilters.collapse(str,filename) -- not really tested (we could preallocate a table)
- if skippable[filesuffix(filename)] then
- return str
- end
- if str and str ~= "" then
- local nstr = #str
- if nstr > 1 then
- if initialize then -- saves a call
- initialize()
- end
- local tokens, t, first, done, n = { }, 0, false, false, 0
- for second in utfcharacters(str) do
- if done then
- local crs = high[second]
- if crs then
- if first then
- t = t + 1
- tokens[t] = first
- end
- first = crs
- else
- local cgf = graphemes[first]
- if cgf and cgf[second] then
- first = cgf[second]
- elseif first then
- t = t + 1
- tokens[t] = first
- first = second
- else
- first = second
- end
- end
- else
- local crs = high[second]
- if crs then
- for s in utfcharacters(str) do
- if n == 1 then
- break
- else
- t = t + 1
- tokens[t] = s
- n = n - 1
- end
- end
- if first then
- t = t + 1
- tokens[t] = first
- end
- first = crs
- done = true
- else
- local cgf = graphemes[first]
- if cgf and cgf[second] then
- for s in utfcharacters(str) do
- if n == 1 then
- break
- else
- t = t + 1
- tokens[t] = s
- n = n - 1
- end
- end
- first = cgf[second]
- done = true
- else
- first = second
- n = n + 1
- end
- end
- end
- end
- if done then
- if first then
- t = t + 1
- tokens[t] = first
- end
- return concat(tokens) -- seldom called
- end
- elseif nstr > 0 then
- return high[str] or str
- end
- end
- return str
-end
-
-function utffilters.decompose(str)
- if str and str ~= "" then
- local nstr = #str
- if nstr > 1 then
- -- if initialize then -- saves a call
- -- initialize()
- -- end
- local tokens, t, done, n = { }, 0, false, 0
- for s in utfcharacters(str) do
- local dec = decomposed[s]
- if dec then
- if not done then
- if n > 0 then
- for s in utfcharacters(str) do
- if n == 1 then
- break
- else
- t = t + 1
- tokens[t] = s
- n = n - 1
- end
- end
- end
- done = true
- end
- t = t + 1
- tokens[t] = dec
- elseif done then
- t = t + 1
- tokens[t] = s
- else
- n = n + 1
- end
- end
- if done then
- return concat(tokens) -- seldom called
- end
- end
- end
- return str
-end
-
-local sequencers = utilities.sequencers
-
-if sequencers then
-
- local textfileactions = resolvers.openers.helpers.textfileactions
-
- sequencers.appendaction (textfileactions,"system","characters.filters.utf.collapse")
- sequencers.disableaction(textfileactions,"characters.filters.utf.collapse")
-
- sequencers.appendaction (textfileactions,"system","characters.filters.utf.decompose")
- sequencers.disableaction(textfileactions,"characters.filters.utf.decompose")
-
- function characters.filters.utf.enable()
- sequencers.enableaction(textfileactions,"characters.filters.utf.collapse")
- sequencers.enableaction(textfileactions,"characters.filters.utf.decompose")
- end
-
- directives.register("filters.utf.collapse", function(v)
- sequencers[v and "enableaction" or "disableaction"](textfileactions,"characters.filters.utf.collapse")
- end)
-
- directives.register("filters.utf.decompose", function(v)
- sequencers[v and "enableaction" or "disableaction"](textfileactions,"characters.filters.utf.decompose")
- end)
-
-end
-
---[[ldx--
-<p>Next we implement some commands that are used in the user interface.</p>
---ldx]]--
-
--- commands = commands or { }
---
--- function commands.uchar(first,second)
--- context(utfchar(first*256+second))
--- end
-
---[[ldx--
-<p>A few helpers (used to be <t>luat-uni<t/>).</p>
---ldx]]--
-
--- obsolete:
---
--- function utf.split(str)
--- local t, n = { }, 0
--- for snippet in utfcharacters(str) do
--- n = n + 1
--- t[n+1] = snippet
--- end
--- return t
--- end
---
--- function utf.each(str,fnc)
--- for snippet in utfcharacters(str) do
--- fnc(snippet)
--- end
--- end
+if not modules then modules = { } end modules ['char-utf'] = {
+ version = 1.001,
+ comment = "companion to char-utf.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[ldx--
+<p>When a sequence of <l n='utf'/> characters enters the application, it may
+be neccessary to collapse subsequences into their composed variant.</p>
+
+<p>This module implements methods for collapsing and expanding <l n='utf'/>
+sequences. We also provide means to deal with characters that are
+special to <l n='tex'/> as well as 8-bit characters that need to end up
+in special kinds of output (for instance <l n='pdf'/>).</p>
+
+<p>We implement these manipulations as filters. One can run multiple filters
+over a string.</p>
+--ldx]]--
+
+local concat, gmatch, gsub, find = table.concat, string.gmatch, string.gsub, string.find
+local utfchar, utfbyte, utfcharacters, utfvalues = utf.char, utf.byte, utf.characters, utf.values
+local allocate = utilities.storage.allocate
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+
+local charfromnumber = characters.fromnumber
+
+-- todo: trackers
+-- graphemes: basic symbols
+
+characters = characters or { }
+local characters = characters
+
+characters.graphemes = allocate()
+local graphemes = characters.graphemes
+
+characters.combined = allocate()
+local combined = characters.combined
+
+characters.decomposed = allocate()
+local decomposed = characters.decomposed
+
+characters.mathpairs = allocate()
+local mathpairs = characters.mathpairs
+
+characters.filters = allocate()
+local filters = characters.filters
+
+filters.utf = filters.utf or { }
+local utffilters = characters.filters.utf
+
+-- is characters.combined cached?
+
+--[[ldx--
+<p>It only makes sense to collapse at runtime, since we don't expect
+source code to depend on collapsing.</p>
+--ldx]]--
+
+-- for the moment, will be entries in char-def.lua
+
+local decomposed = allocate {
+ ["IJ"] = "IJ",
+ ["ij"] = "ij",
+ ["և"] = "եւ",
+ ["ff"] = "ff",
+ ["fi"] = "fi",
+ ["fl"] = "fl",
+ ["ffi"] = "ffi",
+ ["ffl"] = "ffl",
+ ["ſt"] = "ſt",
+ ["st"] = "st",
+ ["ﬓ"] = "մն",
+ ["ﬔ"] = "մե",
+ ["ﬕ"] = "մի",
+ ["ﬖ"] = "վն",
+ ["ﬗ"] = "մխ",
+}
+
+characters.decomposed = decomposed
+
+local function initialize() -- maybe only 'mn'
+ local data = characters.data
+ for unicode, v in next, data do
+ -- using vs and first testing for length is faster (.02->.01 s)
+ local vs = v.specials
+ local vc = vs and #vs == 3 and vs[1]
+ if vc == "char" then
+ local one, two = vs[2], vs[3]
+ if data[two].category == "mn" then
+ local cgf = combined[one]
+ if not cgf then
+ cgf = { [two] = unicode }
+ combined[one] = cgf
+ else
+ cgf[two] = unicode
+ end
+ end
+ local first, second, combination = utfchar(one), utfchar(two), utfchar(unicode)
+ local cgf = graphemes[first]
+ if not cgf then
+ cgf = { [second] = combination }
+ graphemes[first] = cgf
+ else
+ cgf[second] = combination
+ end
+ if v.mathclass or v.mathspec then
+ local mps = mathpairs[two]
+ if not mps then
+ mps = { [one] = unicode }
+ mathpairs[two] = mps
+ else
+ mps[one] = unicode -- here unicode
+ end
+ local mps = mathpairs[second]
+ if not mps then
+ mps = { [first] = combination }
+ mathpairs[second] = mps
+ else
+ mps[first] = combination
+ end
+ end
+ -- elseif vc == "compat" then
+ -- else
+ -- local description = v.description
+ -- if find(description,"LIGATURE") then
+ -- if vs then
+ -- local t = { }
+ -- for i=2,#vs do
+ -- t[#t+1] = utfchar(vs[i])
+ -- end
+ -- decomposed[utfchar(unicode)] = concat(t)
+ -- else
+ -- local vs = v.shcode
+ -- if vs then
+ -- local t = { }
+ -- for i=1,#vs do
+ -- t[i] = utfchar(vs[i])
+ -- end
+ -- decomposed[utfchar(unicode)] = concat(t)
+ -- end
+ -- end
+ -- end
+ end
+ end
+ initialize = false
+ characters.initialize = function() end -- when used outside tex
+end
+
+characters.initialize = initialize
+
+-- utffilters.addgrapheme(utfchar(318),'l','\string~')
+-- utffilters.addgrapheme('c','a','b')
+
+function utffilters.addgrapheme(result,first,second) -- can be U+ 0x string or utf or number
+ local result = charfromnumber(result)
+ local first = charfromnumber(first)
+ local second = charfromnumber(second)
+ if not graphemes[first] then
+ graphemes[first] = { [second] = result }
+ else
+ graphemes[first][second] = result
+ end
+end
+
+--[[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.</p>
+
+<p>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.</p>
+
+<p>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
+
+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
+
+local function set(ch)
+ 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
+
+private.set = set
+
+-- function private.escape (str) return gsub(str,"(.)", escapes) end
+-- function private.replace(str) return utfgsub(str,"(.)", low ) end
+-- function private.revert (str) return utfgsub(str,"(.)", high ) end
+
+private.escape = utf.remapper(escapes)
+private.replace = utf.remapper(low)
+private.revert = utf.remapper(high)
+
+for ch in gmatch(special,".") do set(ch) end
+
+--[[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>
+
+<p>The next variant has lazy token collecting, on a 140 page mk.tex this saves
+about .25 seconds, which is understandable because we have no graphmes and
+not collecting tokens is not only faster but also saves garbage collecting.
+</p>
+--ldx]]--
+
+-- lpeg variant is not faster
+--
+-- I might use the combined loop at some point for the filter
+-- some day.
+
+-- function utffilters.collapse(str) -- not really tested (we could preallocate a table)
+-- if str and str ~= "" then
+-- local nstr = #str
+-- if nstr > 1 then
+-- if initialize then -- saves a call
+-- initialize()
+-- end
+-- local tokens, t, first, done, n = { }, 0, false, false, 0
+-- for second in utfcharacters(str) do
+-- local dec = decomposed[second]
+-- if dec then
+-- if not done then
+-- if n > 0 then
+-- for s in utfcharacters(str) do
+-- if n == 1 then
+-- break
+-- else
+-- t = t + 1
+-- tokens[t] = s
+-- n = n - 1
+-- end
+-- end
+-- end
+-- done = true
+-- elseif first then
+-- t = t + 1
+-- tokens[t] = first
+-- end
+-- t = t + 1
+-- tokens[t] = dec
+-- first = false
+-- elseif done then
+-- local crs = high[second]
+-- if crs then
+-- if first then
+-- t = t + 1
+-- tokens[t] = first
+-- end
+-- first = crs
+-- else
+-- local cgf = graphemes[first]
+-- if cgf and cgf[second] then
+-- first = cgf[second]
+-- elseif first then
+-- t = t + 1
+-- tokens[t] = first
+-- first = second
+-- else
+-- first = second
+-- end
+-- end
+-- else
+-- local crs = high[second]
+-- if crs then
+-- for s in utfcharacters(str) do
+-- if n == 1 then
+-- break
+-- else
+-- t = t + 1
+-- tokens[t] = s
+-- n = n - 1
+-- end
+-- end
+-- if first then
+-- t = t + 1
+-- tokens[t] = first
+-- end
+-- first = crs
+-- done = true
+-- else
+-- local cgf = graphemes[first]
+-- if cgf and cgf[second] then
+-- for s in utfcharacters(str) do
+-- if n == 1 then
+-- break
+-- else
+-- t = t + 1
+-- tokens[t] = s
+-- n = n - 1
+-- end
+-- end
+-- first = cgf[second]
+-- done = true
+-- else
+-- first = second
+-- n = n + 1
+-- end
+-- end
+-- end
+-- end
+-- if done then
+-- if first then
+-- t = t + 1
+-- tokens[t] = first
+-- end
+-- return concat(tokens) -- seldom called
+-- end
+-- elseif nstr > 0 then
+-- return high[str] or str
+-- end
+-- end
+-- return str
+-- end
+
+local skippable = table.tohash { "mkiv", "mkvi" }
+local filesuffix = file.suffix
+
+-- we could reuse tokens but it's seldom populated anyway
+
+function utffilters.collapse(str,filename) -- not really tested (we could preallocate a table)
+ if skippable[filesuffix(filename)] then
+ return str
+ end
+ if str and str ~= "" then
+ local nstr = #str
+ if nstr > 1 then
+ if initialize then -- saves a call
+ initialize()
+ end
+ local tokens, t, first, done, n = { }, 0, false, false, 0
+ for second in utfcharacters(str) do
+ if done then
+ local crs = high[second]
+ if crs then
+ if first then
+ t = t + 1
+ tokens[t] = first
+ end
+ first = crs
+ else
+ local cgf = graphemes[first]
+ if cgf and cgf[second] then
+ first = cgf[second]
+ elseif first then
+ t = t + 1
+ tokens[t] = first
+ first = second
+ else
+ first = second
+ end
+ end
+ else
+ local crs = high[second]
+ if crs then
+ for s in utfcharacters(str) do
+ if n == 1 then
+ break
+ else
+ t = t + 1
+ tokens[t] = s
+ n = n - 1
+ end
+ end
+ if first then
+ t = t + 1
+ tokens[t] = first
+ end
+ first = crs
+ done = true
+ else
+ local cgf = graphemes[first]
+ if cgf and cgf[second] then
+ for s in utfcharacters(str) do
+ if n == 1 then
+ break
+ else
+ t = t + 1
+ tokens[t] = s
+ n = n - 1
+ end
+ end
+ first = cgf[second]
+ done = true
+ else
+ first = second
+ n = n + 1
+ end
+ end
+ end
+ end
+ if done then
+ if first then
+ t = t + 1
+ tokens[t] = first
+ end
+ return concat(tokens) -- seldom called
+ end
+ elseif nstr > 0 then
+ return high[str] or str
+ end
+ end
+ return str
+end
+
+function utffilters.decompose(str)
+ if str and str ~= "" then
+ local nstr = #str
+ if nstr > 1 then
+ -- if initialize then -- saves a call
+ -- initialize()
+ -- end
+ local tokens, t, done, n = { }, 0, false, 0
+ for s in utfcharacters(str) do
+ local dec = decomposed[s]
+ if dec then
+ if not done then
+ if n > 0 then
+ for s in utfcharacters(str) do
+ if n == 1 then
+ break
+ else
+ t = t + 1
+ tokens[t] = s
+ n = n - 1
+ end
+ end
+ end
+ done = true
+ end
+ t = t + 1
+ tokens[t] = dec
+ elseif done then
+ t = t + 1
+ tokens[t] = s
+ else
+ n = n + 1
+ end
+ end
+ if done then
+ return concat(tokens) -- seldom called
+ end
+ end
+ end
+ return str
+end
+
+local sequencers = utilities.sequencers
+
+if sequencers then
+
+ local textfileactions = resolvers.openers.helpers.textfileactions
+
+ sequencers.appendaction (textfileactions,"system","characters.filters.utf.collapse")
+ sequencers.disableaction(textfileactions,"characters.filters.utf.collapse")
+
+ sequencers.appendaction (textfileactions,"system","characters.filters.utf.decompose")
+ sequencers.disableaction(textfileactions,"characters.filters.utf.decompose")
+
+ function characters.filters.utf.enable()
+ sequencers.enableaction(textfileactions,"characters.filters.utf.collapse")
+ sequencers.enableaction(textfileactions,"characters.filters.utf.decompose")
+ end
+
+ directives.register("filters.utf.collapse", function(v)
+ sequencers[v and "enableaction" or "disableaction"](textfileactions,"characters.filters.utf.collapse")
+ end)
+
+ directives.register("filters.utf.decompose", function(v)
+ sequencers[v and "enableaction" or "disableaction"](textfileactions,"characters.filters.utf.decompose")
+ end)
+
+end
+
+--[[ldx--
+<p>Next we implement some commands that are used in the user interface.</p>
+--ldx]]--
+
+-- commands = commands or { }
+--
+-- function commands.uchar(first,second)
+-- context(utfchar(first*256+second))
+-- end
+
+--[[ldx--
+<p>A few helpers (used to be <t>luat-uni<t/>).</p>
+--ldx]]--
+
+-- obsolete:
+--
+-- function utf.split(str)
+-- local t, n = { }, 0
+-- for snippet in utfcharacters(str) do
+-- n = n + 1
+-- t[n+1] = snippet
+-- end
+-- return t
+-- end
+--
+-- function utf.each(str,fnc)
+-- for snippet in utfcharacters(str) do
+-- fnc(snippet)
+-- end
+-- end
diff --git a/tex/context/base/chem-ini.lua b/tex/context/base/chem-ini.lua
index e694a92de..10db1a1e4 100644
--- a/tex/context/base/chem-ini.lua
+++ b/tex/context/base/chem-ini.lua
@@ -1,43 +1,43 @@
-if not modules then modules = { } end modules ['chem-ini'] = {
- version = 1.001,
- comment = "companion to chem-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 lpegmatch, patterns = lpeg.match, lpeg.patterns
-
-local trace_molecules = false trackers.register("chemistry.molecules", function(v) trace_molecules = v end)
-
-local report_chemistry = logs.reporter("chemistry")
-
-local context = context
-local cpatterns = patterns.context
-
-chemistry = chemistry or { }
-local chemistry = chemistry
-
---[[
-<p>The next code started out as adaptation of code from Wolfgang Schuster as
-posted on the mailing list. The current version supports nested braces and
-unbraced integers as scripts.</p>
-]]--
-
-local moleculeparser = cpatterns.scripted
-chemistry.moleculeparser = moleculeparser
-
-function chemistry.molecule(str)
- return lpegmatch(moleculeparser,str)
-end
-
-function commands.molecule(str)
- if trace_molecules then
- local rep = lpegmatch(moleculeparser,str)
- report_chemistry("molecule %a becomes %a",str,rep)
- context(rep)
- else
- context(lpegmatch(moleculeparser,str))
- end
-end
+if not modules then modules = { } end modules ['chem-ini'] = {
+ version = 1.001,
+ comment = "companion to chem-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 lpegmatch, patterns = lpeg.match, lpeg.patterns
+
+local trace_molecules = false trackers.register("chemistry.molecules", function(v) trace_molecules = v end)
+
+local report_chemistry = logs.reporter("chemistry")
+
+local context = context
+local cpatterns = patterns.context
+
+chemistry = chemistry or { }
+local chemistry = chemistry
+
+--[[
+<p>The next code started out as adaptation of code from Wolfgang Schuster as
+posted on the mailing list. The current version supports nested braces and
+unbraced integers as scripts.</p>
+]]--
+
+local moleculeparser = cpatterns.scripted
+chemistry.moleculeparser = moleculeparser
+
+function chemistry.molecule(str)
+ return lpegmatch(moleculeparser,str)
+end
+
+function commands.molecule(str)
+ if trace_molecules then
+ local rep = lpegmatch(moleculeparser,str)
+ report_chemistry("molecule %a becomes %a",str,rep)
+ context(rep)
+ else
+ context(lpegmatch(moleculeparser,str))
+ end
+end
diff --git a/tex/context/base/chem-str.lua b/tex/context/base/chem-str.lua
index 679314e2d..dfcf0a3e1 100644
--- a/tex/context/base/chem-str.lua
+++ b/tex/context/base/chem-str.lua
@@ -1,820 +1,820 @@
-if not modules then modules = { } end modules ['chem-str'] = {
- version = 1.001,
- comment = "companion to chem-str.mkiv",
- author = "Hans Hagen and Alan Braslau",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- The original \PPCHTEX\ code was written in pure \TEX\, although later we made
--- the move from \PICTEX\ to \METAPOST\. The current implementation is a mix between
--- \TEX\, \LUA\ and \METAPOST. Although the first objective is to get a compatible
--- but better implementation, later versions might provide more.
---
--- Well, the later version has arrived as Alan took it upon him to make the code
--- deviate even further from the original implementation. The original (early \MKII)
--- variant operated within the boundaries of \PICTEX\ and as it supported MetaPost as
--- alternative output. As a consequence it still used a stepwise graphic construction
--- approach. As we used \TEX\ for parsing, the syntax was more rigid than it is now.
--- This new variant uses a more mathematical and metapostisch approach. In the process
--- more rendering variants have been added and alignment has been automated. As a result
--- the current user interface is slightly different from the old one but hopefully users
--- will like the added value.
-
--- directive_strictorder: one might set this to off when associated texts are disordered too
-
-local trace_structure = false trackers .register("chemistry.structure", function(v) trace_structure = v end)
-local trace_metapost = false trackers .register("chemistry.metapost", function(v) trace_metapost = v end)
-local trace_boundingbox = false trackers .register("chemistry.boundingbox", function(v) trace_boundingbox = v end)
-local trace_textstack = false trackers .register("chemistry.textstack", function(v) trace_textstack = v end)
-local directive_strictorder = true directives.register("chemistry.strictorder", function(v) directive_strictorder = v end)
-local directive_strictindex = false directives.register("chemistry.strictindex", function(v) directive_strictindex = v end)
-
-local report_chemistry = logs.reporter("chemistry")
-
-local format, gmatch, match, lower, gsub = string.format, string.gmatch, string.match, string.lower, string.gsub
-local concat, insert, remove, unique, sorted = table.concat, table.insert, table.remove, table.unique, table.sorted
-local processor_tostring = typesetters and typesetters.processors.tostring
-local settings_to_array = utilities.parsers.settings_to_array
-local settings_to_array_with_repeat = utilities.parsers.settings_to_array_with_repeat
-local formatters = string.formatters
-
-local lpegmatch = lpeg.match
-local P, R, S, C, Cs, Ct, Cc, Cmt = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Ct, lpeg.Cc, lpeg.Cmt
-
-local variables = interfaces and interfaces.variables
-local context = context
-local formatters = string.formatters
-local texcount = tex.count
-
-local v_default = variables.default
-local v_small = variables.small
-local v_medium = variables.medium
-local v_big = variables.big
-local v_normal = variables.normal
-local v_fit = variables.fit
-local v_on = variables.on
-local v_none = variables.none
-
-local mpnamedcolor = attributes.colors.mpnamedcolor
-local topoints = number.topoints
-local todimen = string.todimen
-
-chemistry = chemistry or { }
-local chemistry = chemistry
-
-chemistry.instance = "chemistry"
-chemistry.format = "metafun"
-chemistry.structures = 0
-
-local common_keys = {
- b = "line",
- r = "line",
- sb = "line",
- sr = "line",
- rd = "line",
- rh = "line",
- rb = "line",
- rbd = "line",
- cc = "line",
- ccd = "line",
- line = "line",
- dash = "line",
- arrow = "line",
- c = "fixed",
- cd = "fixed",
- z = "text",
- zt = "text",
- zlt = "text",
- zrt = "text",
- rz = "text",
- rt = "text",
- lrt = "text",
- rrt = "text",
- label = "text",
- zln = "number",
- zrn = "number",
- rn = "number",
- lrn = "number",
- rrn = "number",
- zn = "number",
- number = "number",
- mov = "transform",
- mark = "transform",
- move = "transform",
- diff = "transform",
- off = "transform",
- adj = "transform",
- sub = "transform",
-}
-
-local front_keys = {
- bb = "line",
- eb = "line",
- rr = "line",
- lr = "line",
- lsr = "line",
- rsr = "line",
- lrd = "line",
- rrd = "line",
- lrh = "line",
- rrh = "line",
- lrbd = "line",
- rrbd = "line",
- lrb = "line",
- rrb = "line",
- lrz = "text",
- rrz = "text",
- lsub = "transform",
- rsub = "transform",
-}
-
-local one_keys = {
- db = "line",
- tb = "line",
- bb = "line",
- dr = "line",
- hb = "line",
- bd = "line",
- bw = "line",
- oe = "line",
- sd = "line",
- rdb = "line",
- ldb = "line",
- ldd = "line",
- rdd = "line",
- ep = "line",
- es = "line",
- ed = "line",
- et = "line",
- cz = "text",
- rot = "transform",
- dir = "transform",
- rm = "transform",
- mir = "transform",
-}
-
-local ring_keys = {
- db = "line",
- br = "line",
- lr = "line",
- rr = "line",
- lsr = "line",
- rsr = "line",
- lrd = "line",
- rrd = "line",
- lrb = "line",
- rrb = "line",
- lrh = "line",
- rrh = "line",
- lrbd = "line",
- rrbd = "line",
- dr = "line",
- eb = "line",
- er = "line",
- ed = "line",
- au = "line",
- ad = "line",
- s = "line",
- ss = "line",
- mid = "line",
- mids = "line",
- midz = "text",
- lrz = "text",
- rrz = "text",
- crz = "text",
- rot = "transform",
- mir = "transform",
- adj = "transform",
- lsub = "transform",
- rsub = "transform",
- rm = "transform",
-}
-
--- table.setmetatableindex(front_keys,common_keys)
--- table.setmetatableindex(one_keys,common_keys)
--- table.setmetatableindex(ring_keys,common_keys)
-
--- or (faster but not needed here):
-
-front_keys = table.merged(front_keys,common_keys)
-one_keys = table.merged(one_keys,common_keys)
-ring_keys = table.merged(ring_keys,common_keys)
-
-local syntax = {
- carbon = { max = 4, keys = one_keys, },
- alkyl = { max = 4, keys = one_keys, },
- newmanstagger = { max = 6, keys = one_keys, },
- newmaneclipsed = { max = 6, keys = one_keys, },
- one = { max = 8, keys = one_keys, },
- three = { max = 3, keys = ring_keys, },
- four = { max = 4, keys = ring_keys, },
- five = { max = 5, keys = ring_keys, },
- six = { max = 6, keys = ring_keys, },
- seven = { max = 7, keys = ring_keys, },
- eight = { max = 8, keys = ring_keys, },
- nine = { max = 9, keys = ring_keys, },
- fivefront = { max = 5, keys = front_keys, },
- sixfront = { max = 6, keys = front_keys, },
- chair = { max = 6, keys = front_keys, },
- boat = { max = 6, keys = front_keys, },
- pb = { direct = 'chem_pb;' },
- pe = { direct = 'chem_pe;' },
- save = { direct = 'chem_save;' },
- restore = { direct = 'chem_restore;' },
- chem = { direct = formatters['chem_symbol("\\chemicaltext{%s}");'], arguments = 1 },
- space = { direct = 'chem_symbol("\\chemicalsymbol[space]");' },
- plus = { direct = 'chem_symbol("\\chemicalsymbol[plus]");' },
- minus = { direct = 'chem_symbol("\\chemicalsymbol[minus]");' },
- gives = { direct = formatters['chem_symbol("\\chemicalsymbol[gives]{%s}{%s}");'], arguments = 2 },
- equilibrium = { direct = formatters['chem_symbol("\\chemicalsymbol[equilibrium]{%s}{%s}");'], arguments = 2 },
- mesomeric = { direct = formatters['chem_symbol("\\chemicalsymbol[mesomeric]{%s}{%s}");'], arguments = 2 },
- opencomplex = { direct = 'chem_symbol("\\chemicalsymbol[opencomplex]");' },
- closecomplex = { direct = 'chem_symbol("\\chemicalsymbol[closecomplex]");' },
- reset = { direct = 'chem_reset;' },
- mp = { direct = formatters['%s'], arguments = 1 }, -- backdoor MP code - dangerous!
-}
-
-chemistry.definitions = chemistry.definitions or { }
-local definitions = chemistry.definitions
-
-storage.register("chemistry/definitions",definitions,"chemistry.definitions")
-
-function chemistry.undefine(name)
- definitions[lower(name)] = nil
-end
-
-function chemistry.define(name,spec,text)
- name = lower(name)
- local dn = definitions[name]
- if not dn then
- dn = { }
- definitions[name] = dn
- end
- dn[#dn+1] = {
- spec = settings_to_array_with_repeat(spec,true),
- text = settings_to_array_with_repeat(text,true),
- }
-end
-
-local metacode, variant, keys, max, txt, pstack, sstack, align
-local molecule = chemistry.molecule -- or use lpegmatch(chemistry.moleculeparser,...)
-
-local function fetch(txt)
- local st = stack[txt]
- local t = st.text[st.n]
- while not t and txt > 1 do
- txt = txt - 1
- st = stack[txt]
- t = st.text[st.n]
- end
- if t then
- if trace_textstack then
- report_chemistry("fetching from stack %a, slot %a, data %a",txt,st.n,t)
- end
- st.n = st.n + 1
- end
- return txt, t
-end
-
-local remapper = {
- ["+"] = "p",
- ["-"] = "m",
-}
-
-local dchrs = R("09")
-local sign = S("+-")
-local digit = dchrs / tonumber
-local amount = (sign^-1 * (dchrs^0 * P('.'))^-1 * dchrs^1) / tonumber
-local single = digit
-local range = digit * P("..") * digit
-local set = Ct(digit^2)
-local colon = P(":")
-local equal = P("=")
-local other = 1 - digit - colon - equal
-local remapped = sign / remapper
-local operation = Cs(other^1)
-local special = (colon * C(other^1)) + Cc("")
-local text = (equal * C(P(1)^0)) + Cc(false)
-
-local pattern =
- (amount + Cc(1))
- * (remapped + Cc(""))
- * Cs(operation/lower)
- * Cs(special/lower) * (
- range * Cc(false) * text +
- Cc(false) * Cc(false) * set * text +
- single * Cc(false) * Cc(false) * text +
- Cc(false) * Cc(false) * Cc(false) * text
- )
-
--- local n, operation, index, upto, set, text = lpegmatch(pattern,"RZ1357")
-
--- print(lpegmatch(pattern,"RZ=x")) -- 1 RZ false false false x
--- print(lpegmatch(pattern,"RZ1=x")) -- 1 RZ 1 false false x
--- print(lpegmatch(pattern,"RZ1..3=x")) -- 1 RZ 1 3 false x
--- print(lpegmatch(pattern,"RZ13=x")) -- 1 RZ false false table x
-
-local f_initialize = 'if unknown context_chem : input mp-chem.mpiv ; fi ;'
-local f_start_structure = formatters['chem_start_structure(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s);']
-local f_set_trace_bounds = formatters['chem_trace_boundingbox := %l ;']
-local f_stop_structure = 'chem_stop_structure;'
-local f_start_component = 'chem_start_component;'
-local f_stop_component = 'chem_stop_component;'
-local f_line = formatters['chem_%s%s(%s,%s,%s,%s,%s);']
-local f_set = formatters['chem_set(%s);']
-local f_number = formatters['chem_%s%s(%s,%s,"\\chemicaltext{%s}");']
-local f_text = f_number
-local f_empty_normal = formatters['chem_%s(%s,%s,"");']
-local f_empty_center = formatters['chem_c%s(%s,%s,"");']
-local f_transform = formatters['chem_%s(%s,%s,%s);']
-
-local prepareMPvariable = commands and commands.prepareMPvariable
-
-local function process(level,spec,text,n,rulethickness,rulecolor,offset,default_variant)
- insert(stack,{ spec = spec, text = text, n = n })
- local txt = #stack
- local m = #metacode
- local saved_rulethickness = rulethickness
- local saved_rulecolor = rulecolor
- local saved_align = align
- local current_variant = default_variant or "six"
- for i=1,#spec do
- local step = spec[i]
- local s = lower(step)
- local n = current_variant .. ":" .. s
- local d = definitions[n]
- if not d then
- n = s
- d = definitions[n]
- end
- if d then
- if trace_structure then
- report_chemistry("level %a, step %a, definition %a, snippets %a",level,step,n,#d)
- end
- for i=1,#d do
- local di = d[i]
- current_variant = process(level+1,di.spec,di.text,1,rulethickness,rulecolor,offset,current_variant) -- offset?
- end
- else
- local factor, osign, operation, special, index, upto, set, text = lpegmatch(pattern,step)
- if trace_structure then
- local set = set and concat(set," ") or "-"
- report_chemistry("level %a, step %a, factor %a, osign %a, operation %a, special %a, index %a, upto %a, set %a, text %a",
- level,step,factor,osign,operation,special,index,upto,set,text)
- end
- if operation == "rulecolor" then
- local t = text
- if not t then
- txt, t = fetch(txt)
- end
- if t == v_default or t == v_normal or t == "" then
- rulecolor = saved_rulecolor
- elseif t then
- rulecolor = mpnamedcolor(t)
- end
- elseif operation == "rulethickness" then
- local t = text
- if not t then
- txt, t = fetch(txt)
- end
- if t == v_default or t == v_normal or t == t_medium or t == "" then
- rulethickness = saved_rulethickness
- elseif t == v_small then
- rulethickness = topoints(1/1.2 * todimen(saved_rulethickness))
- elseif t == v_big then
- rulethickness = topoints(1.2 * todimen(saved_rulethickness))
- elseif t then
- -- rulethickness = topoints(todimen(t)) -- mp can't handle sp
- rulethickness = topoints(tonumber(t) * todimen(saved_rulethickness))
- end
- elseif operation == "symalign" then
- local t = text
- if not t then
- txt, t = fetch(txt)
- end
- if t == v_default or t == v_normal then
- align = saved_align
- elseif t and t ~= "" then
- align = "." .. t
- end
- elseif operation == "pb" then
- insert(pstack,variant)
- m = m + 1 ; metacode[m] = syntax.pb.direct
- if keys[special] == "text" and index then
- if keys["c"..special] == "text" then -- can be option: auto ...
- m = m + 1 ; metacode[m] = f_empty_center(special,variant,index)
- else
- m = m + 1 ; metacode[m] = f_empty_normal(special,variant,index)
- end
- end
- elseif operation == "pe" then
- variant = remove(pstack)
- local ss = syntax[variant]
- keys, max = ss.keys, ss.max
- m = m + 1 ; metacode[m] = syntax.pe.direct
- m = m + 1 ; metacode[m] = f_set(variant)
- current_variant = variant
- elseif operation == "save" then
- insert(sstack,variant)
- m = m + 1 ; metacode[m] = syntax.save.direct
- elseif operation == "restore" then
- variant = remove(sstack)
- local ss = syntax[variant]
- keys, max = ss.keys, ss.max
- m = m + 1 ; metacode[m] = syntax.restore.direct
- m = m + 1 ; metacode[m] = f_set(variant)
- current_variant = variant
- elseif operation then
- local ss = syntax[operation]
- local what = keys[operation]
- local ns = 0
- if set then
- local sv = syntax[current_variant]
- local ms = sv and sv.max
- set = unique(set)
- ns = #set
- if directive_strictorder then
- if what == "line" then
- set = sorted(set)
- end
- if directive_strictindex and ms then
- for i=ns,1,-1 do
- local si = set[i]
- if si > ms then
- report_chemistry("level %a, operation %a, max nofsteps %a, ignoring %a",level,operation,ms,si)
- set[i] = nil
- ns = ns - 1
- else
- break
- end
- end
- end
- else
- if directive_strictindex and ms then
- local t, nt = { }, 0
- for i=1,ns do
- local si = set[i]
- if si > ms then
- report_chemistry("level %a, operation %a, max nofsteps %a, ignoring %a",level,operation,ms,si)
- set[i] = nil
- else
- nt = nt + 1
- t[nt] = si
- end
- end
- ns = nt
- set = t
- end
- end
- end
- if ss then
- local ds = ss.direct
- if ds then
- local sa = ss.arguments
- if sa == 1 then
- local one ; txt, one = fetch(txt)
- m = m + 1 ; metacode[m] = ds(one or "")
- elseif sa == 2 then
- local one ; txt, one = fetch(txt)
- local two ; txt, two = fetch(txt)
- m = m + 1 ; metacode[m] = ds(one or "",two or "")
- else
- m = m + 1 ; metacode[m] = ds
- end
- elseif ss.keys then
- variant, keys, max = s, ss.keys, ss.max
- m = m + 1 ; metacode[m] = f_set(variant)
- current_variant = variant
- end
- elseif what == "line" then
- local s = osign
- if s ~= "" then
- s = "." .. s
- end
- if set then
- -- condense consecutive numbers in a set to a range
- local sf, st = set[1]
- for i=1,ns do
- if i > 1 and set[i] ~= set[i-1]+1 then
- m = m + 1 ; metacode[m] = f_line(operation,s,variant,sf,st,rulethickness,rulecolor)
- sf = set[i]
- end
- st = set[i]
- end
- m = m + 1 ; metacode[m] = f_line(operation,s,variant,sf,st,rulethickness,rulecolor)
- elseif upto then
- m = m + 1 ; metacode[m] = f_line(operation,s,variant,index,upto,rulethickness,rulecolor)
- elseif index then
- m = m + 1 ; metacode[m] = f_line(operation,s,variant,index,index,rulethickness,rulecolor)
- else
- m = m + 1 ; metacode[m] = f_line(operation,s,variant,1,max,rulethickness,rulecolor)
- end
- elseif what == "number" then
- if set then
- for i=1,ns do
- local si = set[i]
- m = m + 1 ; metacode[m] = f_number(operation,align,variant,si,si)
- end
- elseif upto then
- for i=index,upto do
- local si = set[i]
- m = m + 1 ; metacode[m] = f_number(operation,align,variant,si,si)
- end
- elseif index then
- m = m + 1 ; metacode[m] = f_number(operation,align,variant,index,index)
- else
- for i=1,max do
- m = m + 1 ; metacode[m] = f_number(operation,align,variant,i,i)
- end
- end
- elseif what == "text" then
- if set then
- for i=1,ns do
- local si = set[i]
- local t = text
- if not t then txt, t = fetch(txt) end
- if t then
- t = molecule(processor_tostring(t))
- m = m + 1 ; metacode[m] = f_text(operation,align,variant,si,t)
- end
- end
- elseif upto then
- for i=index,upto do
- local t = text
- if not t then txt, t = fetch(txt) end
- if t then
- t = molecule(processor_tostring(t))
- m = m + 1 ; metacode[m] = f_text(operation,align,variant,i,t)
- end
- end
- elseif index == 0 then
- local t = text
- if not t then txt, t = fetch(txt) end
- if t then
- t = molecule(processor_tostring(t))
- m = m + 1 ; metacode[m] = f_text(operation,align,variant,index,t)
- end
- elseif index then
- local t = text
- if not t then txt, t = fetch(txt) end
- if t then
- t = molecule(processor_tostring(t))
- m = m + 1 ; metacode[m] = f_text(operation,align,variant,index,t)
- end
- else
- for i=1,max do
- local t = text
- if not t then txt, t = fetch(txt) end
- if t then
- t = molecule(processor_tostring(t))
- m = m + 1 ; metacode[m] = f_text(operation,align,variant,i,t)
- end
- end
- end
- elseif what == "transform" then
- if osign == "m" then
- factor = -factor
- end
- if set then
- for i=1,ns do
- local si = set[i]
- m = m + 1 ; metacode[m] = f_transform(operation,variant,si,factor)
- end
- elseif upto then
- for i=index,upto do
- m = m + 1 ; metacode[m] = f_transform(operation,variant,i,factor)
- end
- else
- m = m + 1 ; metacode[m] = f_transform(operation,variant,index or 1,factor)
- end
- elseif what == "fixed" then
- m = m + 1 ; metacode[m] = f_transform(operation,variant,rulethickness,rulecolor)
- elseif trace_structure then
- report_chemistry("level %a, ignoring undefined operation %s",level,operation)
- end
- end
- end
- end
- remove(stack)
- return current_variant
-end
-
--- the size related values are somewhat special but we want to be
--- compatible
---
--- rulethickness in points
-
-local function checked(d,factor,unit,scale)
- if d == v_none then
- return 0
- end
- local n = tonumber(d)
- if not n then
- -- assume dimen
- elseif n >= 10 or n <= -10 then
- return factor * unit * n / 1000
- else
- return factor * unit * n
- end
- local n = todimen(d)
- if n then
- return scale * n
- else
- return v_fit
- end
-end
-
-local function calculated(height,bottom,top,factor,unit,scale)
- local scaled = 0
- if height == v_none then
- -- this always wins
- height = "0pt"
- bottom = "0pt"
- top = "0pt"
- elseif height == v_fit then
- height = "true"
- bottom = bottom == v_fit and "true" or topoints(checked(bottom,factor,unit,scale))
- top = top == v_fit and "true" or topoints(checked(top, factor,unit,scale))
- else
- height = checked(height,factor,unit,scale)
- if bottom == v_fit then
- if top == v_fit then
- bottom = height / 2
- top = bottom
- else
- top = checked(top,factor,unit,scale)
- bottom = height - top
- end
- elseif top == v_fit then
- bottom = checked(bottom,factor,unit,scale)
- top = height - bottom
- else
- bottom = checked(bottom,factor,unit,scale)
- top = checked(top, factor,unit,scale)
- local ratio = height / (bottom+top)
- bottom = bottom * ratio
- top = top * ratio
- end
- scaled = height
- top = topoints(top)
- bottom = topoints(bottom)
- height = topoints(height)
- end
- return height, bottom, top, scaled
-end
-
-function chemistry.start(settings)
- --
- local width = settings.width or v_fit
- local height = settings.height or v_fit
- local unit = settings.unit or 655360
- local factor = settings.factor or 3
- local rulethickness = settings.rulethickness or 65536
- local rulecolor = settings.rulecolor or ""
- local axiscolor = settings.framecolor or ""
- local scale = settings.scale or "normal"
- local rotation = settings.rotation or 0
- local offset = settings.offset or 0
- local left = settings.left or v_fit
- local right = settings.right or v_fit
- local top = settings.top or v_fit
- local bottom = settings.bottom or v_fit
- --
- align = settings.symalign or "auto"
- if trace_structure then
- report_chemistry("unit %p, factor %s, symalign %s",unit,factor,align)
- end
- if align ~= "" then
- align = "." .. align
- end
- if trace_structure then
- report_chemistry("%s scale %a, rotation %a, width %s, height %s, left %s, right %s, top %s, bottom %s","asked",scale,rotation,width,height,left,right,top,bottom)
- end
- if scale == v_small then
- scale = 1/1.2
- elseif scale == v_normal or scale == v_medium or scale == 0 then
- scale = 1
- elseif scale == v_big then
- scale = 1.2
- else
- scale = tonumber(scale)
- if not scale or scale == 0 then
- scale = 1
- elseif scale >= 10 then
- scale = scale / 1000
- elseif scale < .01 then
- scale = .01
- end
- end
- --
- unit = scale * unit
- --
- local sp_width = 0
- local sp_height = 0
- --
- width, left, right, sp_width = calculated(width, left, right,factor,unit,scale)
- height, bottom, top, sp_height = calculated(height,bottom,top, factor,unit,scale)
- --
- if width ~= "true" and height ~= "true" and texcount["@@trialtypesetting"] ~= 0 then
- if trace_structure then
- report_chemistry("skipping trial run")
- end
- context.hrule(sp_width,sp_height,0) -- maybe depth
- return
- end
- --
- chemistry.structures = chemistry.structures + 1
- --
- rotation = tonumber(rotation) or 0
- --
- metacode = { }
- --
- if trace_structure then
- report_chemistry("%s scale %a, rotation %a, width %s, height %s, left %s, right %s, top %s, bottom %s","used",scale,rotation,width,height,left,right,top,bottom)
- end
- metacode[#metacode+1] = f_start_structure(
- chemistry.structures,
- left, right, top, bottom,
- rotation, topoints(unit), factor, topoints(offset),
- tostring(settings.axis == v_on), topoints(rulethickness), tostring(axiscolor)
- )
- metacode[#metacode+1] = f_set_trace_bounds(trace_boundingbox) ;
- --
- variant, keys, stack, pstack, sstack = "one", { }, { }, { }, { }
-end
-
-function chemistry.stop()
- if metacode then
- metacode[#metacode+1] = f_stop_structure
- local mpcode = concat(metacode,"\n")
- if trace_metapost then
- report_chemistry("metapost code:\n%s", mpcode)
- end
- if metapost.instance(chemistry.instance) then
- f_initialize = nil
- end
- metapost.graphic {
- instance = chemistry.instance,
- format = chemistry.format,
- data = mpcode,
- definitions = f_initialize,
- }
- t_initialize = ""
- metacode = nil
- end
-end
-
-function chemistry.component(spec,text,settings)
- if metacode then
- rulethickness, rulecolor, offset = settings.rulethickness, settings.rulecolor
- local spec = settings_to_array_with_repeat(spec,true) -- no lower?
- local text = settings_to_array_with_repeat(text,true)
- -- inspect(spec)
- metacode[#metacode+1] = f_start_component
- process(1,spec,text,1,rulethickness,rulecolor) -- offset?
- metacode[#metacode+1] = f_stop_component
- end
-end
-
-statistics.register("chemical formulas", function()
- if chemistry.structures > 0 then
- return format("%s chemical structure formulas",chemistry.structures) -- no timing needed, part of metapost
- end
-end)
-
--- interfaces
-
-commands.undefinechemical = chemistry.undefine
-commands.definechemical = chemistry.define
-commands.startchemical = chemistry.start
-commands.stopchemical = chemistry.stop
-commands.chemicalcomponent = chemistry.component
-
--- todo: top / bottom
--- maybe add "=" for double and "≡" for triple?
-
-local inline = {
- ["single"] = "\\chemicalsinglebond", ["-"] = "\\chemicalsinglebond",
- ["double"] = "\\chemicaldoublebond", ["--"] = "\\chemicaldoublebond",
- ["triple"] = "\\chemicaltriplebond", ["---"] = "\\chemicaltriplebond",
- ["gives"] = "\\chemicalgives", ["->"] = "\\chemicalgives",
- ["equilibrium"] = "\\chemicalequilibrium", ["<->"] = "\\chemicalequilibrium",
- ["mesomeric"] = "\\chemicalmesomeric", ["<>"] = "\\chemicalmesomeric",
- ["plus"] = "\\chemicalplus", ["+"] = "\\chemicalplus",
- ["minus"] = "\\chemicalminus",
- ["space"] = "\\chemicalspace",
-}
-
-function commands.inlinechemical(spec)
- local spec = settings_to_array_with_repeat(spec,true)
- for i=1,#spec do
- local s = spec[i]
- local inl = inline[lower(s)]
- if inl then
- context(inl) -- could be a fast context.sprint
- else
- context.chemicalinline(molecule(s))
- end
- end
-end
+if not modules then modules = { } end modules ['chem-str'] = {
+ version = 1.001,
+ comment = "companion to chem-str.mkiv",
+ author = "Hans Hagen and Alan Braslau",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- The original \PPCHTEX\ code was written in pure \TEX\, although later we made
+-- the move from \PICTEX\ to \METAPOST\. The current implementation is a mix between
+-- \TEX\, \LUA\ and \METAPOST. Although the first objective is to get a compatible
+-- but better implementation, later versions might provide more.
+--
+-- Well, the later version has arrived as Alan took it upon him to make the code
+-- deviate even further from the original implementation. The original (early \MKII)
+-- variant operated within the boundaries of \PICTEX\ and as it supported MetaPost as
+-- alternative output. As a consequence it still used a stepwise graphic construction
+-- approach. As we used \TEX\ for parsing, the syntax was more rigid than it is now.
+-- This new variant uses a more mathematical and metapostisch approach. In the process
+-- more rendering variants have been added and alignment has been automated. As a result
+-- the current user interface is slightly different from the old one but hopefully users
+-- will like the added value.
+
+-- directive_strictorder: one might set this to off when associated texts are disordered too
+
+local trace_structure = false trackers .register("chemistry.structure", function(v) trace_structure = v end)
+local trace_metapost = false trackers .register("chemistry.metapost", function(v) trace_metapost = v end)
+local trace_boundingbox = false trackers .register("chemistry.boundingbox", function(v) trace_boundingbox = v end)
+local trace_textstack = false trackers .register("chemistry.textstack", function(v) trace_textstack = v end)
+local directive_strictorder = true directives.register("chemistry.strictorder", function(v) directive_strictorder = v end)
+local directive_strictindex = false directives.register("chemistry.strictindex", function(v) directive_strictindex = v end)
+
+local report_chemistry = logs.reporter("chemistry")
+
+local format, gmatch, match, lower, gsub = string.format, string.gmatch, string.match, string.lower, string.gsub
+local concat, insert, remove, unique, sorted = table.concat, table.insert, table.remove, table.unique, table.sorted
+local processor_tostring = typesetters and typesetters.processors.tostring
+local settings_to_array = utilities.parsers.settings_to_array
+local settings_to_array_with_repeat = utilities.parsers.settings_to_array_with_repeat
+local formatters = string.formatters
+
+local lpegmatch = lpeg.match
+local P, R, S, C, Cs, Ct, Cc, Cmt = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Ct, lpeg.Cc, lpeg.Cmt
+
+local variables = interfaces and interfaces.variables
+local context = context
+local formatters = string.formatters
+local texcount = tex.count
+
+local v_default = variables.default
+local v_small = variables.small
+local v_medium = variables.medium
+local v_big = variables.big
+local v_normal = variables.normal
+local v_fit = variables.fit
+local v_on = variables.on
+local v_none = variables.none
+
+local mpnamedcolor = attributes.colors.mpnamedcolor
+local topoints = number.topoints
+local todimen = string.todimen
+
+chemistry = chemistry or { }
+local chemistry = chemistry
+
+chemistry.instance = "chemistry"
+chemistry.format = "metafun"
+chemistry.structures = 0
+
+local common_keys = {
+ b = "line",
+ r = "line",
+ sb = "line",
+ sr = "line",
+ rd = "line",
+ rh = "line",
+ rb = "line",
+ rbd = "line",
+ cc = "line",
+ ccd = "line",
+ line = "line",
+ dash = "line",
+ arrow = "line",
+ c = "fixed",
+ cd = "fixed",
+ z = "text",
+ zt = "text",
+ zlt = "text",
+ zrt = "text",
+ rz = "text",
+ rt = "text",
+ lrt = "text",
+ rrt = "text",
+ label = "text",
+ zln = "number",
+ zrn = "number",
+ rn = "number",
+ lrn = "number",
+ rrn = "number",
+ zn = "number",
+ number = "number",
+ mov = "transform",
+ mark = "transform",
+ move = "transform",
+ diff = "transform",
+ off = "transform",
+ adj = "transform",
+ sub = "transform",
+}
+
+local front_keys = {
+ bb = "line",
+ eb = "line",
+ rr = "line",
+ lr = "line",
+ lsr = "line",
+ rsr = "line",
+ lrd = "line",
+ rrd = "line",
+ lrh = "line",
+ rrh = "line",
+ lrbd = "line",
+ rrbd = "line",
+ lrb = "line",
+ rrb = "line",
+ lrz = "text",
+ rrz = "text",
+ lsub = "transform",
+ rsub = "transform",
+}
+
+local one_keys = {
+ db = "line",
+ tb = "line",
+ bb = "line",
+ dr = "line",
+ hb = "line",
+ bd = "line",
+ bw = "line",
+ oe = "line",
+ sd = "line",
+ rdb = "line",
+ ldb = "line",
+ ldd = "line",
+ rdd = "line",
+ ep = "line",
+ es = "line",
+ ed = "line",
+ et = "line",
+ cz = "text",
+ rot = "transform",
+ dir = "transform",
+ rm = "transform",
+ mir = "transform",
+}
+
+local ring_keys = {
+ db = "line",
+ br = "line",
+ lr = "line",
+ rr = "line",
+ lsr = "line",
+ rsr = "line",
+ lrd = "line",
+ rrd = "line",
+ lrb = "line",
+ rrb = "line",
+ lrh = "line",
+ rrh = "line",
+ lrbd = "line",
+ rrbd = "line",
+ dr = "line",
+ eb = "line",
+ er = "line",
+ ed = "line",
+ au = "line",
+ ad = "line",
+ s = "line",
+ ss = "line",
+ mid = "line",
+ mids = "line",
+ midz = "text",
+ lrz = "text",
+ rrz = "text",
+ crz = "text",
+ rot = "transform",
+ mir = "transform",
+ adj = "transform",
+ lsub = "transform",
+ rsub = "transform",
+ rm = "transform",
+}
+
+-- table.setmetatableindex(front_keys,common_keys)
+-- table.setmetatableindex(one_keys,common_keys)
+-- table.setmetatableindex(ring_keys,common_keys)
+
+-- or (faster but not needed here):
+
+front_keys = table.merged(front_keys,common_keys)
+one_keys = table.merged(one_keys,common_keys)
+ring_keys = table.merged(ring_keys,common_keys)
+
+local syntax = {
+ carbon = { max = 4, keys = one_keys, },
+ alkyl = { max = 4, keys = one_keys, },
+ newmanstagger = { max = 6, keys = one_keys, },
+ newmaneclipsed = { max = 6, keys = one_keys, },
+ one = { max = 8, keys = one_keys, },
+ three = { max = 3, keys = ring_keys, },
+ four = { max = 4, keys = ring_keys, },
+ five = { max = 5, keys = ring_keys, },
+ six = { max = 6, keys = ring_keys, },
+ seven = { max = 7, keys = ring_keys, },
+ eight = { max = 8, keys = ring_keys, },
+ nine = { max = 9, keys = ring_keys, },
+ fivefront = { max = 5, keys = front_keys, },
+ sixfront = { max = 6, keys = front_keys, },
+ chair = { max = 6, keys = front_keys, },
+ boat = { max = 6, keys = front_keys, },
+ pb = { direct = 'chem_pb;' },
+ pe = { direct = 'chem_pe;' },
+ save = { direct = 'chem_save;' },
+ restore = { direct = 'chem_restore;' },
+ chem = { direct = formatters['chem_symbol("\\chemicaltext{%s}");'], arguments = 1 },
+ space = { direct = 'chem_symbol("\\chemicalsymbol[space]");' },
+ plus = { direct = 'chem_symbol("\\chemicalsymbol[plus]");' },
+ minus = { direct = 'chem_symbol("\\chemicalsymbol[minus]");' },
+ gives = { direct = formatters['chem_symbol("\\chemicalsymbol[gives]{%s}{%s}");'], arguments = 2 },
+ equilibrium = { direct = formatters['chem_symbol("\\chemicalsymbol[equilibrium]{%s}{%s}");'], arguments = 2 },
+ mesomeric = { direct = formatters['chem_symbol("\\chemicalsymbol[mesomeric]{%s}{%s}");'], arguments = 2 },
+ opencomplex = { direct = 'chem_symbol("\\chemicalsymbol[opencomplex]");' },
+ closecomplex = { direct = 'chem_symbol("\\chemicalsymbol[closecomplex]");' },
+ reset = { direct = 'chem_reset;' },
+ mp = { direct = formatters['%s'], arguments = 1 }, -- backdoor MP code - dangerous!
+}
+
+chemistry.definitions = chemistry.definitions or { }
+local definitions = chemistry.definitions
+
+storage.register("chemistry/definitions",definitions,"chemistry.definitions")
+
+function chemistry.undefine(name)
+ definitions[lower(name)] = nil
+end
+
+function chemistry.define(name,spec,text)
+ name = lower(name)
+ local dn = definitions[name]
+ if not dn then
+ dn = { }
+ definitions[name] = dn
+ end
+ dn[#dn+1] = {
+ spec = settings_to_array_with_repeat(spec,true),
+ text = settings_to_array_with_repeat(text,true),
+ }
+end
+
+local metacode, variant, keys, max, txt, pstack, sstack, align
+local molecule = chemistry.molecule -- or use lpegmatch(chemistry.moleculeparser,...)
+
+local function fetch(txt)
+ local st = stack[txt]
+ local t = st.text[st.n]
+ while not t and txt > 1 do
+ txt = txt - 1
+ st = stack[txt]
+ t = st.text[st.n]
+ end
+ if t then
+ if trace_textstack then
+ report_chemistry("fetching from stack %a, slot %a, data %a",txt,st.n,t)
+ end
+ st.n = st.n + 1
+ end
+ return txt, t
+end
+
+local remapper = {
+ ["+"] = "p",
+ ["-"] = "m",
+}
+
+local dchrs = R("09")
+local sign = S("+-")
+local digit = dchrs / tonumber
+local amount = (sign^-1 * (dchrs^0 * P('.'))^-1 * dchrs^1) / tonumber
+local single = digit
+local range = digit * P("..") * digit
+local set = Ct(digit^2)
+local colon = P(":")
+local equal = P("=")
+local other = 1 - digit - colon - equal
+local remapped = sign / remapper
+local operation = Cs(other^1)
+local special = (colon * C(other^1)) + Cc("")
+local text = (equal * C(P(1)^0)) + Cc(false)
+
+local pattern =
+ (amount + Cc(1))
+ * (remapped + Cc(""))
+ * Cs(operation/lower)
+ * Cs(special/lower) * (
+ range * Cc(false) * text +
+ Cc(false) * Cc(false) * set * text +
+ single * Cc(false) * Cc(false) * text +
+ Cc(false) * Cc(false) * Cc(false) * text
+ )
+
+-- local n, operation, index, upto, set, text = lpegmatch(pattern,"RZ1357")
+
+-- print(lpegmatch(pattern,"RZ=x")) -- 1 RZ false false false x
+-- print(lpegmatch(pattern,"RZ1=x")) -- 1 RZ 1 false false x
+-- print(lpegmatch(pattern,"RZ1..3=x")) -- 1 RZ 1 3 false x
+-- print(lpegmatch(pattern,"RZ13=x")) -- 1 RZ false false table x
+
+local f_initialize = 'if unknown context_chem : input mp-chem.mpiv ; fi ;'
+local f_start_structure = formatters['chem_start_structure(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s);']
+local f_set_trace_bounds = formatters['chem_trace_boundingbox := %l ;']
+local f_stop_structure = 'chem_stop_structure;'
+local f_start_component = 'chem_start_component;'
+local f_stop_component = 'chem_stop_component;'
+local f_line = formatters['chem_%s%s(%s,%s,%s,%s,%s);']
+local f_set = formatters['chem_set(%s);']
+local f_number = formatters['chem_%s%s(%s,%s,"\\chemicaltext{%s}");']
+local f_text = f_number
+local f_empty_normal = formatters['chem_%s(%s,%s,"");']
+local f_empty_center = formatters['chem_c%s(%s,%s,"");']
+local f_transform = formatters['chem_%s(%s,%s,%s);']
+
+local prepareMPvariable = commands and commands.prepareMPvariable
+
+local function process(level,spec,text,n,rulethickness,rulecolor,offset,default_variant)
+ insert(stack,{ spec = spec, text = text, n = n })
+ local txt = #stack
+ local m = #metacode
+ local saved_rulethickness = rulethickness
+ local saved_rulecolor = rulecolor
+ local saved_align = align
+ local current_variant = default_variant or "six"
+ for i=1,#spec do
+ local step = spec[i]
+ local s = lower(step)
+ local n = current_variant .. ":" .. s
+ local d = definitions[n]
+ if not d then
+ n = s
+ d = definitions[n]
+ end
+ if d then
+ if trace_structure then
+ report_chemistry("level %a, step %a, definition %a, snippets %a",level,step,n,#d)
+ end
+ for i=1,#d do
+ local di = d[i]
+ current_variant = process(level+1,di.spec,di.text,1,rulethickness,rulecolor,offset,current_variant) -- offset?
+ end
+ else
+ local factor, osign, operation, special, index, upto, set, text = lpegmatch(pattern,step)
+ if trace_structure then
+ local set = set and concat(set," ") or "-"
+ report_chemistry("level %a, step %a, factor %a, osign %a, operation %a, special %a, index %a, upto %a, set %a, text %a",
+ level,step,factor,osign,operation,special,index,upto,set,text)
+ end
+ if operation == "rulecolor" then
+ local t = text
+ if not t then
+ txt, t = fetch(txt)
+ end
+ if t == v_default or t == v_normal or t == "" then
+ rulecolor = saved_rulecolor
+ elseif t then
+ rulecolor = mpnamedcolor(t)
+ end
+ elseif operation == "rulethickness" then
+ local t = text
+ if not t then
+ txt, t = fetch(txt)
+ end
+ if t == v_default or t == v_normal or t == t_medium or t == "" then
+ rulethickness = saved_rulethickness
+ elseif t == v_small then
+ rulethickness = topoints(1/1.2 * todimen(saved_rulethickness))
+ elseif t == v_big then
+ rulethickness = topoints(1.2 * todimen(saved_rulethickness))
+ elseif t then
+ -- rulethickness = topoints(todimen(t)) -- mp can't handle sp
+ rulethickness = topoints(tonumber(t) * todimen(saved_rulethickness))
+ end
+ elseif operation == "symalign" then
+ local t = text
+ if not t then
+ txt, t = fetch(txt)
+ end
+ if t == v_default or t == v_normal then
+ align = saved_align
+ elseif t and t ~= "" then
+ align = "." .. t
+ end
+ elseif operation == "pb" then
+ insert(pstack,variant)
+ m = m + 1 ; metacode[m] = syntax.pb.direct
+ if keys[special] == "text" and index then
+ if keys["c"..special] == "text" then -- can be option: auto ...
+ m = m + 1 ; metacode[m] = f_empty_center(special,variant,index)
+ else
+ m = m + 1 ; metacode[m] = f_empty_normal(special,variant,index)
+ end
+ end
+ elseif operation == "pe" then
+ variant = remove(pstack)
+ local ss = syntax[variant]
+ keys, max = ss.keys, ss.max
+ m = m + 1 ; metacode[m] = syntax.pe.direct
+ m = m + 1 ; metacode[m] = f_set(variant)
+ current_variant = variant
+ elseif operation == "save" then
+ insert(sstack,variant)
+ m = m + 1 ; metacode[m] = syntax.save.direct
+ elseif operation == "restore" then
+ variant = remove(sstack)
+ local ss = syntax[variant]
+ keys, max = ss.keys, ss.max
+ m = m + 1 ; metacode[m] = syntax.restore.direct
+ m = m + 1 ; metacode[m] = f_set(variant)
+ current_variant = variant
+ elseif operation then
+ local ss = syntax[operation]
+ local what = keys[operation]
+ local ns = 0
+ if set then
+ local sv = syntax[current_variant]
+ local ms = sv and sv.max
+ set = unique(set)
+ ns = #set
+ if directive_strictorder then
+ if what == "line" then
+ set = sorted(set)
+ end
+ if directive_strictindex and ms then
+ for i=ns,1,-1 do
+ local si = set[i]
+ if si > ms then
+ report_chemistry("level %a, operation %a, max nofsteps %a, ignoring %a",level,operation,ms,si)
+ set[i] = nil
+ ns = ns - 1
+ else
+ break
+ end
+ end
+ end
+ else
+ if directive_strictindex and ms then
+ local t, nt = { }, 0
+ for i=1,ns do
+ local si = set[i]
+ if si > ms then
+ report_chemistry("level %a, operation %a, max nofsteps %a, ignoring %a",level,operation,ms,si)
+ set[i] = nil
+ else
+ nt = nt + 1
+ t[nt] = si
+ end
+ end
+ ns = nt
+ set = t
+ end
+ end
+ end
+ if ss then
+ local ds = ss.direct
+ if ds then
+ local sa = ss.arguments
+ if sa == 1 then
+ local one ; txt, one = fetch(txt)
+ m = m + 1 ; metacode[m] = ds(one or "")
+ elseif sa == 2 then
+ local one ; txt, one = fetch(txt)
+ local two ; txt, two = fetch(txt)
+ m = m + 1 ; metacode[m] = ds(one or "",two or "")
+ else
+ m = m + 1 ; metacode[m] = ds
+ end
+ elseif ss.keys then
+ variant, keys, max = s, ss.keys, ss.max
+ m = m + 1 ; metacode[m] = f_set(variant)
+ current_variant = variant
+ end
+ elseif what == "line" then
+ local s = osign
+ if s ~= "" then
+ s = "." .. s
+ end
+ if set then
+ -- condense consecutive numbers in a set to a range
+ local sf, st = set[1]
+ for i=1,ns do
+ if i > 1 and set[i] ~= set[i-1]+1 then
+ m = m + 1 ; metacode[m] = f_line(operation,s,variant,sf,st,rulethickness,rulecolor)
+ sf = set[i]
+ end
+ st = set[i]
+ end
+ m = m + 1 ; metacode[m] = f_line(operation,s,variant,sf,st,rulethickness,rulecolor)
+ elseif upto then
+ m = m + 1 ; metacode[m] = f_line(operation,s,variant,index,upto,rulethickness,rulecolor)
+ elseif index then
+ m = m + 1 ; metacode[m] = f_line(operation,s,variant,index,index,rulethickness,rulecolor)
+ else
+ m = m + 1 ; metacode[m] = f_line(operation,s,variant,1,max,rulethickness,rulecolor)
+ end
+ elseif what == "number" then
+ if set then
+ for i=1,ns do
+ local si = set[i]
+ m = m + 1 ; metacode[m] = f_number(operation,align,variant,si,si)
+ end
+ elseif upto then
+ for i=index,upto do
+ local si = set[i]
+ m = m + 1 ; metacode[m] = f_number(operation,align,variant,si,si)
+ end
+ elseif index then
+ m = m + 1 ; metacode[m] = f_number(operation,align,variant,index,index)
+ else
+ for i=1,max do
+ m = m + 1 ; metacode[m] = f_number(operation,align,variant,i,i)
+ end
+ end
+ elseif what == "text" then
+ if set then
+ for i=1,ns do
+ local si = set[i]
+ local t = text
+ if not t then txt, t = fetch(txt) end
+ if t then
+ t = molecule(processor_tostring(t))
+ m = m + 1 ; metacode[m] = f_text(operation,align,variant,si,t)
+ end
+ end
+ elseif upto then
+ for i=index,upto do
+ local t = text
+ if not t then txt, t = fetch(txt) end
+ if t then
+ t = molecule(processor_tostring(t))
+ m = m + 1 ; metacode[m] = f_text(operation,align,variant,i,t)
+ end
+ end
+ elseif index == 0 then
+ local t = text
+ if not t then txt, t = fetch(txt) end
+ if t then
+ t = molecule(processor_tostring(t))
+ m = m + 1 ; metacode[m] = f_text(operation,align,variant,index,t)
+ end
+ elseif index then
+ local t = text
+ if not t then txt, t = fetch(txt) end
+ if t then
+ t = molecule(processor_tostring(t))
+ m = m + 1 ; metacode[m] = f_text(operation,align,variant,index,t)
+ end
+ else
+ for i=1,max do
+ local t = text
+ if not t then txt, t = fetch(txt) end
+ if t then
+ t = molecule(processor_tostring(t))
+ m = m + 1 ; metacode[m] = f_text(operation,align,variant,i,t)
+ end
+ end
+ end
+ elseif what == "transform" then
+ if osign == "m" then
+ factor = -factor
+ end
+ if set then
+ for i=1,ns do
+ local si = set[i]
+ m = m + 1 ; metacode[m] = f_transform(operation,variant,si,factor)
+ end
+ elseif upto then
+ for i=index,upto do
+ m = m + 1 ; metacode[m] = f_transform(operation,variant,i,factor)
+ end
+ else
+ m = m + 1 ; metacode[m] = f_transform(operation,variant,index or 1,factor)
+ end
+ elseif what == "fixed" then
+ m = m + 1 ; metacode[m] = f_transform(operation,variant,rulethickness,rulecolor)
+ elseif trace_structure then
+ report_chemistry("level %a, ignoring undefined operation %s",level,operation)
+ end
+ end
+ end
+ end
+ remove(stack)
+ return current_variant
+end
+
+-- the size related values are somewhat special but we want to be
+-- compatible
+--
+-- rulethickness in points
+
+local function checked(d,factor,unit,scale)
+ if d == v_none then
+ return 0
+ end
+ local n = tonumber(d)
+ if not n then
+ -- assume dimen
+ elseif n >= 10 or n <= -10 then
+ return factor * unit * n / 1000
+ else
+ return factor * unit * n
+ end
+ local n = todimen(d)
+ if n then
+ return scale * n
+ else
+ return v_fit
+ end
+end
+
+local function calculated(height,bottom,top,factor,unit,scale)
+ local scaled = 0
+ if height == v_none then
+ -- this always wins
+ height = "0pt"
+ bottom = "0pt"
+ top = "0pt"
+ elseif height == v_fit then
+ height = "true"
+ bottom = bottom == v_fit and "true" or topoints(checked(bottom,factor,unit,scale))
+ top = top == v_fit and "true" or topoints(checked(top, factor,unit,scale))
+ else
+ height = checked(height,factor,unit,scale)
+ if bottom == v_fit then
+ if top == v_fit then
+ bottom = height / 2
+ top = bottom
+ else
+ top = checked(top,factor,unit,scale)
+ bottom = height - top
+ end
+ elseif top == v_fit then
+ bottom = checked(bottom,factor,unit,scale)
+ top = height - bottom
+ else
+ bottom = checked(bottom,factor,unit,scale)
+ top = checked(top, factor,unit,scale)
+ local ratio = height / (bottom+top)
+ bottom = bottom * ratio
+ top = top * ratio
+ end
+ scaled = height
+ top = topoints(top)
+ bottom = topoints(bottom)
+ height = topoints(height)
+ end
+ return height, bottom, top, scaled
+end
+
+function chemistry.start(settings)
+ --
+ local width = settings.width or v_fit
+ local height = settings.height or v_fit
+ local unit = settings.unit or 655360
+ local factor = settings.factor or 3
+ local rulethickness = settings.rulethickness or 65536
+ local rulecolor = settings.rulecolor or ""
+ local axiscolor = settings.framecolor or ""
+ local scale = settings.scale or "normal"
+ local rotation = settings.rotation or 0
+ local offset = settings.offset or 0
+ local left = settings.left or v_fit
+ local right = settings.right or v_fit
+ local top = settings.top or v_fit
+ local bottom = settings.bottom or v_fit
+ --
+ align = settings.symalign or "auto"
+ if trace_structure then
+ report_chemistry("unit %p, factor %s, symalign %s",unit,factor,align)
+ end
+ if align ~= "" then
+ align = "." .. align
+ end
+ if trace_structure then
+ report_chemistry("%s scale %a, rotation %a, width %s, height %s, left %s, right %s, top %s, bottom %s","asked",scale,rotation,width,height,left,right,top,bottom)
+ end
+ if scale == v_small then
+ scale = 1/1.2
+ elseif scale == v_normal or scale == v_medium or scale == 0 then
+ scale = 1
+ elseif scale == v_big then
+ scale = 1.2
+ else
+ scale = tonumber(scale)
+ if not scale or scale == 0 then
+ scale = 1
+ elseif scale >= 10 then
+ scale = scale / 1000
+ elseif scale < .01 then
+ scale = .01
+ end
+ end
+ --
+ unit = scale * unit
+ --
+ local sp_width = 0
+ local sp_height = 0
+ --
+ width, left, right, sp_width = calculated(width, left, right,factor,unit,scale)
+ height, bottom, top, sp_height = calculated(height,bottom,top, factor,unit,scale)
+ --
+ if width ~= "true" and height ~= "true" and texcount["@@trialtypesetting"] ~= 0 then
+ if trace_structure then
+ report_chemistry("skipping trial run")
+ end
+ context.hrule(sp_width,sp_height,0) -- maybe depth
+ return
+ end
+ --
+ chemistry.structures = chemistry.structures + 1
+ --
+ rotation = tonumber(rotation) or 0
+ --
+ metacode = { }
+ --
+ if trace_structure then
+ report_chemistry("%s scale %a, rotation %a, width %s, height %s, left %s, right %s, top %s, bottom %s","used",scale,rotation,width,height,left,right,top,bottom)
+ end
+ metacode[#metacode+1] = f_start_structure(
+ chemistry.structures,
+ left, right, top, bottom,
+ rotation, topoints(unit), factor, topoints(offset),
+ tostring(settings.axis == v_on), topoints(rulethickness), tostring(axiscolor)
+ )
+ metacode[#metacode+1] = f_set_trace_bounds(trace_boundingbox) ;
+ --
+ variant, keys, stack, pstack, sstack = "one", { }, { }, { }, { }
+end
+
+function chemistry.stop()
+ if metacode then
+ metacode[#metacode+1] = f_stop_structure
+ local mpcode = concat(metacode,"\n")
+ if trace_metapost then
+ report_chemistry("metapost code:\n%s", mpcode)
+ end
+ if metapost.instance(chemistry.instance) then
+ f_initialize = nil
+ end
+ metapost.graphic {
+ instance = chemistry.instance,
+ format = chemistry.format,
+ data = mpcode,
+ definitions = f_initialize,
+ }
+ t_initialize = ""
+ metacode = nil
+ end
+end
+
+function chemistry.component(spec,text,settings)
+ if metacode then
+ rulethickness, rulecolor, offset = settings.rulethickness, settings.rulecolor
+ local spec = settings_to_array_with_repeat(spec,true) -- no lower?
+ local text = settings_to_array_with_repeat(text,true)
+ -- inspect(spec)
+ metacode[#metacode+1] = f_start_component
+ process(1,spec,text,1,rulethickness,rulecolor) -- offset?
+ metacode[#metacode+1] = f_stop_component
+ end
+end
+
+statistics.register("chemical formulas", function()
+ if chemistry.structures > 0 then
+ return format("%s chemical structure formulas",chemistry.structures) -- no timing needed, part of metapost
+ end
+end)
+
+-- interfaces
+
+commands.undefinechemical = chemistry.undefine
+commands.definechemical = chemistry.define
+commands.startchemical = chemistry.start
+commands.stopchemical = chemistry.stop
+commands.chemicalcomponent = chemistry.component
+
+-- todo: top / bottom
+-- maybe add "=" for double and "≡" for triple?
+
+local inline = {
+ ["single"] = "\\chemicalsinglebond", ["-"] = "\\chemicalsinglebond",
+ ["double"] = "\\chemicaldoublebond", ["--"] = "\\chemicaldoublebond",
+ ["triple"] = "\\chemicaltriplebond", ["---"] = "\\chemicaltriplebond",
+ ["gives"] = "\\chemicalgives", ["->"] = "\\chemicalgives",
+ ["equilibrium"] = "\\chemicalequilibrium", ["<->"] = "\\chemicalequilibrium",
+ ["mesomeric"] = "\\chemicalmesomeric", ["<>"] = "\\chemicalmesomeric",
+ ["plus"] = "\\chemicalplus", ["+"] = "\\chemicalplus",
+ ["minus"] = "\\chemicalminus",
+ ["space"] = "\\chemicalspace",
+}
+
+function commands.inlinechemical(spec)
+ local spec = settings_to_array_with_repeat(spec,true)
+ for i=1,#spec do
+ local s = spec[i]
+ local inl = inline[lower(s)]
+ if inl then
+ context(inl) -- could be a fast context.sprint
+ else
+ context.chemicalinline(molecule(s))
+ end
+ end
+end
diff --git a/tex/context/base/cldf-bas.lua b/tex/context/base/cldf-bas.lua
index 6e887f2ba..6adeb2272 100644
--- a/tex/context/base/cldf-bas.lua
+++ b/tex/context/base/cldf-bas.lua
@@ -1,179 +1,179 @@
-if not modules then modules = { } end modules ['cldf-bas'] = {
- version = 1.001,
- comment = "companion to cldf-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- -- speedtest needed:
---
--- local flush, writer = context.getlogger()
---
--- trackers.register("context.trace",function(v)
--- flush, writer = context.getlogger()
--- end)
---
--- function context.bgroup()
--- flush(ctxcatcodes,"{")
--- end
---
--- function context.egroup()
--- flush(ctxcatcodes,"}")
--- end
-
--- maybe use context.generics
-
-local type = type
-local format = string.format
-local utfchar = utf.char
-local concat = table.concat
-
-local context = context
-local generics = context.generics
-local variables = interfaces.variables
-
-local nodepool = nodes.pool
-local new_rule = nodepool.rule
-local new_glyph = nodepool.glyph
-
-local current_font = font.current
-local texcount = tex.count
-
-function context.char(k) -- used as escape too, so don't change to utf
- if type(k) == "table" then
- local n = #k
- if n == 1 then
- context([[\char%s\relax]],k[1])
- elseif n > 0 then
- context([[\char%s\relax]],concat(k,[[\relax\char]]))
- end
- elseif k then
- context([[\char%s\relax]],k)
- end
-end
-
-function context.utfchar(k)
- context(utfchar(k))
-end
-
--- plain variants
-
-function context.chardef(cs,u)
- context([[\chardef\%s=%s\relax]],k)
-end
-
-function context.par()
- context([[\par]]) -- no need to add {} there
-end
-
-function context.bgroup()
- context("{")
-end
-
-function context.egroup()
- context("}")
-end
-
-function context.space()
- context("\\space") -- no " " as that gets intercepted
-end
-
-function context.hrule(w,h,d,dir)
- if type(w) == "table" then
- context(new_rule(w.width,w.height,w.depth,w.dir))
- else
- context(new_rule(w,h,d,dir))
- end
-end
-
-function context.glyph(id,k)
- if id then
- if not k then
- id, k = current_font(), id
- end
- context(new_glyph(id,k))
- end
-end
-
-context.vrule = context.hrule
-
---~ local hbox, bgroup, egroup = context.hbox, context.bgroup, context.egroup
-
---~ function context.hbox(a,...)
---~ if type(a) == "table" then
---~ local s = { }
---~ if a.width then
---~ s[#s+1] = "to " .. a.width -- todo: check for number
---~ elseif a.spread then
---~ s[#s+1] = "spread " .. a.spread -- todo: check for number
---~ end
---~ -- todo: dir, attr etc
---~ hbox(false,table.concat(s," "))
---~ bgroup()
---~ context(string.format(...))
---~ egroup()
---~ else
---~ hbox(a,...)
---~ end
---~ end
-
--- not yet used ... but will get variant at the tex end as well
-
-function context.sethboxregister(n) context([[\setbox %s\hbox]],n) end
-function context.setvboxregister(n) context([[\setbox %s\vbox]],n) end
-
-function context.starthboxregister(n)
- if type(n) == "number" then
- context([[\setbox%s\hbox{]],n)
- else
- context([[\setbox\%s\hbox{]],n)
- end
-end
-
-function context.startvboxregister(n)
- if type(n) == "number" then
- context([[\setbox%s\vbox{]],n)
- else
- context([[\setbox\%s\vbox{]],n)
- end
-end
-
-context.stophboxregister = context.egroup
-context.stopvboxregister = context.egroup
-
-function context.flushboxregister(n)
- if type(n) == "number" then
- context([[\box%s ]],n)
- else
- context([[\box\%s]],n)
- end
-end
-
-function context.beginvbox()
- context([[\vbox{]]) -- we can do \bvbox ... \evbox (less tokens)
-end
-
-function context.beginhbox()
- context([[\hbox{]]) -- todo: use fast one
-end
-
-context.endvbox = context.egroup
-context.endhbox = context.egroup
-
-local function allocate(name,what,cmd)
- local a = format("c_syst_last_allocated_%s",what)
- local n = texcount[a] + 1
- if n <= texcount.c_syst_max_allocated_register then
- texcount[a] = n
- end
- context("\\global\\expandafter\\%sdef\\csname %s\\endcsname %s\\relax",cmd or what,name,n)
- return n
-end
-
-function context.newdimen (name) return allocate(name,"dimen") end
-function context.newskip (name) return allocate(name,"skip") end
-function context.newcount (name) return allocate(name,"count") end
-function context.newmuskip(name) return allocate(name,"muskip") end
-function context.newtoks (name) return allocate(name,"toks") end
-function context.newbox (name) return allocate(name,"box","mathchar") end
+if not modules then modules = { } end modules ['cldf-bas'] = {
+ version = 1.001,
+ comment = "companion to cldf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- -- speedtest needed:
+--
+-- local flush, writer = context.getlogger()
+--
+-- trackers.register("context.trace",function(v)
+-- flush, writer = context.getlogger()
+-- end)
+--
+-- function context.bgroup()
+-- flush(ctxcatcodes,"{")
+-- end
+--
+-- function context.egroup()
+-- flush(ctxcatcodes,"}")
+-- end
+
+-- maybe use context.generics
+
+local type = type
+local format = string.format
+local utfchar = utf.char
+local concat = table.concat
+
+local context = context
+local generics = context.generics
+local variables = interfaces.variables
+
+local nodepool = nodes.pool
+local new_rule = nodepool.rule
+local new_glyph = nodepool.glyph
+
+local current_font = font.current
+local texcount = tex.count
+
+function context.char(k) -- used as escape too, so don't change to utf
+ if type(k) == "table" then
+ local n = #k
+ if n == 1 then
+ context([[\char%s\relax]],k[1])
+ elseif n > 0 then
+ context([[\char%s\relax]],concat(k,[[\relax\char]]))
+ end
+ elseif k then
+ context([[\char%s\relax]],k)
+ end
+end
+
+function context.utfchar(k)
+ context(utfchar(k))
+end
+
+-- plain variants
+
+function context.chardef(cs,u)
+ context([[\chardef\%s=%s\relax]],k)
+end
+
+function context.par()
+ context([[\par]]) -- no need to add {} there
+end
+
+function context.bgroup()
+ context("{")
+end
+
+function context.egroup()
+ context("}")
+end
+
+function context.space()
+ context("\\space") -- no " " as that gets intercepted
+end
+
+function context.hrule(w,h,d,dir)
+ if type(w) == "table" then
+ context(new_rule(w.width,w.height,w.depth,w.dir))
+ else
+ context(new_rule(w,h,d,dir))
+ end
+end
+
+function context.glyph(id,k)
+ if id then
+ if not k then
+ id, k = current_font(), id
+ end
+ context(new_glyph(id,k))
+ end
+end
+
+context.vrule = context.hrule
+
+--~ local hbox, bgroup, egroup = context.hbox, context.bgroup, context.egroup
+
+--~ function context.hbox(a,...)
+--~ if type(a) == "table" then
+--~ local s = { }
+--~ if a.width then
+--~ s[#s+1] = "to " .. a.width -- todo: check for number
+--~ elseif a.spread then
+--~ s[#s+1] = "spread " .. a.spread -- todo: check for number
+--~ end
+--~ -- todo: dir, attr etc
+--~ hbox(false,table.concat(s," "))
+--~ bgroup()
+--~ context(string.format(...))
+--~ egroup()
+--~ else
+--~ hbox(a,...)
+--~ end
+--~ end
+
+-- not yet used ... but will get variant at the tex end as well
+
+function context.sethboxregister(n) context([[\setbox %s\hbox]],n) end
+function context.setvboxregister(n) context([[\setbox %s\vbox]],n) end
+
+function context.starthboxregister(n)
+ if type(n) == "number" then
+ context([[\setbox%s\hbox{]],n)
+ else
+ context([[\setbox\%s\hbox{]],n)
+ end
+end
+
+function context.startvboxregister(n)
+ if type(n) == "number" then
+ context([[\setbox%s\vbox{]],n)
+ else
+ context([[\setbox\%s\vbox{]],n)
+ end
+end
+
+context.stophboxregister = context.egroup
+context.stopvboxregister = context.egroup
+
+function context.flushboxregister(n)
+ if type(n) == "number" then
+ context([[\box%s ]],n)
+ else
+ context([[\box\%s]],n)
+ end
+end
+
+function context.beginvbox()
+ context([[\vbox{]]) -- we can do \bvbox ... \evbox (less tokens)
+end
+
+function context.beginhbox()
+ context([[\hbox{]]) -- todo: use fast one
+end
+
+context.endvbox = context.egroup
+context.endhbox = context.egroup
+
+local function allocate(name,what,cmd)
+ local a = format("c_syst_last_allocated_%s",what)
+ local n = texcount[a] + 1
+ if n <= texcount.c_syst_max_allocated_register then
+ texcount[a] = n
+ end
+ context("\\global\\expandafter\\%sdef\\csname %s\\endcsname %s\\relax",cmd or what,name,n)
+ return n
+end
+
+function context.newdimen (name) return allocate(name,"dimen") end
+function context.newskip (name) return allocate(name,"skip") end
+function context.newcount (name) return allocate(name,"count") end
+function context.newmuskip(name) return allocate(name,"muskip") end
+function context.newtoks (name) return allocate(name,"toks") end
+function context.newbox (name) return allocate(name,"box","mathchar") end
diff --git a/tex/context/base/cldf-com.lua b/tex/context/base/cldf-com.lua
index 5046343c9..fa0dbed3e 100644
--- a/tex/context/base/cldf-com.lua
+++ b/tex/context/base/cldf-com.lua
@@ -1,36 +1,36 @@
-if not modules then modules = { } end modules ['cldf-com'] = {
- version = 1.001,
- comment = "companion to cldf-com.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local tostring = tostring
-local context = context
-local generics = context.generics -- needs documentation
-local variables = interfaces.variables
-
-generics.starttabulate = "starttabulate" -- "start" .. variables.tabulate -- todo: e!start
-generics.stoptabulate = "stoptabulate" -- "stop" .. variables.tabulate -- todo: e!stop
-
-local NC, NR = context.NC, context.NR
-
-local function tabulaterow(how,...)
- for i=1,select("#",...) do
- local ti = tostring(select(i,...))
- NC()
- if how then
- context[how](ti)
- else
- context(ti)
- end
- end
- NC()
- NR()
-end
-
-function context.tabulaterow (...) tabulaterow(false, ...) end
-function context.tabulaterowbold(...) tabulaterow("bold",...) end
-function context.tabulaterowtype(...) tabulaterow("type",...) end
-function context.tabulaterowtyp (...) tabulaterow("typ", ...) end
+if not modules then modules = { } end modules ['cldf-com'] = {
+ version = 1.001,
+ comment = "companion to cldf-com.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local tostring = tostring
+local context = context
+local generics = context.generics -- needs documentation
+local variables = interfaces.variables
+
+generics.starttabulate = "starttabulate" -- "start" .. variables.tabulate -- todo: e!start
+generics.stoptabulate = "stoptabulate" -- "stop" .. variables.tabulate -- todo: e!stop
+
+local NC, NR = context.NC, context.NR
+
+local function tabulaterow(how,...)
+ for i=1,select("#",...) do
+ local ti = tostring(select(i,...))
+ NC()
+ if how then
+ context[how](ti)
+ else
+ context(ti)
+ end
+ end
+ NC()
+ NR()
+end
+
+function context.tabulaterow (...) tabulaterow(false, ...) end
+function context.tabulaterowbold(...) tabulaterow("bold",...) end
+function context.tabulaterowtype(...) tabulaterow("type",...) end
+function context.tabulaterowtyp (...) tabulaterow("typ", ...) end
diff --git a/tex/context/base/cldf-ini.lua b/tex/context/base/cldf-ini.lua
index c61a5b523..4a7d9f025 100644
--- a/tex/context/base/cldf-ini.lua
+++ b/tex/context/base/cldf-ini.lua
@@ -1,1066 +1,1066 @@
-if not modules then modules = { } end modules ['cldf-ini'] = {
- version = 1.001,
- comment = "companion to cldf-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: generating context code at the lua end. After all
--- it is surprisingly simple to implement due to metatables. I was wondering if
--- there was a more natural way to deal with commands at the lua end. Of course it's
--- a bit slower but often more readable when mixed with lua code. It can also be handy
--- when generating documents from databases or when constructing large tables or so.
---
--- maybe optional checking against interface
--- currently no coroutine trickery
--- we could always use prtcatcodes (context.a_b_c) but then we loose protection
--- tflush needs checking ... sort of weird that it's not a table
--- __flushlines is an experiment and rather ugly so it will go away
---
--- tex.print == line with endlinechar appended
-
--- todo: context("%bold{total: }%s",total)
--- todo: context.documentvariable("title")
-
-local tex = tex
-
-context = context or { }
-local context = context
-
-local format, gsub, validstring = string.format, string.gsub, string.valid
-local next, type, tostring, tonumber, setmetatable, unpack, select = next, type, tostring, tonumber, setmetatable, unpack, select
-local insert, remove, concat = table.insert, table.remove, table.concat
-local lpegmatch, lpegC, lpegS, lpegP, lpegCc, patterns = lpeg.match, lpeg.C, lpeg.S, lpeg.P, lpeg.Cc, lpeg.patterns
-local formatters = string.formatters -- using formatteds is slower in this case
-
-local texsprint = tex.sprint
-local textprint = tex.tprint
-local texprint = tex.print
-local texwrite = tex.write
-local texcount = tex.count
-
-local isnode = node.is_node -- after 0.65 just node.type
-local writenode = node.write
-local copynodelist = node.copy_list
-
-local catcodenumbers = catcodes.numbers
-
-local ctxcatcodes = catcodenumbers.ctxcatcodes
-local prtcatcodes = catcodenumbers.prtcatcodes
-local texcatcodes = catcodenumbers.texcatcodes
-local txtcatcodes = catcodenumbers.txtcatcodes
-local vrbcatcodes = catcodenumbers.vrbcatcodes
-local xmlcatcodes = catcodenumbers.xmlcatcodes
-
-local flush = texsprint
-local flushdirect = texprint
-local flushraw = texwrite
-
-local report_context = logs.reporter("cld","tex")
-local report_cld = logs.reporter("cld","stack")
-
-local processlines = true -- experiments.register("context.processlines", function(v) processlines = v end)
-
--- for tracing it's easier to have two stacks
-
-local _stack_f_, _n_f_ = { }, 0
-local _stack_n_, _n_n_ = { }, 0
-
-local function _store_f_(ti)
- _n_f_ = _n_f_ + 1
- _stack_f_[_n_f_] = ti
- return _n_f_
-end
-
-local function _store_n_(ti)
- _n_n_ = _n_n_ + 1
- _stack_n_[_n_n_] = ti
- return _n_n_
-end
-
-local function _flush_f_(n)
- local sn = _stack_f_[n]
- if not sn then
- report_cld("data with id %a cannot be found on stack",n)
- else
- local tn = type(sn)
- if tn == "function" then
- if not sn() and texcount["@@trialtypesetting"] == 0 then -- @@trialtypesetting is private!
- _stack_f_[n] = nil
- else
- -- keep, beware, that way the stack can grow
- end
- else
- if texcount["@@trialtypesetting"] == 0 then -- @@trialtypesetting is private!
- writenode(sn)
- _stack_f_[n] = nil
- else
- writenode(copynodelist(sn))
- -- keep, beware, that way the stack can grow
- end
- end
- end
-end
-
-local function _flush_n_(n)
- local sn = _stack_n_[n]
- if not sn then
- report_cld("data with id %a cannot be found on stack",n)
- elseif texcount["@@trialtypesetting"] == 0 then -- @@trialtypesetting is private!
- writenode(sn)
- _stack_n_[n] = nil
- else
- writenode(copynodelist(sn))
- -- keep, beware, that way the stack can grow
- end
-end
-
-function context.restart()
- _stack_f_, _n_f_ = { }, 0
- _stack_n_, _n_n_ = { }, 0
-end
-
-context._stack_f_ = _stack_f_
-context._store_f_ = _store_f_
-context._flush_f_ = _flush_f_ _cldf_ = _flush_f_
-
-context._stack_n_ = _stack_n_
-context._store_n_ = _store_n_
-context._flush_n_ = _flush_n_ _cldn_ = _flush_n_
-
--- Should we keep the catcodes with the function?
-
-local catcodestack = { }
-local currentcatcodes = ctxcatcodes
-local contentcatcodes = ctxcatcodes
-
-local catcodes = {
- ctx = ctxcatcodes, ctxcatcodes = ctxcatcodes, context = ctxcatcodes,
- prt = prtcatcodes, prtcatcodes = prtcatcodes, protect = prtcatcodes,
- tex = texcatcodes, texcatcodes = texcatcodes, plain = texcatcodes,
- txt = txtcatcodes, txtcatcodes = txtcatcodes, text = txtcatcodes,
- vrb = vrbcatcodes, vrbcatcodes = vrbcatcodes, verbatim = vrbcatcodes,
- xml = xmlcatcodes, xmlcatcodes = xmlcatcodes,
-}
-
-local function pushcatcodes(c)
- insert(catcodestack,currentcatcodes)
- currentcatcodes = (c and catcodes[c] or tonumber(c)) or currentcatcodes
- contentcatcodes = currentcatcodes
-end
-
-local function popcatcodes()
- currentcatcodes = remove(catcodestack) or currentcatcodes
- contentcatcodes = currentcatcodes
-end
-
-context.pushcatcodes = pushcatcodes
-context.popcatcodes = popcatcodes
-
--- -- --
-
-local newline = patterns.newline
-local space = patterns.spacer
-local spacing = newline * space^0
-local content = lpegC((1-spacing)^1) -- texsprint
-local emptyline = space^0 * newline^2 -- texprint("")
-local endofline = space^0 * newline * space^0 -- texsprint(" ")
-local simpleline = endofline * lpegP(-1) --
-
-local verbose = lpegC((1-space-newline)^1)
-local beginstripper = (lpegS(" \t")^1 * newline^1) / ""
-local endstripper = beginstripper * lpegP(-1)
-
-local justaspace = space * lpegCc("")
-local justanewline = newline * lpegCc("")
-
-local function n_content(s)
- flush(contentcatcodes,s)
-end
-
-local function n_verbose(s)
- flush(vrbcatcodes,s)
-end
-
-local function n_endofline()
- flush(currentcatcodes," \r")
-end
-
-local function n_emptyline()
- flushdirect(currentcatcodes,"\r")
-end
-
-local function n_simpleline()
- flush(currentcatcodes," \r")
-end
-
-local n_exception = ""
-
--- better a table specification
-
-function context.newtexthandler(specification) -- can also be used for verbose
- specification = specification or { }
- --
- local s_catcodes = specification.catcodes
- --
- local f_before = specification.before
- local f_after = specification.after
- --
- local f_endofline = specification.endofline or n_endofline
- local f_emptyline = specification.emptyline or n_emptyline
- local f_simpleline = specification.simpleline or n_simpleline
- local f_content = specification.content or n_content
- local f_space = specification.space
- --
- local p_exception = specification.exception
- --
- if s_catcodes then
- f_content = function(s)
- flush(s_catcodes,s)
- end
- end
- --
- local pattern
- if f_space then
- if p_exception then
- local content = lpegC((1-spacing-p_exception)^1)
- pattern =
- (
- justaspace / f_space
- + justanewline / f_endofline
- + p_exception
- + content / f_content
- )^0
- else
- local content = lpegC((1-space-endofline)^1)
- pattern =
- (
- justaspace / f_space
- + justanewline / f_endofline
- + content / f_content
- )^0
- end
- else
- if p_exception then
- local content = lpegC((1-spacing-p_exception)^1)
- pattern =
- simpleline / f_simpleline
- +
- (
- emptyline / f_emptyline
- + endofline / f_endofline
- + p_exception
- + content / f_content
- )^0
- else
- local content = lpegC((1-spacing)^1)
- pattern =
- simpleline / f_simpleline
- +
- (
- emptyline / f_emptyline
- + endofline / f_endofline
- + content / f_content
- )^0
- end
- end
- --
- if f_before then
- pattern = (P(true) / f_before) * pattern
- end
- --
- if f_after then
- pattern = pattern * (P(true) / f_after)
- end
- --
- return function(str) return lpegmatch(pattern,str) end, pattern
-end
-
-function context.newverbosehandler(specification) -- a special variant for e.g. cdata in lxml-tex
- specification = specification or { }
- --
- local f_line = specification.line or function() flushdirect("\r") end
- local f_space = specification.space or function() flush(" ") end
- local f_content = specification.content or n_verbose
- local f_before = specification.before
- local f_after = specification.after
- --
- local pattern =
- justanewline / f_line -- so we get call{}
- + verbose / f_content
- + justaspace / f_space -- so we get call{}
- --
- if specification.strip then
- pattern = beginstripper^0 * (endstripper + pattern)^0
- else
- pattern = pattern^0
- end
- --
- if f_before then
- pattern = (lpegP(true) / f_before) * pattern
- end
- --
- if f_after then
- pattern = pattern * (lpegP(true) / f_after)
- end
- --
- return function(str) return lpegmatch(pattern,str) end, pattern
-end
-
-local flushlines = context.newtexthandler {
- content = n_content,
- endofline = n_endofline,
- emptyline = n_emptyline,
- simpleline = n_simpleline,
-}
-
-context.__flushlines = flushlines -- maybe context.helpers.flushtexlines
-context.__flush = flush
-context.__flushdirect = flushdirect
-
--- The next variant is only used in rare cases (buffer to mp):
-
-local printlines_ctx = (
- (newline) / function() texprint("") end +
- (1-newline)^1 / function(s) texprint(ctxcatcodes,s) end * newline^-1
-)^0
-
-local printlines_raw = (
- (newline) / function() texprint("") end +
- (1-newline)^1 / function(s) texprint(s) end * newline^-1
-)^0
-
-function context.printlines(str,raw) -- todo: see if via file is useable
- if raw then
- lpegmatch(printlines_raw,str)
- else
- lpegmatch(printlines_ctx,str)
- end
-end
-
--- This is the most reliable way to deal with nested buffers and other
--- catcode sensitive data.
-
-local methodhandler = resolvers.methodhandler
-
-function context.viafile(data,tag)
- if data and data ~= "" then
- local filename = resolvers.savers.byscheme("virtual",validstring(tag,"viafile"),data)
- -- context.startregime { "utf" }
- context.input(filename)
- -- context.stopregime()
- end
-end
-
--- -- -- "{" .. ti .. "}" is somewhat slower in a cld-mkiv run than "{",ti,"}"
-
-local containseol = patterns.containseol
-
-local function writer(parent,command,first,...) -- already optimized before call
- local t = { first, ... }
- flush(currentcatcodes,command) -- todo: ctx|prt|texcatcodes
- local direct = false
- for i=1,#t do
- local ti = t[i]
- local typ = type(ti)
- if direct then
- if typ == "string" or typ == "number" then
- flush(currentcatcodes,ti)
- else -- node.write
- report_context("error: invalid use of direct in %a, only strings and numbers can be flushed directly, not %a",command,typ)
- end
- direct = false
- elseif ti == nil then
- -- nothing
- elseif ti == "" then
- flush(currentcatcodes,"{}")
- elseif typ == "string" then
- -- is processelines seen ?
- if processlines and lpegmatch(containseol,ti) then
- flush(currentcatcodes,"{")
- local flushlines = parent.__flushlines or flushlines
- flushlines(ti)
- flush(currentcatcodes,"}")
- elseif currentcatcodes == contentcatcodes then
- flush(currentcatcodes,"{",ti,"}")
- else
- flush(currentcatcodes,"{")
- flush(contentcatcodes,ti)
- flush(currentcatcodes,"}")
- end
- elseif typ == "number" then
- -- numbers never have funny catcodes
- flush(currentcatcodes,"{",ti,"}")
- elseif typ == "table" then
- local tn = #ti
- if tn == 0 then
- local done = false
- for k, v in next, ti do
- if done then
- if v == "" then
- flush(currentcatcodes,",",k,'=')
- else
- flush(currentcatcodes,",",k,"={",v,"}")
- end
- else
- if v == "" then
- flush(currentcatcodes,"[",k,"=")
- else
- flush(currentcatcodes,"[",k,"={",v,"}")
- end
- done = true
- end
- end
- if done then
- flush(currentcatcodes,"]")
- else
- flush(currentcatcodes,"[]")
- end
- elseif tn == 1 then -- some 20% faster than the next loop
- local tj = ti[1]
- if type(tj) == "function" then
- flush(currentcatcodes,"[\\cldf{",_store_f_(tj),"}]")
- else
- flush(currentcatcodes,"[",tj,"]")
- end
- else -- is concat really faster than flushes here? probably needed anyway (print artifacts)
- for j=1,tn do
- local tj = ti[j]
- if type(tj) == "function" then
- ti[j] = "\\cldf{" .. _store_f_(tj) .. "}"
- end
- end
- flush(currentcatcodes,"[",concat(ti,","),"]")
- end
- elseif typ == "function" then
- flush(currentcatcodes,"{\\cldf{",_store_f_(ti),"}}") -- todo: ctx|prt|texcatcodes
- elseif typ == "boolean" then
- if ti then
- flushdirect(currentcatcodes,"\r")
- else
- direct = true
- end
- elseif typ == "thread" then
- report_context("coroutines not supported as we cannot yield across boundaries")
- elseif isnode(ti) then -- slow
- flush(currentcatcodes,"{\\cldn{",_store_n_(ti),"}}")
- else
- report_context("error: %a gets a weird argument %a",command,ti)
- end
- end
-end
-
-local generics = { } context.generics = generics
-
-local function indexer(parent,k)
- if type(k) == "string" then
- local c = "\\" .. tostring(generics[k] or k)
- local f = function(first,...)
- if first == nil then
- flush(currentcatcodes,c)
- else
- return writer(parent,c,first,...)
- end
- end
- parent[k] = f
- return f
- else
- return context -- catch
- end
-end
-
--- Potential optimization: after the first call we know if there will be an
--- argument. Of course there is the side effect that for instance abuse like
--- context.NC(str) fails as well as optional arguments. So, we don't do this
--- in practice. We just keep the next trick commented. The gain on some
--- 100000 calls is not that large: 0.100 => 0.95 which is neglectable.
---
--- local function constructor(parent,k,c,first,...)
--- if first == nil then
--- local f = function()
--- flush(currentcatcodes,c)
--- end
--- parent[k] = f
--- return f()
--- else
--- local f = function(...)
--- return writer(parent,c,...)
--- end
--- parent[k] = f
--- return f(first,...)
--- end
--- end
---
--- local function indexer(parent,k)
--- local c = "\\" .. tostring(generics[k] or k)
--- local f = function(...)
--- return constructor(parent,k,c,...)
--- end
--- parent[k] = f
--- return f
--- end
-
--- only for internal usage:
-
-function context.constructcsonly(k) -- not much faster than the next but more mem efficient
- local c = "\\" .. tostring(generics[k] or k)
- rawset(context, k, function()
- flush(prtcatcodes,c)
- end)
-end
-
-function context.constructcs(k)
- local c = "\\" .. tostring(generics[k] or k)
- rawset(context, k, function(first,...)
- if first == nil then
- flush(prtcatcodes,c)
- else
- return writer(context,c,first,...)
- end
- end)
-end
-
-local function caller(parent,f,a,...)
- if not parent then
- -- so we don't need to test in the calling (slower but often no issue)
- elseif f ~= nil then
- local typ = type(f)
- if typ == "string" then
- if a then
- flush(contentcatcodes,formatters[f](a,...)) -- was currentcatcodes
- elseif processlines and lpegmatch(containseol,f) then
- local flushlines = parent.__flushlines or flushlines
- flushlines(f)
- else
- flush(contentcatcodes,f)
- end
- elseif typ == "number" then
- if a then
- flush(currentcatcodes,f,a,...)
- else
- flush(currentcatcodes,f)
- end
- elseif typ == "function" then
- -- ignored: a ...
- flush(currentcatcodes,"{\\cldf{",_store_f_(f),"}}") -- todo: ctx|prt|texcatcodes
- elseif typ == "boolean" then
- if f then
- if a ~= nil then
- local flushlines = parent.__flushlines or flushlines
- flushlines(a)
- else
- flushdirect(currentcatcodes,"\n") -- no \r, else issues with \startlines ... use context.par() otherwise
- end
- else
- if a ~= nil then
- -- no command, same as context(a,...)
- writer(parent,"",a,...)
- else
- -- ignored
- end
- end
- elseif typ == "thread" then
- report_context("coroutines not supported as we cannot yield across boundaries")
- elseif isnode(f) then -- slow
- -- writenode(f)
- flush(currentcatcodes,"\\cldn{",_store_n_(f),"}")
- else
- report_context("error: %a gets a weird argument %a","context",f)
- end
- end
-end
-
-local defaultcaller = caller
-
-setmetatable(context, { __index = indexer, __call = caller } )
-
--- now we tweak unprotect and protect
-
-function context.unprotect()
- -- at the lua end
- insert(catcodestack,currentcatcodes)
- currentcatcodes = prtcatcodes
- contentcatcodes = currentcatcodes
- -- at the tex end
- flush("\\unprotect")
-end
-
-function context.protect()
- -- at the tex end
- flush("\\protect")
- -- at the lua end
- currentcatcodes = remove(catcodestack) or currentcatcodes
- contentcatcodes = currentcatcodes
-end
-
-function context.sprint(...) -- takes catcodes as first argument
- flush(...)
-end
-
-function context.fprint(catcodes,fmt,first,...)
- if type(catcodes) == "number" then
- if first then
- flush(catcodes,formatters[fmt](first,...))
- else
- flush(catcodes,fmt)
- end
- else
- if fmt then
- flush(formatters[catcodes](fmt,first,...))
- else
- flush(catcodes)
- end
- end
-end
-
-function tex.fprint(fmt,first,...) -- goodie
- if first then
- flush(currentcatcodes,formatters[fmt](first,...))
- else
- flush(currentcatcodes,fmt)
- end
-end
-
--- logging
-
-local trace_stack = { }
-
-local normalflush = flush
-local normalflushdirect = flushdirect
-local normalflushraw = flushraw
-local normalwriter = writer
-local currenttrace = nil
-local nofwriters = 0
-local nofflushes = 0
-
-local visualizer = lpeg.replacer {
- { "\n","<<newline>>" },
- { "\r","<<par>>" },
-}
-
-statistics.register("traced context", function()
- if nofwriters > 0 or nofflushes > 0 then
- return format("writers: %s, flushes: %s, maxstack: %s",nofwriters,nofflushes,_n_f_)
- end
-end)
-
-local tracedwriter = function(parent,...) -- also catcodes ?
- nofwriters = nofwriters + 1
- local savedflush = flush
- local savedflushdirect = flushdirect -- unlikely to be used here
- local t, n = { "w : - : " }, 1
- local traced = function(normal,catcodes,...) -- todo: check for catcodes
- local s = concat({...})
- s = lpegmatch(visualizer,s)
- n = n + 1
- t[n] = s
- normal(catcodes,...)
- end
- flush = function(...) traced(normalflush, ...) end
- flushdirect = function(...) traced(normalflushdirect,...) end
- normalwriter(parent,...)
- flush = savedflush
- flushdirect = savedflushdirect
- currenttrace(concat(t))
-end
-
--- we could reuse collapsed
-
-local traced = function(normal,one,two,...)
- nofflushes = nofflushes + 1
- if two then
- -- only catcodes if 'one' is number
- normal(one,two,...)
- local catcodes = type(one) == "number" and one
- local arguments = catcodes and { two, ... } or { one, two, ... }
- local collapsed, c = { formatters["f : %s : "](catcodes or '-') }, 1
- for i=1,#arguments do
- local argument = arguments[i]
- local argtype = type(argument)
- c = c + 1
- if argtype == "string" then
- collapsed[c] = lpegmatch(visualizer,argument)
- elseif argtype == "number" then
- collapsed[c] = argument
- else
- collapsed[c] = formatters["<<%S>>"](argument)
- end
- end
- currenttrace(concat(collapsed))
- else
- -- no catcodes
- normal(one)
- local argtype = type(one)
- if argtype == "string" then
- currenttrace(formatters["f : - : %s"](lpegmatch(visualizer,one)))
- elseif argtype == "number" then
- currenttrace(formatters["f : - : %s"](one))
- else
- currenttrace(formatters["f : - : <<%S>>"](one))
- end
- end
-end
-
-local tracedflush = function(...) traced(normalflush, ...) end
-local tracedflushdirect = function(...) traced(normalflushdirect,...) end
-
-local function pushlogger(trace)
- trace = trace or report_context
- insert(trace_stack,currenttrace)
- currenttrace = trace
- --
- flush = tracedflush
- flushdirect = tracedflushdirect
- writer = tracedwriter
- --
- context.__flush = flush
- context.__flushdirect = flushdirect
- --
- return flush, writer, flushdirect
-end
-
-local function poplogger()
- currenttrace = remove(trace_stack)
- if not currenttrace then
- flush = normalflush
- flushdirect = normalflushdirect
- writer = normalwriter
- --
- context.__flush = flush
- context.__flushdirect = flushdirect
- end
- return flush, writer, flushdirect
-end
-
-local function settracing(v)
- if v then
- return pushlogger(report_context)
- else
- return poplogger()
- end
-end
-
--- todo: share flushers so that we can define in other files
-
-trackers.register("context.trace",settracing)
-
-context.pushlogger = pushlogger
-context.poplogger = poplogger
-context.settracing = settracing
-
--- -- untested, no time now:
---
--- local tracestack, tracestacktop = { }, false
---
--- function context.pushtracing(v)
--- insert(tracestack,tracestacktop)
--- if type(v) == "function" then
--- pushlogger(v)
--- v = true
--- else
--- pushlogger()
--- end
--- tracestacktop = v
--- settracing(v)
--- end
---
--- function context.poptracing()
--- poplogger()
--- tracestacktop = remove(tracestack) or false
--- settracing(tracestacktop)
--- end
-
-function context.getlogger()
- return flush, writer, flush_direct
-end
-
-local trace_cld = false trackers.register("context.files", function(v) trace_cld = v end)
-
-function context.runfile(filename)
- local foundname = resolvers.findtexfile(file.addsuffix(filename,"cld")) or ""
- if foundname ~= "" then
- local ok = dofile(foundname)
- if type(ok) == "function" then
- if trace_cld then
- report_context("begin of file %a (function call)",foundname)
- end
- ok()
- if trace_cld then
- report_context("end of file %a (function call)",foundname)
- end
- elseif ok then
- report_context("file %a is processed and returns true",foundname)
- else
- report_context("file %a is processed and returns nothing",foundname)
- end
- else
- report_context("unknown file %a",filename)
- end
-end
-
--- some functions
-
-function context.direct(first,...)
- if first ~= nil then
- return writer(context,"",first,...)
- end
-end
-
--- context.delayed (todo: lines)
-
-local delayed = { } context.delayed = delayed -- maybe also store them
-
-local function indexer(parent,k)
- local f = function(...)
- local a = { ... }
- return function()
- return context[k](unpack(a))
- end
- end
- parent[k] = f
- return f
-end
-
-local function caller(parent,...) -- todo: nodes
- local a = { ... }
- return function()
- return context(unpack(a))
- end
-end
-
--- local function indexer(parent,k)
--- local f = function(a,...)
--- if not a then
--- return function()
--- return context[k]()
--- end
--- elseif select("#",...) == 0 then
--- return function()
--- return context[k](a)
--- end
--- elseif a then
--- local t = { ... }
--- return function()
--- return context[k](a,unpack(t))
--- end
--- end
--- end
--- parent[k] = f
--- return f
--- end
---
--- local function caller(parent,a,...) -- todo: nodes
--- if not a then
--- return function()
--- return context()
--- end
--- elseif select("#",...) == 0 then
--- return function()
--- return context(a)
--- end
--- elseif a then
--- local t = { ... }
--- return function()
--- return context(a,unpack(t))
--- end
--- end
--- end
-
-setmetatable(delayed, { __index = indexer, __call = caller } )
-
--- context.nested (todo: lines)
-
-local nested = { } context.nested = nested
-
-local function indexer(parent,k)
- local f = function(...)
- local t, savedflush, n = { }, flush, 0
- flush = function(c,f,s,...) -- catcodes are ignored
- n = n + 1
- t[n] = s and concat{f,s,...} or f -- optimized for #args == 1
- end
- context[k](...)
- flush = savedflush
- return concat(t)
- end
- parent[k] = f
- return f
-end
-
-local function caller(parent,...)
- local t, savedflush, n = { }, flush, 0
- flush = function(c,f,s,...) -- catcodes are ignored
- n = n + 1
- t[n] = s and concat{f,s,...} or f -- optimized for #args == 1
- end
- context(...)
- flush = savedflush
- return concat(t)
-end
-
-setmetatable(nested, { __index = indexer, __call = caller } )
-
--- verbatim
-
-local verbatim = { } context.verbatim = verbatim
-
-local function indexer(parent,k)
- local command = context[k]
- local f = function(...)
- local savedcatcodes = contentcatcodes
- contentcatcodes = vrbcatcodes
- command(...)
- contentcatcodes = savedcatcodes
- end
- parent[k] = f
- return f
-end
-
-local function caller(parent,...)
- local savedcatcodes = contentcatcodes
- contentcatcodes = vrbcatcodes
- defaultcaller(parent,...)
- contentcatcodes = savedcatcodes
-end
-
-setmetatable(verbatim, { __index = indexer, __call = caller } )
-
--- formatted
-
-local formatted = { } context.formatted = formatted
-
--- local function indexer(parent,k)
--- local command = context[k]
--- local f = function(fmt,...)
--- command(formatters[fmt](...))
--- end
--- parent[k] = f
--- return f
--- end
-
-local function indexer(parent,k)
- if type(k) == "string" then
- local c = "\\" .. tostring(generics[k] or k)
- local f = function(first,second,...)
- if first == nil then
- flush(currentcatcodes,c)
- elseif second then
- return writer(parent,c,formatters[first](second,...))
- else
- return writer(parent,c,first)
- end
- end
- parent[k] = f
- return f
- else
- return context -- catch
- end
-end
-
--- local function caller(parent,...)
--- context.fprint(...)
--- end
-
-local function caller(parent,catcodes,fmt,first,...)
- if type(catcodes) == "number" then
- if first then
- flush(catcodes,formatters[fmt](first,...))
- else
- flush(catcodes,fmt)
- end
- else
- if fmt then
- flush(formatters[catcodes](fmt,first,...))
- else
- flush(catcodes)
- end
- end
-end
-
-setmetatable(formatted, { __index = indexer, __call = caller } )
-
--- metafun (this will move to another file)
-
-local metafun = { } context.metafun = metafun
-
-local mpdrawing = "\\MPdrawing"
-
-local function caller(parent,f,a,...)
- if not parent then
- -- skip
- elseif f then
- local typ = type(f)
- if typ == "string" then
- if a then
- flush(currentcatcodes,mpdrawing,"{",formatters[f](a,...),"}")
- else
- flush(currentcatcodes,mpdrawing,"{",f,"}")
- end
- elseif typ == "number" then
- if a then
- flush(currentcatcodes,mpdrawing,"{",f,a,...,"}")
- else
- flush(currentcatcodes,mpdrawing,"{",f,"}")
- end
- elseif typ == "function" then
- -- ignored: a ...
- flush(currentcatcodes,mpdrawing,"{\\cldf{",store_(f),"}}")
- elseif typ == "boolean" then
- -- ignored: a ...
- if f then
- flush(currentcatcodes,mpdrawing,"{^^M}")
- else
- report_context("warning: %a gets argument 'false' which is currently unsupported","metafun")
- end
- else
- report_context("error: %a gets a weird argument %a","metafun",tostring(f))
- end
- end
-end
-
-setmetatable(metafun, { __call = caller } )
-
-function metafun.start()
- context.resetMPdrawing()
-end
-
-function metafun.stop()
- context.MPdrawingdonetrue()
- context.getMPdrawing()
-end
-
-function metafun.color(name)
- return formatters[ [[\MPcolor{%s}]] ](name)
-end
-
--- metafun.delayed
-
-local delayed = { } metafun.delayed = delayed
-
-local function indexer(parent,k)
- local f = function(...)
- local a = { ... }
- return function()
- return metafun[k](unpack(a))
- end
- end
- parent[k] = f
- return f
-end
-
-
-local function caller(parent,...)
- local a = { ... }
- return function()
- return metafun(unpack(a))
- end
-end
-
-setmetatable(delayed, { __index = indexer, __call = caller } )
-
--- helpers:
-
-function context.concat(...)
- context(concat(...))
-end
+if not modules then modules = { } end modules ['cldf-ini'] = {
+ version = 1.001,
+ comment = "companion to cldf-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: generating context code at the lua end. After all
+-- it is surprisingly simple to implement due to metatables. I was wondering if
+-- there was a more natural way to deal with commands at the lua end. Of course it's
+-- a bit slower but often more readable when mixed with lua code. It can also be handy
+-- when generating documents from databases or when constructing large tables or so.
+--
+-- maybe optional checking against interface
+-- currently no coroutine trickery
+-- we could always use prtcatcodes (context.a_b_c) but then we loose protection
+-- tflush needs checking ... sort of weird that it's not a table
+-- __flushlines is an experiment and rather ugly so it will go away
+--
+-- tex.print == line with endlinechar appended
+
+-- todo: context("%bold{total: }%s",total)
+-- todo: context.documentvariable("title")
+
+local tex = tex
+
+context = context or { }
+local context = context
+
+local format, gsub, validstring = string.format, string.gsub, string.valid
+local next, type, tostring, tonumber, setmetatable, unpack, select = next, type, tostring, tonumber, setmetatable, unpack, select
+local insert, remove, concat = table.insert, table.remove, table.concat
+local lpegmatch, lpegC, lpegS, lpegP, lpegCc, patterns = lpeg.match, lpeg.C, lpeg.S, lpeg.P, lpeg.Cc, lpeg.patterns
+local formatters = string.formatters -- using formatteds is slower in this case
+
+local texsprint = tex.sprint
+local textprint = tex.tprint
+local texprint = tex.print
+local texwrite = tex.write
+local texcount = tex.count
+
+local isnode = node.is_node -- after 0.65 just node.type
+local writenode = node.write
+local copynodelist = node.copy_list
+
+local catcodenumbers = catcodes.numbers
+
+local ctxcatcodes = catcodenumbers.ctxcatcodes
+local prtcatcodes = catcodenumbers.prtcatcodes
+local texcatcodes = catcodenumbers.texcatcodes
+local txtcatcodes = catcodenumbers.txtcatcodes
+local vrbcatcodes = catcodenumbers.vrbcatcodes
+local xmlcatcodes = catcodenumbers.xmlcatcodes
+
+local flush = texsprint
+local flushdirect = texprint
+local flushraw = texwrite
+
+local report_context = logs.reporter("cld","tex")
+local report_cld = logs.reporter("cld","stack")
+
+local processlines = true -- experiments.register("context.processlines", function(v) processlines = v end)
+
+-- for tracing it's easier to have two stacks
+
+local _stack_f_, _n_f_ = { }, 0
+local _stack_n_, _n_n_ = { }, 0
+
+local function _store_f_(ti)
+ _n_f_ = _n_f_ + 1
+ _stack_f_[_n_f_] = ti
+ return _n_f_
+end
+
+local function _store_n_(ti)
+ _n_n_ = _n_n_ + 1
+ _stack_n_[_n_n_] = ti
+ return _n_n_
+end
+
+local function _flush_f_(n)
+ local sn = _stack_f_[n]
+ if not sn then
+ report_cld("data with id %a cannot be found on stack",n)
+ else
+ local tn = type(sn)
+ if tn == "function" then
+ if not sn() and texcount["@@trialtypesetting"] == 0 then -- @@trialtypesetting is private!
+ _stack_f_[n] = nil
+ else
+ -- keep, beware, that way the stack can grow
+ end
+ else
+ if texcount["@@trialtypesetting"] == 0 then -- @@trialtypesetting is private!
+ writenode(sn)
+ _stack_f_[n] = nil
+ else
+ writenode(copynodelist(sn))
+ -- keep, beware, that way the stack can grow
+ end
+ end
+ end
+end
+
+local function _flush_n_(n)
+ local sn = _stack_n_[n]
+ if not sn then
+ report_cld("data with id %a cannot be found on stack",n)
+ elseif texcount["@@trialtypesetting"] == 0 then -- @@trialtypesetting is private!
+ writenode(sn)
+ _stack_n_[n] = nil
+ else
+ writenode(copynodelist(sn))
+ -- keep, beware, that way the stack can grow
+ end
+end
+
+function context.restart()
+ _stack_f_, _n_f_ = { }, 0
+ _stack_n_, _n_n_ = { }, 0
+end
+
+context._stack_f_ = _stack_f_
+context._store_f_ = _store_f_
+context._flush_f_ = _flush_f_ _cldf_ = _flush_f_
+
+context._stack_n_ = _stack_n_
+context._store_n_ = _store_n_
+context._flush_n_ = _flush_n_ _cldn_ = _flush_n_
+
+-- Should we keep the catcodes with the function?
+
+local catcodestack = { }
+local currentcatcodes = ctxcatcodes
+local contentcatcodes = ctxcatcodes
+
+local catcodes = {
+ ctx = ctxcatcodes, ctxcatcodes = ctxcatcodes, context = ctxcatcodes,
+ prt = prtcatcodes, prtcatcodes = prtcatcodes, protect = prtcatcodes,
+ tex = texcatcodes, texcatcodes = texcatcodes, plain = texcatcodes,
+ txt = txtcatcodes, txtcatcodes = txtcatcodes, text = txtcatcodes,
+ vrb = vrbcatcodes, vrbcatcodes = vrbcatcodes, verbatim = vrbcatcodes,
+ xml = xmlcatcodes, xmlcatcodes = xmlcatcodes,
+}
+
+local function pushcatcodes(c)
+ insert(catcodestack,currentcatcodes)
+ currentcatcodes = (c and catcodes[c] or tonumber(c)) or currentcatcodes
+ contentcatcodes = currentcatcodes
+end
+
+local function popcatcodes()
+ currentcatcodes = remove(catcodestack) or currentcatcodes
+ contentcatcodes = currentcatcodes
+end
+
+context.pushcatcodes = pushcatcodes
+context.popcatcodes = popcatcodes
+
+-- -- --
+
+local newline = patterns.newline
+local space = patterns.spacer
+local spacing = newline * space^0
+local content = lpegC((1-spacing)^1) -- texsprint
+local emptyline = space^0 * newline^2 -- texprint("")
+local endofline = space^0 * newline * space^0 -- texsprint(" ")
+local simpleline = endofline * lpegP(-1) --
+
+local verbose = lpegC((1-space-newline)^1)
+local beginstripper = (lpegS(" \t")^1 * newline^1) / ""
+local endstripper = beginstripper * lpegP(-1)
+
+local justaspace = space * lpegCc("")
+local justanewline = newline * lpegCc("")
+
+local function n_content(s)
+ flush(contentcatcodes,s)
+end
+
+local function n_verbose(s)
+ flush(vrbcatcodes,s)
+end
+
+local function n_endofline()
+ flush(currentcatcodes," \r")
+end
+
+local function n_emptyline()
+ flushdirect(currentcatcodes,"\r")
+end
+
+local function n_simpleline()
+ flush(currentcatcodes," \r")
+end
+
+local n_exception = ""
+
+-- better a table specification
+
+function context.newtexthandler(specification) -- can also be used for verbose
+ specification = specification or { }
+ --
+ local s_catcodes = specification.catcodes
+ --
+ local f_before = specification.before
+ local f_after = specification.after
+ --
+ local f_endofline = specification.endofline or n_endofline
+ local f_emptyline = specification.emptyline or n_emptyline
+ local f_simpleline = specification.simpleline or n_simpleline
+ local f_content = specification.content or n_content
+ local f_space = specification.space
+ --
+ local p_exception = specification.exception
+ --
+ if s_catcodes then
+ f_content = function(s)
+ flush(s_catcodes,s)
+ end
+ end
+ --
+ local pattern
+ if f_space then
+ if p_exception then
+ local content = lpegC((1-spacing-p_exception)^1)
+ pattern =
+ (
+ justaspace / f_space
+ + justanewline / f_endofline
+ + p_exception
+ + content / f_content
+ )^0
+ else
+ local content = lpegC((1-space-endofline)^1)
+ pattern =
+ (
+ justaspace / f_space
+ + justanewline / f_endofline
+ + content / f_content
+ )^0
+ end
+ else
+ if p_exception then
+ local content = lpegC((1-spacing-p_exception)^1)
+ pattern =
+ simpleline / f_simpleline
+ +
+ (
+ emptyline / f_emptyline
+ + endofline / f_endofline
+ + p_exception
+ + content / f_content
+ )^0
+ else
+ local content = lpegC((1-spacing)^1)
+ pattern =
+ simpleline / f_simpleline
+ +
+ (
+ emptyline / f_emptyline
+ + endofline / f_endofline
+ + content / f_content
+ )^0
+ end
+ end
+ --
+ if f_before then
+ pattern = (P(true) / f_before) * pattern
+ end
+ --
+ if f_after then
+ pattern = pattern * (P(true) / f_after)
+ end
+ --
+ return function(str) return lpegmatch(pattern,str) end, pattern
+end
+
+function context.newverbosehandler(specification) -- a special variant for e.g. cdata in lxml-tex
+ specification = specification or { }
+ --
+ local f_line = specification.line or function() flushdirect("\r") end
+ local f_space = specification.space or function() flush(" ") end
+ local f_content = specification.content or n_verbose
+ local f_before = specification.before
+ local f_after = specification.after
+ --
+ local pattern =
+ justanewline / f_line -- so we get call{}
+ + verbose / f_content
+ + justaspace / f_space -- so we get call{}
+ --
+ if specification.strip then
+ pattern = beginstripper^0 * (endstripper + pattern)^0
+ else
+ pattern = pattern^0
+ end
+ --
+ if f_before then
+ pattern = (lpegP(true) / f_before) * pattern
+ end
+ --
+ if f_after then
+ pattern = pattern * (lpegP(true) / f_after)
+ end
+ --
+ return function(str) return lpegmatch(pattern,str) end, pattern
+end
+
+local flushlines = context.newtexthandler {
+ content = n_content,
+ endofline = n_endofline,
+ emptyline = n_emptyline,
+ simpleline = n_simpleline,
+}
+
+context.__flushlines = flushlines -- maybe context.helpers.flushtexlines
+context.__flush = flush
+context.__flushdirect = flushdirect
+
+-- The next variant is only used in rare cases (buffer to mp):
+
+local printlines_ctx = (
+ (newline) / function() texprint("") end +
+ (1-newline)^1 / function(s) texprint(ctxcatcodes,s) end * newline^-1
+)^0
+
+local printlines_raw = (
+ (newline) / function() texprint("") end +
+ (1-newline)^1 / function(s) texprint(s) end * newline^-1
+)^0
+
+function context.printlines(str,raw) -- todo: see if via file is useable
+ if raw then
+ lpegmatch(printlines_raw,str)
+ else
+ lpegmatch(printlines_ctx,str)
+ end
+end
+
+-- This is the most reliable way to deal with nested buffers and other
+-- catcode sensitive data.
+
+local methodhandler = resolvers.methodhandler
+
+function context.viafile(data,tag)
+ if data and data ~= "" then
+ local filename = resolvers.savers.byscheme("virtual",validstring(tag,"viafile"),data)
+ -- context.startregime { "utf" }
+ context.input(filename)
+ -- context.stopregime()
+ end
+end
+
+-- -- -- "{" .. ti .. "}" is somewhat slower in a cld-mkiv run than "{",ti,"}"
+
+local containseol = patterns.containseol
+
+local function writer(parent,command,first,...) -- already optimized before call
+ local t = { first, ... }
+ flush(currentcatcodes,command) -- todo: ctx|prt|texcatcodes
+ local direct = false
+ for i=1,#t do
+ local ti = t[i]
+ local typ = type(ti)
+ if direct then
+ if typ == "string" or typ == "number" then
+ flush(currentcatcodes,ti)
+ else -- node.write
+ report_context("error: invalid use of direct in %a, only strings and numbers can be flushed directly, not %a",command,typ)
+ end
+ direct = false
+ elseif ti == nil then
+ -- nothing
+ elseif ti == "" then
+ flush(currentcatcodes,"{}")
+ elseif typ == "string" then
+ -- is processelines seen ?
+ if processlines and lpegmatch(containseol,ti) then
+ flush(currentcatcodes,"{")
+ local flushlines = parent.__flushlines or flushlines
+ flushlines(ti)
+ flush(currentcatcodes,"}")
+ elseif currentcatcodes == contentcatcodes then
+ flush(currentcatcodes,"{",ti,"}")
+ else
+ flush(currentcatcodes,"{")
+ flush(contentcatcodes,ti)
+ flush(currentcatcodes,"}")
+ end
+ elseif typ == "number" then
+ -- numbers never have funny catcodes
+ flush(currentcatcodes,"{",ti,"}")
+ elseif typ == "table" then
+ local tn = #ti
+ if tn == 0 then
+ local done = false
+ for k, v in next, ti do
+ if done then
+ if v == "" then
+ flush(currentcatcodes,",",k,'=')
+ else
+ flush(currentcatcodes,",",k,"={",v,"}")
+ end
+ else
+ if v == "" then
+ flush(currentcatcodes,"[",k,"=")
+ else
+ flush(currentcatcodes,"[",k,"={",v,"}")
+ end
+ done = true
+ end
+ end
+ if done then
+ flush(currentcatcodes,"]")
+ else
+ flush(currentcatcodes,"[]")
+ end
+ elseif tn == 1 then -- some 20% faster than the next loop
+ local tj = ti[1]
+ if type(tj) == "function" then
+ flush(currentcatcodes,"[\\cldf{",_store_f_(tj),"}]")
+ else
+ flush(currentcatcodes,"[",tj,"]")
+ end
+ else -- is concat really faster than flushes here? probably needed anyway (print artifacts)
+ for j=1,tn do
+ local tj = ti[j]
+ if type(tj) == "function" then
+ ti[j] = "\\cldf{" .. _store_f_(tj) .. "}"
+ end
+ end
+ flush(currentcatcodes,"[",concat(ti,","),"]")
+ end
+ elseif typ == "function" then
+ flush(currentcatcodes,"{\\cldf{",_store_f_(ti),"}}") -- todo: ctx|prt|texcatcodes
+ elseif typ == "boolean" then
+ if ti then
+ flushdirect(currentcatcodes,"\r")
+ else
+ direct = true
+ end
+ elseif typ == "thread" then
+ report_context("coroutines not supported as we cannot yield across boundaries")
+ elseif isnode(ti) then -- slow
+ flush(currentcatcodes,"{\\cldn{",_store_n_(ti),"}}")
+ else
+ report_context("error: %a gets a weird argument %a",command,ti)
+ end
+ end
+end
+
+local generics = { } context.generics = generics
+
+local function indexer(parent,k)
+ if type(k) == "string" then
+ local c = "\\" .. tostring(generics[k] or k)
+ local f = function(first,...)
+ if first == nil then
+ flush(currentcatcodes,c)
+ else
+ return writer(parent,c,first,...)
+ end
+ end
+ parent[k] = f
+ return f
+ else
+ return context -- catch
+ end
+end
+
+-- Potential optimization: after the first call we know if there will be an
+-- argument. Of course there is the side effect that for instance abuse like
+-- context.NC(str) fails as well as optional arguments. So, we don't do this
+-- in practice. We just keep the next trick commented. The gain on some
+-- 100000 calls is not that large: 0.100 => 0.95 which is neglectable.
+--
+-- local function constructor(parent,k,c,first,...)
+-- if first == nil then
+-- local f = function()
+-- flush(currentcatcodes,c)
+-- end
+-- parent[k] = f
+-- return f()
+-- else
+-- local f = function(...)
+-- return writer(parent,c,...)
+-- end
+-- parent[k] = f
+-- return f(first,...)
+-- end
+-- end
+--
+-- local function indexer(parent,k)
+-- local c = "\\" .. tostring(generics[k] or k)
+-- local f = function(...)
+-- return constructor(parent,k,c,...)
+-- end
+-- parent[k] = f
+-- return f
+-- end
+
+-- only for internal usage:
+
+function context.constructcsonly(k) -- not much faster than the next but more mem efficient
+ local c = "\\" .. tostring(generics[k] or k)
+ rawset(context, k, function()
+ flush(prtcatcodes,c)
+ end)
+end
+
+function context.constructcs(k)
+ local c = "\\" .. tostring(generics[k] or k)
+ rawset(context, k, function(first,...)
+ if first == nil then
+ flush(prtcatcodes,c)
+ else
+ return writer(context,c,first,...)
+ end
+ end)
+end
+
+local function caller(parent,f,a,...)
+ if not parent then
+ -- so we don't need to test in the calling (slower but often no issue)
+ elseif f ~= nil then
+ local typ = type(f)
+ if typ == "string" then
+ if a then
+ flush(contentcatcodes,formatters[f](a,...)) -- was currentcatcodes
+ elseif processlines and lpegmatch(containseol,f) then
+ local flushlines = parent.__flushlines or flushlines
+ flushlines(f)
+ else
+ flush(contentcatcodes,f)
+ end
+ elseif typ == "number" then
+ if a then
+ flush(currentcatcodes,f,a,...)
+ else
+ flush(currentcatcodes,f)
+ end
+ elseif typ == "function" then
+ -- ignored: a ...
+ flush(currentcatcodes,"{\\cldf{",_store_f_(f),"}}") -- todo: ctx|prt|texcatcodes
+ elseif typ == "boolean" then
+ if f then
+ if a ~= nil then
+ local flushlines = parent.__flushlines or flushlines
+ flushlines(a)
+ else
+ flushdirect(currentcatcodes,"\n") -- no \r, else issues with \startlines ... use context.par() otherwise
+ end
+ else
+ if a ~= nil then
+ -- no command, same as context(a,...)
+ writer(parent,"",a,...)
+ else
+ -- ignored
+ end
+ end
+ elseif typ == "thread" then
+ report_context("coroutines not supported as we cannot yield across boundaries")
+ elseif isnode(f) then -- slow
+ -- writenode(f)
+ flush(currentcatcodes,"\\cldn{",_store_n_(f),"}")
+ else
+ report_context("error: %a gets a weird argument %a","context",f)
+ end
+ end
+end
+
+local defaultcaller = caller
+
+setmetatable(context, { __index = indexer, __call = caller } )
+
+-- now we tweak unprotect and protect
+
+function context.unprotect()
+ -- at the lua end
+ insert(catcodestack,currentcatcodes)
+ currentcatcodes = prtcatcodes
+ contentcatcodes = currentcatcodes
+ -- at the tex end
+ flush("\\unprotect")
+end
+
+function context.protect()
+ -- at the tex end
+ flush("\\protect")
+ -- at the lua end
+ currentcatcodes = remove(catcodestack) or currentcatcodes
+ contentcatcodes = currentcatcodes
+end
+
+function context.sprint(...) -- takes catcodes as first argument
+ flush(...)
+end
+
+function context.fprint(catcodes,fmt,first,...)
+ if type(catcodes) == "number" then
+ if first then
+ flush(catcodes,formatters[fmt](first,...))
+ else
+ flush(catcodes,fmt)
+ end
+ else
+ if fmt then
+ flush(formatters[catcodes](fmt,first,...))
+ else
+ flush(catcodes)
+ end
+ end
+end
+
+function tex.fprint(fmt,first,...) -- goodie
+ if first then
+ flush(currentcatcodes,formatters[fmt](first,...))
+ else
+ flush(currentcatcodes,fmt)
+ end
+end
+
+-- logging
+
+local trace_stack = { }
+
+local normalflush = flush
+local normalflushdirect = flushdirect
+local normalflushraw = flushraw
+local normalwriter = writer
+local currenttrace = nil
+local nofwriters = 0
+local nofflushes = 0
+
+local visualizer = lpeg.replacer {
+ { "\n","<<newline>>" },
+ { "\r","<<par>>" },
+}
+
+statistics.register("traced context", function()
+ if nofwriters > 0 or nofflushes > 0 then
+ return format("writers: %s, flushes: %s, maxstack: %s",nofwriters,nofflushes,_n_f_)
+ end
+end)
+
+local tracedwriter = function(parent,...) -- also catcodes ?
+ nofwriters = nofwriters + 1
+ local savedflush = flush
+ local savedflushdirect = flushdirect -- unlikely to be used here
+ local t, n = { "w : - : " }, 1
+ local traced = function(normal,catcodes,...) -- todo: check for catcodes
+ local s = concat({...})
+ s = lpegmatch(visualizer,s)
+ n = n + 1
+ t[n] = s
+ normal(catcodes,...)
+ end
+ flush = function(...) traced(normalflush, ...) end
+ flushdirect = function(...) traced(normalflushdirect,...) end
+ normalwriter(parent,...)
+ flush = savedflush
+ flushdirect = savedflushdirect
+ currenttrace(concat(t))
+end
+
+-- we could reuse collapsed
+
+local traced = function(normal,one,two,...)
+ nofflushes = nofflushes + 1
+ if two then
+ -- only catcodes if 'one' is number
+ normal(one,two,...)
+ local catcodes = type(one) == "number" and one
+ local arguments = catcodes and { two, ... } or { one, two, ... }
+ local collapsed, c = { formatters["f : %s : "](catcodes or '-') }, 1
+ for i=1,#arguments do
+ local argument = arguments[i]
+ local argtype = type(argument)
+ c = c + 1
+ if argtype == "string" then
+ collapsed[c] = lpegmatch(visualizer,argument)
+ elseif argtype == "number" then
+ collapsed[c] = argument
+ else
+ collapsed[c] = formatters["<<%S>>"](argument)
+ end
+ end
+ currenttrace(concat(collapsed))
+ else
+ -- no catcodes
+ normal(one)
+ local argtype = type(one)
+ if argtype == "string" then
+ currenttrace(formatters["f : - : %s"](lpegmatch(visualizer,one)))
+ elseif argtype == "number" then
+ currenttrace(formatters["f : - : %s"](one))
+ else
+ currenttrace(formatters["f : - : <<%S>>"](one))
+ end
+ end
+end
+
+local tracedflush = function(...) traced(normalflush, ...) end
+local tracedflushdirect = function(...) traced(normalflushdirect,...) end
+
+local function pushlogger(trace)
+ trace = trace or report_context
+ insert(trace_stack,currenttrace)
+ currenttrace = trace
+ --
+ flush = tracedflush
+ flushdirect = tracedflushdirect
+ writer = tracedwriter
+ --
+ context.__flush = flush
+ context.__flushdirect = flushdirect
+ --
+ return flush, writer, flushdirect
+end
+
+local function poplogger()
+ currenttrace = remove(trace_stack)
+ if not currenttrace then
+ flush = normalflush
+ flushdirect = normalflushdirect
+ writer = normalwriter
+ --
+ context.__flush = flush
+ context.__flushdirect = flushdirect
+ end
+ return flush, writer, flushdirect
+end
+
+local function settracing(v)
+ if v then
+ return pushlogger(report_context)
+ else
+ return poplogger()
+ end
+end
+
+-- todo: share flushers so that we can define in other files
+
+trackers.register("context.trace",settracing)
+
+context.pushlogger = pushlogger
+context.poplogger = poplogger
+context.settracing = settracing
+
+-- -- untested, no time now:
+--
+-- local tracestack, tracestacktop = { }, false
+--
+-- function context.pushtracing(v)
+-- insert(tracestack,tracestacktop)
+-- if type(v) == "function" then
+-- pushlogger(v)
+-- v = true
+-- else
+-- pushlogger()
+-- end
+-- tracestacktop = v
+-- settracing(v)
+-- end
+--
+-- function context.poptracing()
+-- poplogger()
+-- tracestacktop = remove(tracestack) or false
+-- settracing(tracestacktop)
+-- end
+
+function context.getlogger()
+ return flush, writer, flush_direct
+end
+
+local trace_cld = false trackers.register("context.files", function(v) trace_cld = v end)
+
+function context.runfile(filename)
+ local foundname = resolvers.findtexfile(file.addsuffix(filename,"cld")) or ""
+ if foundname ~= "" then
+ local ok = dofile(foundname)
+ if type(ok) == "function" then
+ if trace_cld then
+ report_context("begin of file %a (function call)",foundname)
+ end
+ ok()
+ if trace_cld then
+ report_context("end of file %a (function call)",foundname)
+ end
+ elseif ok then
+ report_context("file %a is processed and returns true",foundname)
+ else
+ report_context("file %a is processed and returns nothing",foundname)
+ end
+ else
+ report_context("unknown file %a",filename)
+ end
+end
+
+-- some functions
+
+function context.direct(first,...)
+ if first ~= nil then
+ return writer(context,"",first,...)
+ end
+end
+
+-- context.delayed (todo: lines)
+
+local delayed = { } context.delayed = delayed -- maybe also store them
+
+local function indexer(parent,k)
+ local f = function(...)
+ local a = { ... }
+ return function()
+ return context[k](unpack(a))
+ end
+ end
+ parent[k] = f
+ return f
+end
+
+local function caller(parent,...) -- todo: nodes
+ local a = { ... }
+ return function()
+ return context(unpack(a))
+ end
+end
+
+-- local function indexer(parent,k)
+-- local f = function(a,...)
+-- if not a then
+-- return function()
+-- return context[k]()
+-- end
+-- elseif select("#",...) == 0 then
+-- return function()
+-- return context[k](a)
+-- end
+-- elseif a then
+-- local t = { ... }
+-- return function()
+-- return context[k](a,unpack(t))
+-- end
+-- end
+-- end
+-- parent[k] = f
+-- return f
+-- end
+--
+-- local function caller(parent,a,...) -- todo: nodes
+-- if not a then
+-- return function()
+-- return context()
+-- end
+-- elseif select("#",...) == 0 then
+-- return function()
+-- return context(a)
+-- end
+-- elseif a then
+-- local t = { ... }
+-- return function()
+-- return context(a,unpack(t))
+-- end
+-- end
+-- end
+
+setmetatable(delayed, { __index = indexer, __call = caller } )
+
+-- context.nested (todo: lines)
+
+local nested = { } context.nested = nested
+
+local function indexer(parent,k)
+ local f = function(...)
+ local t, savedflush, n = { }, flush, 0
+ flush = function(c,f,s,...) -- catcodes are ignored
+ n = n + 1
+ t[n] = s and concat{f,s,...} or f -- optimized for #args == 1
+ end
+ context[k](...)
+ flush = savedflush
+ return concat(t)
+ end
+ parent[k] = f
+ return f
+end
+
+local function caller(parent,...)
+ local t, savedflush, n = { }, flush, 0
+ flush = function(c,f,s,...) -- catcodes are ignored
+ n = n + 1
+ t[n] = s and concat{f,s,...} or f -- optimized for #args == 1
+ end
+ context(...)
+ flush = savedflush
+ return concat(t)
+end
+
+setmetatable(nested, { __index = indexer, __call = caller } )
+
+-- verbatim
+
+local verbatim = { } context.verbatim = verbatim
+
+local function indexer(parent,k)
+ local command = context[k]
+ local f = function(...)
+ local savedcatcodes = contentcatcodes
+ contentcatcodes = vrbcatcodes
+ command(...)
+ contentcatcodes = savedcatcodes
+ end
+ parent[k] = f
+ return f
+end
+
+local function caller(parent,...)
+ local savedcatcodes = contentcatcodes
+ contentcatcodes = vrbcatcodes
+ defaultcaller(parent,...)
+ contentcatcodes = savedcatcodes
+end
+
+setmetatable(verbatim, { __index = indexer, __call = caller } )
+
+-- formatted
+
+local formatted = { } context.formatted = formatted
+
+-- local function indexer(parent,k)
+-- local command = context[k]
+-- local f = function(fmt,...)
+-- command(formatters[fmt](...))
+-- end
+-- parent[k] = f
+-- return f
+-- end
+
+local function indexer(parent,k)
+ if type(k) == "string" then
+ local c = "\\" .. tostring(generics[k] or k)
+ local f = function(first,second,...)
+ if first == nil then
+ flush(currentcatcodes,c)
+ elseif second then
+ return writer(parent,c,formatters[first](second,...))
+ else
+ return writer(parent,c,first)
+ end
+ end
+ parent[k] = f
+ return f
+ else
+ return context -- catch
+ end
+end
+
+-- local function caller(parent,...)
+-- context.fprint(...)
+-- end
+
+local function caller(parent,catcodes,fmt,first,...)
+ if type(catcodes) == "number" then
+ if first then
+ flush(catcodes,formatters[fmt](first,...))
+ else
+ flush(catcodes,fmt)
+ end
+ else
+ if fmt then
+ flush(formatters[catcodes](fmt,first,...))
+ else
+ flush(catcodes)
+ end
+ end
+end
+
+setmetatable(formatted, { __index = indexer, __call = caller } )
+
+-- metafun (this will move to another file)
+
+local metafun = { } context.metafun = metafun
+
+local mpdrawing = "\\MPdrawing"
+
+local function caller(parent,f,a,...)
+ if not parent then
+ -- skip
+ elseif f then
+ local typ = type(f)
+ if typ == "string" then
+ if a then
+ flush(currentcatcodes,mpdrawing,"{",formatters[f](a,...),"}")
+ else
+ flush(currentcatcodes,mpdrawing,"{",f,"}")
+ end
+ elseif typ == "number" then
+ if a then
+ flush(currentcatcodes,mpdrawing,"{",f,a,...,"}")
+ else
+ flush(currentcatcodes,mpdrawing,"{",f,"}")
+ end
+ elseif typ == "function" then
+ -- ignored: a ...
+ flush(currentcatcodes,mpdrawing,"{\\cldf{",store_(f),"}}")
+ elseif typ == "boolean" then
+ -- ignored: a ...
+ if f then
+ flush(currentcatcodes,mpdrawing,"{^^M}")
+ else
+ report_context("warning: %a gets argument 'false' which is currently unsupported","metafun")
+ end
+ else
+ report_context("error: %a gets a weird argument %a","metafun",tostring(f))
+ end
+ end
+end
+
+setmetatable(metafun, { __call = caller } )
+
+function metafun.start()
+ context.resetMPdrawing()
+end
+
+function metafun.stop()
+ context.MPdrawingdonetrue()
+ context.getMPdrawing()
+end
+
+function metafun.color(name)
+ return formatters[ [[\MPcolor{%s}]] ](name)
+end
+
+-- metafun.delayed
+
+local delayed = { } metafun.delayed = delayed
+
+local function indexer(parent,k)
+ local f = function(...)
+ local a = { ... }
+ return function()
+ return metafun[k](unpack(a))
+ end
+ end
+ parent[k] = f
+ return f
+end
+
+
+local function caller(parent,...)
+ local a = { ... }
+ return function()
+ return metafun(unpack(a))
+ end
+end
+
+setmetatable(delayed, { __index = indexer, __call = caller } )
+
+-- helpers:
+
+function context.concat(...)
+ context(concat(...))
+end
diff --git a/tex/context/base/cldf-int.lua b/tex/context/base/cldf-int.lua
index 6ead8e841..6cbfd666f 100644
--- a/tex/context/base/cldf-int.lua
+++ b/tex/context/base/cldf-int.lua
@@ -1,221 +1,221 @@
-if not modules then modules = { } end modules ['cldf-int'] = {
- version = 1.001,
- comment = "companion to mult-clm.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 format, insert, remove, concat = string.format, table.insert, table.remove, table.concat
-local unpack = unpack or table.unpack
-
-local catcodenumbers = catcodes.numbers
-
-local ctxcatcodes = catcodenumbers.ctxcatcodes
-local vrbcatcodes = catcodenumbers.vrbcatcodes
-
-local contextsprint = context.sprint
-
-local trace_define = false trackers.register("context.define", function(v) trace_define = v end)
-
-interfaces = interfaces or { }
-
-_clmh_ = utilities.parsers.settings_to_array
-_clma_ = utilities.parsers.settings_to_array
-
-local starters, stoppers, macros, stack = { }, { }, { }, { }
-
-local checkers = {
- [0] = "",
- "\\dosingleempty",
- "\\dodoubleempty",
- "\\dotripleempty",
- "\\doquadrupleempty",
- "\\doquintupleempty",
- "\\dosixtupleempty",
-}
-
-function _clmm_(name,...)
- macros[name](...)
-end
-
-function _clmb_(name,...)
- local sn = stack[name]
- insert(sn,{...})
- starters[name](...)
-end
-
-function _clme_(name)
- local sn = stack[name]
- local sv = remove(sn)
- if sv then
- stoppers[name](unpack(sv))
- else
- -- nesting error
- end
-end
-
-_clmn_ = tonumber
-
-local estart = interfaces.elements.start
-local estop = interfaces.elements.stop
-
-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 arguments = specification.arguments
- local na = (arguments and #arguments) or 0
- local environment = specification.environment
- if na == 0 then
- if environment then
- contextsprint(ctxcatcodes,"\\setuvalue{",estart,name,"}{\\ctxlua{_clmb_('",name,"')}}")
- contextsprint(ctxcatcodes,"\\setuvalue{",estop, name,"}{\\ctxlua{_clme_('",name,"')}}")
- end
- if not environment or environment == "both" then
- contextsprint(ctxcatcodes,"\\setuvalue{", name,"}{\\ctxlua{_clmm_('",name,"')}}")
- end
- else
- -- we could flush immediate but tracing is bad then
- stack[name] = { }
- local opt, done = 0, false
- local snippets = { } -- we can reuse it
- local mkivdo = "\\mkivdo" .. name -- maybe clddo
- snippets[#snippets+1] = "\\def"
- snippets[#snippets+1] = mkivdo
- for i=1,na do
- local a = arguments[i]
- local variant = a[1]
- if variant == "option" then
- snippets[#snippets+1] = "[#"
- snippets[#snippets+1] = i
- snippets[#snippets+1] = "]"
- if not done then
- opt = opt + 1
- end
- else
- done = true -- no more optional checking after this
- snippets[#snippets+1] = "#"
- snippets[#snippets+1] = i
- end
- end
- if environment then
- snippets[#snippets+1] = "{\\ctxlua{_clmb_('"
- snippets[#snippets+1] = name
- snippets[#snippets+1] = "'"
- else
- snippets[#snippets+1] = "{\\ctxlua{_clmm_('"
- snippets[#snippets+1] = name
- snippets[#snippets+1] = "'"
- end
- for i=1,na do
- local a = arguments[i]
- local variant = a[2]
- if variant == "list" then
- snippets[#snippets+1] = ",_clma_([[#"
- snippets[#snippets+1] = i
- snippets[#snippets+1] = "]])"
- elseif variant == "hash" then
- snippets[#snippets+1] = ",_clmh_([[#"
- snippets[#snippets+1] = i
- snippets[#snippets+1] = "]])"
- elseif variant == "number" then
- snippets[#snippets+1] = ",_clmn_([[#"
- snippets[#snippets+1] = i
- snippets[#snippets+1] = "]])"
- else
- snippets[#snippets+1] = ",[[#"
- snippets[#snippets+1] = i
- snippets[#snippets+1] = "]]"
- end
- end
- snippets[#snippets+1] = ")}}"
- contextsprint(ctxcatcodes,unpack(snippets))
- if environment then
- -- needs checking
- contextsprint(ctxcatcodes,"\\setuvalue{",estart,name,"}{",checkers[opt],mkivdo,"}")
- contextsprint(ctxcatcodes,"\\setuvalue{",estop, name,"}{\\ctxlua{_clme_('",name,"')}}")
- end
- if not environment or environment == "both" then
- contextsprint(ctxcatcodes,"\\setuvalue{", name,"}{",checkers[opt],mkivdo,"}")
- end
- end
- if environment then
- starters[name] = specification.starter
- stoppers[name] = specification.stopper
- else
- macros[name] = specification.macro
- end
- end
-end
-
-function interfaces.tolist(t)
- 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,", ")
-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(opt_1)
---~ context.stopnarrower()
---~ context("stop more, options: %s",interfaces.tolist(opt_1))
---~ 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
+if not modules then modules = { } end modules ['cldf-int'] = {
+ version = 1.001,
+ comment = "companion to mult-clm.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 format, insert, remove, concat = string.format, table.insert, table.remove, table.concat
+local unpack = unpack or table.unpack
+
+local catcodenumbers = catcodes.numbers
+
+local ctxcatcodes = catcodenumbers.ctxcatcodes
+local vrbcatcodes = catcodenumbers.vrbcatcodes
+
+local contextsprint = context.sprint
+
+local trace_define = false trackers.register("context.define", function(v) trace_define = v end)
+
+interfaces = interfaces or { }
+
+_clmh_ = utilities.parsers.settings_to_array
+_clma_ = utilities.parsers.settings_to_array
+
+local starters, stoppers, macros, stack = { }, { }, { }, { }
+
+local checkers = {
+ [0] = "",
+ "\\dosingleempty",
+ "\\dodoubleempty",
+ "\\dotripleempty",
+ "\\doquadrupleempty",
+ "\\doquintupleempty",
+ "\\dosixtupleempty",
+}
+
+function _clmm_(name,...)
+ macros[name](...)
+end
+
+function _clmb_(name,...)
+ local sn = stack[name]
+ insert(sn,{...})
+ starters[name](...)
+end
+
+function _clme_(name)
+ local sn = stack[name]
+ local sv = remove(sn)
+ if sv then
+ stoppers[name](unpack(sv))
+ else
+ -- nesting error
+ end
+end
+
+_clmn_ = tonumber
+
+local estart = interfaces.elements.start
+local estop = interfaces.elements.stop
+
+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 arguments = specification.arguments
+ local na = (arguments and #arguments) or 0
+ local environment = specification.environment
+ if na == 0 then
+ if environment then
+ contextsprint(ctxcatcodes,"\\setuvalue{",estart,name,"}{\\ctxlua{_clmb_('",name,"')}}")
+ contextsprint(ctxcatcodes,"\\setuvalue{",estop, name,"}{\\ctxlua{_clme_('",name,"')}}")
+ end
+ if not environment or environment == "both" then
+ contextsprint(ctxcatcodes,"\\setuvalue{", name,"}{\\ctxlua{_clmm_('",name,"')}}")
+ end
+ else
+ -- we could flush immediate but tracing is bad then
+ stack[name] = { }
+ local opt, done = 0, false
+ local snippets = { } -- we can reuse it
+ local mkivdo = "\\mkivdo" .. name -- maybe clddo
+ snippets[#snippets+1] = "\\def"
+ snippets[#snippets+1] = mkivdo
+ for i=1,na do
+ local a = arguments[i]
+ local variant = a[1]
+ if variant == "option" then
+ snippets[#snippets+1] = "[#"
+ snippets[#snippets+1] = i
+ snippets[#snippets+1] = "]"
+ if not done then
+ opt = opt + 1
+ end
+ else
+ done = true -- no more optional checking after this
+ snippets[#snippets+1] = "#"
+ snippets[#snippets+1] = i
+ end
+ end
+ if environment then
+ snippets[#snippets+1] = "{\\ctxlua{_clmb_('"
+ snippets[#snippets+1] = name
+ snippets[#snippets+1] = "'"
+ else
+ snippets[#snippets+1] = "{\\ctxlua{_clmm_('"
+ snippets[#snippets+1] = name
+ snippets[#snippets+1] = "'"
+ end
+ for i=1,na do
+ local a = arguments[i]
+ local variant = a[2]
+ if variant == "list" then
+ snippets[#snippets+1] = ",_clma_([[#"
+ snippets[#snippets+1] = i
+ snippets[#snippets+1] = "]])"
+ elseif variant == "hash" then
+ snippets[#snippets+1] = ",_clmh_([[#"
+ snippets[#snippets+1] = i
+ snippets[#snippets+1] = "]])"
+ elseif variant == "number" then
+ snippets[#snippets+1] = ",_clmn_([[#"
+ snippets[#snippets+1] = i
+ snippets[#snippets+1] = "]])"
+ else
+ snippets[#snippets+1] = ",[[#"
+ snippets[#snippets+1] = i
+ snippets[#snippets+1] = "]]"
+ end
+ end
+ snippets[#snippets+1] = ")}}"
+ contextsprint(ctxcatcodes,unpack(snippets))
+ if environment then
+ -- needs checking
+ contextsprint(ctxcatcodes,"\\setuvalue{",estart,name,"}{",checkers[opt],mkivdo,"}")
+ contextsprint(ctxcatcodes,"\\setuvalue{",estop, name,"}{\\ctxlua{_clme_('",name,"')}}")
+ end
+ if not environment or environment == "both" then
+ contextsprint(ctxcatcodes,"\\setuvalue{", name,"}{",checkers[opt],mkivdo,"}")
+ end
+ end
+ if environment then
+ starters[name] = specification.starter
+ stoppers[name] = specification.stopper
+ else
+ macros[name] = specification.macro
+ end
+ end
+end
+
+function interfaces.tolist(t)
+ 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,", ")
+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(opt_1)
+--~ context.stopnarrower()
+--~ context("stop more, options: %s",interfaces.tolist(opt_1))
+--~ 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
diff --git a/tex/context/base/cldf-prs.lua b/tex/context/base/cldf-prs.lua
index 7715e8695..9fbdba0c8 100644
--- a/tex/context/base/cldf-prs.lua
+++ b/tex/context/base/cldf-prs.lua
@@ -1,54 +1,54 @@
-if not modules then modules = { } end modules ['cldf-bas'] = {
- version = 1.001,
- comment = "companion to cldf-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local lpegmatch, patterns = lpeg.match, lpeg.patterns
-local P, R, V, Cc, Cs = lpeg.P, lpeg.R, lpeg.V, lpeg.Cc, lpeg.Cs
-local format = string.format
-
-local cpatterns = patterns.context or { }
-patterns.context = cpatterns
-
-local backslash = P("\\")
-local csname = backslash * P(1) * (1-backslash)^0
-local sign = P("+") / "\\textplus "
- + P("-") / "\\textminus "
-local leftbrace = P("{")
-local rightbrace = P("}")
-local nested = P { leftbrace * (V(1) + (1-rightbrace))^0 * rightbrace }
-local subscript = P("_")
-local superscript = P("^")
-local utf8char = patterns.utf8char
-local cardinal = patterns.cardinal
-
--- local scripts = P { "start",
--- start = V("csname") + V("lowfirst") + V("highfirst"),
--- csname = csname,
--- content = Cs(V("csname") + nested + sign^-1 * (cardinal + utf8char)),
--- lowfirst = subscript * ( Cc("\\lohi{%s}{%s}") * V("content") * superscript + Cc("\\low{%s}" ) ) * V("content") / format,
--- highfirst = superscript * ( Cc("\\hilo{%s}{%s}") * V("content") * subscript + Cc("\\high{%s}") ) * V("content") / format,
--- }
-
-local scripts = P { "start",
- start = V("csname") + V("lowfirst") + V("highfirst"),
- csname = csname,
- content = Cs(V("csname") + nested + sign^-1 * (cardinal + utf8char)),
- lowfirst = (subscript /"") * ( Cc("\\lohi{") * V("content") * Cc("}{") * (superscript/"") + Cc("\\low{" ) ) * V("content") * Cc("}"),
- highfirst = (superscript/"") * ( Cc("\\hilo{") * V("content") * Cc("}{") * (subscript /"") + Cc("\\high{") ) * V("content") * Cc("}"),
- }
-
-local scripted = Cs((csname + scripts + utf8char)^0)
-
-cpatterns.scripts = scripts
-cpatterns.csname = csname
-cpatterns.scripted = scripted
-cpatterns.nested = nested
-
--- inspect(scripted)
--- print(lpegmatch(scripted,"10^-3_x"))
--- print(lpegmatch(scripted,"10^-a"))
-
+if not modules then modules = { } end modules ['cldf-bas'] = {
+ version = 1.001,
+ comment = "companion to cldf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local lpegmatch, patterns = lpeg.match, lpeg.patterns
+local P, R, V, Cc, Cs = lpeg.P, lpeg.R, lpeg.V, lpeg.Cc, lpeg.Cs
+local format = string.format
+
+local cpatterns = patterns.context or { }
+patterns.context = cpatterns
+
+local backslash = P("\\")
+local csname = backslash * P(1) * (1-backslash)^0
+local sign = P("+") / "\\textplus "
+ + P("-") / "\\textminus "
+local leftbrace = P("{")
+local rightbrace = P("}")
+local nested = P { leftbrace * (V(1) + (1-rightbrace))^0 * rightbrace }
+local subscript = P("_")
+local superscript = P("^")
+local utf8char = patterns.utf8char
+local cardinal = patterns.cardinal
+
+-- local scripts = P { "start",
+-- start = V("csname") + V("lowfirst") + V("highfirst"),
+-- csname = csname,
+-- content = Cs(V("csname") + nested + sign^-1 * (cardinal + utf8char)),
+-- lowfirst = subscript * ( Cc("\\lohi{%s}{%s}") * V("content") * superscript + Cc("\\low{%s}" ) ) * V("content") / format,
+-- highfirst = superscript * ( Cc("\\hilo{%s}{%s}") * V("content") * subscript + Cc("\\high{%s}") ) * V("content") / format,
+-- }
+
+local scripts = P { "start",
+ start = V("csname") + V("lowfirst") + V("highfirst"),
+ csname = csname,
+ content = Cs(V("csname") + nested + sign^-1 * (cardinal + utf8char)),
+ lowfirst = (subscript /"") * ( Cc("\\lohi{") * V("content") * Cc("}{") * (superscript/"") + Cc("\\low{" ) ) * V("content") * Cc("}"),
+ highfirst = (superscript/"") * ( Cc("\\hilo{") * V("content") * Cc("}{") * (subscript /"") + Cc("\\high{") ) * V("content") * Cc("}"),
+ }
+
+local scripted = Cs((csname + scripts + utf8char)^0)
+
+cpatterns.scripts = scripts
+cpatterns.csname = csname
+cpatterns.scripted = scripted
+cpatterns.nested = nested
+
+-- inspect(scripted)
+-- print(lpegmatch(scripted,"10^-3_x"))
+-- print(lpegmatch(scripted,"10^-a"))
+
diff --git a/tex/context/base/cldf-ver.lua b/tex/context/base/cldf-ver.lua
index 601c98e89..b48fd253a 100644
--- a/tex/context/base/cldf-ver.lua
+++ b/tex/context/base/cldf-ver.lua
@@ -1,75 +1,75 @@
-if not modules then modules = { } end modules ['cldf-ver'] = {
- version = 1.001,
- comment = "companion to cldf-ver.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- We have better verbatim: context.verbatim so that needs to be looked
--- into. We can also directly store in buffers although this variant works
--- better when used mixed with other code (synchronization issue).
-
-local concat, tohandle = table.concat, table.tohandle
-local find, splitlines = string.find, string.splitlines
-local tostring, type = tostring, type
-
-local context = context
-
-local function flush(...)
- context(concat{...,"\r"}) -- was \n
-end
-
-local function t_tocontext(...)
- context.starttyping { "typing" } -- else [1] is intercepted
- context.pushcatcodes("verbatim")
- tohandle(flush,...) -- ok?
- context.stoptyping()
- context.popcatcodes()
-end
-
-local function s_tocontext(...) -- we need to catch {\}
- context.type()
- context("{")
- context.pushcatcodes("verbatim")
- context(concat({...}," "))
- context.popcatcodes()
- context("}")
-end
-
-local function b_tocontext(b)
- s_tocontext(tostring(b))
-end
-
-table .tocontext = t_tocontext
-string .tocontext = s_tocontext
-boolean.tocontext = b_tocontext
-
-function context.tocontext(first,...)
- local t = type(first)
- if t == "string" then
- s_tocontext(first,...)
- elseif t == "table" then
- t_tocontext(first,...)
- elseif t == "boolean" then
- b_tocontext(first,...)
- end
-end
-
-function context.tobuffer(name,str)
- context.startbuffer { name }
- context.pushcatcodes("verbatim")
- local lines = (type(str) == "string" and find(str,"[\n\r]") and splitlines(str)) or str
- for i=1,#lines do
- context(lines[i] .. " ")
- end
- context.stopbuffer()
- context.popcatcodes()
-end
-
-function context.tolines(str)
- local lines = type(str) == "string" and splitlines(str) or str
- for i=1,#lines do
- context(lines[i] .. " ")
- end
-end
+if not modules then modules = { } end modules ['cldf-ver'] = {
+ version = 1.001,
+ comment = "companion to cldf-ver.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- We have better verbatim: context.verbatim so that needs to be looked
+-- into. We can also directly store in buffers although this variant works
+-- better when used mixed with other code (synchronization issue).
+
+local concat, tohandle = table.concat, table.tohandle
+local find, splitlines = string.find, string.splitlines
+local tostring, type = tostring, type
+
+local context = context
+
+local function flush(...)
+ context(concat{...,"\r"}) -- was \n
+end
+
+local function t_tocontext(...)
+ context.starttyping { "typing" } -- else [1] is intercepted
+ context.pushcatcodes("verbatim")
+ tohandle(flush,...) -- ok?
+ context.stoptyping()
+ context.popcatcodes()
+end
+
+local function s_tocontext(...) -- we need to catch {\}
+ context.type()
+ context("{")
+ context.pushcatcodes("verbatim")
+ context(concat({...}," "))
+ context.popcatcodes()
+ context("}")
+end
+
+local function b_tocontext(b)
+ s_tocontext(tostring(b))
+end
+
+table .tocontext = t_tocontext
+string .tocontext = s_tocontext
+boolean.tocontext = b_tocontext
+
+function context.tocontext(first,...)
+ local t = type(first)
+ if t == "string" then
+ s_tocontext(first,...)
+ elseif t == "table" then
+ t_tocontext(first,...)
+ elseif t == "boolean" then
+ b_tocontext(first,...)
+ end
+end
+
+function context.tobuffer(name,str)
+ context.startbuffer { name }
+ context.pushcatcodes("verbatim")
+ local lines = (type(str) == "string" and find(str,"[\n\r]") and splitlines(str)) or str
+ for i=1,#lines do
+ context(lines[i] .. " ")
+ end
+ context.stopbuffer()
+ context.popcatcodes()
+end
+
+function context.tolines(str)
+ local lines = type(str) == "string" and splitlines(str) or str
+ for i=1,#lines do
+ context(lines[i] .. " ")
+ end
+end
diff --git a/tex/context/base/colo-icc.lua b/tex/context/base/colo-icc.lua
index 7880e0778..f7ed561c1 100644
--- a/tex/context/base/colo-icc.lua
+++ b/tex/context/base/colo-icc.lua
@@ -1,120 +1,120 @@
-if not modules then modules = { } end modules ['colo-icc'] = {
- version = 1.000,
- comment = "companion to colo-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local char, byte, gsub, match, format, strip = string.char, string.byte, string.gsub, string.match, string.format, string.strip
-local readstring, readnumber = io.readstring, io.readnumber
-local formatters = string.formatters
-
-local colors = attributes and attributes.colors or { } -- when used in mtxrun
-
-local report_colors = logs.reporter("colors","icc")
-
-local R, Cs, lpegmatch = lpeg.R, lpeg.Cs, lpeg.match
-
-local invalid = R(char(0)..char(31))
-local cleaned = invalid^0 * Cs((1-invalid)^0)
-
-function colors.iccprofile(filename,verbose)
- local fullname = resolvers.findfile(filename,"icc") or ""
- if fullname == "" then
- local locate = resolvers.finders.byscheme -- not in mtxrun
- if locate then
- fullname = locate("loc",filename)
- end
- end
- if fullname == "" then
- report_colors("profile %a cannot be found",filename)
- return nil, false
- end
- local f = io.open(fullname,"rb")
- if not f then
- report_colors("profile %a cannot be loaded",fullname)
- return nil, false
- end
- local header = {
- size = readnumber(f,4),
- cmmtype = readnumber(f,4),
- version = readnumber(f,4),
- deviceclass = strip(readstring(f,4)),
- colorspace = strip(readstring(f,4)),
- connectionspace = strip(readstring(f,4)),
- datetime = {
- year = readnumber(f,2),
- month = readnumber(f,2),
- day = readnumber(f,2),
- hour = readnumber(f,2),
- minutes = readnumber(f,2),
- seconds = readnumber(f,2),
- },
- filesignature = strip(readstring(f,4)),
- platformsignature = strip(readstring(f,4)),
- options = readnumber(f,4),
- devicemanufacturer = strip(readstring(f,4)),
- devicemodel = strip(readstring(f,4)),
- deviceattributes = readnumber(f,4),
- renderingintent = readnumber(f,4),
- illuminantxyz = {
- x = readnumber(f,4),
- y = readnumber(f,4),
- z = readnumber(f,4),
- },
- profilecreator = readnumber(f,4),
- id = strip(readstring(f,16)),
- }
- local tags = { }
- for i=1,readnumber(f,128,4) do
- tags[readstring(f,4)] = {
- offset = readnumber(f,4),
- length = readnumber(f,4),
- }
- end
- local o = header.options
- header.options =
- o == 0 and "embedded" or
- o == 1 and "dependent" or "unknown"
- local d = header.deviceattributes
- header.deviceattributes = {
- [number.hasbit(d,1) and "transparency" or "reflective"] = true,
- [number.hasbit(d,2) and "mate" or "glossy" ] = true,
- [number.hasbit(d,3) and "negative" or "positive" ] = true,
- [number.hasbit(d,4) and "bw" or "color" ] = true,
- }
- local r = header.renderingintent
- header.renderingintent =
- r == 0 and "perceptual" or
- r == 1 and "relative" or
- r == 2 and "saturation" or
- r == 3 and "absolute" or "unknown"
- for tag, spec in next, tags do
- if tag then
- local offset, length = spec.offset, spec.length
- local variant = readstring(f,offset,4)
- if variant == "text" or variant == "desc" then
- local str = readstring(f,length-4)
- tags[tag] = {
- data = str,
- cleaned = lpegmatch(cleaned,str),
- }
- else
- if verbose then
- report_colors("ignoring tag %a or type %a in profile %a",tag,variant,fullname)
- end
- tags[tag] = nil
- end
- end
- end
- f:close()
- local profile = {
- filename = filename,
- fullname = fullname,
- header = header,
- tags = tags,
- }
- report_colors("profile %a loaded",fullname)
- return profile, true
-end
+if not modules then modules = { } end modules ['colo-icc'] = {
+ version = 1.000,
+ comment = "companion to colo-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local char, byte, gsub, match, format, strip = string.char, string.byte, string.gsub, string.match, string.format, string.strip
+local readstring, readnumber = io.readstring, io.readnumber
+local formatters = string.formatters
+
+local colors = attributes and attributes.colors or { } -- when used in mtxrun
+
+local report_colors = logs.reporter("colors","icc")
+
+local R, Cs, lpegmatch = lpeg.R, lpeg.Cs, lpeg.match
+
+local invalid = R(char(0)..char(31))
+local cleaned = invalid^0 * Cs((1-invalid)^0)
+
+function colors.iccprofile(filename,verbose)
+ local fullname = resolvers.findfile(filename,"icc") or ""
+ if fullname == "" then
+ local locate = resolvers.finders.byscheme -- not in mtxrun
+ if locate then
+ fullname = locate("loc",filename)
+ end
+ end
+ if fullname == "" then
+ report_colors("profile %a cannot be found",filename)
+ return nil, false
+ end
+ local f = io.open(fullname,"rb")
+ if not f then
+ report_colors("profile %a cannot be loaded",fullname)
+ return nil, false
+ end
+ local header = {
+ size = readnumber(f,4),
+ cmmtype = readnumber(f,4),
+ version = readnumber(f,4),
+ deviceclass = strip(readstring(f,4)),
+ colorspace = strip(readstring(f,4)),
+ connectionspace = strip(readstring(f,4)),
+ datetime = {
+ year = readnumber(f,2),
+ month = readnumber(f,2),
+ day = readnumber(f,2),
+ hour = readnumber(f,2),
+ minutes = readnumber(f,2),
+ seconds = readnumber(f,2),
+ },
+ filesignature = strip(readstring(f,4)),
+ platformsignature = strip(readstring(f,4)),
+ options = readnumber(f,4),
+ devicemanufacturer = strip(readstring(f,4)),
+ devicemodel = strip(readstring(f,4)),
+ deviceattributes = readnumber(f,4),
+ renderingintent = readnumber(f,4),
+ illuminantxyz = {
+ x = readnumber(f,4),
+ y = readnumber(f,4),
+ z = readnumber(f,4),
+ },
+ profilecreator = readnumber(f,4),
+ id = strip(readstring(f,16)),
+ }
+ local tags = { }
+ for i=1,readnumber(f,128,4) do
+ tags[readstring(f,4)] = {
+ offset = readnumber(f,4),
+ length = readnumber(f,4),
+ }
+ end
+ local o = header.options
+ header.options =
+ o == 0 and "embedded" or
+ o == 1 and "dependent" or "unknown"
+ local d = header.deviceattributes
+ header.deviceattributes = {
+ [number.hasbit(d,1) and "transparency" or "reflective"] = true,
+ [number.hasbit(d,2) and "mate" or "glossy" ] = true,
+ [number.hasbit(d,3) and "negative" or "positive" ] = true,
+ [number.hasbit(d,4) and "bw" or "color" ] = true,
+ }
+ local r = header.renderingintent
+ header.renderingintent =
+ r == 0 and "perceptual" or
+ r == 1 and "relative" or
+ r == 2 and "saturation" or
+ r == 3 and "absolute" or "unknown"
+ for tag, spec in next, tags do
+ if tag then
+ local offset, length = spec.offset, spec.length
+ local variant = readstring(f,offset,4)
+ if variant == "text" or variant == "desc" then
+ local str = readstring(f,length-4)
+ tags[tag] = {
+ data = str,
+ cleaned = lpegmatch(cleaned,str),
+ }
+ else
+ if verbose then
+ report_colors("ignoring tag %a or type %a in profile %a",tag,variant,fullname)
+ end
+ tags[tag] = nil
+ end
+ end
+ end
+ f:close()
+ local profile = {
+ filename = filename,
+ fullname = fullname,
+ header = header,
+ tags = tags,
+ }
+ report_colors("profile %a loaded",fullname)
+ return profile, true
+end
diff --git a/tex/context/base/colo-run.lua b/tex/context/base/colo-run.lua
index c7ff0b159..27f7c6b12 100644
--- a/tex/context/base/colo-run.lua
+++ b/tex/context/base/colo-run.lua
@@ -1,68 +1,68 @@
-if not modules then modules = { } end modules ['colo-run'] = {
- version = 1.000,
- comment = "companion to colo-run.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- For historic reasons the core has a couple of tracing features. Nowadays
--- these would end up in modules.
-
-local colors, commands, context, utilities = colors, commands, context, utilities
-
-local colors= attributes.colors
-
-function commands.showcolorset(name)
- local set = colors.setlist(name)
- context.starttabulate { "|l|l|l|l|l|l|l|" }
- for i=1,#set do
- local s = set[i]
- local r = { width = "4em", height = "max", depth = "max", color = s }
- context.NC()
- context.setcolormodel { "gray" }
- context.blackrule(r)
- context.NC()
- context.blackrule(r)
- context.NC()
- context.grayvalue(s)
- context.NC()
- context.colorvalue(s)
- context.NC()
- context(s)
- context.NC()
- context.NR()
- end
- context.stoptabulate()
-end
-
-function commands.showcolorcomponents(list)
- local set = utilities.parsers.settings_to_array(list)
- context.starttabulate { "|lT|lT|lT|lT|" }
- context.NC()
- context("color")
- context.NC()
- context("name")
- context.NC()
- context("transparency")
- context.NC()
- context("specification ")
- context.NC()
- context.NR()
- context.TB()
- for i=1,#set do
- local s = set[i]
- context.NC()
- context.showcolorbar { s }
- context.NC()
- context(s)
- context.NC()
- context.transparencycomponents(s)
- context.NC()
- context.colorcomponents(s)
- context.NC()
- context.NR()
- end
- context.stoptabulate()
-end
-
+if not modules then modules = { } end modules ['colo-run'] = {
+ version = 1.000,
+ comment = "companion to colo-run.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- For historic reasons the core has a couple of tracing features. Nowadays
+-- these would end up in modules.
+
+local colors, commands, context, utilities = colors, commands, context, utilities
+
+local colors= attributes.colors
+
+function commands.showcolorset(name)
+ local set = colors.setlist(name)
+ context.starttabulate { "|l|l|l|l|l|l|l|" }
+ for i=1,#set do
+ local s = set[i]
+ local r = { width = "4em", height = "max", depth = "max", color = s }
+ context.NC()
+ context.setcolormodel { "gray" }
+ context.blackrule(r)
+ context.NC()
+ context.blackrule(r)
+ context.NC()
+ context.grayvalue(s)
+ context.NC()
+ context.colorvalue(s)
+ context.NC()
+ context(s)
+ context.NC()
+ context.NR()
+ end
+ context.stoptabulate()
+end
+
+function commands.showcolorcomponents(list)
+ local set = utilities.parsers.settings_to_array(list)
+ context.starttabulate { "|lT|lT|lT|lT|" }
+ context.NC()
+ context("color")
+ context.NC()
+ context("name")
+ context.NC()
+ context("transparency")
+ context.NC()
+ context("specification ")
+ context.NC()
+ context.NR()
+ context.TB()
+ for i=1,#set do
+ local s = set[i]
+ context.NC()
+ context.showcolorbar { s }
+ context.NC()
+ context(s)
+ context.NC()
+ context.transparencycomponents(s)
+ context.NC()
+ context.colorcomponents(s)
+ context.NC()
+ context.NR()
+ end
+ context.stoptabulate()
+end
+
diff --git a/tex/context/base/cont-new.mkiv b/tex/context/base/cont-new.mkiv
index 098763903..ecf92987c 100644
--- a/tex/context/base/cont-new.mkiv
+++ b/tex/context/base/cont-new.mkiv
@@ -11,7 +11,7 @@
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.
-\newcontextversion{2013.05.19 19:27}
+\newcontextversion{2013.05.20 02:00}
%D This file is loaded at runtime, thereby providing an excellent place for
%D hacks, patches, extensions and new features.
diff --git a/tex/context/base/context-version.pdf b/tex/context/base/context-version.pdf
index fbe0f4a22..87a6493a9 100644
--- a/tex/context/base/context-version.pdf
+++ b/tex/context/base/context-version.pdf
Binary files differ
diff --git a/tex/context/base/context.mkiv b/tex/context/base/context.mkiv
index e9e0006bd..1ec2a4366 100644
--- a/tex/context/base/context.mkiv
+++ b/tex/context/base/context.mkiv
@@ -25,7 +25,7 @@
%D up and the dependencies are more consistent.
\edef\contextformat {\jobname}
-\edef\contextversion{2013.05.19 19:27}
+\edef\contextversion{2013.05.20 02:00}
\edef\contextkind {beta}
%D For those who want to use this:
diff --git a/tex/context/base/core-ctx.lua b/tex/context/base/core-ctx.lua
index d5cdc3143..18978a530 100644
--- a/tex/context/base/core-ctx.lua
+++ b/tex/context/base/core-ctx.lua
@@ -1,347 +1,347 @@
-if not modules then modules = { } end modules ['core-ctx'] = {
- version = 1.001,
- comment = "companion to core-ctx.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
---[[
-Job control files aka ctx files are rather old and date from the mkii times.
-They were handled in texexec and mtx-context and deals with modes, modules,
-environments and preprocessing in projects where one such file drives the
-processing of lots of files without the need to provide command line
-arguments.
-
-In mkiv this concept was of course supported as well. The first implementation
-of mtx-context took much of the approach of texexec, but by now we have gotten
-rid of the option file (for passing modes, modules and environments), the stubs
-(for directly processing cld and xml) as well as the preprocessing component
-of the ctx files. Special helper features, like typesetting listings, were
-already moved to the extras (a direct side effect of the ability to pass along
-command line arguments.) All this made mtx-context more simple than its ancestor
-texexec.
-
-Because some of the modes might affect the mtx-context end, the ctx file is
-still loaded there but only for getting the modes. The file is loaded again
-during the run but as loading and basic processing takes less than a
-millisecond it's not that much of a burden.
---]]
-
--- the ctxrunner tabel might either become private or move to the job namespace
--- which also affects the loading order
-
-local trace_prepfiles = false trackers.register("system.prepfiles", function(v) trace_prepfiles = v end)
-
-local gsub, find, match, validstring = string.gsub, string.find, string.match, string.valid
-local concat = table.concat
-local xmltext = xml.text
-
-local report_prepfiles = logs.reporter("system","prepfiles")
-
-commands = commands or { }
-local commands = commands
-
-ctxrunner = ctxrunner or { }
-
-ctxrunner.prepfiles = utilities.storage.allocate()
-
-local function dontpreparefile(t,k)
- return k -- we only store when we have a prepper
-end
-
-table.setmetatableindex(ctxrunner.prepfiles,dontpreparefile)
-
-local function filtered(str,method) -- in resolvers?
- str = tostring(str)
- if method == 'name' then str = file.nameonly(str)
- elseif method == 'path' then str = file.dirname(str)
- elseif method == 'suffix' then str = file.suffix(str)
- elseif method == 'nosuffix' then str = file.removesuffix(str)
- elseif method == 'nopath' then str = file.basename(str)
- elseif method == 'base' then str = file.basename(str)
--- elseif method == 'full' then
--- elseif method == 'complete' then
--- elseif method == 'expand' then -- str = file.expandpath(str)
- end
- return (gsub(str,"\\","/"))
-end
-
--- local function substitute(e,str)
--- local attributes = e.at
--- if str and attributes then
--- if attributes['method'] then
--- str = filtered(str,attributes['method'])
--- end
--- if str == "" and attributes['default'] then
--- str = attributes['default']
--- end
--- end
--- return str
--- end
-
-local function substitute(str)
- return str
-end
-
-local function justtext(str)
- str = xml.unescaped(tostring(str))
- str = xml.cleansed(str)
- str = gsub(str,"\\+",'/')
- str = gsub(str,"%s+",' ')
- return str
-end
-
-function ctxrunner.load(ctxname)
-
- local xmldata = xml.load(ctxname)
-
- local jobname = tex.jobname -- todo
-
- local variables = { job = jobname }
- local commands = { }
- local flags = { }
- local paths = { } -- todo
- local treatments = { }
- local suffix = "prep"
-
- xml.include(xmldata,'ctx:include','name', {'.', file.dirname(ctxname), "..", "../.." })
-
- for e in xml.collected(xmldata,"/ctx:job/ctx:flags/ctx:flag") do
- local flag = xmltext(e)
- local key, value = match(flag,"^(.-)=(.+)$")
- if key and value then
- environment.setargument(key,value)
- else
- environment.setargument(flag,true)
- end
- end
-
- -- add to document.options.ctxfile[...]
-
- local ctxfile = document.options.ctxfile
-
- local modes = ctxfile.modes
- local modules = ctxfile.modules
- local environments = ctxfile.environments
-
- for e in xml.collected(xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:mode") do
- modes[#modes+1] = xmltext(e)
- end
-
- for e in xml.collected(xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:module") do
- modules[#modules+1] = xmltext(e)
- end
-
- for e in xml.collected(xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:environment") do
- environments[#environments+1] = xmltext(e)
- end
-
- for e in xml.collected(xmldata,"ctx:message") do
- report_prepfiles("ctx comment: %s", xmltext(e))
- end
-
- for r, d, k in xml.elements(xmldata,"ctx:value[@name='job']") do
- d[k] = variables['job'] or ""
- end
-
- for e in xml.collected(xmldata,"/ctx:job/ctx:preprocess/ctx:processors/ctx:processor") do
- local name = e.at and e.at['name'] or "unknown"
- local suffix = e.at and e.at['suffix'] or "prep"
- for r, d, k in xml.elements(command,"ctx:old") do
- d[k] = "%old%"
- end
- for r, d, k in xml.elements(e,"ctx:new") do
- d[k] = "%new%"
- end
- for r, d, k in xml.elements(e,"ctx:value") do
- local tag = d[k].at['name']
- if tag then
- d[k] = "%" .. tag .. "%"
- end
- end
- local runner = xml.textonly(e)
- if runner and runner ~= "" then
- commands[name] = {
- suffix = suffix,
- runner = runner,
- }
- end
- end
-
- local suffix = xml.filter(xmldata,"xml:///ctx:job/ctx:preprocess/attribute('suffix')") or suffix
- local runlocal = xml.filter(xmldata,"xml:///ctx:job/ctx:preprocess/ctx:processors/attribute('local')")
-
- runlocal = toboolean(runlocal)
-
- -- todo: only collect, then plug into file handler
-
- local inputfile = validstring(environment.arguments.input) or jobname
-
- variables.old = inputfile
-
- for files in xml.collected(xmldata,"/ctx:job/ctx:preprocess/ctx:files") do
- for pattern in xml.collected(files,"ctx:file") do
- local preprocessor = pattern.at['processor'] or ""
- for r, d, k in xml.elements(pattern,"/ctx:old") do
- d[k] = jobname
- end
- for r, d, k in xml.elements(pattern,"/ctx:value[@name='old'") do
- d[k] = jobname
- end
- pattern =justtext(xml.tostring(pattern))
- if preprocessor and preprocessor ~= "" and pattern and pattern ~= "" then
- local noftreatments = #treatments + 1
- local findpattern = string.topattern(pattern)
- local preprocessors = utilities.parsers.settings_to_array(preprocessor)
- treatments[noftreatments] = {
- pattern = findpattern,
- preprocessors = preprocessors,
- }
- report_prepfiles("step %s, pattern %a, preprocessor: %a",noftreatments,findpattern,preprocessors)
- end
- end
- end
-
- local function needstreatment(oldfile)
- for i=1,#treatments do
- local treatment = treatments[i]
- local pattern = treatment.pattern
- if find(oldfile,pattern) then
- return treatment
- end
- end
- end
-
- local preparefile = #treatments > 0 and function(prepfiles,filename)
-
- local treatment = needstreatment(filename)
- local oldfile = filename
- local newfile = false
- if treatment then
- local preprocessors = treatment.preprocessors
- local runners = { }
- for i=1,#preprocessors do
- local preprocessor = preprocessors[i]
- local command = commands[preprocessor]
- if command then
- local runner = command.runner
- local suffix = command.suffix
- local result = filename .. "." .. suffix
- if runlocal then
- result = file.basename(result)
- end
- variables.old = oldfile
- variables.new = result
- runner = utilities.templates.replace(runner,variables)
- if runner and runner ~= "" then
- runners[#runners+1] = runner
- oldfile = result
- if runlocal then
- oldfile = file.basename(oldfile)
- end
- newfile = oldfile
- end
- end
- end
- if not newfile then
- newfile = filename
- elseif file.needsupdating(filename,newfile) then
- for i=1,#runners do
- report_prepfiles("step %i: %s",i,runners[i])
- end
- --
- for i=1,#runners do
- local command = runners[i]
- report_prepfiles("command: %s",command)
- local result = os.spawn(command) or 0
- -- if result > 0 then
- -- report_prepfiles("error, return code: %s",result)
- -- end
- end
- if lfs.isfile(newfile) then
- file.syncmtimes(filename,newfile)
- report_prepfiles("%a is converted to %a",filename,newfile)
- else
- report_prepfiles("%a is not converted to %a",filename,newfile)
- newfile = filename
- end
- elseif lfs.isfile(newfile) then
- report_prepfiles("%a is already converted to %a",filename,newfile)
- end
- else
- newfile = filename
- end
- prepfiles[filename] = newfile
- -- in case we ask twice (with the prepped name) ... todo: avoid this mess
- prepfiles[newfile] = newfile
- return newfile
- end
-
- table.setmetatableindex(ctxrunner.prepfiles,preparefile or dontpreparefile)
-
- -- we need to deal with the input filename as it has already be resolved
-
-end
-
--- print("\n")
--- document = {
--- options = {
--- ctxfile = {
--- modes = { },
--- modules = { },
--- environments = { },
--- }
--- }
--- }
--- environment.arguments.input = "test.tex"
--- ctxrunner.load("x-ldx.ctx")
-
-local function resolve(name) -- used a few times later on
- return ctxrunner.prepfiles[file.collapsepath(name)] or false
-end
-
-local processfile = commands.processfile
-local doifinputfileelse = commands.doifinputfileelse
-
-function commands.processfile(name,maxreadlevel) -- overloaded
- local prepname = resolve(name)
- if prepname then
- return processfile(prepname,0)
- end
- return processfile(name,maxreadlevel)
-end
-
-function commands.doifinputfileelse(name,depth)
- local prepname = resolve(name)
- if prepname then
- return doifinputfileelse(prepname,0)
- end
- return doifinputfileelse(name,depth)
-end
-
-function commands.preparedfile(name)
- return resolve(name) or name
-end
-
-function commands.getctxfile()
- local ctxfile = document.arguments.ctx or ""
- if ctxfile ~= "" then
- ctxrunner.load(ctxfile) -- do we need to locate it?
- end
-end
-
-function ctxrunner.resolve(name) -- used a few times later on
- local collapsedname = file.collapsepath(name,".")
- return ctxrunner.prepfiles[collapsedname] or collapsedname
-end
-
--- ctxrunner.load("t:/sources/core-ctx.ctx")
-
--- context(ctxrunner.prepfiles["one-a.xml"]) context.par()
--- context(ctxrunner.prepfiles["one-b.xml"]) context.par()
--- context(ctxrunner.prepfiles["two-c.xml"]) context.par()
--- context(ctxrunner.prepfiles["two-d.xml"]) context.par()
--- context(ctxrunner.prepfiles["all-x.xml"]) context.par()
-
--- inspect(ctxrunner.prepfiles)
+if not modules then modules = { } end modules ['core-ctx'] = {
+ version = 1.001,
+ comment = "companion to core-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[
+Job control files aka ctx files are rather old and date from the mkii times.
+They were handled in texexec and mtx-context and deals with modes, modules,
+environments and preprocessing in projects where one such file drives the
+processing of lots of files without the need to provide command line
+arguments.
+
+In mkiv this concept was of course supported as well. The first implementation
+of mtx-context took much of the approach of texexec, but by now we have gotten
+rid of the option file (for passing modes, modules and environments), the stubs
+(for directly processing cld and xml) as well as the preprocessing component
+of the ctx files. Special helper features, like typesetting listings, were
+already moved to the extras (a direct side effect of the ability to pass along
+command line arguments.) All this made mtx-context more simple than its ancestor
+texexec.
+
+Because some of the modes might affect the mtx-context end, the ctx file is
+still loaded there but only for getting the modes. The file is loaded again
+during the run but as loading and basic processing takes less than a
+millisecond it's not that much of a burden.
+--]]
+
+-- the ctxrunner tabel might either become private or move to the job namespace
+-- which also affects the loading order
+
+local trace_prepfiles = false trackers.register("system.prepfiles", function(v) trace_prepfiles = v end)
+
+local gsub, find, match, validstring = string.gsub, string.find, string.match, string.valid
+local concat = table.concat
+local xmltext = xml.text
+
+local report_prepfiles = logs.reporter("system","prepfiles")
+
+commands = commands or { }
+local commands = commands
+
+ctxrunner = ctxrunner or { }
+
+ctxrunner.prepfiles = utilities.storage.allocate()
+
+local function dontpreparefile(t,k)
+ return k -- we only store when we have a prepper
+end
+
+table.setmetatableindex(ctxrunner.prepfiles,dontpreparefile)
+
+local function filtered(str,method) -- in resolvers?
+ str = tostring(str)
+ if method == 'name' then str = file.nameonly(str)
+ elseif method == 'path' then str = file.dirname(str)
+ elseif method == 'suffix' then str = file.suffix(str)
+ elseif method == 'nosuffix' then str = file.removesuffix(str)
+ elseif method == 'nopath' then str = file.basename(str)
+ elseif method == 'base' then str = file.basename(str)
+-- elseif method == 'full' then
+-- elseif method == 'complete' then
+-- elseif method == 'expand' then -- str = file.expandpath(str)
+ end
+ return (gsub(str,"\\","/"))
+end
+
+-- local function substitute(e,str)
+-- local attributes = e.at
+-- if str and attributes then
+-- if attributes['method'] then
+-- str = filtered(str,attributes['method'])
+-- end
+-- if str == "" and attributes['default'] then
+-- str = attributes['default']
+-- end
+-- end
+-- return str
+-- end
+
+local function substitute(str)
+ return str
+end
+
+local function justtext(str)
+ str = xml.unescaped(tostring(str))
+ str = xml.cleansed(str)
+ str = gsub(str,"\\+",'/')
+ str = gsub(str,"%s+",' ')
+ return str
+end
+
+function ctxrunner.load(ctxname)
+
+ local xmldata = xml.load(ctxname)
+
+ local jobname = tex.jobname -- todo
+
+ local variables = { job = jobname }
+ local commands = { }
+ local flags = { }
+ local paths = { } -- todo
+ local treatments = { }
+ local suffix = "prep"
+
+ xml.include(xmldata,'ctx:include','name', {'.', file.dirname(ctxname), "..", "../.." })
+
+ for e in xml.collected(xmldata,"/ctx:job/ctx:flags/ctx:flag") do
+ local flag = xmltext(e)
+ local key, value = match(flag,"^(.-)=(.+)$")
+ if key and value then
+ environment.setargument(key,value)
+ else
+ environment.setargument(flag,true)
+ end
+ end
+
+ -- add to document.options.ctxfile[...]
+
+ local ctxfile = document.options.ctxfile
+
+ local modes = ctxfile.modes
+ local modules = ctxfile.modules
+ local environments = ctxfile.environments
+
+ for e in xml.collected(xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:mode") do
+ modes[#modes+1] = xmltext(e)
+ end
+
+ for e in xml.collected(xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:module") do
+ modules[#modules+1] = xmltext(e)
+ end
+
+ for e in xml.collected(xmldata,"/ctx:job/ctx:process/ctx:resources/ctx:environment") do
+ environments[#environments+1] = xmltext(e)
+ end
+
+ for e in xml.collected(xmldata,"ctx:message") do
+ report_prepfiles("ctx comment: %s", xmltext(e))
+ end
+
+ for r, d, k in xml.elements(xmldata,"ctx:value[@name='job']") do
+ d[k] = variables['job'] or ""
+ end
+
+ for e in xml.collected(xmldata,"/ctx:job/ctx:preprocess/ctx:processors/ctx:processor") do
+ local name = e.at and e.at['name'] or "unknown"
+ local suffix = e.at and e.at['suffix'] or "prep"
+ for r, d, k in xml.elements(command,"ctx:old") do
+ d[k] = "%old%"
+ end
+ for r, d, k in xml.elements(e,"ctx:new") do
+ d[k] = "%new%"
+ end
+ for r, d, k in xml.elements(e,"ctx:value") do
+ local tag = d[k].at['name']
+ if tag then
+ d[k] = "%" .. tag .. "%"
+ end
+ end
+ local runner = xml.textonly(e)
+ if runner and runner ~= "" then
+ commands[name] = {
+ suffix = suffix,
+ runner = runner,
+ }
+ end
+ end
+
+ local suffix = xml.filter(xmldata,"xml:///ctx:job/ctx:preprocess/attribute('suffix')") or suffix
+ local runlocal = xml.filter(xmldata,"xml:///ctx:job/ctx:preprocess/ctx:processors/attribute('local')")
+
+ runlocal = toboolean(runlocal)
+
+ -- todo: only collect, then plug into file handler
+
+ local inputfile = validstring(environment.arguments.input) or jobname
+
+ variables.old = inputfile
+
+ for files in xml.collected(xmldata,"/ctx:job/ctx:preprocess/ctx:files") do
+ for pattern in xml.collected(files,"ctx:file") do
+ local preprocessor = pattern.at['processor'] or ""
+ for r, d, k in xml.elements(pattern,"/ctx:old") do
+ d[k] = jobname
+ end
+ for r, d, k in xml.elements(pattern,"/ctx:value[@name='old'") do
+ d[k] = jobname
+ end
+ pattern =justtext(xml.tostring(pattern))
+ if preprocessor and preprocessor ~= "" and pattern and pattern ~= "" then
+ local noftreatments = #treatments + 1
+ local findpattern = string.topattern(pattern)
+ local preprocessors = utilities.parsers.settings_to_array(preprocessor)
+ treatments[noftreatments] = {
+ pattern = findpattern,
+ preprocessors = preprocessors,
+ }
+ report_prepfiles("step %s, pattern %a, preprocessor: %a",noftreatments,findpattern,preprocessors)
+ end
+ end
+ end
+
+ local function needstreatment(oldfile)
+ for i=1,#treatments do
+ local treatment = treatments[i]
+ local pattern = treatment.pattern
+ if find(oldfile,pattern) then
+ return treatment
+ end
+ end
+ end
+
+ local preparefile = #treatments > 0 and function(prepfiles,filename)
+
+ local treatment = needstreatment(filename)
+ local oldfile = filename
+ local newfile = false
+ if treatment then
+ local preprocessors = treatment.preprocessors
+ local runners = { }
+ for i=1,#preprocessors do
+ local preprocessor = preprocessors[i]
+ local command = commands[preprocessor]
+ if command then
+ local runner = command.runner
+ local suffix = command.suffix
+ local result = filename .. "." .. suffix
+ if runlocal then
+ result = file.basename(result)
+ end
+ variables.old = oldfile
+ variables.new = result
+ runner = utilities.templates.replace(runner,variables)
+ if runner and runner ~= "" then
+ runners[#runners+1] = runner
+ oldfile = result
+ if runlocal then
+ oldfile = file.basename(oldfile)
+ end
+ newfile = oldfile
+ end
+ end
+ end
+ if not newfile then
+ newfile = filename
+ elseif file.needsupdating(filename,newfile) then
+ for i=1,#runners do
+ report_prepfiles("step %i: %s",i,runners[i])
+ end
+ --
+ for i=1,#runners do
+ local command = runners[i]
+ report_prepfiles("command: %s",command)
+ local result = os.spawn(command) or 0
+ -- if result > 0 then
+ -- report_prepfiles("error, return code: %s",result)
+ -- end
+ end
+ if lfs.isfile(newfile) then
+ file.syncmtimes(filename,newfile)
+ report_prepfiles("%a is converted to %a",filename,newfile)
+ else
+ report_prepfiles("%a is not converted to %a",filename,newfile)
+ newfile = filename
+ end
+ elseif lfs.isfile(newfile) then
+ report_prepfiles("%a is already converted to %a",filename,newfile)
+ end
+ else
+ newfile = filename
+ end
+ prepfiles[filename] = newfile
+ -- in case we ask twice (with the prepped name) ... todo: avoid this mess
+ prepfiles[newfile] = newfile
+ return newfile
+ end
+
+ table.setmetatableindex(ctxrunner.prepfiles,preparefile or dontpreparefile)
+
+ -- we need to deal with the input filename as it has already be resolved
+
+end
+
+-- print("\n")
+-- document = {
+-- options = {
+-- ctxfile = {
+-- modes = { },
+-- modules = { },
+-- environments = { },
+-- }
+-- }
+-- }
+-- environment.arguments.input = "test.tex"
+-- ctxrunner.load("x-ldx.ctx")
+
+local function resolve(name) -- used a few times later on
+ return ctxrunner.prepfiles[file.collapsepath(name)] or false
+end
+
+local processfile = commands.processfile
+local doifinputfileelse = commands.doifinputfileelse
+
+function commands.processfile(name,maxreadlevel) -- overloaded
+ local prepname = resolve(name)
+ if prepname then
+ return processfile(prepname,0)
+ end
+ return processfile(name,maxreadlevel)
+end
+
+function commands.doifinputfileelse(name,depth)
+ local prepname = resolve(name)
+ if prepname then
+ return doifinputfileelse(prepname,0)
+ end
+ return doifinputfileelse(name,depth)
+end
+
+function commands.preparedfile(name)
+ return resolve(name) or name
+end
+
+function commands.getctxfile()
+ local ctxfile = document.arguments.ctx or ""
+ if ctxfile ~= "" then
+ ctxrunner.load(ctxfile) -- do we need to locate it?
+ end
+end
+
+function ctxrunner.resolve(name) -- used a few times later on
+ local collapsedname = file.collapsepath(name,".")
+ return ctxrunner.prepfiles[collapsedname] or collapsedname
+end
+
+-- ctxrunner.load("t:/sources/core-ctx.ctx")
+
+-- context(ctxrunner.prepfiles["one-a.xml"]) context.par()
+-- context(ctxrunner.prepfiles["one-b.xml"]) context.par()
+-- context(ctxrunner.prepfiles["two-c.xml"]) context.par()
+-- context(ctxrunner.prepfiles["two-d.xml"]) context.par()
+-- context(ctxrunner.prepfiles["all-x.xml"]) context.par()
+
+-- inspect(ctxrunner.prepfiles)
diff --git a/tex/context/base/core-dat.lua b/tex/context/base/core-dat.lua
index 80e0f60f7..826d3a675 100644
--- a/tex/context/base/core-dat.lua
+++ b/tex/context/base/core-dat.lua
@@ -1,269 +1,269 @@
-if not modules then modules = { } end modules ['core-dat'] = {
- version = 1.001,
- comment = "companion to core-dat.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
---[[ldx--
-<p>This module provides a (multipass) container for arbitrary data. It
-replaces the twopass data mechanism.</p>
---ldx]]--
-
-local tonumber, tostring, type = tonumber, tostring, type
-
-local context, commands = context, commands
-
-local trace_datasets = false trackers.register("job.datasets" , function(v) trace_datasets = v end)
-local trace_pagestates = false trackers.register("job.pagestates", function(v) trace_pagestates = v end)
-
-local report_dataset = logs.reporter("dataset")
-local report_pagestate = logs.reporter("pagestate")
-
-local allocate = utilities.storage.allocate
-local settings_to_hash = utilities.parsers.settings_to_hash
-local texcount = tex.count
-local formatters = string.formatters
-local v_yes = interfaces.variables.yes
-
-local new_latelua = nodes.pool.latelua
-
-local collected = allocate()
-local tobesaved = allocate()
-
-local datasets = {
- collected = collected,
- tobesaved = tobesaved,
-}
-
-job.datasets = datasets
-
-local function initializer()
- collected = datasets.collected
- tobesaved = datasets.tobesaved
-end
-
-job.register('job.datasets.collected', tobesaved, initializer, nil)
-
-local sets = { }
-
-table.setmetatableindex(tobesaved, function(t,k)
- local v = { }
- t[k] = v
- return v
-end)
-
-table.setmetatableindex(sets, function(t,k)
- local v = {
- index = 0,
- order = 0,
- }
- t[k] = v
- return v
-end)
-
-local function setdata(settings)
- local name = settings.name
- local tag = settings.tag
- local data = settings.data
- local list = tobesaved[name]
- if settings.convert and type(data) == "string" then
- data = settings_to_hash(data)
- end
- if type(data) ~= "table" then
- data = { data = settings.data }
- end
- if not tag then
- tag = #list + 1
- else
- tag = tonumber(tag) or tag -- autonumber saves keys
- end
- list[tag] = data
- if settings.delay == v_yes then
- local set = sets[name]
- local index = set.index + 1
- set.index = index
- data.index = index
- data.order = index
- data.realpage = texcount.realpageno
- if trace_datasets then
- report_dataset("action %a, name %a, tag %a, index %a","assign delayed",name,tag,index)
- end
- elseif trace_datasets then
- report_dataset("action %a, name %a, tag %a","assign immediate",name,tag)
- end
- return name, tag, data
-end
-
-datasets.setdata = setdata
-
-function datasets.extend(name,tag)
- local set = sets[name]
- local order = set.order + 1
- local realpage = texcount.realpageno
- set.order = order
- local t = tobesaved[name][tag]
- t.realpage = realpage
- t.order = order
- if trace_datasets then
- report_dataset("action %a, name %a, tag %a, page %a, index %a","flush by order",name,tag,t.index or 0,order,realpage)
- end
-end
-
-function datasets.getdata(name,tag,key,default)
- local t = collected[name]
- if t == nil then
- if trace_datasets then
- report_dataset("error: unknown dataset, name %a",name)
- end
- elseif type(t) ~= "table" then
- return t
- else
- t = t[tag] or t[tonumber(tag)]
- if not t then
- if trace_datasets then
- report_dataset("error: unknown dataset, name %a, tag %a",name,tag)
- end
- elseif key then
- return t[key] or default
- else
- return t
- end
- end
- return default
-end
-
-function commands.setdataset(settings)
- settings.convert = true
- local name, tag = setdata(settings)
- if settings.delay ~= v_yes then
- --
- elseif type(tag) == "number" then
- context(new_latelua(formatters["job.datasets.extend(%q,%i)"](name,tag)))
- else
- context(new_latelua(formatters["job.datasets.extend(%q,%q)"](name,tag)))
- end
-end
-
-function commands.datasetvariable(name,tag,key)
- local t = collected[name]
- if t == nil then
- if trace_datasets then
- report_dataset("error: unknown dataset, name %a, tag %a, not passed to tex",name) -- no tag
- end
- elseif type(t) ~= "table" then
- context(tostring(t))
- else
- t = t and (t[tag] or t[tonumber(tag)])
- if not t then
- if trace_datasets then
- report_dataset("error: unknown dataset, name %a, tag %a, not passed to tex",name,tag)
- end
- elseif type(t) == "table" then
- local s = t[key]
- if type(s) ~= "table" then
- context(tostring(s))
- elseif trace_datasets then
- report_dataset("error: unknown dataset, name %a, tag %a, not passed to tex",name,tag)
- end
- end
- end
-end
-
---[[ldx--
-<p>We also provide an efficient variant for page states.</p>
---ldx]]--
-
-local collected = allocate()
-local tobesaved = allocate()
-
-local pagestates = {
- collected = collected,
- tobesaved = tobesaved,
-}
-
-job.pagestates = pagestates
-
-local function initializer()
- collected = pagestates.collected
- tobesaved = pagestates.tobesaved
-end
-
-job.register('job.pagestates.collected', tobesaved, initializer, nil)
-
-table.setmetatableindex(tobesaved, function(t,k)
- local v = { }
- t[k] = v
- return v
-end)
-
-local function setstate(settings)
- local name = settings.name
- local tag = settings.tag
- local list = tobesaved[name]
- if not tag then
- tag = #list + 1
- else
- tag = tonumber(tag) or tag -- autonumber saves keys
- end
- local realpage = texcount.realpageno
- local data = realpage
- list[tag] = data
- if trace_pagestates then
- report_pagestate("action %a, name %a, tag %a, preset %a","set",name,tag,realpage)
- end
- return name, tag, data
-end
-
-pagestates.setstate = setstate
-
-function pagestates.extend(name,tag)
- local realpage = texcount.realpageno
- if trace_pagestates then
- report_pagestate("action %a, name %a, tag %a, preset %a","synchronize",name,tag,realpage)
- end
- tobesaved[name][tag] = realpage
-end
-
-function pagestates.realpage(name,tag,default)
- local t = collected[name]
- if t then
- t = t[tag] or t[tonumber(tag)]
- if t then
- return tonumber(t or default)
- elseif trace_pagestates then
- report_pagestate("error: unknown dataset, name %a, tag %a",name,tag)
- end
- elseif trace_pagestates then
- report_pagestate("error: unknown dataset, name %a, tag %a",name) -- nil
- end
- return default
-end
-
-function commands.setpagestate(settings)
- local name, tag, data = setstate(settings)
- if type(tag) == "number" then
- context(new_latelua(formatters["job.pagestates.extend(%q,%i)"](name,tag)))
- else
- context(new_latelua(formatters["job.pagestates.extend(%q,%q)"](name,tag)))
- end
-end
-
-function commands.pagestaterealpage(name,tag)
- local t = collected[name]
- t = t and (t[tag] or t[tonumber(tag)])
- if t then
- context(t)
- end
-end
-
-function commands.setpagestaterealpageno(name,tag)
- local t = collected[name]
- t = t and (t[tag] or t[tonumber(tag)])
- if t then
- texcount.realpagestateno = t
- else
- texcount.realpagestateno = texcount.realpageno
- end
-end
+if not modules then modules = { } end modules ['core-dat'] = {
+ version = 1.001,
+ comment = "companion to core-dat.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[ldx--
+<p>This module provides a (multipass) container for arbitrary data. It
+replaces the twopass data mechanism.</p>
+--ldx]]--
+
+local tonumber, tostring, type = tonumber, tostring, type
+
+local context, commands = context, commands
+
+local trace_datasets = false trackers.register("job.datasets" , function(v) trace_datasets = v end)
+local trace_pagestates = false trackers.register("job.pagestates", function(v) trace_pagestates = v end)
+
+local report_dataset = logs.reporter("dataset")
+local report_pagestate = logs.reporter("pagestate")
+
+local allocate = utilities.storage.allocate
+local settings_to_hash = utilities.parsers.settings_to_hash
+local texcount = tex.count
+local formatters = string.formatters
+local v_yes = interfaces.variables.yes
+
+local new_latelua = nodes.pool.latelua
+
+local collected = allocate()
+local tobesaved = allocate()
+
+local datasets = {
+ collected = collected,
+ tobesaved = tobesaved,
+}
+
+job.datasets = datasets
+
+local function initializer()
+ collected = datasets.collected
+ tobesaved = datasets.tobesaved
+end
+
+job.register('job.datasets.collected', tobesaved, initializer, nil)
+
+local sets = { }
+
+table.setmetatableindex(tobesaved, function(t,k)
+ local v = { }
+ t[k] = v
+ return v
+end)
+
+table.setmetatableindex(sets, function(t,k)
+ local v = {
+ index = 0,
+ order = 0,
+ }
+ t[k] = v
+ return v
+end)
+
+local function setdata(settings)
+ local name = settings.name
+ local tag = settings.tag
+ local data = settings.data
+ local list = tobesaved[name]
+ if settings.convert and type(data) == "string" then
+ data = settings_to_hash(data)
+ end
+ if type(data) ~= "table" then
+ data = { data = settings.data }
+ end
+ if not tag then
+ tag = #list + 1
+ else
+ tag = tonumber(tag) or tag -- autonumber saves keys
+ end
+ list[tag] = data
+ if settings.delay == v_yes then
+ local set = sets[name]
+ local index = set.index + 1
+ set.index = index
+ data.index = index
+ data.order = index
+ data.realpage = texcount.realpageno
+ if trace_datasets then
+ report_dataset("action %a, name %a, tag %a, index %a","assign delayed",name,tag,index)
+ end
+ elseif trace_datasets then
+ report_dataset("action %a, name %a, tag %a","assign immediate",name,tag)
+ end
+ return name, tag, data
+end
+
+datasets.setdata = setdata
+
+function datasets.extend(name,tag)
+ local set = sets[name]
+ local order = set.order + 1
+ local realpage = texcount.realpageno
+ set.order = order
+ local t = tobesaved[name][tag]
+ t.realpage = realpage
+ t.order = order
+ if trace_datasets then
+ report_dataset("action %a, name %a, tag %a, page %a, index %a","flush by order",name,tag,t.index or 0,order,realpage)
+ end
+end
+
+function datasets.getdata(name,tag,key,default)
+ local t = collected[name]
+ if t == nil then
+ if trace_datasets then
+ report_dataset("error: unknown dataset, name %a",name)
+ end
+ elseif type(t) ~= "table" then
+ return t
+ else
+ t = t[tag] or t[tonumber(tag)]
+ if not t then
+ if trace_datasets then
+ report_dataset("error: unknown dataset, name %a, tag %a",name,tag)
+ end
+ elseif key then
+ return t[key] or default
+ else
+ return t
+ end
+ end
+ return default
+end
+
+function commands.setdataset(settings)
+ settings.convert = true
+ local name, tag = setdata(settings)
+ if settings.delay ~= v_yes then
+ --
+ elseif type(tag) == "number" then
+ context(new_latelua(formatters["job.datasets.extend(%q,%i)"](name,tag)))
+ else
+ context(new_latelua(formatters["job.datasets.extend(%q,%q)"](name,tag)))
+ end
+end
+
+function commands.datasetvariable(name,tag,key)
+ local t = collected[name]
+ if t == nil then
+ if trace_datasets then
+ report_dataset("error: unknown dataset, name %a, tag %a, not passed to tex",name) -- no tag
+ end
+ elseif type(t) ~= "table" then
+ context(tostring(t))
+ else
+ t = t and (t[tag] or t[tonumber(tag)])
+ if not t then
+ if trace_datasets then
+ report_dataset("error: unknown dataset, name %a, tag %a, not passed to tex",name,tag)
+ end
+ elseif type(t) == "table" then
+ local s = t[key]
+ if type(s) ~= "table" then
+ context(tostring(s))
+ elseif trace_datasets then
+ report_dataset("error: unknown dataset, name %a, tag %a, not passed to tex",name,tag)
+ end
+ end
+ end
+end
+
+--[[ldx--
+<p>We also provide an efficient variant for page states.</p>
+--ldx]]--
+
+local collected = allocate()
+local tobesaved = allocate()
+
+local pagestates = {
+ collected = collected,
+ tobesaved = tobesaved,
+}
+
+job.pagestates = pagestates
+
+local function initializer()
+ collected = pagestates.collected
+ tobesaved = pagestates.tobesaved
+end
+
+job.register('job.pagestates.collected', tobesaved, initializer, nil)
+
+table.setmetatableindex(tobesaved, function(t,k)
+ local v = { }
+ t[k] = v
+ return v
+end)
+
+local function setstate(settings)
+ local name = settings.name
+ local tag = settings.tag
+ local list = tobesaved[name]
+ if not tag then
+ tag = #list + 1
+ else
+ tag = tonumber(tag) or tag -- autonumber saves keys
+ end
+ local realpage = texcount.realpageno
+ local data = realpage
+ list[tag] = data
+ if trace_pagestates then
+ report_pagestate("action %a, name %a, tag %a, preset %a","set",name,tag,realpage)
+ end
+ return name, tag, data
+end
+
+pagestates.setstate = setstate
+
+function pagestates.extend(name,tag)
+ local realpage = texcount.realpageno
+ if trace_pagestates then
+ report_pagestate("action %a, name %a, tag %a, preset %a","synchronize",name,tag,realpage)
+ end
+ tobesaved[name][tag] = realpage
+end
+
+function pagestates.realpage(name,tag,default)
+ local t = collected[name]
+ if t then
+ t = t[tag] or t[tonumber(tag)]
+ if t then
+ return tonumber(t or default)
+ elseif trace_pagestates then
+ report_pagestate("error: unknown dataset, name %a, tag %a",name,tag)
+ end
+ elseif trace_pagestates then
+ report_pagestate("error: unknown dataset, name %a, tag %a",name) -- nil
+ end
+ return default
+end
+
+function commands.setpagestate(settings)
+ local name, tag, data = setstate(settings)
+ if type(tag) == "number" then
+ context(new_latelua(formatters["job.pagestates.extend(%q,%i)"](name,tag)))
+ else
+ context(new_latelua(formatters["job.pagestates.extend(%q,%q)"](name,tag)))
+ end
+end
+
+function commands.pagestaterealpage(name,tag)
+ local t = collected[name]
+ t = t and (t[tag] or t[tonumber(tag)])
+ if t then
+ context(t)
+ end
+end
+
+function commands.setpagestaterealpageno(name,tag)
+ local t = collected[name]
+ t = t and (t[tag] or t[tonumber(tag)])
+ if t then
+ texcount.realpagestateno = t
+ else
+ texcount.realpagestateno = texcount.realpageno
+ end
+end
diff --git a/tex/context/base/core-env.lua b/tex/context/base/core-env.lua
index c85a5e144..025192d4b 100644
--- a/tex/context/base/core-env.lua
+++ b/tex/context/base/core-env.lua
@@ -1,154 +1,154 @@
-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 P, C, S, Cc, lpegmatch, patterns = lpeg.P, lpeg.C, lpeg.S, lpeg.Cc, lpeg.match, lpeg.patterns
-
-local csname_id = token.csname_id
-local create = token.create
-local texcount = tex.count
-local texsetcount = tex.setcount
-
-local allocate = utilities.storage.allocate
-local setmetatableindex = table.setmetatableindex
-
-local undefined = csname_id("*undefined*crap*")
-local iftrue = create("iftrue")[2] -- inefficient hack
-
-tex.modes = allocate { }
-tex.systemmodes = allocate { }
-tex.constants = allocate { }
-tex.conditionals = allocate { }
-tex.ifs = allocate { }
-
-local modes = { }
-local systemmodes = { }
-
-setmetatableindex(tex.modes, function(t,k)
- local m = modes[k]
- if m then
- return m()
- else
- local n = "mode>" .. k
- if csname_id(n) == undefined then
- return false
- else
- modes[k] = function() return texcount[n] >= 1 end
- return texcount[n] >= 1
- end
- end
-end)
-
-setmetatableindex(tex.systemmodes, function(t,k)
- local m = systemmodes[k]
- if m then
- return m()
- else
- local n = "mode>*" .. k
- if csname_id(n) == undefined then
- return false
- else
- systemmodes[k] = function() return texcount[n] >= 1 end
- return texcount[n] >= 1
- end
- end
-end)
-
-setmetatableindex(tex.constants, function(t,k)
- return csname_id(k) ~= undefined and texcount[k] or 0
-end)
-
-setmetatableindex(tex.conditionals, function(t,k) -- 0 == true
- return csname_id(k) ~= undefined and texcount[k] == 0
-end)
-
-setmetatableindex(tex.ifs, function(t,k)
- -- k = "if" .. k -- better not
- return csname_id(k) ~= undefined and create(k)[2] == iftrue -- inefficient, this create, we need a helper
-end)
-
--- todo : global
-
--- not possible as we let at the tex end to zerocount and plusone
---
--- function tex.settrue(name,glob)
--- if glob then
--- texsetcount("global",name,0)
--- else
--- texcount[name] = 0
--- end
--- end
---
--- function tex.setfalse(name,glob)
--- if glob then
--- texsetcount("global",name,1)
--- else
--- texcount[name] = 1
--- end
--- end
-
----- arg = P("{") * C(patterns.nested) * P("}") + Cc("")
-
-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 / context.luasetup
- + P("xml") * arg / context.setupwithargument -- or xmlw as xmlsetup has swapped arguments
- + (P("tex") * tag + str) / context.texsetup
- + sep^1
-)^1
-
-function commands.autosetups(str)
- lpegmatch(pattern,str)
-end
-
--- new (inefficient)
-
-local lookuptoken = token.lookup
-
-local dimencode = lookuptoken("scratchdimen" )[1]
-local countcode = lookuptoken("scratchcounter")[1]
-local tokencode = lookuptoken("scratchtoken" )[1]
-local skipcode = lookuptoken("scratchskip" )[1]
-
-local types = {
- [dimencode] = "dimen",
- [countcode] = "count",
- [tokencode] = "token",
- [skipcode ] = "skip",
-}
-
-function tex.isdimen(name)
- return lookuptoken(name)[1] == dimencode
-end
-
-function tex.iscount(name)
- return lookuptoken(name)[1] == countcode
-end
-
-function tex.istoken(name)
- return lookuptoken(name)[1] == tokencode
-end
-
-function tex.isskip(name)
- return lookuptoken(name)[1] == skipcode
-end
-
-function tex.type(name)
- return types[lookuptoken(name)[1]] or "macro"
-end
-
--- inspect(tex.isdimen("xxxxxxxxxxxxxxx"))
--- inspect(tex.isdimen("textwidth"))
+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 P, C, S, Cc, lpegmatch, patterns = lpeg.P, lpeg.C, lpeg.S, lpeg.Cc, lpeg.match, lpeg.patterns
+
+local csname_id = token.csname_id
+local create = token.create
+local texcount = tex.count
+local texsetcount = tex.setcount
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+
+local undefined = csname_id("*undefined*crap*")
+local iftrue = create("iftrue")[2] -- inefficient hack
+
+tex.modes = allocate { }
+tex.systemmodes = allocate { }
+tex.constants = allocate { }
+tex.conditionals = allocate { }
+tex.ifs = allocate { }
+
+local modes = { }
+local systemmodes = { }
+
+setmetatableindex(tex.modes, function(t,k)
+ local m = modes[k]
+ if m then
+ return m()
+ else
+ local n = "mode>" .. k
+ if csname_id(n) == undefined then
+ return false
+ else
+ modes[k] = function() return texcount[n] >= 1 end
+ return texcount[n] >= 1
+ end
+ end
+end)
+
+setmetatableindex(tex.systemmodes, function(t,k)
+ local m = systemmodes[k]
+ if m then
+ return m()
+ else
+ local n = "mode>*" .. k
+ if csname_id(n) == undefined then
+ return false
+ else
+ systemmodes[k] = function() return texcount[n] >= 1 end
+ return texcount[n] >= 1
+ end
+ end
+end)
+
+setmetatableindex(tex.constants, function(t,k)
+ return csname_id(k) ~= undefined and texcount[k] or 0
+end)
+
+setmetatableindex(tex.conditionals, function(t,k) -- 0 == true
+ return csname_id(k) ~= undefined and texcount[k] == 0
+end)
+
+setmetatableindex(tex.ifs, function(t,k)
+ -- k = "if" .. k -- better not
+ return csname_id(k) ~= undefined and create(k)[2] == iftrue -- inefficient, this create, we need a helper
+end)
+
+-- todo : global
+
+-- not possible as we let at the tex end to zerocount and plusone
+--
+-- function tex.settrue(name,glob)
+-- if glob then
+-- texsetcount("global",name,0)
+-- else
+-- texcount[name] = 0
+-- end
+-- end
+--
+-- function tex.setfalse(name,glob)
+-- if glob then
+-- texsetcount("global",name,1)
+-- else
+-- texcount[name] = 1
+-- end
+-- end
+
+---- arg = P("{") * C(patterns.nested) * P("}") + Cc("")
+
+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 / context.luasetup
+ + P("xml") * arg / context.setupwithargument -- or xmlw as xmlsetup has swapped arguments
+ + (P("tex") * tag + str) / context.texsetup
+ + sep^1
+)^1
+
+function commands.autosetups(str)
+ lpegmatch(pattern,str)
+end
+
+-- new (inefficient)
+
+local lookuptoken = token.lookup
+
+local dimencode = lookuptoken("scratchdimen" )[1]
+local countcode = lookuptoken("scratchcounter")[1]
+local tokencode = lookuptoken("scratchtoken" )[1]
+local skipcode = lookuptoken("scratchskip" )[1]
+
+local types = {
+ [dimencode] = "dimen",
+ [countcode] = "count",
+ [tokencode] = "token",
+ [skipcode ] = "skip",
+}
+
+function tex.isdimen(name)
+ return lookuptoken(name)[1] == dimencode
+end
+
+function tex.iscount(name)
+ return lookuptoken(name)[1] == countcode
+end
+
+function tex.istoken(name)
+ return lookuptoken(name)[1] == tokencode
+end
+
+function tex.isskip(name)
+ return lookuptoken(name)[1] == skipcode
+end
+
+function tex.type(name)
+ return types[lookuptoken(name)[1]] or "macro"
+end
+
+-- inspect(tex.isdimen("xxxxxxxxxxxxxxx"))
+-- inspect(tex.isdimen("textwidth"))
diff --git a/tex/context/base/core-sys.lua b/tex/context/base/core-sys.lua
index 2c76dac5f..009ec16ea 100644
--- a/tex/context/base/core-sys.lua
+++ b/tex/context/base/core-sys.lua
@@ -1,101 +1,101 @@
-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 report_files = logs.reporter("system","files")
-
--- function commands.updatefilenames(jobname,fulljobname,inputfilename,outputfilename)
--- --
--- environment.jobname = jobname
--- --
--- local jobfilename = gsub(fulljobname or jobname or inputfilename or tex.jobname or "","%./","")
--- --
--- environment.jobfilename = jobfilename
--- environment.jobfilesuffix = lower(suffixonly(environment.jobfilename))
--- --
--- local inputfilename = gsub(inputfilename or "","%./","")
--- environment.inputfilename = inputfilename
--- environment.inputfilebarename = removesuffix(basename(inputfilename))
--- --
--- local inputfilerealsuffix = suffixonly(inputfilename)
--- environment.inputfilerealsuffix = inputfilerealsuffix
--- --
--- local inputfilesuffix = inputfilerealsuffix == "" and "tex" or lower(inputfilerealsuffix)
--- environment.inputfilesuffix = inputfilesuffix
--- --
--- local outputfilename = outputfilename or environment.inputfilebarename or ""
--- environment.outputfilename = outputfilename
--- --
--- local runpath = resolvers.cleanpath(lfs.currentdir())
--- environment.runpath = runpath
--- --
--- statistics.register("running on path", function()
--- return environment.runpath
--- end)
--- --
--- statistics.register("job file properties", function()
--- return format("jobname %a, input %a, suffix %a",jobfilename,inputfilename,inputfilesuffix)
--- end)
--- --
--- end
-
-function environment.initializefilenames() -- commands.updatefilenames(jobname,fulljobname,input,result)
-
- 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.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
-
- report_files("jobname %a, input %a, result %a",jobfilename,inputfilename,outputfilename)
-
- function environment.initializefilenames() end
-end
-
-statistics.register("result saved in file", function()
- -- suffix will be fetched from backend
- local outputfilename = environment.outputfilename or environment.jobname or tex.jobname or "<unset>"
- if tex.pdfoutput > 0 then
- return format("%s.%s, compresslevel %s, objectcompreslevel %s",outputfilename,"pdf",tex.pdfcompresslevel, tex.pdfobjcompresslevel)
- else
- return format("%s.%s",outputfilename,"dvi") -- hard to imagine
- end
-end)
+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 report_files = logs.reporter("system","files")
+
+-- function commands.updatefilenames(jobname,fulljobname,inputfilename,outputfilename)
+-- --
+-- environment.jobname = jobname
+-- --
+-- local jobfilename = gsub(fulljobname or jobname or inputfilename or tex.jobname or "","%./","")
+-- --
+-- environment.jobfilename = jobfilename
+-- environment.jobfilesuffix = lower(suffixonly(environment.jobfilename))
+-- --
+-- local inputfilename = gsub(inputfilename or "","%./","")
+-- environment.inputfilename = inputfilename
+-- environment.inputfilebarename = removesuffix(basename(inputfilename))
+-- --
+-- local inputfilerealsuffix = suffixonly(inputfilename)
+-- environment.inputfilerealsuffix = inputfilerealsuffix
+-- --
+-- local inputfilesuffix = inputfilerealsuffix == "" and "tex" or lower(inputfilerealsuffix)
+-- environment.inputfilesuffix = inputfilesuffix
+-- --
+-- local outputfilename = outputfilename or environment.inputfilebarename or ""
+-- environment.outputfilename = outputfilename
+-- --
+-- local runpath = resolvers.cleanpath(lfs.currentdir())
+-- environment.runpath = runpath
+-- --
+-- statistics.register("running on path", function()
+-- return environment.runpath
+-- end)
+-- --
+-- statistics.register("job file properties", function()
+-- return format("jobname %a, input %a, suffix %a",jobfilename,inputfilename,inputfilesuffix)
+-- end)
+-- --
+-- end
+
+function environment.initializefilenames() -- commands.updatefilenames(jobname,fulljobname,input,result)
+
+ 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.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
+
+ report_files("jobname %a, input %a, result %a",jobfilename,inputfilename,outputfilename)
+
+ function environment.initializefilenames() end
+end
+
+statistics.register("result saved in file", function()
+ -- suffix will be fetched from backend
+ local outputfilename = environment.outputfilename or environment.jobname or tex.jobname or "<unset>"
+ if tex.pdfoutput > 0 then
+ return format("%s.%s, compresslevel %s, objectcompreslevel %s",outputfilename,"pdf",tex.pdfcompresslevel, tex.pdfobjcompresslevel)
+ else
+ return format("%s.%s",outputfilename,"dvi") -- hard to imagine
+ end
+end)
diff --git a/tex/context/base/core-two.lua b/tex/context/base/core-two.lua
index 734ad8e31..d6e006e04 100644
--- a/tex/context/base/core-two.lua
+++ b/tex/context/base/core-two.lua
@@ -1,157 +1,157 @@
-if not modules then modules = { } end modules ['core-two'] = {
- version = 1.001,
- comment = "companion to core-two.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local remove, concat = table.remove, table.concat
-local allocate = utilities.storage.allocate
-
---[[ldx--
-<p>We save multi-pass information in the main utility table. This is a
-bit of a mess because we support old and new methods.</p>
---ldx]]--
-
-local collected = allocate()
-local tobesaved = allocate()
-
-local jobpasses = {
- collected = collected,
- tobesaved = tobesaved,
-}
-
-job.passes = jobpasses
-
-local function initializer()
- collected = jobpasses.collected
- tobesaved = jobpasses.tobesaved
-end
-
-job.register('job.passes.collected', tobesaved, initializer, nil)
-
-local function allocate(id)
- local p = tobesaved[id]
- if not p then
- p = { }
- tobesaved[id] = p
- end
- return p
-end
-
-jobpasses.define = allocate
-
-function jobpasses.save(id,str)
- local jti = allocate(id)
- jti[#jti+1] = str
-end
-
-function jobpasses.savetagged(id,tag,str)
- local jti = allocate(id)
- jti[tag] = str
-end
-
-function jobpasses.getdata(id,index,default)
- local jti = collected[id]
- local value = jit and jti[index]
- return value ~= "" and value or default or ""
-end
-
-function jobpasses.getfield(id,index,tag,default)
- local jti = collected[id]
- jti = jti and jti[index]
- local value = jti and jti[tag]
- return value ~= "" and value or default or ""
-end
-
-function jobpasses.getcollected(id)
- return collected[id] or { }
-end
-
-function jobpasses.gettobesaved(id)
- return allocate(id)
-end
-
-local function get(id)
- local jti = collected[id]
- if jti and #jti > 0 then
- return remove(jti,1)
- end
-end
-
-local function first(id)
- local jti = collected[id]
- if jti and #jti > 0 then
- return jti[1]
- end
-end
-
-local function last(id)
- local jti = collected[id]
- if jti and #jti > 0 then
- return jti[#jti]
- end
-end
-
-local function find(id,n)
- local jti = collected[id]
- if jti and jti[n] then
- return jti[n]
- end
-end
-
-local function count(id)
- local jti = collected[id]
- return jti and #jti or 0
-end
-
-local function list(id)
- local jti = collected[id]
- if jti then
- return concat(jti,',')
- end
-end
-
-local function inlist(id,str)
- local jti = collected[id]
- if jti then
- for _, v in next, jti do
- if v == str then
- return true
- end
- end
- end
- return false
-end
-
-local check = first
-
---
-
-jobpasses.get = get
-jobpasses.first = first
-jobpasses.last = last
-jobpasses.find = find
-jobpasses.list = list
-jobpasses.count = count
-jobpasses.check = check
-jobpasses.inlist = inlist
-
--- interface
-
-function commands.gettwopassdata (id) local r = get (id) if r then context(r) end end
-function commands.getfirsttwopassdata(id) local r = first(id) if r then context(r) end end
-function commands.getlasttwopassdata (id) local r = last (id) if r then context(r) end end
-function commands.findtwopassdata (id,n) local r = find (id,n) if r then context(r) end end
-function commands.gettwopassdatalist (id) local r = list (id) if r then context(r) end end
-function commands.counttwopassdata (id) local r = count(id) if r then context(r) end end
-function commands.checktwopassdata (id) local r = check(id) if r then context(r) end end
-
-commands.definetwopasslist = jobpasses.define
-commands.savetwopassdata = jobpasses.save
-commands.savetaggedtwopassdata = jobpasses.savetagged
-
-function commands.doifelseintwopassdata(id,str)
- commands.doifelse(inlist(id,str))
-end
+if not modules then modules = { } end modules ['core-two'] = {
+ version = 1.001,
+ comment = "companion to core-two.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local remove, concat = table.remove, table.concat
+local allocate = utilities.storage.allocate
+
+--[[ldx--
+<p>We save multi-pass information in the main utility table. This is a
+bit of a mess because we support old and new methods.</p>
+--ldx]]--
+
+local collected = allocate()
+local tobesaved = allocate()
+
+local jobpasses = {
+ collected = collected,
+ tobesaved = tobesaved,
+}
+
+job.passes = jobpasses
+
+local function initializer()
+ collected = jobpasses.collected
+ tobesaved = jobpasses.tobesaved
+end
+
+job.register('job.passes.collected', tobesaved, initializer, nil)
+
+local function allocate(id)
+ local p = tobesaved[id]
+ if not p then
+ p = { }
+ tobesaved[id] = p
+ end
+ return p
+end
+
+jobpasses.define = allocate
+
+function jobpasses.save(id,str)
+ local jti = allocate(id)
+ jti[#jti+1] = str
+end
+
+function jobpasses.savetagged(id,tag,str)
+ local jti = allocate(id)
+ jti[tag] = str
+end
+
+function jobpasses.getdata(id,index,default)
+ local jti = collected[id]
+ local value = jit and jti[index]
+ return value ~= "" and value or default or ""
+end
+
+function jobpasses.getfield(id,index,tag,default)
+ local jti = collected[id]
+ jti = jti and jti[index]
+ local value = jti and jti[tag]
+ return value ~= "" and value or default or ""
+end
+
+function jobpasses.getcollected(id)
+ return collected[id] or { }
+end
+
+function jobpasses.gettobesaved(id)
+ return allocate(id)
+end
+
+local function get(id)
+ local jti = collected[id]
+ if jti and #jti > 0 then
+ return remove(jti,1)
+ end
+end
+
+local function first(id)
+ local jti = collected[id]
+ if jti and #jti > 0 then
+ return jti[1]
+ end
+end
+
+local function last(id)
+ local jti = collected[id]
+ if jti and #jti > 0 then
+ return jti[#jti]
+ end
+end
+
+local function find(id,n)
+ local jti = collected[id]
+ if jti and jti[n] then
+ return jti[n]
+ end
+end
+
+local function count(id)
+ local jti = collected[id]
+ return jti and #jti or 0
+end
+
+local function list(id)
+ local jti = collected[id]
+ if jti then
+ return concat(jti,',')
+ end
+end
+
+local function inlist(id,str)
+ local jti = collected[id]
+ if jti then
+ for _, v in next, jti do
+ if v == str then
+ return true
+ end
+ end
+ end
+ return false
+end
+
+local check = first
+
+--
+
+jobpasses.get = get
+jobpasses.first = first
+jobpasses.last = last
+jobpasses.find = find
+jobpasses.list = list
+jobpasses.count = count
+jobpasses.check = check
+jobpasses.inlist = inlist
+
+-- interface
+
+function commands.gettwopassdata (id) local r = get (id) if r then context(r) end end
+function commands.getfirsttwopassdata(id) local r = first(id) if r then context(r) end end
+function commands.getlasttwopassdata (id) local r = last (id) if r then context(r) end end
+function commands.findtwopassdata (id,n) local r = find (id,n) if r then context(r) end end
+function commands.gettwopassdatalist (id) local r = list (id) if r then context(r) end end
+function commands.counttwopassdata (id) local r = count(id) if r then context(r) end end
+function commands.checktwopassdata (id) local r = check(id) if r then context(r) end end
+
+commands.definetwopasslist = jobpasses.define
+commands.savetwopassdata = jobpasses.save
+commands.savetaggedtwopassdata = jobpasses.savetagged
+
+function commands.doifelseintwopassdata(id,str)
+ commands.doifelse(inlist(id,str))
+end
diff --git a/tex/context/base/core-uti.lua b/tex/context/base/core-uti.lua
index f5003a132..96ccdca48 100644
--- a/tex/context/base/core-uti.lua
+++ b/tex/context/base/core-uti.lua
@@ -1,294 +1,294 @@
-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 format, match = string.format, string.match
-local next, type, tostring = next, type, tostring
-local concat = table.concat
-local texcount = tex.count
-
-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 report_passes = logs.reporter("job","passes")
-
-job = job or { }
-local job = job
-
-job.version = 1.22 -- make sure we don't have old lua 5.1 hash leftovers
-job.packversion = 1.02 -- make sure we don't have old lua 5.1 hash leftovers
-
--- 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)
- comment[key] = value
-end
-
-job.comment("version",job.version)
-
-local enabled = true
-
-directives.register("job.save",function(v) enabled = v end)
-
-function job.disablesave() -- can be command
- enabled = false
-end
-
-function job.initialize(loadname,savename)
- job.load(loadname) -- has to come after structure is defined !
- luatex.registerstopactions(function()
- if enabled and not status.lasterrorstring or status.lasterrorstring == "" then
- job.save(savename)
- end
- end)
-end
-
-function job.register(collected, tobesaved, initializer, finalizer)
- savelist[#savelist+1] = { collected, tobesaved, initializer, finalizer }
-end
-
--- as an example we implement variables
-
-local tobesaved, collected, checksums = allocate(), allocate(), allocate()
-
-local jobvariables = {
- collected = collected,
- tobesaved = tobesaved,
- checksums = checksums,
-}
-
-job.variables = jobvariables
-
-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.register('job.variables.checksums', checksums)
-
-local rmethod, rvalue
-
-local function initializer()
- tobesaved = jobvariables.tobesaved
- collected = jobvariables.collected
- checksums = jobvariables.checksums
- rvalue = collected.randomseed
- if not rvalue then
- rvalue = math.random()
- math.setrandomseedi(rvalue,"initialize")
- rmethod = "initialized"
- else
- math.setrandomseedi(rvalue,"previous run")
- rmethod = "resumed"
- end
- tobesaved.randomseed = rvalue
- for cs, value in next, collected do
- context.setxvalue(cs,value)
- end
-end
-
-job.register('job.variables.collected', tobesaved, initializer)
-
-function jobvariables.save(cs,value)
- tobesaved[cs] = value
-end
-
-local packlist = {
- "numbers",
- "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 jobpacker = packers.new(packlist,job.packversion) -- jump number when changs in hash
-
-job.pack = true
--- job.pack = false
-
-directives.register("job.pack",function(v) 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,true),"\n\n")
- for l=1,#savelist do
- local list = savelist[l]
- local target = format("utilitydata.%s",list[1])
- local data = list[2]
- local finalizer = list[4]
- 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
- f:write(definer,"\n\n",serialize(data,name,true,true),"\n\n")
- end
- if job.pack then
- packers.strip(jobpacker)
- f:write(serialize(jobpacker,"utilitydata.job.packed",true,true),"\n\n")
- end
- f:write("return utilitydata")
- f:close()
- end
- statistics.stoptiming(_save_)
-end
-
-local function load(filename)
- if lfs.isfile(filename) then
- 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
- report_passes("removing stale job data file %a, restart job",filename)
- 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
- for l=1,#savelist do
- local list = savelist[l]
- local target = list[1]
- local initializer = list[3]
- local result = accesstable(target,utilitydata)
- local done = packers.unpack(result,jobpacker,true)
- if done then
- migratetable(target,mark(result))
- if type(initializer) == "function" then
- initializer(result)
- end
- else
- report_passes("pack version mismatch")
- end
- 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
-
--- 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 total, indirect = status.callbacks or 0, status.indirect_callbacks or 0
- local pages = texcount['realpageno'] - 1
- if pages > 1 then
- return format("direct: %s, indirect: %s, total: %s (%i per page)", total-indirect, indirect, total, total/pages)
- else
- return format("direct: %s, indirect: %s, total: %s", total-indirect, indirect, total)
- end
-end)
-
-statistics.register("randomizer", function()
- if rmethod and rvalue then
- return format("%s with value %s",rmethod,rvalue)
- end
-end)
-
-function statistics.formatruntime(runtime)
- if not environment.initex then -- else error when testing as not counters yet
- local shipped = texcount['nofshipouts']
- local pages = texcount['realpageno']
- if pages > shipped then
- pages = shipped
- end
- if shipped > 0 or pages > 0 then
- local persecond = shipped / runtime
- if pages == 0 then pages = shipped end
- return format("%s seconds, %i processed pages, %i shipped pages, %.3f pages/second",runtime,pages,shipped,persecond)
- else
- return format("%s seconds",runtime)
- end
- end
-end
+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 format, match = string.format, string.match
+local next, type, tostring = next, type, tostring
+local concat = table.concat
+local texcount = tex.count
+
+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 report_passes = logs.reporter("job","passes")
+
+job = job or { }
+local job = job
+
+job.version = 1.22 -- make sure we don't have old lua 5.1 hash leftovers
+job.packversion = 1.02 -- make sure we don't have old lua 5.1 hash leftovers
+
+-- 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)
+ comment[key] = value
+end
+
+job.comment("version",job.version)
+
+local enabled = true
+
+directives.register("job.save",function(v) enabled = v end)
+
+function job.disablesave() -- can be command
+ enabled = false
+end
+
+function job.initialize(loadname,savename)
+ job.load(loadname) -- has to come after structure is defined !
+ luatex.registerstopactions(function()
+ if enabled and not status.lasterrorstring or status.lasterrorstring == "" then
+ job.save(savename)
+ end
+ end)
+end
+
+function job.register(collected, tobesaved, initializer, finalizer)
+ savelist[#savelist+1] = { collected, tobesaved, initializer, finalizer }
+end
+
+-- as an example we implement variables
+
+local tobesaved, collected, checksums = allocate(), allocate(), allocate()
+
+local jobvariables = {
+ collected = collected,
+ tobesaved = tobesaved,
+ checksums = checksums,
+}
+
+job.variables = jobvariables
+
+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.register('job.variables.checksums', checksums)
+
+local rmethod, rvalue
+
+local function initializer()
+ tobesaved = jobvariables.tobesaved
+ collected = jobvariables.collected
+ checksums = jobvariables.checksums
+ rvalue = collected.randomseed
+ if not rvalue then
+ rvalue = math.random()
+ math.setrandomseedi(rvalue,"initialize")
+ rmethod = "initialized"
+ else
+ math.setrandomseedi(rvalue,"previous run")
+ rmethod = "resumed"
+ end
+ tobesaved.randomseed = rvalue
+ for cs, value in next, collected do
+ context.setxvalue(cs,value)
+ end
+end
+
+job.register('job.variables.collected', tobesaved, initializer)
+
+function jobvariables.save(cs,value)
+ tobesaved[cs] = value
+end
+
+local packlist = {
+ "numbers",
+ "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 jobpacker = packers.new(packlist,job.packversion) -- jump number when changs in hash
+
+job.pack = true
+-- job.pack = false
+
+directives.register("job.pack",function(v) 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,true),"\n\n")
+ for l=1,#savelist do
+ local list = savelist[l]
+ local target = format("utilitydata.%s",list[1])
+ local data = list[2]
+ local finalizer = list[4]
+ 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
+ f:write(definer,"\n\n",serialize(data,name,true,true),"\n\n")
+ end
+ if job.pack then
+ packers.strip(jobpacker)
+ f:write(serialize(jobpacker,"utilitydata.job.packed",true,true),"\n\n")
+ end
+ f:write("return utilitydata")
+ f:close()
+ end
+ statistics.stoptiming(_save_)
+end
+
+local function load(filename)
+ if lfs.isfile(filename) then
+ 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
+ report_passes("removing stale job data file %a, restart job",filename)
+ 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
+ for l=1,#savelist do
+ local list = savelist[l]
+ local target = list[1]
+ local initializer = list[3]
+ local result = accesstable(target,utilitydata)
+ local done = packers.unpack(result,jobpacker,true)
+ if done then
+ migratetable(target,mark(result))
+ if type(initializer) == "function" then
+ initializer(result)
+ end
+ else
+ report_passes("pack version mismatch")
+ end
+ 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
+
+-- 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 total, indirect = status.callbacks or 0, status.indirect_callbacks or 0
+ local pages = texcount['realpageno'] - 1
+ if pages > 1 then
+ return format("direct: %s, indirect: %s, total: %s (%i per page)", total-indirect, indirect, total, total/pages)
+ else
+ return format("direct: %s, indirect: %s, total: %s", total-indirect, indirect, total)
+ end
+end)
+
+statistics.register("randomizer", function()
+ if rmethod and rvalue then
+ return format("%s with value %s",rmethod,rvalue)
+ end
+end)
+
+function statistics.formatruntime(runtime)
+ if not environment.initex then -- else error when testing as not counters yet
+ local shipped = texcount['nofshipouts']
+ local pages = texcount['realpageno']
+ if pages > shipped then
+ pages = shipped
+ end
+ if shipped > 0 or pages > 0 then
+ local persecond = shipped / runtime
+ if pages == 0 then pages = shipped end
+ return format("%s seconds, %i processed pages, %i shipped pages, %.3f pages/second",runtime,pages,shipped,persecond)
+ else
+ return format("%s seconds",runtime)
+ end
+ end
+end
diff --git a/tex/context/base/data-aux.lua b/tex/context/base/data-aux.lua
index 805d289b2..b969e6070 100644
--- a/tex/context/base/data-aux.lua
+++ b/tex/context/base/data-aux.lua
@@ -1,62 +1,62 @@
-if not modules then modules = { } end modules ['data-aux'] = {
- 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 find = string.find
-local type, next = type, next
-
-local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
-
-local resolvers = resolvers
-
-local report_scripts = logs.reporter("resolvers","scripts")
-
-function resolvers.updatescript(oldname,newname) -- oldname -> own.name, not per se a suffix
- local scriptpath = "scripts/context/lua"
- newname = file.addsuffix(newname,"lua")
- local oldscript = resolvers.cleanpath(oldname)
- if trace_locating then
- report_scripts("to be replaced old script %a", oldscript)
- end
- local newscripts = resolvers.findfiles(newname) or { }
- if #newscripts == 0 then
- if trace_locating then
- report_scripts("unable to locate new script")
- end
- else
- for i=1,#newscripts do
- local newscript = resolvers.cleanpath(newscripts[i])
- if trace_locating then
- report_scripts("checking new script %a", newscript)
- end
- if oldscript == newscript then
- if trace_locating then
- report_scripts("old and new script are the same")
- end
- elseif not find(newscript,scriptpath) then
- if trace_locating then
- report_scripts("new script should come from %a",scriptpath)
- end
- elseif not (find(oldscript,file.removesuffix(newname).."$") or find(oldscript,newname.."$")) then
- if trace_locating then
- report_scripts("invalid new script name")
- end
- else
- local newdata = io.loaddata(newscript)
- if newdata then
- if trace_locating then
- report_scripts("old script content replaced by new content")
- end
- io.savedata(oldscript,newdata)
- break
- elseif trace_locating then
- report_scripts("unable to load new script")
- end
- end
- end
- end
-end
+if not modules then modules = { } end modules ['data-aux'] = {
+ 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 find = string.find
+local type, next = type, next
+
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+
+local resolvers = resolvers
+
+local report_scripts = logs.reporter("resolvers","scripts")
+
+function resolvers.updatescript(oldname,newname) -- oldname -> own.name, not per se a suffix
+ local scriptpath = "scripts/context/lua"
+ newname = file.addsuffix(newname,"lua")
+ local oldscript = resolvers.cleanpath(oldname)
+ if trace_locating then
+ report_scripts("to be replaced old script %a", oldscript)
+ end
+ local newscripts = resolvers.findfiles(newname) or { }
+ if #newscripts == 0 then
+ if trace_locating then
+ report_scripts("unable to locate new script")
+ end
+ else
+ for i=1,#newscripts do
+ local newscript = resolvers.cleanpath(newscripts[i])
+ if trace_locating then
+ report_scripts("checking new script %a", newscript)
+ end
+ if oldscript == newscript then
+ if trace_locating then
+ report_scripts("old and new script are the same")
+ end
+ elseif not find(newscript,scriptpath) then
+ if trace_locating then
+ report_scripts("new script should come from %a",scriptpath)
+ end
+ elseif not (find(oldscript,file.removesuffix(newname).."$") or find(oldscript,newname.."$")) then
+ if trace_locating then
+ report_scripts("invalid new script name")
+ end
+ else
+ local newdata = io.loaddata(newscript)
+ if newdata then
+ if trace_locating then
+ report_scripts("old script content replaced by new content")
+ end
+ io.savedata(oldscript,newdata)
+ break
+ elseif trace_locating then
+ report_scripts("unable to load new script")
+ end
+ end
+ end
+ end
+end
diff --git a/tex/context/base/data-bin.lua b/tex/context/base/data-bin.lua
index 341d844fe..1d1e8b749 100644
--- a/tex/context/base/data-bin.lua
+++ b/tex/context/base/data-bin.lua
@@ -1,27 +1,27 @@
-if not modules then modules = { } end modules ['data-bin'] = {
- 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 resolvers = resolvers
-local methodhandler = resolvers.methodhandler
-
-function resolvers.findbinfile(filename,filetype)
- return methodhandler('finders',filename,filetype)
-end
-
-function resolvers.openbinfile(filename)
- return methodhandler('loaders',filename) -- a bit weird: load
-end
-
-function resolvers.loadbinfile(filename,filetype)
- local fname = methodhandler('finders',filename,filetype)
- if fname and fname ~= "" then
- return resolvers.openbinfile(fname) -- a bit weird: open
- else
- return resolvers.loaders.notfound()
- end
-end
+if not modules then modules = { } end modules ['data-bin'] = {
+ 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 resolvers = resolvers
+local methodhandler = resolvers.methodhandler
+
+function resolvers.findbinfile(filename,filetype)
+ return methodhandler('finders',filename,filetype)
+end
+
+function resolvers.openbinfile(filename)
+ return methodhandler('loaders',filename) -- a bit weird: load
+end
+
+function resolvers.loadbinfile(filename,filetype)
+ local fname = methodhandler('finders',filename,filetype)
+ if fname and fname ~= "" then
+ return resolvers.openbinfile(fname) -- a bit weird: open
+ else
+ return resolvers.loaders.notfound()
+ end
+end
diff --git a/tex/context/base/data-con.lua b/tex/context/base/data-con.lua
index 9b893df9c..240538df2 100644
--- a/tex/context/base/data-con.lua
+++ b/tex/context/base/data-con.lua
@@ -1,138 +1,138 @@
-if not modules then modules = { } end modules ['data-con'] = {
- version = 1.100,
- 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, lower, gsub = string.format, string.lower, string.gsub
-
-local trace_cache = false trackers.register("resolvers.cache", function(v) trace_cache = v end)
-local trace_containers = false trackers.register("resolvers.containers", function(v) trace_containers = v end)
-local trace_storage = false trackers.register("resolvers.storage", function(v) trace_storage = v end)
-
---[[ldx--
-<p>Once we found ourselves defining similar cache constructs
-several times, containers were introduced. Containers are used
-to collect tables in memory and reuse them when possible based
-on (unique) hashes (to be provided by the calling function).</p>
-
-<p>Caching to disk is disabled by default. Version numbers are
-stored in the saved table which makes it possible to change the
-table structures without bothering about the disk cache.</p>
-
-<p>Examples of usage can be found in the font related code.</p>
---ldx]]--
-
-containers = containers or { }
-local containers = containers
-containers.usecache = true
-
-local report_containers = logs.reporter("resolvers","containers")
-
-local allocated = { }
-
-local mt = {
- __index = function(t,k)
- if k == "writable" then
- local writable = caches.getwritablepath(t.category,t.subcategory) or { "." }
- t.writable = writable
- return writable
- elseif k == "readables" then
- local readables = caches.getreadablepaths(t.category,t.subcategory) or { "." }
- t.readables = readables
- return readables
- end
- end,
- __storage__ = true
-}
-
-function containers.define(category, subcategory, version, enabled)
- if category and subcategory then
- local c = allocated[category]
- if not c then
- c = { }
- allocated[category] = c
- end
- local s = c[subcategory]
- if not s then
- s = {
- category = category,
- subcategory = subcategory,
- storage = { },
- enabled = enabled,
- version = version or math.pi, -- after all, this is TeX
- trace = false,
- -- writable = caches.getwritablepath and caches.getwritablepath (category,subcategory) or { "." },
- -- readables = caches.getreadablepaths and caches.getreadablepaths(category,subcategory) or { "." },
- }
- setmetatable(s,mt)
- c[subcategory] = s
- end
- return s
- end
-end
-
-function containers.is_usable(container,name)
- return container.enabled and caches and caches.is_writable(container.writable, name)
-end
-
-function containers.is_valid(container,name)
- if name and name ~= "" then
- local storage = container.storage[name]
- return storage and storage.cache_version == container.version
- else
- return false
- end
-end
-
-function containers.read(container,name)
- local storage = container.storage
- local stored = storage[name]
- if not stored and container.enabled and caches and containers.usecache then
- stored = caches.loaddata(container.readables,name)
- if stored and stored.cache_version == container.version then
- if trace_cache or trace_containers then
- report_containers("action %a, category %a, name %a","load",container.subcategory,name)
- end
- else
- stored = nil
- end
- storage[name] = stored
- elseif stored then
- if trace_cache or trace_containers then
- report_containers("action %a, category %a, name %a","reuse",container.subcategory,name)
- end
- end
- return stored
-end
-
-function containers.write(container, name, data)
- if data then
- data.cache_version = container.version
- if container.enabled and caches then
- local unique, shared = data.unique, data.shared
- data.unique, data.shared = nil, nil
- caches.savedata(container.writable, name, data)
- if trace_cache or trace_containers then
- report_containers("action %a, category %a, name %a","save",container.subcategory,name)
- end
- data.unique, data.shared = unique, shared
- end
- if trace_cache or trace_containers then
- report_containers("action %a, category %a, name %a","store",container.subcategory,name)
- end
- container.storage[name] = data
- end
- return data
-end
-
-function containers.content(container,name)
- return container.storage[name]
-end
-
-function containers.cleanname(name)
- -- return (gsub(lower(name),"[^%w]+","-"))
- return (gsub(lower(name),"[^%w\128-\255]+","-")) -- more utf friendly
-end
+if not modules then modules = { } end modules ['data-con'] = {
+ version = 1.100,
+ 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, lower, gsub = string.format, string.lower, string.gsub
+
+local trace_cache = false trackers.register("resolvers.cache", function(v) trace_cache = v end)
+local trace_containers = false trackers.register("resolvers.containers", function(v) trace_containers = v end)
+local trace_storage = false trackers.register("resolvers.storage", function(v) trace_storage = v end)
+
+--[[ldx--
+<p>Once we found ourselves defining similar cache constructs
+several times, containers were introduced. Containers are used
+to collect tables in memory and reuse them when possible based
+on (unique) hashes (to be provided by the calling function).</p>
+
+<p>Caching to disk is disabled by default. Version numbers are
+stored in the saved table which makes it possible to change the
+table structures without bothering about the disk cache.</p>
+
+<p>Examples of usage can be found in the font related code.</p>
+--ldx]]--
+
+containers = containers or { }
+local containers = containers
+containers.usecache = true
+
+local report_containers = logs.reporter("resolvers","containers")
+
+local allocated = { }
+
+local mt = {
+ __index = function(t,k)
+ if k == "writable" then
+ local writable = caches.getwritablepath(t.category,t.subcategory) or { "." }
+ t.writable = writable
+ return writable
+ elseif k == "readables" then
+ local readables = caches.getreadablepaths(t.category,t.subcategory) or { "." }
+ t.readables = readables
+ return readables
+ end
+ end,
+ __storage__ = true
+}
+
+function containers.define(category, subcategory, version, enabled)
+ if category and subcategory then
+ local c = allocated[category]
+ if not c then
+ c = { }
+ allocated[category] = c
+ end
+ local s = c[subcategory]
+ if not s then
+ s = {
+ category = category,
+ subcategory = subcategory,
+ storage = { },
+ enabled = enabled,
+ version = version or math.pi, -- after all, this is TeX
+ trace = false,
+ -- writable = caches.getwritablepath and caches.getwritablepath (category,subcategory) or { "." },
+ -- readables = caches.getreadablepaths and caches.getreadablepaths(category,subcategory) or { "." },
+ }
+ setmetatable(s,mt)
+ c[subcategory] = s
+ end
+ return s
+ end
+end
+
+function containers.is_usable(container,name)
+ return container.enabled and caches and caches.is_writable(container.writable, name)
+end
+
+function containers.is_valid(container,name)
+ if name and name ~= "" then
+ local storage = container.storage[name]
+ return storage and storage.cache_version == container.version
+ else
+ return false
+ end
+end
+
+function containers.read(container,name)
+ local storage = container.storage
+ local stored = storage[name]
+ if not stored and container.enabled and caches and containers.usecache then
+ stored = caches.loaddata(container.readables,name)
+ if stored and stored.cache_version == container.version then
+ if trace_cache or trace_containers then
+ report_containers("action %a, category %a, name %a","load",container.subcategory,name)
+ end
+ else
+ stored = nil
+ end
+ storage[name] = stored
+ elseif stored then
+ if trace_cache or trace_containers then
+ report_containers("action %a, category %a, name %a","reuse",container.subcategory,name)
+ end
+ end
+ return stored
+end
+
+function containers.write(container, name, data)
+ if data then
+ data.cache_version = container.version
+ if container.enabled and caches then
+ local unique, shared = data.unique, data.shared
+ data.unique, data.shared = nil, nil
+ caches.savedata(container.writable, name, data)
+ if trace_cache or trace_containers then
+ report_containers("action %a, category %a, name %a","save",container.subcategory,name)
+ end
+ data.unique, data.shared = unique, shared
+ end
+ if trace_cache or trace_containers then
+ report_containers("action %a, category %a, name %a","store",container.subcategory,name)
+ end
+ container.storage[name] = data
+ end
+ return data
+end
+
+function containers.content(container,name)
+ return container.storage[name]
+end
+
+function containers.cleanname(name)
+ -- return (gsub(lower(name),"[^%w]+","-"))
+ return (gsub(lower(name),"[^%w\128-\255]+","-")) -- more utf friendly
+end
diff --git a/tex/context/base/data-crl.lua b/tex/context/base/data-crl.lua
index 303c0fa9f..445bd5b0a 100644
--- a/tex/context/base/data-crl.lua
+++ b/tex/context/base/data-crl.lua
@@ -1,61 +1,61 @@
-if not modules then modules = { } end modules ['data-crl'] = {
- 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"
-}
-
--- this one is replaced by data-sch.lua --
-
-local gsub = string.gsub
-
-local resolvers = resolvers
-
-local finders, openers, loaders = resolvers.finders, resolvers.openers, resolvers.loaders
-
-resolvers.curl = resolvers.curl or { }
-local curl = resolvers.curl
-
-local cached = { }
-
-local function runcurl(specification)
- local original = specification.original
- -- local scheme = specification.scheme
- local cleanname = gsub(original,"[^%a%d%.]+","-")
- local cachename = caches.setfirstwritablefile(cleanname,"curl")
- if not cached[original] then
- if not io.exists(cachename) then
- cached[original] = cachename
- local command = "curl --silent --create-dirs --output " .. cachename .. " " .. original
- os.spawn(command)
- end
- if io.exists(cachename) then
- cached[original] = cachename
- else
- cached[original] = ""
- end
- end
- return cached[original]
-end
-
--- old code: we could be cleaner using specification (see schemes)
-
-local function finder(specification,filetype)
- return resolvers.methodhandler("finders",runcurl(specification),filetype)
-end
-
-local opener = openers.file
-local loader = loaders.file
-
-local function install(scheme)
- finders[scheme] = finder
- openers[scheme] = opener
- loaders[scheme] = loader
-end
-
-resolvers.curl.install = install
-
-install('http')
-install('https')
-install('ftp')
+if not modules then modules = { } end modules ['data-crl'] = {
+ 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"
+}
+
+-- this one is replaced by data-sch.lua --
+
+local gsub = string.gsub
+
+local resolvers = resolvers
+
+local finders, openers, loaders = resolvers.finders, resolvers.openers, resolvers.loaders
+
+resolvers.curl = resolvers.curl or { }
+local curl = resolvers.curl
+
+local cached = { }
+
+local function runcurl(specification)
+ local original = specification.original
+ -- local scheme = specification.scheme
+ local cleanname = gsub(original,"[^%a%d%.]+","-")
+ local cachename = caches.setfirstwritablefile(cleanname,"curl")
+ if not cached[original] then
+ if not io.exists(cachename) then
+ cached[original] = cachename
+ local command = "curl --silent --create-dirs --output " .. cachename .. " " .. original
+ os.spawn(command)
+ end
+ if io.exists(cachename) then
+ cached[original] = cachename
+ else
+ cached[original] = ""
+ end
+ end
+ return cached[original]
+end
+
+-- old code: we could be cleaner using specification (see schemes)
+
+local function finder(specification,filetype)
+ return resolvers.methodhandler("finders",runcurl(specification),filetype)
+end
+
+local opener = openers.file
+local loader = loaders.file
+
+local function install(scheme)
+ finders[scheme] = finder
+ openers[scheme] = opener
+ loaders[scheme] = loader
+end
+
+resolvers.curl.install = install
+
+install('http')
+install('https')
+install('ftp')
diff --git a/tex/context/base/data-ctx.lua b/tex/context/base/data-ctx.lua
index c3fc1e62f..345e9c741 100644
--- a/tex/context/base/data-ctx.lua
+++ b/tex/context/base/data-ctx.lua
@@ -1,9 +1,9 @@
-if not modules then modules = { } end modules ['data-ctx'] = {
- 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"
-}
-
--- empty
+if not modules then modules = { } end modules ['data-ctx'] = {
+ 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"
+}
+
+-- empty
diff --git a/tex/context/base/data-env.lua b/tex/context/base/data-env.lua
index 8aba977a3..2ee25120e 100644
--- a/tex/context/base/data-env.lua
+++ b/tex/context/base/data-env.lua
@@ -1,291 +1,291 @@
-if not modules then modules = { } end modules ['data-env'] = {
- 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 lower, gsub = string.lower, string.gsub
-
-local resolvers = resolvers
-
-local allocate = utilities.storage.allocate
-local setmetatableindex = table.setmetatableindex
-local suffixonly = file.suffixonly
-
-local formats = allocate()
-local suffixes = allocate()
-local dangerous = allocate()
-local suffixmap = allocate()
-
-resolvers.formats = formats
-resolvers.suffixes = suffixes
-resolvers.dangerous = dangerous
-resolvers.suffixmap = suffixmap
-
-local luasuffixes = utilities.lua.suffixes
-
-local relations = allocate { -- todo: handlers also here
- core = {
- ofm = { -- will become obsolete
- names = { "ofm", "omega font metric", "omega font metrics" },
- variable = 'OFMFONTS',
- suffixes = { 'ofm', 'tfm' },
- },
- ovf = { -- will become obsolete
- names = { "ovf", "omega virtual font", "omega virtual fonts" },
- variable = 'OVFFONTS',
- suffixes = { 'ovf', 'vf' },
- },
- tfm = {
- names = { "tfm", "tex font metric", "tex font metrics" },
- variable = 'TFMFONTS',
- suffixes = { 'tfm' },
- },
- vf = {
- names = { "vf", "virtual font", "virtual fonts" },
- variable = 'VFFONTS',
- suffixes = { 'vf' },
- },
- otf = {
- names = { "otf", "opentype", "opentype font", "opentype fonts"},
- variable = 'OPENTYPEFONTS',
- suffixes = { 'otf' },
- },
- ttf = {
- names = { "ttf", "truetype", "truetype font", "truetype fonts", "truetype collection", "truetype collections", "truetype dictionary", "truetype dictionaries" },
- variable = 'TTFONTS',
- suffixes = { 'ttf', 'ttc', 'dfont' },
- },
- afm = {
- names = { "afm", "adobe font metric", "adobe font metrics" },
- variable = "AFMFONTS",
- suffixes = { "afm" },
- },
- pfb = {
- names = { "pfb", "type1", "type 1", "type1 font", "type 1 font", "type1 fonts", "type 1 fonts" },
- variable = 'T1FONTS',
- suffixes = { 'pfb', 'pfa' },
- },
- fea = {
- names = { "fea", "font feature", "font features", "font feature file", "font feature files" },
- variable = 'FONTFEATURES',
- suffixes = { 'fea' },
- },
- cid = {
- names = { "cid", "cid map", "cid maps", "cid file", "cid files" },
- variable = 'FONTCIDMAPS',
- suffixes = { 'cid', 'cidmap' },
- },
- fmt = {
- names = { "fmt", "format", "tex format" },
- variable = 'TEXFORMATS',
- suffixes = { 'fmt' },
- },
- mem = { -- will become obsolete
- names = { 'mem', "metapost format" },
- variable = 'MPMEMS',
- suffixes = { 'mem' },
- },
- mp = {
- names = { "mp" },
- variable = 'MPINPUTS',
- suffixes = { 'mp', 'mpvi', 'mpiv', 'mpii' },
- },
- tex = {
- names = { "tex" },
- variable = 'TEXINPUTS',
- suffixes = { 'tex', "mkvi", "mkiv", "mkii" },
- },
- icc = {
- names = { "icc", "icc profile", "icc profiles" },
- variable = 'ICCPROFILES',
- suffixes = { 'icc' },
- },
- texmfscripts = {
- names = { "texmfscript", "texmfscripts", "script", "scripts" },
- variable = 'TEXMFSCRIPTS',
- suffixes = { 'rb', 'pl', 'py' },
- },
- lua = {
- names = { "lua" },
- variable = 'LUAINPUTS',
- suffixes = { luasuffixes.lua, luasuffixes.luc, luasuffixes.tma, luasuffixes.tmc },
- },
- lib = {
- names = { "lib" },
- variable = 'CLUAINPUTS',
- suffixes = os.libsuffix and { os.libsuffix } or { 'dll', 'so' },
- },
- bib = {
- names = { 'bib' },
- suffixes = { 'bib' },
- },
- bst = {
- names = { 'bst' },
- suffixes = { 'bst' },
- },
- fontconfig = {
- names = { 'fontconfig', 'fontconfig file', 'fontconfig files' },
- variable = 'FONTCONFIG_PATH',
- },
- },
- obsolete = {
- enc = {
- names = { "enc", "enc files", "enc file", "encoding files", "encoding file" },
- variable = 'ENCFONTS',
- suffixes = { 'enc' },
- },
- map = {
- names = { "map", "map files", "map file" },
- variable = 'TEXFONTMAPS',
- suffixes = { 'map' },
- },
- lig = {
- names = { "lig files", "lig file", "ligature file", "ligature files" },
- variable = 'LIGFONTS',
- suffixes = { 'lig' },
- },
- opl = {
- names = { "opl" },
- variable = 'OPLFONTS',
- suffixes = { 'opl' },
- },
- ovp = {
- names = { "ovp" },
- variable = 'OVPFONTS',
- suffixes = { 'ovp' },
- },
- },
- kpse = { -- subset
- base = {
- names = { 'base', "metafont format" },
- variable = 'MFBASES',
- suffixes = { 'base', 'bas' },
- },
- cmap = {
- names = { 'cmap', 'cmap files', 'cmap file' },
- variable = 'CMAPFONTS',
- suffixes = { 'cmap' },
- },
- cnf = {
- names = { 'cnf' },
- suffixes = { 'cnf' },
- },
- web = {
- names = { 'web' },
- suffixes = { 'web', 'ch' }
- },
- cweb = {
- names = { 'cweb' },
- suffixes = { 'w', 'web', 'ch' },
- },
- gf = {
- names = { 'gf' },
- suffixes = { '<resolution>gf' },
- },
- mf = {
- names = { 'mf' },
- variable = 'MFINPUTS',
- suffixes = { 'mf' },
- },
- mft = {
- names = { 'mft' },
- suffixes = { 'mft' },
- },
- pk = {
- names = { 'pk' },
- suffixes = { '<resolution>pk' },
- },
- },
-}
-
-resolvers.relations = relations
-
--- formats: maps a format onto a variable
-
-function resolvers.updaterelations()
- for category, categories in next, relations do
- for name, relation in next, categories do
- local rn = relation.names
- local rv = relation.variable
- local rs = relation.suffixes
- if rn and rv then
- for i=1,#rn do
- local rni = lower(gsub(rn[i]," ",""))
- formats[rni] = rv
- if rs then
- suffixes[rni] = rs
- for i=1,#rs do
- local rsi = rs[i]
- suffixmap[rsi] = rni
- end
- end
- end
- end
- if rs then
- end
- end
- end
-end
-
-resolvers.updaterelations() -- push this in the metatable -> newindex
-
-local function simplified(t,k)
- return k and rawget(t,lower(gsub(k," ",""))) or nil
-end
-
-setmetatableindex(formats, simplified)
-setmetatableindex(suffixes, simplified)
-setmetatableindex(suffixmap, simplified)
-
--- A few accessors, mostly for command line tool.
-
-function resolvers.suffixofformat(str)
- local s = suffixes[str]
- return s and s[1] or ""
-end
-
-function resolvers.suffixofformat(str)
- return suffixes[str] or { }
-end
-
-for name, format in next, formats do
- dangerous[name] = true -- still needed ?
-end
-
--- because vf searching is somewhat dangerous, we want to prevent
--- too liberal searching esp because we do a lookup on the current
--- path anyway; only tex (or any) is safe
-
-dangerous.tex = nil
-
---~ print(table.serialize(dangerous))
-
--- more helpers
-
-function resolvers.formatofvariable(str)
- return formats[str] or ''
-end
-
-function resolvers.formatofsuffix(str) -- of file
- return suffixmap[suffixonly(str)] or 'tex' -- so many map onto tex (like mkiv, cld etc)
-end
-
-function resolvers.variableofformat(str)
- return formats[str] or ''
-end
-
-function resolvers.variableofformatorsuffix(str)
- local v = formats[str]
- if v then
- return v
- end
- v = suffixmap[suffixonly(str)]
- if v then
- return formats[v]
- end
- return ''
-end
-
+if not modules then modules = { } end modules ['data-env'] = {
+ 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 lower, gsub = string.lower, string.gsub
+
+local resolvers = resolvers
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+local suffixonly = file.suffixonly
+
+local formats = allocate()
+local suffixes = allocate()
+local dangerous = allocate()
+local suffixmap = allocate()
+
+resolvers.formats = formats
+resolvers.suffixes = suffixes
+resolvers.dangerous = dangerous
+resolvers.suffixmap = suffixmap
+
+local luasuffixes = utilities.lua.suffixes
+
+local relations = allocate { -- todo: handlers also here
+ core = {
+ ofm = { -- will become obsolete
+ names = { "ofm", "omega font metric", "omega font metrics" },
+ variable = 'OFMFONTS',
+ suffixes = { 'ofm', 'tfm' },
+ },
+ ovf = { -- will become obsolete
+ names = { "ovf", "omega virtual font", "omega virtual fonts" },
+ variable = 'OVFFONTS',
+ suffixes = { 'ovf', 'vf' },
+ },
+ tfm = {
+ names = { "tfm", "tex font metric", "tex font metrics" },
+ variable = 'TFMFONTS',
+ suffixes = { 'tfm' },
+ },
+ vf = {
+ names = { "vf", "virtual font", "virtual fonts" },
+ variable = 'VFFONTS',
+ suffixes = { 'vf' },
+ },
+ otf = {
+ names = { "otf", "opentype", "opentype font", "opentype fonts"},
+ variable = 'OPENTYPEFONTS',
+ suffixes = { 'otf' },
+ },
+ ttf = {
+ names = { "ttf", "truetype", "truetype font", "truetype fonts", "truetype collection", "truetype collections", "truetype dictionary", "truetype dictionaries" },
+ variable = 'TTFONTS',
+ suffixes = { 'ttf', 'ttc', 'dfont' },
+ },
+ afm = {
+ names = { "afm", "adobe font metric", "adobe font metrics" },
+ variable = "AFMFONTS",
+ suffixes = { "afm" },
+ },
+ pfb = {
+ names = { "pfb", "type1", "type 1", "type1 font", "type 1 font", "type1 fonts", "type 1 fonts" },
+ variable = 'T1FONTS',
+ suffixes = { 'pfb', 'pfa' },
+ },
+ fea = {
+ names = { "fea", "font feature", "font features", "font feature file", "font feature files" },
+ variable = 'FONTFEATURES',
+ suffixes = { 'fea' },
+ },
+ cid = {
+ names = { "cid", "cid map", "cid maps", "cid file", "cid files" },
+ variable = 'FONTCIDMAPS',
+ suffixes = { 'cid', 'cidmap' },
+ },
+ fmt = {
+ names = { "fmt", "format", "tex format" },
+ variable = 'TEXFORMATS',
+ suffixes = { 'fmt' },
+ },
+ mem = { -- will become obsolete
+ names = { 'mem', "metapost format" },
+ variable = 'MPMEMS',
+ suffixes = { 'mem' },
+ },
+ mp = {
+ names = { "mp" },
+ variable = 'MPINPUTS',
+ suffixes = { 'mp', 'mpvi', 'mpiv', 'mpii' },
+ },
+ tex = {
+ names = { "tex" },
+ variable = 'TEXINPUTS',
+ suffixes = { 'tex', "mkvi", "mkiv", "mkii" },
+ },
+ icc = {
+ names = { "icc", "icc profile", "icc profiles" },
+ variable = 'ICCPROFILES',
+ suffixes = { 'icc' },
+ },
+ texmfscripts = {
+ names = { "texmfscript", "texmfscripts", "script", "scripts" },
+ variable = 'TEXMFSCRIPTS',
+ suffixes = { 'rb', 'pl', 'py' },
+ },
+ lua = {
+ names = { "lua" },
+ variable = 'LUAINPUTS',
+ suffixes = { luasuffixes.lua, luasuffixes.luc, luasuffixes.tma, luasuffixes.tmc },
+ },
+ lib = {
+ names = { "lib" },
+ variable = 'CLUAINPUTS',
+ suffixes = os.libsuffix and { os.libsuffix } or { 'dll', 'so' },
+ },
+ bib = {
+ names = { 'bib' },
+ suffixes = { 'bib' },
+ },
+ bst = {
+ names = { 'bst' },
+ suffixes = { 'bst' },
+ },
+ fontconfig = {
+ names = { 'fontconfig', 'fontconfig file', 'fontconfig files' },
+ variable = 'FONTCONFIG_PATH',
+ },
+ },
+ obsolete = {
+ enc = {
+ names = { "enc", "enc files", "enc file", "encoding files", "encoding file" },
+ variable = 'ENCFONTS',
+ suffixes = { 'enc' },
+ },
+ map = {
+ names = { "map", "map files", "map file" },
+ variable = 'TEXFONTMAPS',
+ suffixes = { 'map' },
+ },
+ lig = {
+ names = { "lig files", "lig file", "ligature file", "ligature files" },
+ variable = 'LIGFONTS',
+ suffixes = { 'lig' },
+ },
+ opl = {
+ names = { "opl" },
+ variable = 'OPLFONTS',
+ suffixes = { 'opl' },
+ },
+ ovp = {
+ names = { "ovp" },
+ variable = 'OVPFONTS',
+ suffixes = { 'ovp' },
+ },
+ },
+ kpse = { -- subset
+ base = {
+ names = { 'base', "metafont format" },
+ variable = 'MFBASES',
+ suffixes = { 'base', 'bas' },
+ },
+ cmap = {
+ names = { 'cmap', 'cmap files', 'cmap file' },
+ variable = 'CMAPFONTS',
+ suffixes = { 'cmap' },
+ },
+ cnf = {
+ names = { 'cnf' },
+ suffixes = { 'cnf' },
+ },
+ web = {
+ names = { 'web' },
+ suffixes = { 'web', 'ch' }
+ },
+ cweb = {
+ names = { 'cweb' },
+ suffixes = { 'w', 'web', 'ch' },
+ },
+ gf = {
+ names = { 'gf' },
+ suffixes = { '<resolution>gf' },
+ },
+ mf = {
+ names = { 'mf' },
+ variable = 'MFINPUTS',
+ suffixes = { 'mf' },
+ },
+ mft = {
+ names = { 'mft' },
+ suffixes = { 'mft' },
+ },
+ pk = {
+ names = { 'pk' },
+ suffixes = { '<resolution>pk' },
+ },
+ },
+}
+
+resolvers.relations = relations
+
+-- formats: maps a format onto a variable
+
+function resolvers.updaterelations()
+ for category, categories in next, relations do
+ for name, relation in next, categories do
+ local rn = relation.names
+ local rv = relation.variable
+ local rs = relation.suffixes
+ if rn and rv then
+ for i=1,#rn do
+ local rni = lower(gsub(rn[i]," ",""))
+ formats[rni] = rv
+ if rs then
+ suffixes[rni] = rs
+ for i=1,#rs do
+ local rsi = rs[i]
+ suffixmap[rsi] = rni
+ end
+ end
+ end
+ end
+ if rs then
+ end
+ end
+ end
+end
+
+resolvers.updaterelations() -- push this in the metatable -> newindex
+
+local function simplified(t,k)
+ return k and rawget(t,lower(gsub(k," ",""))) or nil
+end
+
+setmetatableindex(formats, simplified)
+setmetatableindex(suffixes, simplified)
+setmetatableindex(suffixmap, simplified)
+
+-- A few accessors, mostly for command line tool.
+
+function resolvers.suffixofformat(str)
+ local s = suffixes[str]
+ return s and s[1] or ""
+end
+
+function resolvers.suffixofformat(str)
+ return suffixes[str] or { }
+end
+
+for name, format in next, formats do
+ dangerous[name] = true -- still needed ?
+end
+
+-- because vf searching is somewhat dangerous, we want to prevent
+-- too liberal searching esp because we do a lookup on the current
+-- path anyway; only tex (or any) is safe
+
+dangerous.tex = nil
+
+--~ print(table.serialize(dangerous))
+
+-- more helpers
+
+function resolvers.formatofvariable(str)
+ return formats[str] or ''
+end
+
+function resolvers.formatofsuffix(str) -- of file
+ return suffixmap[suffixonly(str)] or 'tex' -- so many map onto tex (like mkiv, cld etc)
+end
+
+function resolvers.variableofformat(str)
+ return formats[str] or ''
+end
+
+function resolvers.variableofformatorsuffix(str)
+ local v = formats[str]
+ if v then
+ return v
+ end
+ v = suffixmap[suffixonly(str)]
+ if v then
+ return formats[v]
+ end
+ return ''
+end
+
diff --git a/tex/context/base/data-exp.lua b/tex/context/base/data-exp.lua
index 1bf620a09..8a2fd0320 100644
--- a/tex/context/base/data-exp.lua
+++ b/tex/context/base/data-exp.lua
@@ -1,470 +1,470 @@
-if not modules then modules = { } end modules ['data-exp'] = {
- 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, find, gmatch, lower, char, sub = string.format, string.find, string.gmatch, string.lower, string.char, string.sub
-local concat, sort = table.concat, table.sort
-local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
-local Ct, Cs, Cc, P, C, S = lpeg.Ct, lpeg.Cs, lpeg.Cc, lpeg.P, lpeg.C, lpeg.S
-local type, next = type, next
-
-local ostype = os.type
-local collapsepath = file.collapsepath
-
-local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
-local trace_expansions = false trackers.register("resolvers.expansions", function(v) trace_expansions = v end)
-
-local report_expansions = logs.reporter("resolvers","expansions")
-
-local resolvers = resolvers
-
--- As this bit of code is somewhat special it gets its own module. After
--- all, when working on the main resolver code, I don't want to scroll
--- past this every time. See data-obs.lua for the gsub variant.
-
-local function f_first(a,b)
- local t, n = { }, 0
- for s in gmatch(b,"[^,]+") do
- n = n + 1 ; t[n] = a .. s
- end
- return concat(t,",")
-end
-
-local function f_second(a,b)
- local t, n = { }, 0
- for s in gmatch(a,"[^,]+") do
- n = n + 1 ; t[n] = s .. b
- end
- return concat(t,",")
-end
-
--- kpsewhich --expand-braces '{a,b}{c,d}'
--- ac:bc:ad:bd
-
--- old {a,b}{c,d} => ac ad bc bd
---
--- local function f_both(a,b)
--- local t, n = { }, 0
--- for sa in gmatch(a,"[^,]+") do
--- for sb in gmatch(b,"[^,]+") do
--- n = n + 1 ; t[n] = sa .. sb
--- end
--- end
--- return concat(t,",")
--- end
---
--- new {a,b}{c,d} => ac bc ad bd
-
-local function f_both(a,b)
- local t, n = { }, 0
- for sb in gmatch(b,"[^,]+") do -- and not sa
- for sa in gmatch(a,"[^,]+") do -- sb
- n = n + 1 ; t[n] = sa .. sb
- end
- end
- return concat(t,",")
-end
-
-local left = P("{")
-local right = P("}")
-local var = P((1 - S("{}" ))^0)
-local set = P((1 - S("{},"))^0)
-local other = P(1)
-
-local l_first = Cs( ( Cc("{") * (C(set) * left * C(var) * right / f_first) * Cc("}") + other )^0 )
-local l_second = Cs( ( Cc("{") * (left * C(var) * right * C(set) / f_second) * Cc("}") + other )^0 )
-local l_both = Cs( ( Cc("{") * (left * C(var) * right * left * C(var) * right / f_both) * Cc("}") + other )^0 )
-local l_rest = Cs( ( left * var * (left/"") * var * (right/"") * var * right + other )^0 )
-
-local stripper_1 = lpeg.stripper ("{}@")
-local replacer_1 = lpeg.replacer { { ",}", ",@}" }, { "{,", "{@," }, }
-
-local function splitpathexpr(str, newlist, validate) -- I couldn't resist lpegging it (nice exercise).
- if trace_expansions then
- report_expansions("expanding variable %a",str)
- end
- local t, ok, done = newlist or { }, false, false
- local n = #t
- str = lpegmatch(replacer_1,str)
- repeat
- local old = str
- repeat
- local old = str
- str = lpegmatch(l_first, str)
- until old == str
- repeat
- local old = str
- str = lpegmatch(l_second,str)
- until old == str
- repeat
- local old = str
- str = lpegmatch(l_both, str)
- until old == str
- repeat
- local old = str
- str = lpegmatch(l_rest, str)
- until old == str
- until old == str -- or not find(str,"{")
- str = lpegmatch(stripper_1,str)
- if validate then
- for s in gmatch(str,"[^,]+") do
- s = validate(s)
- if s then
- n = n + 1
- t[n] = s
- end
- end
- else
- for s in gmatch(str,"[^,]+") do
- n = n + 1
- t[n] = s
- end
- end
- if trace_expansions then
- for k=1,#t do
- report_expansions("% 4i: %s",k,t[k])
- end
- end
- return t
-end
-
--- We could make the previous one public.
-
-local function validate(s)
- s = collapsepath(s) -- already keeps the trailing / and //
- return s ~= "" and not find(s,"^!*unset/*$") and s
-end
-
-resolvers.validatedpath = validate -- keeps the trailing //
-
-function resolvers.expandedpathfromlist(pathlist)
- local newlist = { }
- for k=1,#pathlist do
- splitpathexpr(pathlist[k],newlist,validate)
- end
- return newlist
-end
-
--- {a,b,c,d}
--- a,b,c/{p,q,r},d
--- a,b,c/{p,q,r}/d/{x,y,z}//
--- a,b,c/{p,q/{x,y,z},r},d/{p,q,r}
--- a,b,c/{p,q/{x,y,z},r},d/{p,q,r}
--- a{b,c}{d,e}f
--- {a,b,c,d}
--- {a,b,c/{p,q,r},d}
--- {a,b,c/{p,q,r}/d/{x,y,z}//}
--- {a,b,c/{p,q/{x,y,z}},d/{p,q,r}}
--- {a,b,c/{p,q/{x,y,z},w}v,d/{p,q,r}}
--- {$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,.local,}/web2c}
-
-local cleanup = lpeg.replacer {
- { "!" , "" },
- { "\\" , "/" },
-}
-
-function resolvers.cleanpath(str) -- tricky, maybe only simple paths
- local doslashes = (P("\\")/"/" + 1)^0
- local donegation = (P("!") /"" )^0
- local homedir = lpegmatch(Cs(donegation * doslashes),environment.homedir or "")
- if homedir == "~" or homedir == "" or not lfs.isdir(homedir) then
- if trace_expansions then
- report_expansions("no home dir set, ignoring dependent paths")
- end
- function resolvers.cleanpath(str)
- if not str or find(str,"~") then
- return "" -- special case
- else
- return lpegmatch(cleanup,str)
- end
- end
- else
- local dohome = ((P("~")+P("$HOME"))/homedir)^0
- local cleanup = Cs(donegation * dohome * doslashes)
- function resolvers.cleanpath(str)
- return str and lpegmatch(cleanup,str) or ""
- end
- end
- return resolvers.cleanpath(str)
-end
-
--- print(resolvers.cleanpath(""))
--- print(resolvers.cleanpath("!"))
--- print(resolvers.cleanpath("~"))
--- print(resolvers.cleanpath("~/test"))
--- print(resolvers.cleanpath("!~/test"))
--- print(resolvers.cleanpath("~/test~test"))
-
--- This one strips quotes and funny tokens.
-
-local expandhome = P("~") / "$HOME" -- environment.homedir or "home:"
-
-local dodouble = P('"')/"" * (expandhome + (1 - P('"')))^0 * P('"')/""
-local dosingle = P("'")/"" * (expandhome + (1 - P("'")))^0 * P("'")/""
-local dostring = (expandhome + 1 )^0
-
-local stripper = Cs(
- lpegpatterns.unspacer * (dosingle + dodouble + dostring) * lpegpatterns.unspacer
-)
-
-function resolvers.checkedvariable(str) -- assumes str is a string
- return type(str) == "string" and lpegmatch(stripper,str) or str
-end
-
--- The path splitter:
-
--- A config (optionally) has the paths split in tables. Internally
--- we join them and split them after the expansion has taken place. This
--- is more convenient.
-
-local cache = { }
-
------ splitter = lpeg.tsplitat(S(ostype == "windows" and ";" or ":;")) -- maybe add ,
-local splitter = lpeg.tsplitat(";") -- as we move towards urls, prefixes and use tables we no longer do :
-
-local backslashswapper = lpeg.replacer("\\","/")
-
-local function splitconfigurationpath(str) -- beware, this can be either a path or a { specification }
- if str then
- local found = cache[str]
- if not found then
- if str == "" then
- found = { }
- else
- local split = lpegmatch(splitter,lpegmatch(backslashswapper,str)) -- can be combined
- found = { }
- local noffound = 0
- for i=1,#split do
- local s = split[i]
- if not find(s,"^{*unset}*") then
- noffound = noffound + 1
- found[noffound] = s
- end
- end
- if trace_expansions then
- report_expansions("splitting path specification %a",str)
- for k=1,noffound do
- report_expansions("% 4i: %s",k,found[k])
- end
- end
- cache[str] = found
- end
- end
- return found
- end
-end
-
-resolvers.splitconfigurationpath = splitconfigurationpath
-
-function resolvers.splitpath(str)
- if type(str) == 'table' then
- return str
- else
- return splitconfigurationpath(str)
- end
-end
-
-function resolvers.joinpath(str)
- if type(str) == 'table' then
- return file.joinpath(str)
- else
- return str
- end
-end
-
--- The next function scans directories and returns a hash where the
--- entries are either strings or tables.
-
--- starting with . or .. etc or funny char
-
---~ local l_forbidden = S("~`!#$%^&*()={}[]:;\"\'||\\/<>,?\n\r\t")
---~ local l_confusing = P(" ")
---~ local l_character = lpegpatterns.utf8
---~ local l_dangerous = P(".")
-
---~ local l_normal = (l_character - l_forbidden - l_confusing - l_dangerous) * (l_character - l_forbidden - l_confusing^2)^0 * P(-1)
---~ ----- l_normal = l_normal * Cc(true) + Cc(false)
-
---~ local function test(str)
---~ print(str,lpegmatch(l_normal,str))
---~ end
---~ test("ヒラギノ明朝 Pro W3")
---~ test("..ヒラギノ明朝 Pro W3")
---~ test(":ヒラギノ明朝 Pro W3;")
---~ test("ヒラギノ明朝 /Pro W3;")
---~ test("ヒラギノ明朝 Pro W3")
-
--- a lot of this caching can be stripped away when we have ssd's everywhere
---
--- we could cache all the (sub)paths here if needed
-
-local attributes, directory = lfs.attributes, lfs.dir
-
-local weird = P(".")^1 + lpeg.anywhere(S("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t"))
-local timer = { }
-local scanned = { }
-local nofscans = 0
-local scancache = { }
-
-local function scan(files,spec,path,n,m,r)
- local full = (path == "" and spec) or (spec .. path .. '/')
- local dirs = { }
- local nofdirs = 0
- for name in directory(full) do
- if not lpegmatch(weird,name) then
- local mode = attributes(full..name,'mode')
- if mode == 'file' then
- n = n + 1
- local f = files[name]
- if f then
- if type(f) == 'string' then
- files[name] = { f, path }
- else
- f[#f+1] = path
- end
- else -- probably unique anyway
- files[name] = path
- local lower = lower(name)
- if name ~= lower then
- files["remap:"..lower] = name
- r = r + 1
- end
- end
- elseif mode == 'directory' then
- m = m + 1
- nofdirs = nofdirs + 1
- if path ~= "" then
- dirs[nofdirs] = path..'/'..name
- else
- dirs[nofdirs] = name
- end
- end
- end
- end
- if nofdirs > 0 then
- sort(dirs)
- for i=1,nofdirs do
- files, n, m, r = scan(files,spec,dirs[i],n,m,r)
- end
- end
- scancache[sub(full,1,-2)] = files
- return files, n, m, r
-end
-
-local fullcache = { }
-
-function resolvers.scanfiles(path,branch,usecache)
- statistics.starttiming(timer)
- local realpath = resolvers.resolve(path) -- no shortcut
- if usecache then
- local files = fullcache[realpath]
- if files then
- if trace_locating then
- report_expansions("using caches scan of path %a, branch %a",path,branch or path)
- end
- return files
- end
- end
- if trace_locating then
- report_expansions("scanning path %a, branch %a",path,branch or path)
- end
- local files, n, m, r = scan({ },realpath .. '/',"",0,0,0)
- files.__path__ = path -- can be selfautoparent:texmf-whatever
- files.__files__ = n
- files.__directories__ = m
- files.__remappings__ = r
- if trace_locating then
- report_expansions("%s files found on %s directories with %s uppercase remappings",n,m,r)
- end
- if usecache then
- scanned[#scanned+1] = realpath
- fullcache[realpath] = files
- end
- nofscans = nofscans + 1
- statistics.stoptiming(timer)
- return files
-end
-
-local function simplescan(files,spec,path) -- first match only, no map and such
- local full = (path == "" and spec) or (spec .. path .. '/')
- local dirs = { }
- local nofdirs = 0
- for name in directory(full) do
- if not lpegmatch(weird,name) then
- local mode = attributes(full..name,'mode')
- if mode == 'file' then
- if not files[name] then
- -- only first match
- files[name] = path
- end
- elseif mode == 'directory' then
- nofdirs = nofdirs + 1
- if path ~= "" then
- dirs[nofdirs] = path..'/'..name
- else
- dirs[nofdirs] = name
- end
- end
- end
- end
- if nofdirs > 0 then
- sort(dirs)
- for i=1,nofdirs do
- files = simplescan(files,spec,dirs[i])
- end
- end
- return files
-end
-
-local simplecache = { }
-local nofsharedscans = 0
-
-function resolvers.simplescanfiles(path,branch,usecache)
- statistics.starttiming(timer)
- local realpath = resolvers.resolve(path) -- no shortcut
- if usecache then
- local files = simplecache[realpath]
- if not files then
- files = scancache[realpath]
- if files then
- nofsharedscans = nofsharedscans + 1
- end
- end
- if files then
- if trace_locating then
- report_expansions("using caches scan of path %a, branch %a",path,branch or path)
- end
- return files
- end
- end
- if trace_locating then
- report_expansions("scanning path %a, branch %a",path,branch or path)
- end
- local files = simplescan({ },realpath .. '/',"")
- if trace_locating then
- report_expansions("%s files found",table.count(files))
- end
- if usecache then
- scanned[#scanned+1] = realpath
- simplecache[realpath] = files
- end
- nofscans = nofscans + 1
- statistics.stoptiming(timer)
- return files
-end
-
-function resolvers.scandata()
- table.sort(scanned)
- return {
- n = nofscans,
- shared = nofsharedscans,
- time = statistics.elapsedtime(timer),
- paths = scanned,
- }
-end
-
---~ print(table.serialize(resolvers.scanfiles("t:/sources")))
+if not modules then modules = { } end modules ['data-exp'] = {
+ 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, find, gmatch, lower, char, sub = string.format, string.find, string.gmatch, string.lower, string.char, string.sub
+local concat, sort = table.concat, table.sort
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+local Ct, Cs, Cc, P, C, S = lpeg.Ct, lpeg.Cs, lpeg.Cc, lpeg.P, lpeg.C, lpeg.S
+local type, next = type, next
+
+local ostype = os.type
+local collapsepath = file.collapsepath
+
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+local trace_expansions = false trackers.register("resolvers.expansions", function(v) trace_expansions = v end)
+
+local report_expansions = logs.reporter("resolvers","expansions")
+
+local resolvers = resolvers
+
+-- As this bit of code is somewhat special it gets its own module. After
+-- all, when working on the main resolver code, I don't want to scroll
+-- past this every time. See data-obs.lua for the gsub variant.
+
+local function f_first(a,b)
+ local t, n = { }, 0
+ for s in gmatch(b,"[^,]+") do
+ n = n + 1 ; t[n] = a .. s
+ end
+ return concat(t,",")
+end
+
+local function f_second(a,b)
+ local t, n = { }, 0
+ for s in gmatch(a,"[^,]+") do
+ n = n + 1 ; t[n] = s .. b
+ end
+ return concat(t,",")
+end
+
+-- kpsewhich --expand-braces '{a,b}{c,d}'
+-- ac:bc:ad:bd
+
+-- old {a,b}{c,d} => ac ad bc bd
+--
+-- local function f_both(a,b)
+-- local t, n = { }, 0
+-- for sa in gmatch(a,"[^,]+") do
+-- for sb in gmatch(b,"[^,]+") do
+-- n = n + 1 ; t[n] = sa .. sb
+-- end
+-- end
+-- return concat(t,",")
+-- end
+--
+-- new {a,b}{c,d} => ac bc ad bd
+
+local function f_both(a,b)
+ local t, n = { }, 0
+ for sb in gmatch(b,"[^,]+") do -- and not sa
+ for sa in gmatch(a,"[^,]+") do -- sb
+ n = n + 1 ; t[n] = sa .. sb
+ end
+ end
+ return concat(t,",")
+end
+
+local left = P("{")
+local right = P("}")
+local var = P((1 - S("{}" ))^0)
+local set = P((1 - S("{},"))^0)
+local other = P(1)
+
+local l_first = Cs( ( Cc("{") * (C(set) * left * C(var) * right / f_first) * Cc("}") + other )^0 )
+local l_second = Cs( ( Cc("{") * (left * C(var) * right * C(set) / f_second) * Cc("}") + other )^0 )
+local l_both = Cs( ( Cc("{") * (left * C(var) * right * left * C(var) * right / f_both) * Cc("}") + other )^0 )
+local l_rest = Cs( ( left * var * (left/"") * var * (right/"") * var * right + other )^0 )
+
+local stripper_1 = lpeg.stripper ("{}@")
+local replacer_1 = lpeg.replacer { { ",}", ",@}" }, { "{,", "{@," }, }
+
+local function splitpathexpr(str, newlist, validate) -- I couldn't resist lpegging it (nice exercise).
+ if trace_expansions then
+ report_expansions("expanding variable %a",str)
+ end
+ local t, ok, done = newlist or { }, false, false
+ local n = #t
+ str = lpegmatch(replacer_1,str)
+ repeat
+ local old = str
+ repeat
+ local old = str
+ str = lpegmatch(l_first, str)
+ until old == str
+ repeat
+ local old = str
+ str = lpegmatch(l_second,str)
+ until old == str
+ repeat
+ local old = str
+ str = lpegmatch(l_both, str)
+ until old == str
+ repeat
+ local old = str
+ str = lpegmatch(l_rest, str)
+ until old == str
+ until old == str -- or not find(str,"{")
+ str = lpegmatch(stripper_1,str)
+ if validate then
+ for s in gmatch(str,"[^,]+") do
+ s = validate(s)
+ if s then
+ n = n + 1
+ t[n] = s
+ end
+ end
+ else
+ for s in gmatch(str,"[^,]+") do
+ n = n + 1
+ t[n] = s
+ end
+ end
+ if trace_expansions then
+ for k=1,#t do
+ report_expansions("% 4i: %s",k,t[k])
+ end
+ end
+ return t
+end
+
+-- We could make the previous one public.
+
+local function validate(s)
+ s = collapsepath(s) -- already keeps the trailing / and //
+ return s ~= "" and not find(s,"^!*unset/*$") and s
+end
+
+resolvers.validatedpath = validate -- keeps the trailing //
+
+function resolvers.expandedpathfromlist(pathlist)
+ local newlist = { }
+ for k=1,#pathlist do
+ splitpathexpr(pathlist[k],newlist,validate)
+ end
+ return newlist
+end
+
+-- {a,b,c,d}
+-- a,b,c/{p,q,r},d
+-- a,b,c/{p,q,r}/d/{x,y,z}//
+-- a,b,c/{p,q/{x,y,z},r},d/{p,q,r}
+-- a,b,c/{p,q/{x,y,z},r},d/{p,q,r}
+-- a{b,c}{d,e}f
+-- {a,b,c,d}
+-- {a,b,c/{p,q,r},d}
+-- {a,b,c/{p,q,r}/d/{x,y,z}//}
+-- {a,b,c/{p,q/{x,y,z}},d/{p,q,r}}
+-- {a,b,c/{p,q/{x,y,z},w}v,d/{p,q,r}}
+-- {$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,.local,}/web2c}
+
+local cleanup = lpeg.replacer {
+ { "!" , "" },
+ { "\\" , "/" },
+}
+
+function resolvers.cleanpath(str) -- tricky, maybe only simple paths
+ local doslashes = (P("\\")/"/" + 1)^0
+ local donegation = (P("!") /"" )^0
+ local homedir = lpegmatch(Cs(donegation * doslashes),environment.homedir or "")
+ if homedir == "~" or homedir == "" or not lfs.isdir(homedir) then
+ if trace_expansions then
+ report_expansions("no home dir set, ignoring dependent paths")
+ end
+ function resolvers.cleanpath(str)
+ if not str or find(str,"~") then
+ return "" -- special case
+ else
+ return lpegmatch(cleanup,str)
+ end
+ end
+ else
+ local dohome = ((P("~")+P("$HOME"))/homedir)^0
+ local cleanup = Cs(donegation * dohome * doslashes)
+ function resolvers.cleanpath(str)
+ return str and lpegmatch(cleanup,str) or ""
+ end
+ end
+ return resolvers.cleanpath(str)
+end
+
+-- print(resolvers.cleanpath(""))
+-- print(resolvers.cleanpath("!"))
+-- print(resolvers.cleanpath("~"))
+-- print(resolvers.cleanpath("~/test"))
+-- print(resolvers.cleanpath("!~/test"))
+-- print(resolvers.cleanpath("~/test~test"))
+
+-- This one strips quotes and funny tokens.
+
+local expandhome = P("~") / "$HOME" -- environment.homedir or "home:"
+
+local dodouble = P('"')/"" * (expandhome + (1 - P('"')))^0 * P('"')/""
+local dosingle = P("'")/"" * (expandhome + (1 - P("'")))^0 * P("'")/""
+local dostring = (expandhome + 1 )^0
+
+local stripper = Cs(
+ lpegpatterns.unspacer * (dosingle + dodouble + dostring) * lpegpatterns.unspacer
+)
+
+function resolvers.checkedvariable(str) -- assumes str is a string
+ return type(str) == "string" and lpegmatch(stripper,str) or str
+end
+
+-- The path splitter:
+
+-- A config (optionally) has the paths split in tables. Internally
+-- we join them and split them after the expansion has taken place. This
+-- is more convenient.
+
+local cache = { }
+
+----- splitter = lpeg.tsplitat(S(ostype == "windows" and ";" or ":;")) -- maybe add ,
+local splitter = lpeg.tsplitat(";") -- as we move towards urls, prefixes and use tables we no longer do :
+
+local backslashswapper = lpeg.replacer("\\","/")
+
+local function splitconfigurationpath(str) -- beware, this can be either a path or a { specification }
+ if str then
+ local found = cache[str]
+ if not found then
+ if str == "" then
+ found = { }
+ else
+ local split = lpegmatch(splitter,lpegmatch(backslashswapper,str)) -- can be combined
+ found = { }
+ local noffound = 0
+ for i=1,#split do
+ local s = split[i]
+ if not find(s,"^{*unset}*") then
+ noffound = noffound + 1
+ found[noffound] = s
+ end
+ end
+ if trace_expansions then
+ report_expansions("splitting path specification %a",str)
+ for k=1,noffound do
+ report_expansions("% 4i: %s",k,found[k])
+ end
+ end
+ cache[str] = found
+ end
+ end
+ return found
+ end
+end
+
+resolvers.splitconfigurationpath = splitconfigurationpath
+
+function resolvers.splitpath(str)
+ if type(str) == 'table' then
+ return str
+ else
+ return splitconfigurationpath(str)
+ end
+end
+
+function resolvers.joinpath(str)
+ if type(str) == 'table' then
+ return file.joinpath(str)
+ else
+ return str
+ end
+end
+
+-- The next function scans directories and returns a hash where the
+-- entries are either strings or tables.
+
+-- starting with . or .. etc or funny char
+
+--~ local l_forbidden = S("~`!#$%^&*()={}[]:;\"\'||\\/<>,?\n\r\t")
+--~ local l_confusing = P(" ")
+--~ local l_character = lpegpatterns.utf8
+--~ local l_dangerous = P(".")
+
+--~ local l_normal = (l_character - l_forbidden - l_confusing - l_dangerous) * (l_character - l_forbidden - l_confusing^2)^0 * P(-1)
+--~ ----- l_normal = l_normal * Cc(true) + Cc(false)
+
+--~ local function test(str)
+--~ print(str,lpegmatch(l_normal,str))
+--~ end
+--~ test("ヒラギノ明朝 Pro W3")
+--~ test("..ヒラギノ明朝 Pro W3")
+--~ test(":ヒラギノ明朝 Pro W3;")
+--~ test("ヒラギノ明朝 /Pro W3;")
+--~ test("ヒラギノ明朝 Pro W3")
+
+-- a lot of this caching can be stripped away when we have ssd's everywhere
+--
+-- we could cache all the (sub)paths here if needed
+
+local attributes, directory = lfs.attributes, lfs.dir
+
+local weird = P(".")^1 + lpeg.anywhere(S("~`!#$%^&*()={}[]:;\"\'||<>,?\n\r\t"))
+local timer = { }
+local scanned = { }
+local nofscans = 0
+local scancache = { }
+
+local function scan(files,spec,path,n,m,r)
+ local full = (path == "" and spec) or (spec .. path .. '/')
+ local dirs = { }
+ local nofdirs = 0
+ for name in directory(full) do
+ if not lpegmatch(weird,name) then
+ local mode = attributes(full..name,'mode')
+ if mode == 'file' then
+ n = n + 1
+ local f = files[name]
+ if f then
+ if type(f) == 'string' then
+ files[name] = { f, path }
+ else
+ f[#f+1] = path
+ end
+ else -- probably unique anyway
+ files[name] = path
+ local lower = lower(name)
+ if name ~= lower then
+ files["remap:"..lower] = name
+ r = r + 1
+ end
+ end
+ elseif mode == 'directory' then
+ m = m + 1
+ nofdirs = nofdirs + 1
+ if path ~= "" then
+ dirs[nofdirs] = path..'/'..name
+ else
+ dirs[nofdirs] = name
+ end
+ end
+ end
+ end
+ if nofdirs > 0 then
+ sort(dirs)
+ for i=1,nofdirs do
+ files, n, m, r = scan(files,spec,dirs[i],n,m,r)
+ end
+ end
+ scancache[sub(full,1,-2)] = files
+ return files, n, m, r
+end
+
+local fullcache = { }
+
+function resolvers.scanfiles(path,branch,usecache)
+ statistics.starttiming(timer)
+ local realpath = resolvers.resolve(path) -- no shortcut
+ if usecache then
+ local files = fullcache[realpath]
+ if files then
+ if trace_locating then
+ report_expansions("using caches scan of path %a, branch %a",path,branch or path)
+ end
+ return files
+ end
+ end
+ if trace_locating then
+ report_expansions("scanning path %a, branch %a",path,branch or path)
+ end
+ local files, n, m, r = scan({ },realpath .. '/',"",0,0,0)
+ files.__path__ = path -- can be selfautoparent:texmf-whatever
+ files.__files__ = n
+ files.__directories__ = m
+ files.__remappings__ = r
+ if trace_locating then
+ report_expansions("%s files found on %s directories with %s uppercase remappings",n,m,r)
+ end
+ if usecache then
+ scanned[#scanned+1] = realpath
+ fullcache[realpath] = files
+ end
+ nofscans = nofscans + 1
+ statistics.stoptiming(timer)
+ return files
+end
+
+local function simplescan(files,spec,path) -- first match only, no map and such
+ local full = (path == "" and spec) or (spec .. path .. '/')
+ local dirs = { }
+ local nofdirs = 0
+ for name in directory(full) do
+ if not lpegmatch(weird,name) then
+ local mode = attributes(full..name,'mode')
+ if mode == 'file' then
+ if not files[name] then
+ -- only first match
+ files[name] = path
+ end
+ elseif mode == 'directory' then
+ nofdirs = nofdirs + 1
+ if path ~= "" then
+ dirs[nofdirs] = path..'/'..name
+ else
+ dirs[nofdirs] = name
+ end
+ end
+ end
+ end
+ if nofdirs > 0 then
+ sort(dirs)
+ for i=1,nofdirs do
+ files = simplescan(files,spec,dirs[i])
+ end
+ end
+ return files
+end
+
+local simplecache = { }
+local nofsharedscans = 0
+
+function resolvers.simplescanfiles(path,branch,usecache)
+ statistics.starttiming(timer)
+ local realpath = resolvers.resolve(path) -- no shortcut
+ if usecache then
+ local files = simplecache[realpath]
+ if not files then
+ files = scancache[realpath]
+ if files then
+ nofsharedscans = nofsharedscans + 1
+ end
+ end
+ if files then
+ if trace_locating then
+ report_expansions("using caches scan of path %a, branch %a",path,branch or path)
+ end
+ return files
+ end
+ end
+ if trace_locating then
+ report_expansions("scanning path %a, branch %a",path,branch or path)
+ end
+ local files = simplescan({ },realpath .. '/',"")
+ if trace_locating then
+ report_expansions("%s files found",table.count(files))
+ end
+ if usecache then
+ scanned[#scanned+1] = realpath
+ simplecache[realpath] = files
+ end
+ nofscans = nofscans + 1
+ statistics.stoptiming(timer)
+ return files
+end
+
+function resolvers.scandata()
+ table.sort(scanned)
+ return {
+ n = nofscans,
+ shared = nofsharedscans,
+ time = statistics.elapsedtime(timer),
+ paths = scanned,
+ }
+end
+
+--~ print(table.serialize(resolvers.scanfiles("t:/sources")))
diff --git a/tex/context/base/data-fil.lua b/tex/context/base/data-fil.lua
index 5ef2612e9..09129e03c 100644
--- a/tex/context/base/data-fil.lua
+++ b/tex/context/base/data-fil.lua
@@ -1,113 +1,113 @@
-if not modules then modules = { } end modules ['data-fil'] = {
- 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 trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
-
-local report_files = logs.reporter("resolvers","files")
-
-local resolvers = resolvers
-
-local finders, openers, loaders, savers = resolvers.finders, resolvers.openers, resolvers.loaders, resolvers.savers
-local locators, hashers, generators, concatinators = resolvers.locators, resolvers.hashers, resolvers.generators, resolvers.concatinators
-
-local checkgarbage = utilities.garbagecollector and utilities.garbagecollector.check
-
-function locators.file(specification)
- local name = specification.filename
- local realname = resolvers.resolve(name) -- no shortcut
- if realname and realname ~= '' and lfs.isdir(realname) then
- if trace_locating then
- report_files("file locator %a found as %a",name,realname)
- end
- resolvers.appendhash('file',name,true) -- cache
- elseif trace_locating then
- report_files("file locator %a not found",name)
- end
-end
-
-function hashers.file(specification)
- local name = specification.filename
- local content = caches.loadcontent(name,'files')
- resolvers.registerfilehash(name,content,content==nil)
-end
-
-function generators.file(specification)
- local path = specification.filename
- local content = resolvers.scanfiles(path,false,true) -- scan once
---~ inspect(content)
- resolvers.registerfilehash(path,content,true)
-end
-
-concatinators.file = file.join
-
-function finders.file(specification,filetype)
- local filename = specification.filename
- local foundname = resolvers.findfile(filename,filetype)
- if foundname and foundname ~= "" then
- if trace_locating then
- report_files("file finder: %a found",filename)
- end
- return foundname
- else
- if trace_locating then
- report_files("file finder: %a not found",filename)
- end
- return finders.notfound()
- end
-end
-
--- The default textopener will be overloaded later on.
-
-function openers.helpers.textopener(tag,filename,f)
- return {
- reader = function() return f:read () end,
- close = function() logs.show_close(filename) return f:close() end,
- }
-end
-
-function openers.file(specification,filetype)
- local filename = specification.filename
- if filename and filename ~= "" then
- local f = io.open(filename,"r")
- if f then
- if trace_locating then
- report_files("file opener: %a opened",filename)
- end
- return openers.helpers.textopener("file",filename,f)
- end
- end
- if trace_locating then
- report_files("file opener: %a not found",filename)
- end
- return openers.notfound()
-end
-
-function loaders.file(specification,filetype)
- local filename = specification.filename
- if filename and filename ~= "" then
- local f = io.open(filename,"rb")
- if f then
- logs.show_load(filename)
- if trace_locating then
- report_files("file loader: %a loaded",filename)
- end
- local s = f:read("*a") -- io.readall(f) is faster but we never have large files here
- if checkgarbage then
- checkgarbage(#s)
- end
- f:close()
- if s then
- return true, s, #s
- end
- end
- end
- if trace_locating then
- report_files("file loader: %a not found",filename)
- end
- return loaders.notfound()
-end
+if not modules then modules = { } end modules ['data-fil'] = {
+ 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 trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+
+local report_files = logs.reporter("resolvers","files")
+
+local resolvers = resolvers
+
+local finders, openers, loaders, savers = resolvers.finders, resolvers.openers, resolvers.loaders, resolvers.savers
+local locators, hashers, generators, concatinators = resolvers.locators, resolvers.hashers, resolvers.generators, resolvers.concatinators
+
+local checkgarbage = utilities.garbagecollector and utilities.garbagecollector.check
+
+function locators.file(specification)
+ local name = specification.filename
+ local realname = resolvers.resolve(name) -- no shortcut
+ if realname and realname ~= '' and lfs.isdir(realname) then
+ if trace_locating then
+ report_files("file locator %a found as %a",name,realname)
+ end
+ resolvers.appendhash('file',name,true) -- cache
+ elseif trace_locating then
+ report_files("file locator %a not found",name)
+ end
+end
+
+function hashers.file(specification)
+ local name = specification.filename
+ local content = caches.loadcontent(name,'files')
+ resolvers.registerfilehash(name,content,content==nil)
+end
+
+function generators.file(specification)
+ local path = specification.filename
+ local content = resolvers.scanfiles(path,false,true) -- scan once
+--~ inspect(content)
+ resolvers.registerfilehash(path,content,true)
+end
+
+concatinators.file = file.join
+
+function finders.file(specification,filetype)
+ local filename = specification.filename
+ local foundname = resolvers.findfile(filename,filetype)
+ if foundname and foundname ~= "" then
+ if trace_locating then
+ report_files("file finder: %a found",filename)
+ end
+ return foundname
+ else
+ if trace_locating then
+ report_files("file finder: %a not found",filename)
+ end
+ return finders.notfound()
+ end
+end
+
+-- The default textopener will be overloaded later on.
+
+function openers.helpers.textopener(tag,filename,f)
+ return {
+ reader = function() return f:read () end,
+ close = function() logs.show_close(filename) return f:close() end,
+ }
+end
+
+function openers.file(specification,filetype)
+ local filename = specification.filename
+ if filename and filename ~= "" then
+ local f = io.open(filename,"r")
+ if f then
+ if trace_locating then
+ report_files("file opener: %a opened",filename)
+ end
+ return openers.helpers.textopener("file",filename,f)
+ end
+ end
+ if trace_locating then
+ report_files("file opener: %a not found",filename)
+ end
+ return openers.notfound()
+end
+
+function loaders.file(specification,filetype)
+ local filename = specification.filename
+ if filename and filename ~= "" then
+ local f = io.open(filename,"rb")
+ if f then
+ logs.show_load(filename)
+ if trace_locating then
+ report_files("file loader: %a loaded",filename)
+ end
+ local s = f:read("*a") -- io.readall(f) is faster but we never have large files here
+ if checkgarbage then
+ checkgarbage(#s)
+ end
+ f:close()
+ if s then
+ return true, s, #s
+ end
+ end
+ end
+ if trace_locating then
+ report_files("file loader: %a not found",filename)
+ end
+ return loaders.notfound()
+end
diff --git a/tex/context/base/data-gen.lua b/tex/context/base/data-gen.lua
index c1861fea1..5a0755831 100644
--- a/tex/context/base/data-gen.lua
+++ b/tex/context/base/data-gen.lua
@@ -1,9 +1,9 @@
-if not modules then modules = { } end modules ['data-gen'] = {
- 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"
-}
-
--- move generators here
+if not modules then modules = { } end modules ['data-gen'] = {
+ 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"
+}
+
+-- move generators here
diff --git a/tex/context/base/data-ini.lua b/tex/context/base/data-ini.lua
index 16f61a8c4..201c6a2d7 100644
--- a/tex/context/base/data-ini.lua
+++ b/tex/context/base/data-ini.lua
@@ -1,232 +1,232 @@
-if not modules then modules = { } end modules ['data-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",
-}
-
-local gsub, find, gmatch, char = string.gsub, string.find, string.gmatch, string.char
-local next, type = next, type
-
-local filedirname, filebasename, filejoin = file.dirname, file.basename, file.join
-
-local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
-local trace_detail = false trackers.register("resolvers.details", function(v) trace_detail = v end)
-local trace_expansions = false trackers.register("resolvers.expansions", function(v) trace_expansions = v end)
-
-local report_initialization = logs.reporter("resolvers","initialization")
-
-local ostype, osname, ossetenv, osgetenv = os.type, os.name, os.setenv, os.getenv
-
--- The code here used to be part of a data-res but for convenience
--- we now split it over multiple files. As this file is now the
--- starting point we introduce resolvers here.
-
-resolvers = resolvers or { }
-local resolvers = resolvers
-
--- We don't want the kpse library to kick in. Also, we want to be able to
--- execute programs. Control over execution is implemented later.
-
-texconfig.kpse_init = false
-texconfig.shell_escape = 't'
-
-if not (environment and environment.default_texmfcnf) and kpse and kpse.default_texmfcnf then
- local default_texmfcnf = kpse.default_texmfcnf()
- -- looks more like context:
- default_texmfcnf = gsub(default_texmfcnf,"$SELFAUTOLOC","selfautoloc:")
- default_texmfcnf = gsub(default_texmfcnf,"$SELFAUTODIR","selfautodir:")
- default_texmfcnf = gsub(default_texmfcnf,"$SELFAUTOPARENT","selfautoparent:")
- default_texmfcnf = gsub(default_texmfcnf,"$HOME","home:")
- --
- environment.default_texmfcnf = default_texmfcnf
-end
-
-kpse = { original = kpse }
-
-setmetatable(kpse, {
- __index = function(kp,name)
- report_initialization("fatal error: kpse library is accessed (key: %s)",name)
- os.exit()
- end
-} )
-
--- First we check a couple of environment variables. Some might be
--- set already but we need then later on. We start with the system
--- font path.
-
-do
-
- local osfontdir = osgetenv("OSFONTDIR")
-
- if osfontdir and osfontdir ~= "" then
- -- ok
- elseif osname == "windows" then
- ossetenv("OSFONTDIR","c:/windows/fonts//")
- elseif osname == "macosx" then
- ossetenv("OSFONTDIR","$HOME/Library/Fonts//;/Library/Fonts//;/System/Library/Fonts//")
- end
-
-end
-
--- Next comes the user's home path. We need this as later on we have
--- to replace ~ with its value.
-
-do
-
- local homedir = osgetenv(ostype == "windows" and 'USERPROFILE' or 'HOME') or ''
-
- if not homedir or homedir == "" then
- homedir = char(127) -- we need a value, later we wil trigger on it
- end
-
- homedir = file.collapsepath(homedir)
-
- ossetenv("HOME", homedir) -- can be used in unix cnf files
- ossetenv("USERPROFILE",homedir) -- can be used in windows cnf files
-
- environment.homedir = homedir
-
-end
-
--- The following code sets the name of the own binary and its
--- path. This is fallback code as we have os.selfdir now.
-
-do
-
- local args = environment.originalarguments or arg -- this needs a cleanup
-
- if not environment.ownmain then
- environment.ownmain = status and string.match(string.lower(status.banner),"this is ([%a]+)") or "luatex"
- end
-
- local ownbin = environment.ownbin or args[-2] or arg[-2] or args[-1] or arg[-1] or arg[0] or "luatex"
- local ownpath = environment.ownpath or os.selfdir
-
- ownbin = file.collapsepath(ownbin)
- ownpath = file.collapsepath(ownpath)
-
- if not ownpath or ownpath == "" or ownpath == "unset" then
- ownpath = args[-1] or arg[-1]
- ownpath = ownpath and filedirname(gsub(ownpath,"\\","/"))
- if not ownpath or ownpath == "" then
- ownpath = args[-0] or arg[-0]
- ownpath = ownpath and filedirname(gsub(ownpath,"\\","/"))
- end
- local binary = ownbin
- if not ownpath or ownpath == "" then
- ownpath = ownpath and filedirname(binary)
- end
- if not ownpath or ownpath == "" then
- if os.binsuffix ~= "" then
- binary = file.replacesuffix(binary,os.binsuffix)
- end
- local path = osgetenv("PATH")
- if path then
- for p in gmatch(path,"[^"..io.pathseparator.."]+") do
- local b = filejoin(p,binary)
- if lfs.isfile(b) then
- -- we assume that after changing to the path the currentdir function
- -- resolves to the real location and use this side effect here; this
- -- trick is needed because on the mac installations use symlinks in the
- -- path instead of real locations
- local olddir = lfs.currentdir()
- if lfs.chdir(p) then
- local pp = lfs.currentdir()
- if trace_locating and p ~= pp then
- report_initialization("following symlink %a to %a",p,pp)
- end
- ownpath = pp
- lfs.chdir(olddir)
- else
- if trace_locating then
- report_initialization("unable to check path %a",p)
- end
- ownpath = p
- end
- break
- end
- end
- end
- end
- if not ownpath or ownpath == "" then
- ownpath = "."
- report_initialization("forcing fallback to ownpath %a",ownpath)
- elseif trace_locating then
- report_initialization("using ownpath %a",ownpath)
- end
- end
-
- environment.ownbin = ownbin
- environment.ownpath = ownpath
-
-end
-
-resolvers.ownpath = environment.ownpath
-
-function resolvers.getownpath()
- return environment.ownpath
-end
-
--- The self variables permit us to use only a few (or even no)
--- environment variables.
-
-do
-
- local ownpath = environment.ownpath or dir.current()
-
- if ownpath then
- ossetenv('SELFAUTOLOC', file.collapsepath(ownpath))
- ossetenv('SELFAUTODIR', file.collapsepath(ownpath .. "/.."))
- ossetenv('SELFAUTOPARENT', file.collapsepath(ownpath .. "/../.."))
- else
- report_initialization("error: unable to locate ownpath")
- os.exit()
- end
-
-end
-
--- The running os:
-
--- todo: check is context sits here os.platform is more trustworthy
--- that the bin check as mtx-update runs from another path
-
-local texos = environment.texos or osgetenv("TEXOS")
-local texmfos = environment.texmfos or osgetenv('SELFAUTODIR')
-
-if not texos or texos == "" then
- texos = file.basename(texmfos)
-end
-
-ossetenv('TEXMFOS', texmfos) -- full bin path
-ossetenv('TEXOS', texos) -- partial bin parent
-ossetenv('SELFAUTOSYSTEM',os.platform) -- bonus
-
-environment.texos = texos
-environment.texmfos = texmfos
-
--- The current root:
-
-local texroot = environment.texroot or osgetenv("TEXROOT")
-
-if not texroot or texroot == "" then
- texroot = osgetenv('SELFAUTOPARENT')
- ossetenv('TEXROOT',texroot)
-end
-
-environment.texroot = file.collapsepath(texroot)
-
-if profiler then
- directives.register("system.profile",function()
- profiler.start("luatex-profile.log")
- end)
-end
-
--- a forward definition
-
-if not resolvers.resolve then
- function resolvers.resolve (s) return s end
- function resolvers.unresolve(s) return s end
- function resolvers.repath (s) return s end
-end
+if not modules then modules = { } end modules ['data-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",
+}
+
+local gsub, find, gmatch, char = string.gsub, string.find, string.gmatch, string.char
+local next, type = next, type
+
+local filedirname, filebasename, filejoin = file.dirname, file.basename, file.join
+
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+local trace_detail = false trackers.register("resolvers.details", function(v) trace_detail = v end)
+local trace_expansions = false trackers.register("resolvers.expansions", function(v) trace_expansions = v end)
+
+local report_initialization = logs.reporter("resolvers","initialization")
+
+local ostype, osname, ossetenv, osgetenv = os.type, os.name, os.setenv, os.getenv
+
+-- The code here used to be part of a data-res but for convenience
+-- we now split it over multiple files. As this file is now the
+-- starting point we introduce resolvers here.
+
+resolvers = resolvers or { }
+local resolvers = resolvers
+
+-- We don't want the kpse library to kick in. Also, we want to be able to
+-- execute programs. Control over execution is implemented later.
+
+texconfig.kpse_init = false
+texconfig.shell_escape = 't'
+
+if not (environment and environment.default_texmfcnf) and kpse and kpse.default_texmfcnf then
+ local default_texmfcnf = kpse.default_texmfcnf()
+ -- looks more like context:
+ default_texmfcnf = gsub(default_texmfcnf,"$SELFAUTOLOC","selfautoloc:")
+ default_texmfcnf = gsub(default_texmfcnf,"$SELFAUTODIR","selfautodir:")
+ default_texmfcnf = gsub(default_texmfcnf,"$SELFAUTOPARENT","selfautoparent:")
+ default_texmfcnf = gsub(default_texmfcnf,"$HOME","home:")
+ --
+ environment.default_texmfcnf = default_texmfcnf
+end
+
+kpse = { original = kpse }
+
+setmetatable(kpse, {
+ __index = function(kp,name)
+ report_initialization("fatal error: kpse library is accessed (key: %s)",name)
+ os.exit()
+ end
+} )
+
+-- First we check a couple of environment variables. Some might be
+-- set already but we need then later on. We start with the system
+-- font path.
+
+do
+
+ local osfontdir = osgetenv("OSFONTDIR")
+
+ if osfontdir and osfontdir ~= "" then
+ -- ok
+ elseif osname == "windows" then
+ ossetenv("OSFONTDIR","c:/windows/fonts//")
+ elseif osname == "macosx" then
+ ossetenv("OSFONTDIR","$HOME/Library/Fonts//;/Library/Fonts//;/System/Library/Fonts//")
+ end
+
+end
+
+-- Next comes the user's home path. We need this as later on we have
+-- to replace ~ with its value.
+
+do
+
+ local homedir = osgetenv(ostype == "windows" and 'USERPROFILE' or 'HOME') or ''
+
+ if not homedir or homedir == "" then
+ homedir = char(127) -- we need a value, later we wil trigger on it
+ end
+
+ homedir = file.collapsepath(homedir)
+
+ ossetenv("HOME", homedir) -- can be used in unix cnf files
+ ossetenv("USERPROFILE",homedir) -- can be used in windows cnf files
+
+ environment.homedir = homedir
+
+end
+
+-- The following code sets the name of the own binary and its
+-- path. This is fallback code as we have os.selfdir now.
+
+do
+
+ local args = environment.originalarguments or arg -- this needs a cleanup
+
+ if not environment.ownmain then
+ environment.ownmain = status and string.match(string.lower(status.banner),"this is ([%a]+)") or "luatex"
+ end
+
+ local ownbin = environment.ownbin or args[-2] or arg[-2] or args[-1] or arg[-1] or arg[0] or "luatex"
+ local ownpath = environment.ownpath or os.selfdir
+
+ ownbin = file.collapsepath(ownbin)
+ ownpath = file.collapsepath(ownpath)
+
+ if not ownpath or ownpath == "" or ownpath == "unset" then
+ ownpath = args[-1] or arg[-1]
+ ownpath = ownpath and filedirname(gsub(ownpath,"\\","/"))
+ if not ownpath or ownpath == "" then
+ ownpath = args[-0] or arg[-0]
+ ownpath = ownpath and filedirname(gsub(ownpath,"\\","/"))
+ end
+ local binary = ownbin
+ if not ownpath or ownpath == "" then
+ ownpath = ownpath and filedirname(binary)
+ end
+ if not ownpath or ownpath == "" then
+ if os.binsuffix ~= "" then
+ binary = file.replacesuffix(binary,os.binsuffix)
+ end
+ local path = osgetenv("PATH")
+ if path then
+ for p in gmatch(path,"[^"..io.pathseparator.."]+") do
+ local b = filejoin(p,binary)
+ if lfs.isfile(b) then
+ -- we assume that after changing to the path the currentdir function
+ -- resolves to the real location and use this side effect here; this
+ -- trick is needed because on the mac installations use symlinks in the
+ -- path instead of real locations
+ local olddir = lfs.currentdir()
+ if lfs.chdir(p) then
+ local pp = lfs.currentdir()
+ if trace_locating and p ~= pp then
+ report_initialization("following symlink %a to %a",p,pp)
+ end
+ ownpath = pp
+ lfs.chdir(olddir)
+ else
+ if trace_locating then
+ report_initialization("unable to check path %a",p)
+ end
+ ownpath = p
+ end
+ break
+ end
+ end
+ end
+ end
+ if not ownpath or ownpath == "" then
+ ownpath = "."
+ report_initialization("forcing fallback to ownpath %a",ownpath)
+ elseif trace_locating then
+ report_initialization("using ownpath %a",ownpath)
+ end
+ end
+
+ environment.ownbin = ownbin
+ environment.ownpath = ownpath
+
+end
+
+resolvers.ownpath = environment.ownpath
+
+function resolvers.getownpath()
+ return environment.ownpath
+end
+
+-- The self variables permit us to use only a few (or even no)
+-- environment variables.
+
+do
+
+ local ownpath = environment.ownpath or dir.current()
+
+ if ownpath then
+ ossetenv('SELFAUTOLOC', file.collapsepath(ownpath))
+ ossetenv('SELFAUTODIR', file.collapsepath(ownpath .. "/.."))
+ ossetenv('SELFAUTOPARENT', file.collapsepath(ownpath .. "/../.."))
+ else
+ report_initialization("error: unable to locate ownpath")
+ os.exit()
+ end
+
+end
+
+-- The running os:
+
+-- todo: check is context sits here os.platform is more trustworthy
+-- that the bin check as mtx-update runs from another path
+
+local texos = environment.texos or osgetenv("TEXOS")
+local texmfos = environment.texmfos or osgetenv('SELFAUTODIR')
+
+if not texos or texos == "" then
+ texos = file.basename(texmfos)
+end
+
+ossetenv('TEXMFOS', texmfos) -- full bin path
+ossetenv('TEXOS', texos) -- partial bin parent
+ossetenv('SELFAUTOSYSTEM',os.platform) -- bonus
+
+environment.texos = texos
+environment.texmfos = texmfos
+
+-- The current root:
+
+local texroot = environment.texroot or osgetenv("TEXROOT")
+
+if not texroot or texroot == "" then
+ texroot = osgetenv('SELFAUTOPARENT')
+ ossetenv('TEXROOT',texroot)
+end
+
+environment.texroot = file.collapsepath(texroot)
+
+if profiler then
+ directives.register("system.profile",function()
+ profiler.start("luatex-profile.log")
+ end)
+end
+
+-- a forward definition
+
+if not resolvers.resolve then
+ function resolvers.resolve (s) return s end
+ function resolvers.unresolve(s) return s end
+ function resolvers.repath (s) return s end
+end
diff --git a/tex/context/base/data-inp.lua b/tex/context/base/data-inp.lua
index 2fed75dab..97fb8904b 100644
--- a/tex/context/base/data-inp.lua
+++ b/tex/context/base/data-inp.lua
@@ -1,25 +1,25 @@
-if not modules then modules = { } end modules ['data-inp'] = {
- 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 allocate = utilities.storage.allocate
-local resolvers = resolvers
-
-local methodhandler = resolvers.methodhandler
-local registermethod = resolvers.registermethod
-
-local finders = allocate { helpers = { }, notfound = function() end }
-local openers = allocate { helpers = { }, notfound = function() end }
-local loaders = allocate { helpers = { }, notfound = function() return false, nil, 0 end }
-
-registermethod("finders", finders, "uri")
-registermethod("openers", openers, "uri")
-registermethod("loaders", loaders, "uri")
-
-resolvers.finders = finders
-resolvers.openers = openers
-resolvers.loaders = loaders
+if not modules then modules = { } end modules ['data-inp'] = {
+ 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 allocate = utilities.storage.allocate
+local resolvers = resolvers
+
+local methodhandler = resolvers.methodhandler
+local registermethod = resolvers.registermethod
+
+local finders = allocate { helpers = { }, notfound = function() end }
+local openers = allocate { helpers = { }, notfound = function() end }
+local loaders = allocate { helpers = { }, notfound = function() return false, nil, 0 end }
+
+registermethod("finders", finders, "uri")
+registermethod("openers", openers, "uri")
+registermethod("loaders", loaders, "uri")
+
+resolvers.finders = finders
+resolvers.openers = openers
+resolvers.loaders = loaders
diff --git a/tex/context/base/data-lst.lua b/tex/context/base/data-lst.lua
index d830c4f1f..8996fa251 100644
--- a/tex/context/base/data-lst.lua
+++ b/tex/context/base/data-lst.lua
@@ -1,77 +1,77 @@
-if not modules then modules = { } end modules ['data-lst'] = {
- 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"
-}
-
--- used in mtxrun, can be loaded later .. todo
-
-local find, concat, upper, format = string.find, table.concat, string.upper, string.format
-local fastcopy, sortedpairs = table.fastcopy, table.sortedpairs
-
-resolvers.listers = resolvers.listers or { }
-
-local resolvers = resolvers
-
-local report_lists = logs.reporter("resolvers","lists")
-
-local function tabstr(str)
- if type(str) == 'table' then
- return concat(str," | ")
- else
- return str
- end
-end
-
-function resolvers.listers.variables(pattern)
- local instance = resolvers.instance
- local environment = instance.environment
- local variables = instance.variables
- local expansions = instance.expansions
- local pattern = upper(pattern or "")
- local configured = { }
- local order = instance.order
- for i=1,#order do
- for k, v in next, order[i] do
- if v ~= nil and configured[k] == nil then
- configured[k] = v
- end
- end
- end
- local env = fastcopy(environment)
- local var = fastcopy(variables)
- local exp = fastcopy(expansions)
- for key, value in sortedpairs(configured) do
- if key ~= "" and (pattern == "" or find(upper(key),pattern)) then
- report_lists(key)
- report_lists(" env: %s",tabstr(rawget(environment,key)) or "unset")
- report_lists(" var: %s",tabstr(configured[key]) or "unset")
- report_lists(" exp: %s",tabstr(expansions[key]) or "unset")
- report_lists(" res: %s",tabstr(resolvers.resolve(expansions[key])) or "unset")
- end
- end
- instance.environment = fastcopy(env)
- instance.variables = fastcopy(var)
- instance.expansions = fastcopy(exp)
-end
-
-local report_resolved = logs.reporter("system","resolved")
-
-function resolvers.listers.configurations()
- local configurations = resolvers.instance.specification
- for i=1,#configurations do
- report_resolved("file : %s",resolvers.resolve(configurations[i]))
- end
- report_resolved("")
- local list = resolvers.expandedpathfromlist(resolvers.splitpath(resolvers.luacnfspec))
- for i=1,#list do
- local li = resolvers.resolve(list[i])
- if lfs.isdir(li) then
- report_resolved("path - %s",li)
- else
- report_resolved("path + %s",li)
- end
- end
-end
+if not modules then modules = { } end modules ['data-lst'] = {
+ 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"
+}
+
+-- used in mtxrun, can be loaded later .. todo
+
+local find, concat, upper, format = string.find, table.concat, string.upper, string.format
+local fastcopy, sortedpairs = table.fastcopy, table.sortedpairs
+
+resolvers.listers = resolvers.listers or { }
+
+local resolvers = resolvers
+
+local report_lists = logs.reporter("resolvers","lists")
+
+local function tabstr(str)
+ if type(str) == 'table' then
+ return concat(str," | ")
+ else
+ return str
+ end
+end
+
+function resolvers.listers.variables(pattern)
+ local instance = resolvers.instance
+ local environment = instance.environment
+ local variables = instance.variables
+ local expansions = instance.expansions
+ local pattern = upper(pattern or "")
+ local configured = { }
+ local order = instance.order
+ for i=1,#order do
+ for k, v in next, order[i] do
+ if v ~= nil and configured[k] == nil then
+ configured[k] = v
+ end
+ end
+ end
+ local env = fastcopy(environment)
+ local var = fastcopy(variables)
+ local exp = fastcopy(expansions)
+ for key, value in sortedpairs(configured) do
+ if key ~= "" and (pattern == "" or find(upper(key),pattern)) then
+ report_lists(key)
+ report_lists(" env: %s",tabstr(rawget(environment,key)) or "unset")
+ report_lists(" var: %s",tabstr(configured[key]) or "unset")
+ report_lists(" exp: %s",tabstr(expansions[key]) or "unset")
+ report_lists(" res: %s",tabstr(resolvers.resolve(expansions[key])) or "unset")
+ end
+ end
+ instance.environment = fastcopy(env)
+ instance.variables = fastcopy(var)
+ instance.expansions = fastcopy(exp)
+end
+
+local report_resolved = logs.reporter("system","resolved")
+
+function resolvers.listers.configurations()
+ local configurations = resolvers.instance.specification
+ for i=1,#configurations do
+ report_resolved("file : %s",resolvers.resolve(configurations[i]))
+ end
+ report_resolved("")
+ local list = resolvers.expandedpathfromlist(resolvers.splitpath(resolvers.luacnfspec))
+ for i=1,#list do
+ local li = resolvers.resolve(list[i])
+ if lfs.isdir(li) then
+ report_resolved("path - %s",li)
+ else
+ report_resolved("path + %s",li)
+ end
+ end
+end
diff --git a/tex/context/base/data-lua.lua b/tex/context/base/data-lua.lua
index cacffcaf8..0e7c81181 100644
--- a/tex/context/base/data-lua.lua
+++ b/tex/context/base/data-lua.lua
@@ -1,131 +1,131 @@
-if not modules then modules = { } end modules ['data-lua'] = {
- 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"
-}
-
--- This is now a plug in into l-lua (as we also use the extra paths elsewhere).
-
-local resolvers, package = resolvers, package
-
-local gsub = string.gsub
-local concat = table.concat
-local addsuffix = file.addsuffix
-
-local P, S, Cs, lpegmatch = lpeg.P, lpeg.S, lpeg.Cs, lpeg.match
-
-local luasuffixes = { 'tex', 'lua' }
-local libsuffixes = { 'lib' }
-local luaformats = { 'TEXINPUTS', 'LUAINPUTS' }
-local libformats = { 'CLUAINPUTS' }
-local helpers = package.helpers or { }
-local methods = helpers.methods or { }
-
-trackers.register("resolvers.libraries", function(v) helpers.trace = v end)
-trackers.register("resolvers.locating", function(v) helpers.trace = v end)
-
-helpers.report = logs.reporter("resolvers","libraries")
-
-helpers.sequence = {
- "already loaded",
- "preload table",
- "lua variable format",
- "lib variable format",
- "lua extra list",
- "lib extra list",
- "path specification",
- "cpath specification",
- "all in one fallback",
- "not loaded",
-}
-
-local pattern = Cs(P("!")^0 / "" * (P("/") * P(-1) / "/" + P("/")^1 / "/" + 1)^0)
-
-function helpers.cleanpath(path) -- hm, don't we have a helper for this?
- return resolvers.resolve(lpegmatch(pattern,path))
-end
-
-local loadedaslib = helpers.loadedaslib
-local getextraluapaths = package.extraluapaths
-local getextralibpaths = package.extralibpaths
-local registerpath = helpers.registerpath
-local lualibfile = helpers.lualibfile
-
-local luaformatpaths
-local libformatpaths
-
-local function getluaformatpaths()
- if not luaformatpaths then
- luaformatpaths = { }
- for i=1,#luaformats do
- registerpath("lua format","lua",luaformatpaths,resolvers.expandedpathlistfromvariable(luaformats[i]))
- end
- end
- return luaformatpaths
-end
-
-local function getlibformatpaths()
- if not libformatpaths then
- libformatpaths = { }
- for i=1,#libformats do
- registerpath("lib format","lib",libformatpaths,resolvers.expandedpathlistfromvariable(libformats[i]))
- end
- end
- return libformatpaths
-end
-
-local function loadedbyformat(name,rawname,suffixes,islib,what)
- local trace = helpers.trace
- local report = helpers.report
- for i=1,#suffixes do -- so we use findfile and not a lookup loop
- local format = suffixes[i]
- local resolved = resolvers.findfile(name,format) or ""
- if trace then
- report("%s format, identifying %a using format %a",what,name,format)
- end
- if resolved ~= "" then
- if trace then
- report("%s format, %a found on %a",what,name,resolved)
- end
- if islib then
- return loadedaslib(resolved,rawname)
- else
- return loadfile(resolved)
- end
- end
- end
-end
-
-helpers.loadedbyformat = loadedbyformat
-
--- print(lualibfile("bar"))
--- print(lualibfile("foo.bar"))
--- print(lualibfile("crap/foo...bar"))
--- print(lualibfile("crap//foo.bar"))
--- print(lualibfile("crap/../foo.bar"))
--- print(lualibfile("crap/.././foo.bar"))
-
--- alternatively we could split in path and base and temporary set the libpath to path
-
--- we could build a list of relevant paths but for tracing it's better to have the
--- whole lot (ok, we could skip the duplicates)
-
-methods["lua variable format"] = function(name)
- if helpers.trace then
- helpers.report("%s format, checking %s paths","lua",#getluaformatpaths()) -- call triggers building
- end
- return loadedbyformat(addsuffix(lualibfile(name),"lua"),name,luasuffixes,false,"lua")
-end
-
-methods["lib variable format"] = function(name)
- if helpers.trace then
- helpers.report("%s format, checking %s paths","lib",#getlibformatpaths()) -- call triggers building
- end
- return loadedbyformat(addsuffix(lualibfile(name),os.libsuffix),name,libsuffixes,true,"lib")
-end
-
--- package.extraclibpath(environment.ownpath)
-
-resolvers.loadlualib = require -- hm
+if not modules then modules = { } end modules ['data-lua'] = {
+ 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"
+}
+
+-- This is now a plug in into l-lua (as we also use the extra paths elsewhere).
+
+local resolvers, package = resolvers, package
+
+local gsub = string.gsub
+local concat = table.concat
+local addsuffix = file.addsuffix
+
+local P, S, Cs, lpegmatch = lpeg.P, lpeg.S, lpeg.Cs, lpeg.match
+
+local luasuffixes = { 'tex', 'lua' }
+local libsuffixes = { 'lib' }
+local luaformats = { 'TEXINPUTS', 'LUAINPUTS' }
+local libformats = { 'CLUAINPUTS' }
+local helpers = package.helpers or { }
+local methods = helpers.methods or { }
+
+trackers.register("resolvers.libraries", function(v) helpers.trace = v end)
+trackers.register("resolvers.locating", function(v) helpers.trace = v end)
+
+helpers.report = logs.reporter("resolvers","libraries")
+
+helpers.sequence = {
+ "already loaded",
+ "preload table",
+ "lua variable format",
+ "lib variable format",
+ "lua extra list",
+ "lib extra list",
+ "path specification",
+ "cpath specification",
+ "all in one fallback",
+ "not loaded",
+}
+
+local pattern = Cs(P("!")^0 / "" * (P("/") * P(-1) / "/" + P("/")^1 / "/" + 1)^0)
+
+function helpers.cleanpath(path) -- hm, don't we have a helper for this?
+ return resolvers.resolve(lpegmatch(pattern,path))
+end
+
+local loadedaslib = helpers.loadedaslib
+local getextraluapaths = package.extraluapaths
+local getextralibpaths = package.extralibpaths
+local registerpath = helpers.registerpath
+local lualibfile = helpers.lualibfile
+
+local luaformatpaths
+local libformatpaths
+
+local function getluaformatpaths()
+ if not luaformatpaths then
+ luaformatpaths = { }
+ for i=1,#luaformats do
+ registerpath("lua format","lua",luaformatpaths,resolvers.expandedpathlistfromvariable(luaformats[i]))
+ end
+ end
+ return luaformatpaths
+end
+
+local function getlibformatpaths()
+ if not libformatpaths then
+ libformatpaths = { }
+ for i=1,#libformats do
+ registerpath("lib format","lib",libformatpaths,resolvers.expandedpathlistfromvariable(libformats[i]))
+ end
+ end
+ return libformatpaths
+end
+
+local function loadedbyformat(name,rawname,suffixes,islib,what)
+ local trace = helpers.trace
+ local report = helpers.report
+ for i=1,#suffixes do -- so we use findfile and not a lookup loop
+ local format = suffixes[i]
+ local resolved = resolvers.findfile(name,format) or ""
+ if trace then
+ report("%s format, identifying %a using format %a",what,name,format)
+ end
+ if resolved ~= "" then
+ if trace then
+ report("%s format, %a found on %a",what,name,resolved)
+ end
+ if islib then
+ return loadedaslib(resolved,rawname)
+ else
+ return loadfile(resolved)
+ end
+ end
+ end
+end
+
+helpers.loadedbyformat = loadedbyformat
+
+-- print(lualibfile("bar"))
+-- print(lualibfile("foo.bar"))
+-- print(lualibfile("crap/foo...bar"))
+-- print(lualibfile("crap//foo.bar"))
+-- print(lualibfile("crap/../foo.bar"))
+-- print(lualibfile("crap/.././foo.bar"))
+
+-- alternatively we could split in path and base and temporary set the libpath to path
+
+-- we could build a list of relevant paths but for tracing it's better to have the
+-- whole lot (ok, we could skip the duplicates)
+
+methods["lua variable format"] = function(name)
+ if helpers.trace then
+ helpers.report("%s format, checking %s paths","lua",#getluaformatpaths()) -- call triggers building
+ end
+ return loadedbyformat(addsuffix(lualibfile(name),"lua"),name,luasuffixes,false,"lua")
+end
+
+methods["lib variable format"] = function(name)
+ if helpers.trace then
+ helpers.report("%s format, checking %s paths","lib",#getlibformatpaths()) -- call triggers building
+ end
+ return loadedbyformat(addsuffix(lualibfile(name),os.libsuffix),name,libsuffixes,true,"lib")
+end
+
+-- package.extraclibpath(environment.ownpath)
+
+resolvers.loadlualib = require -- hm
diff --git a/tex/context/base/data-met.lua b/tex/context/base/data-met.lua
index 7f97fbced..96da70bfd 100644
--- a/tex/context/base/data-met.lua
+++ b/tex/context/base/data-met.lua
@@ -1,133 +1,133 @@
-if not modules then modules = { } end modules ['data-met'] = {
- version = 1.100,
- 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 find, format = string.find, string.format
-local sequenced = table.sequenced
-local addurlscheme, urlhashed = url.addscheme, url.hashed
-local getcurrentdir = lfs.currentdir
-
-local trace_locating = false
-local trace_methods = false
-
-trackers.register("resolvers.locating", function(v) trace_methods = v end)
-trackers.register("resolvers.methods", function(v) trace_methods = v end)
-
---~ trace_methods = true
-
-local report_methods = logs.reporter("resolvers","methods")
-
-local allocate = utilities.storage.allocate
-
-local resolvers = resolvers
-
-local registered = { }
-
-local function splitmethod(filename) -- todo: filetype in specification
- if not filename then
- return { scheme = "unknown", original = filename }
- end
- if type(filename) == "table" then
- return filename -- already split
- end
- filename = file.collapsepath(filename,".") -- hm, we should keep ./ in some cases
-
--- filename = gsub(filename,"^%./",getcurrentdir().."/") -- we will merge dir.expandname and collapse some day
-
- if not find(filename,"://") then
- return { scheme = "file", path = filename, original = filename, filename = filename }
- end
- local specification = url.hashed(filename)
- if not specification.scheme or specification.scheme == "" then
- return { scheme = "file", path = filename, original = filename, filename = filename }
- else
- return specification
- end
-end
-
-resolvers.splitmethod = splitmethod -- bad name but ok
-
--- the second argument is always analyzed (saves time later on) and the original
--- gets passed as original but also as argument
-
-local function methodhandler(what,first,...) -- filename can be nil or false
- local method = registered[what]
- if method then
- local how, namespace = method.how, method.namespace
- if how == "uri" or how == "url" then
- local specification = splitmethod(first)
- local scheme = specification.scheme
- local resolver = namespace and namespace[scheme]
- if resolver then
- if trace_methods then
- report_methods("resolving, method %a, how %a, handler %a, argument %a",what,how,scheme,first)
- end
- return resolver(specification,...)
- else
- resolver = namespace.default or namespace.file
- if resolver then
- if trace_methods then
- report_methods("resolving, method %a, how %a, handler %a, argument %a",what,how,"default",first)
- end
- return resolver(specification,...)
- elseif trace_methods then
- report_methods("resolving, method %a, how %a, handler %a, argument %a",what,how,"unset")
- end
- end
- elseif how == "tag" then
- local resolver = namespace and namespace[first]
- if resolver then
- if trace_methods then
- report_methods("resolving, method %a, how %a, tag %a",what,how,first)
- end
- return resolver(...)
- else
- resolver = namespace.default or namespace.file
- if resolver then
- if trace_methods then
- report_methods("resolving, method %a, how %a, tag %a",what,how,"default")
- end
- return resolver(...)
- elseif trace_methods then
- report_methods("resolving, method %a, how %a, tag %a",what,how,"unset")
- end
- end
- end
- else
- report_methods("resolving, invalid method %a")
- end
-end
-
-resolvers.methodhandler = methodhandler
-
-function resolvers.registermethod(name,namespace,how)
- registered[name] = { how = how or "tag", namespace = namespace }
- namespace["byscheme"] = function(scheme,filename,...)
- if scheme == "file" then
- return methodhandler(name,filename,...)
- else
- return methodhandler(name,addurlscheme(filename,scheme),...)
- end
- end
-end
-
-local concatinators = allocate { notfound = file.join } -- concatinate paths
-local locators = allocate { notfound = function() end } -- locate databases
-local hashers = allocate { notfound = function() end } -- load databases
-local generators = allocate { notfound = function() end } -- generate databases
-
-resolvers.concatinators = concatinators
-resolvers.locators = locators
-resolvers.hashers = hashers
-resolvers.generators = generators
-
-local registermethod = resolvers.registermethod
-
-registermethod("concatinators",concatinators,"tag")
-registermethod("locators", locators, "uri")
-registermethod("hashers", hashers, "uri")
-registermethod("generators", generators, "uri")
+if not modules then modules = { } end modules ['data-met'] = {
+ version = 1.100,
+ 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 find, format = string.find, string.format
+local sequenced = table.sequenced
+local addurlscheme, urlhashed = url.addscheme, url.hashed
+local getcurrentdir = lfs.currentdir
+
+local trace_locating = false
+local trace_methods = false
+
+trackers.register("resolvers.locating", function(v) trace_methods = v end)
+trackers.register("resolvers.methods", function(v) trace_methods = v end)
+
+--~ trace_methods = true
+
+local report_methods = logs.reporter("resolvers","methods")
+
+local allocate = utilities.storage.allocate
+
+local resolvers = resolvers
+
+local registered = { }
+
+local function splitmethod(filename) -- todo: filetype in specification
+ if not filename then
+ return { scheme = "unknown", original = filename }
+ end
+ if type(filename) == "table" then
+ return filename -- already split
+ end
+ filename = file.collapsepath(filename,".") -- hm, we should keep ./ in some cases
+
+-- filename = gsub(filename,"^%./",getcurrentdir().."/") -- we will merge dir.expandname and collapse some day
+
+ if not find(filename,"://") then
+ return { scheme = "file", path = filename, original = filename, filename = filename }
+ end
+ local specification = url.hashed(filename)
+ if not specification.scheme or specification.scheme == "" then
+ return { scheme = "file", path = filename, original = filename, filename = filename }
+ else
+ return specification
+ end
+end
+
+resolvers.splitmethod = splitmethod -- bad name but ok
+
+-- the second argument is always analyzed (saves time later on) and the original
+-- gets passed as original but also as argument
+
+local function methodhandler(what,first,...) -- filename can be nil or false
+ local method = registered[what]
+ if method then
+ local how, namespace = method.how, method.namespace
+ if how == "uri" or how == "url" then
+ local specification = splitmethod(first)
+ local scheme = specification.scheme
+ local resolver = namespace and namespace[scheme]
+ if resolver then
+ if trace_methods then
+ report_methods("resolving, method %a, how %a, handler %a, argument %a",what,how,scheme,first)
+ end
+ return resolver(specification,...)
+ else
+ resolver = namespace.default or namespace.file
+ if resolver then
+ if trace_methods then
+ report_methods("resolving, method %a, how %a, handler %a, argument %a",what,how,"default",first)
+ end
+ return resolver(specification,...)
+ elseif trace_methods then
+ report_methods("resolving, method %a, how %a, handler %a, argument %a",what,how,"unset")
+ end
+ end
+ elseif how == "tag" then
+ local resolver = namespace and namespace[first]
+ if resolver then
+ if trace_methods then
+ report_methods("resolving, method %a, how %a, tag %a",what,how,first)
+ end
+ return resolver(...)
+ else
+ resolver = namespace.default or namespace.file
+ if resolver then
+ if trace_methods then
+ report_methods("resolving, method %a, how %a, tag %a",what,how,"default")
+ end
+ return resolver(...)
+ elseif trace_methods then
+ report_methods("resolving, method %a, how %a, tag %a",what,how,"unset")
+ end
+ end
+ end
+ else
+ report_methods("resolving, invalid method %a")
+ end
+end
+
+resolvers.methodhandler = methodhandler
+
+function resolvers.registermethod(name,namespace,how)
+ registered[name] = { how = how or "tag", namespace = namespace }
+ namespace["byscheme"] = function(scheme,filename,...)
+ if scheme == "file" then
+ return methodhandler(name,filename,...)
+ else
+ return methodhandler(name,addurlscheme(filename,scheme),...)
+ end
+ end
+end
+
+local concatinators = allocate { notfound = file.join } -- concatinate paths
+local locators = allocate { notfound = function() end } -- locate databases
+local hashers = allocate { notfound = function() end } -- load databases
+local generators = allocate { notfound = function() end } -- generate databases
+
+resolvers.concatinators = concatinators
+resolvers.locators = locators
+resolvers.hashers = hashers
+resolvers.generators = generators
+
+local registermethod = resolvers.registermethod
+
+registermethod("concatinators",concatinators,"tag")
+registermethod("locators", locators, "uri")
+registermethod("hashers", hashers, "uri")
+registermethod("generators", generators, "uri")
diff --git a/tex/context/base/data-out.lua b/tex/context/base/data-out.lua
index c427fa4b3..11304c2ce 100644
--- a/tex/context/base/data-out.lua
+++ b/tex/context/base/data-out.lua
@@ -1,18 +1,18 @@
-if not modules then modules = { } end modules ['data-out'] = {
- 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 allocate = utilities.storage.allocate
-local resolvers = resolvers
-
-local registermethod = resolvers.registermethod
-
-local savers = allocate { helpers = { } }
-
-resolvers.savers = savers
-
-registermethod("savers", savers, "uri")
+if not modules then modules = { } end modules ['data-out'] = {
+ 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 allocate = utilities.storage.allocate
+local resolvers = resolvers
+
+local registermethod = resolvers.registermethod
+
+local savers = allocate { helpers = { } }
+
+resolvers.savers = savers
+
+registermethod("savers", savers, "uri")
diff --git a/tex/context/base/data-pre.lua b/tex/context/base/data-pre.lua
index e8499c237..f2f5bddc4 100644
--- a/tex/context/base/data-pre.lua
+++ b/tex/context/base/data-pre.lua
@@ -1,246 +1,246 @@
-if not modules then modules = { } end modules ['data-pre'] = {
- 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"
-}
-
--- It could be interesting to hook the resolver in the file
--- opener so that unresolved prefixes travel around and we
--- get more abstraction.
-
--- As we use this beforehand we will move this up in the chain
--- of loading.
-
---~ print(resolvers.resolve("abc env:tmp file:cont-en.tex path:cont-en.tex full:cont-en.tex rel:zapf/one/p-chars.tex"))
-
-local resolvers = resolvers
-local prefixes = utilities.storage.allocate()
-resolvers.prefixes = prefixes
-
-local cleanpath, findgivenfile, expansion = resolvers.cleanpath, resolvers.findgivenfile, resolvers.expansion
-local getenv = resolvers.getenv -- we can probably also use resolvers.expansion
-local P, S, R, C, Cs, Cc, lpegmatch = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.match
-local joinpath, basename, dirname = file.join, file.basename, file.dirname
-local getmetatable, rawset, type = getmetatable, rawset, type
-
--- getenv = function(...) return resolvers.getenv(...) end -- needs checking (definitions changes later on)
-
-prefixes.environment = function(str)
- return cleanpath(expansion(str))
-end
-
-prefixes.relative = function(str,n) -- lfs.isfile
- if io.exists(str) then
- -- nothing
- elseif io.exists("./" .. str) then
- str = "./" .. str
- else
- local p = "../"
- for i=1,n or 2 do
- if io.exists(p .. str) then
- str = p .. str
- break
- else
- p = p .. "../"
- end
- end
- end
- return cleanpath(str)
-end
-
-prefixes.auto = function(str)
- local fullname = prefixes.relative(str)
- if not lfs.isfile(fullname) then
- fullname = prefixes.locate(str)
- end
- return fullname
-end
-
-prefixes.locate = function(str)
- local fullname = findgivenfile(str) or ""
- return cleanpath((fullname ~= "" and fullname) or str)
-end
-
-prefixes.filename = function(str)
- local fullname = findgivenfile(str) or ""
- return cleanpath(basename((fullname ~= "" and fullname) or str)) -- no cleanpath needed here
-end
-
-prefixes.pathname = function(str)
- local fullname = findgivenfile(str) or ""
- return cleanpath(dirname((fullname ~= "" and fullname) or str))
-end
-
-prefixes.selfautoloc = function(str)
- return cleanpath(joinpath(getenv('SELFAUTOLOC'),str))
-end
-
-prefixes.selfautoparent = function(str)
- return cleanpath(joinpath(getenv('SELFAUTOPARENT'),str))
-end
-
-prefixes.selfautodir = function(str)
- return cleanpath(joinpath(getenv('SELFAUTODIR'),str))
-end
-
-prefixes.home = function(str)
- return cleanpath(joinpath(getenv('HOME'),str))
-end
-
-local function toppath()
- local inputstack = resolvers.inputstack -- dependency, actually the code should move but it's
- if not inputstack then -- more convenient to keep it here
- return "."
- end
- local pathname = dirname(inputstack[#inputstack] or "")
- if pathname == "" then
- return "."
- else
- return pathname
- end
-end
-
-resolvers.toppath = toppath
-
-prefixes.toppath = function(str)
- return cleanpath(joinpath(toppath(),str))
-end
-
-prefixes.env = prefixes.environment
-prefixes.rel = prefixes.relative
-prefixes.loc = prefixes.locate
-prefixes.kpse = prefixes.locate
-prefixes.full = prefixes.locate
-prefixes.file = prefixes.filename
-prefixes.path = prefixes.pathname
-
-function resolvers.allprefixes(separator)
- local all = table.sortedkeys(prefixes)
- if separator then
- for i=1,#all do
- all[i] = all[i] .. ":"
- end
- end
- return all
-end
-
-local function _resolve_(method,target)
- local action = prefixes[method]
- if action then
- return action(target)
- else
- return method .. ":" .. target
- end
-end
-
-local resolved, abstract = { }, { }
-
-function resolvers.resetresolve(str)
- resolved, abstract = { }, { }
-end
-
--- todo: use an lpeg (see data-lua for !! / stripper)
-
--- local function resolve(str) -- use schemes, this one is then for the commandline only
--- if type(str) == "table" then
--- local t = { }
--- for i=1,#str do
--- t[i] = resolve(str[i])
--- end
--- return t
--- else
--- local res = resolved[str]
--- if not res then
--- res = gsub(str,"([a-z][a-z]+):([^ \"\';,]*)",_resolve_) -- home:xx;selfautoparent:xx; etc (comma added)
--- resolved[str] = res
--- abstract[res] = str
--- end
--- return res
--- end
--- end
-
--- home:xx;selfautoparent:xx;
-
-local pattern = Cs((C(R("az")^2) * P(":") * C((1-S(" \"\';,"))^1) / _resolve_ + P(1))^0)
-
-local prefix = C(R("az")^2) * P(":")
-local target = C((1-S(" \"\';,"))^1)
-local notarget = (#S(";,") + P(-1)) * Cc("")
-
-local pattern = Cs(((prefix * (target + notarget)) / _resolve_ + P(1))^0)
-
-local function resolve(str) -- use schemes, this one is then for the commandline only
- if type(str) == "table" then
- local t = { }
- for i=1,#str do
- t[i] = resolve(str[i])
- end
- return t
- else
- local res = resolved[str]
- if not res then
- res = lpegmatch(pattern,str)
- resolved[str] = res
- abstract[res] = str
- end
- return res
- end
-end
-
-local function unresolve(str)
- return abstract[str] or str
-end
-
-resolvers.resolve = resolve
-resolvers.unresolve = unresolve
-
-if type(os.uname) == "function" then
-
- for k, v in next, os.uname() do
- if not prefixes[k] then
- prefixes[k] = function() return v end
- end
- end
-
-end
-
-if os.type == "unix" then
-
- -- We need to distringuish between a prefix and something else : so we
- -- have a special repath variant for linux. Also, when a new prefix is
- -- defined, we need to remake the matcher.
-
- local pattern
-
- local function makepattern(t,k,v)
- if t then
- rawset(t,k,v)
- end
- local colon = P(":")
- for k, v in table.sortedpairs(prefixes) do
- if p then
- p = P(k) + p
- else
- p = P(k)
- end
- end
- pattern = Cs((p * colon + colon/";" + P(1))^0)
- end
-
- makepattern()
-
- getmetatable(prefixes).__newindex = makepattern
-
- function resolvers.repath(str)
- return lpegmatch(pattern,str)
- end
-
-else -- already the default:
-
- function resolvers.repath(str)
- return str
- end
-
-end
+if not modules then modules = { } end modules ['data-pre'] = {
+ 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"
+}
+
+-- It could be interesting to hook the resolver in the file
+-- opener so that unresolved prefixes travel around and we
+-- get more abstraction.
+
+-- As we use this beforehand we will move this up in the chain
+-- of loading.
+
+--~ print(resolvers.resolve("abc env:tmp file:cont-en.tex path:cont-en.tex full:cont-en.tex rel:zapf/one/p-chars.tex"))
+
+local resolvers = resolvers
+local prefixes = utilities.storage.allocate()
+resolvers.prefixes = prefixes
+
+local cleanpath, findgivenfile, expansion = resolvers.cleanpath, resolvers.findgivenfile, resolvers.expansion
+local getenv = resolvers.getenv -- we can probably also use resolvers.expansion
+local P, S, R, C, Cs, Cc, lpegmatch = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.match
+local joinpath, basename, dirname = file.join, file.basename, file.dirname
+local getmetatable, rawset, type = getmetatable, rawset, type
+
+-- getenv = function(...) return resolvers.getenv(...) end -- needs checking (definitions changes later on)
+
+prefixes.environment = function(str)
+ return cleanpath(expansion(str))
+end
+
+prefixes.relative = function(str,n) -- lfs.isfile
+ if io.exists(str) then
+ -- nothing
+ elseif io.exists("./" .. str) then
+ str = "./" .. str
+ else
+ local p = "../"
+ for i=1,n or 2 do
+ if io.exists(p .. str) then
+ str = p .. str
+ break
+ else
+ p = p .. "../"
+ end
+ end
+ end
+ return cleanpath(str)
+end
+
+prefixes.auto = function(str)
+ local fullname = prefixes.relative(str)
+ if not lfs.isfile(fullname) then
+ fullname = prefixes.locate(str)
+ end
+ return fullname
+end
+
+prefixes.locate = function(str)
+ local fullname = findgivenfile(str) or ""
+ return cleanpath((fullname ~= "" and fullname) or str)
+end
+
+prefixes.filename = function(str)
+ local fullname = findgivenfile(str) or ""
+ return cleanpath(basename((fullname ~= "" and fullname) or str)) -- no cleanpath needed here
+end
+
+prefixes.pathname = function(str)
+ local fullname = findgivenfile(str) or ""
+ return cleanpath(dirname((fullname ~= "" and fullname) or str))
+end
+
+prefixes.selfautoloc = function(str)
+ return cleanpath(joinpath(getenv('SELFAUTOLOC'),str))
+end
+
+prefixes.selfautoparent = function(str)
+ return cleanpath(joinpath(getenv('SELFAUTOPARENT'),str))
+end
+
+prefixes.selfautodir = function(str)
+ return cleanpath(joinpath(getenv('SELFAUTODIR'),str))
+end
+
+prefixes.home = function(str)
+ return cleanpath(joinpath(getenv('HOME'),str))
+end
+
+local function toppath()
+ local inputstack = resolvers.inputstack -- dependency, actually the code should move but it's
+ if not inputstack then -- more convenient to keep it here
+ return "."
+ end
+ local pathname = dirname(inputstack[#inputstack] or "")
+ if pathname == "" then
+ return "."
+ else
+ return pathname
+ end
+end
+
+resolvers.toppath = toppath
+
+prefixes.toppath = function(str)
+ return cleanpath(joinpath(toppath(),str))
+end
+
+prefixes.env = prefixes.environment
+prefixes.rel = prefixes.relative
+prefixes.loc = prefixes.locate
+prefixes.kpse = prefixes.locate
+prefixes.full = prefixes.locate
+prefixes.file = prefixes.filename
+prefixes.path = prefixes.pathname
+
+function resolvers.allprefixes(separator)
+ local all = table.sortedkeys(prefixes)
+ if separator then
+ for i=1,#all do
+ all[i] = all[i] .. ":"
+ end
+ end
+ return all
+end
+
+local function _resolve_(method,target)
+ local action = prefixes[method]
+ if action then
+ return action(target)
+ else
+ return method .. ":" .. target
+ end
+end
+
+local resolved, abstract = { }, { }
+
+function resolvers.resetresolve(str)
+ resolved, abstract = { }, { }
+end
+
+-- todo: use an lpeg (see data-lua for !! / stripper)
+
+-- local function resolve(str) -- use schemes, this one is then for the commandline only
+-- if type(str) == "table" then
+-- local t = { }
+-- for i=1,#str do
+-- t[i] = resolve(str[i])
+-- end
+-- return t
+-- else
+-- local res = resolved[str]
+-- if not res then
+-- res = gsub(str,"([a-z][a-z]+):([^ \"\';,]*)",_resolve_) -- home:xx;selfautoparent:xx; etc (comma added)
+-- resolved[str] = res
+-- abstract[res] = str
+-- end
+-- return res
+-- end
+-- end
+
+-- home:xx;selfautoparent:xx;
+
+local pattern = Cs((C(R("az")^2) * P(":") * C((1-S(" \"\';,"))^1) / _resolve_ + P(1))^0)
+
+local prefix = C(R("az")^2) * P(":")
+local target = C((1-S(" \"\';,"))^1)
+local notarget = (#S(";,") + P(-1)) * Cc("")
+
+local pattern = Cs(((prefix * (target + notarget)) / _resolve_ + P(1))^0)
+
+local function resolve(str) -- use schemes, this one is then for the commandline only
+ if type(str) == "table" then
+ local t = { }
+ for i=1,#str do
+ t[i] = resolve(str[i])
+ end
+ return t
+ else
+ local res = resolved[str]
+ if not res then
+ res = lpegmatch(pattern,str)
+ resolved[str] = res
+ abstract[res] = str
+ end
+ return res
+ end
+end
+
+local function unresolve(str)
+ return abstract[str] or str
+end
+
+resolvers.resolve = resolve
+resolvers.unresolve = unresolve
+
+if type(os.uname) == "function" then
+
+ for k, v in next, os.uname() do
+ if not prefixes[k] then
+ prefixes[k] = function() return v end
+ end
+ end
+
+end
+
+if os.type == "unix" then
+
+ -- We need to distringuish between a prefix and something else : so we
+ -- have a special repath variant for linux. Also, when a new prefix is
+ -- defined, we need to remake the matcher.
+
+ local pattern
+
+ local function makepattern(t,k,v)
+ if t then
+ rawset(t,k,v)
+ end
+ local colon = P(":")
+ for k, v in table.sortedpairs(prefixes) do
+ if p then
+ p = P(k) + p
+ else
+ p = P(k)
+ end
+ end
+ pattern = Cs((p * colon + colon/";" + P(1))^0)
+ end
+
+ makepattern()
+
+ getmetatable(prefixes).__newindex = makepattern
+
+ function resolvers.repath(str)
+ return lpegmatch(pattern,str)
+ end
+
+else -- already the default:
+
+ function resolvers.repath(str)
+ return str
+ end
+
+end
diff --git a/tex/context/base/data-sch.lua b/tex/context/base/data-sch.lua
index 16bade8db..41b941c5a 100644
--- a/tex/context/base/data-sch.lua
+++ b/tex/context/base/data-sch.lua
@@ -1,200 +1,200 @@
-if not modules then modules = { } end modules ['data-sch'] = {
- 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 load = load
-local gsub, concat, format = string.gsub, table.concat, string.format
-local finders, openers, loaders = resolvers.finders, resolvers.openers, resolvers.loaders
-
-local trace_schemes = false trackers.register("resolvers.schemes",function(v) trace_schemes = v end)
-local report_schemes = logs.reporter("resolvers","schemes")
-
-local http = require("socket.http")
-local ltn12 = require("ltn12")
-
-local resolvers = resolvers
-local schemes = resolvers.schemes or { }
-resolvers.schemes = schemes
-
-local cleaners = { }
-schemes.cleaners = cleaners
-
-local threshold = 24 * 60 * 60
-
-directives.register("schemes.threshold", function(v) threshold = tonumber(v) or threshold end)
-
-function cleaners.none(specification)
- return specification.original
-end
-
-function cleaners.strip(specification)
- return (gsub(specification.original,"[^%a%d%.]+","-")) -- so we keep periods
-end
-
-function cleaners.md5(specification)
- return file.addsuffix(md5.hex(specification.original),file.suffix(specification.path))
-end
-
-local cleaner = cleaners.strip
-
-directives.register("schemes.cleanmethod", function(v) cleaner = cleaners[v] or cleaners.strip end)
-
-function resolvers.schemes.cleanname(specification)
- local hash = cleaner(specification)
- if trace_schemes then
- report_schemes("hashing %a to %a",specification.original,hash)
- end
- return hash
-end
-
-local cached, loaded, reused, thresholds, handlers = { }, { }, { }, { }, { }
-
-local function runcurl(name,cachename) -- we use sockets instead or the curl library when possible
- local command = "curl --silent --create-dirs --output " .. cachename .. " " .. name
- os.spawn(command)
-end
-
-local function fetch(specification)
- local original = specification.original
- local scheme = specification.scheme
- local cleanname = schemes.cleanname(specification)
- local cachename = caches.setfirstwritablefile(cleanname,"schemes")
- if not cached[original] then
- statistics.starttiming(schemes)
- if not io.exists(cachename) or (os.difftime(os.time(),lfs.attributes(cachename).modification) > (thresholds[protocol] or threshold)) then
- cached[original] = cachename
- local handler = handlers[scheme]
- if handler then
- if trace_schemes then
- report_schemes("fetching %a, protocol %a, method %a",original,scheme,"built-in")
- end
- logs.flush()
- handler(specification,cachename)
- else
- if trace_schemes then
- report_schemes("fetching %a, protocol %a, method %a",original,scheme,"curl")
- end
- logs.flush()
- runcurl(original,cachename)
- end
- end
- if io.exists(cachename) then
- cached[original] = cachename
- if trace_schemes then
- report_schemes("using cached %a, protocol %a, cachename %a",original,scheme,cachename)
- end
- else
- cached[original] = ""
- if trace_schemes then
- report_schemes("using missing %a, protocol %a",original,scheme)
- end
- end
- loaded[scheme] = loaded[scheme] + 1
- statistics.stoptiming(schemes)
- else
- if trace_schemes then
- report_schemes("reusing %a, protocol %a",original,scheme)
- end
- reused[scheme] = reused[scheme] + 1
- end
- return cached[original]
-end
-
-local function finder(specification,filetype)
- return resolvers.methodhandler("finders",fetch(specification),filetype)
-end
-
-local opener = openers.file
-local loader = loaders.file
-
-local function install(scheme,handler,newthreshold)
- handlers [scheme] = handler
- loaded [scheme] = 0
- reused [scheme] = 0
- finders [scheme] = finder
- openers [scheme] = opener
- loaders [scheme] = loader
- thresholds[scheme] = newthreshold or threshold
-end
-
-schemes.install = install
-
-local function http_handler(specification,cachename)
- local tempname = cachename .. ".tmp"
- local f = io.open(tempname,"wb")
- local status, message = http.request {
- url = specification.original,
- sink = ltn12.sink.file(f)
- }
- if not status then
- os.remove(tempname)
- else
- os.remove(cachename)
- os.rename(tempname,cachename)
- end
- return cachename
-end
-
-install('http',http_handler)
-install('https') -- see pod
-install('ftp')
-
-statistics.register("scheme handling time", function()
- local l, r, nl, nr = { }, { }, 0, 0
- for k, v in table.sortedhash(loaded) do
- if v > 0 then
- nl = nl + 1
- l[nl] = k .. ":" .. v
- end
- end
- for k, v in table.sortedhash(reused) do
- if v > 0 then
- nr = nr + 1
- r[nr] = k .. ":" .. v
- end
- end
- local n = nl + nr
- if n > 0 then
- l = nl > 0 and concat(l) or "none"
- r = nr > 0 and concat(r) or "none"
- return format("%s seconds, %s processed, threshold %s seconds, loaded: %s, reused: %s",
- statistics.elapsedtime(schemes), n, threshold, l, r)
- else
- return nil
- end
-end)
-
--- We provide a few more helpers:
-
------ http = require("socket.http")
-local httprequest = http.request
-local toquery = url.toquery
-
--- local function httprequest(url)
--- return os.resultof(format("curl --silent %q", url))
--- end
-
-local function fetchstring(url,data)
- local q = data and toquery(data)
- if q then
- url = url .. "?" .. q
- end
- local reply = httprequest(url)
- return reply -- just one argument
-end
-
-schemes.fetchstring = fetchstring
-
-function schemes.fetchtable(url,data)
- local reply = fetchstring(url,data)
- if reply then
- local s = load("return " .. reply)
- if s then
- return s()
- end
- end
-end
+if not modules then modules = { } end modules ['data-sch'] = {
+ 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 load = load
+local gsub, concat, format = string.gsub, table.concat, string.format
+local finders, openers, loaders = resolvers.finders, resolvers.openers, resolvers.loaders
+
+local trace_schemes = false trackers.register("resolvers.schemes",function(v) trace_schemes = v end)
+local report_schemes = logs.reporter("resolvers","schemes")
+
+local http = require("socket.http")
+local ltn12 = require("ltn12")
+
+local resolvers = resolvers
+local schemes = resolvers.schemes or { }
+resolvers.schemes = schemes
+
+local cleaners = { }
+schemes.cleaners = cleaners
+
+local threshold = 24 * 60 * 60
+
+directives.register("schemes.threshold", function(v) threshold = tonumber(v) or threshold end)
+
+function cleaners.none(specification)
+ return specification.original
+end
+
+function cleaners.strip(specification)
+ return (gsub(specification.original,"[^%a%d%.]+","-")) -- so we keep periods
+end
+
+function cleaners.md5(specification)
+ return file.addsuffix(md5.hex(specification.original),file.suffix(specification.path))
+end
+
+local cleaner = cleaners.strip
+
+directives.register("schemes.cleanmethod", function(v) cleaner = cleaners[v] or cleaners.strip end)
+
+function resolvers.schemes.cleanname(specification)
+ local hash = cleaner(specification)
+ if trace_schemes then
+ report_schemes("hashing %a to %a",specification.original,hash)
+ end
+ return hash
+end
+
+local cached, loaded, reused, thresholds, handlers = { }, { }, { }, { }, { }
+
+local function runcurl(name,cachename) -- we use sockets instead or the curl library when possible
+ local command = "curl --silent --create-dirs --output " .. cachename .. " " .. name
+ os.spawn(command)
+end
+
+local function fetch(specification)
+ local original = specification.original
+ local scheme = specification.scheme
+ local cleanname = schemes.cleanname(specification)
+ local cachename = caches.setfirstwritablefile(cleanname,"schemes")
+ if not cached[original] then
+ statistics.starttiming(schemes)
+ if not io.exists(cachename) or (os.difftime(os.time(),lfs.attributes(cachename).modification) > (thresholds[protocol] or threshold)) then
+ cached[original] = cachename
+ local handler = handlers[scheme]
+ if handler then
+ if trace_schemes then
+ report_schemes("fetching %a, protocol %a, method %a",original,scheme,"built-in")
+ end
+ logs.flush()
+ handler(specification,cachename)
+ else
+ if trace_schemes then
+ report_schemes("fetching %a, protocol %a, method %a",original,scheme,"curl")
+ end
+ logs.flush()
+ runcurl(original,cachename)
+ end
+ end
+ if io.exists(cachename) then
+ cached[original] = cachename
+ if trace_schemes then
+ report_schemes("using cached %a, protocol %a, cachename %a",original,scheme,cachename)
+ end
+ else
+ cached[original] = ""
+ if trace_schemes then
+ report_schemes("using missing %a, protocol %a",original,scheme)
+ end
+ end
+ loaded[scheme] = loaded[scheme] + 1
+ statistics.stoptiming(schemes)
+ else
+ if trace_schemes then
+ report_schemes("reusing %a, protocol %a",original,scheme)
+ end
+ reused[scheme] = reused[scheme] + 1
+ end
+ return cached[original]
+end
+
+local function finder(specification,filetype)
+ return resolvers.methodhandler("finders",fetch(specification),filetype)
+end
+
+local opener = openers.file
+local loader = loaders.file
+
+local function install(scheme,handler,newthreshold)
+ handlers [scheme] = handler
+ loaded [scheme] = 0
+ reused [scheme] = 0
+ finders [scheme] = finder
+ openers [scheme] = opener
+ loaders [scheme] = loader
+ thresholds[scheme] = newthreshold or threshold
+end
+
+schemes.install = install
+
+local function http_handler(specification,cachename)
+ local tempname = cachename .. ".tmp"
+ local f = io.open(tempname,"wb")
+ local status, message = http.request {
+ url = specification.original,
+ sink = ltn12.sink.file(f)
+ }
+ if not status then
+ os.remove(tempname)
+ else
+ os.remove(cachename)
+ os.rename(tempname,cachename)
+ end
+ return cachename
+end
+
+install('http',http_handler)
+install('https') -- see pod
+install('ftp')
+
+statistics.register("scheme handling time", function()
+ local l, r, nl, nr = { }, { }, 0, 0
+ for k, v in table.sortedhash(loaded) do
+ if v > 0 then
+ nl = nl + 1
+ l[nl] = k .. ":" .. v
+ end
+ end
+ for k, v in table.sortedhash(reused) do
+ if v > 0 then
+ nr = nr + 1
+ r[nr] = k .. ":" .. v
+ end
+ end
+ local n = nl + nr
+ if n > 0 then
+ l = nl > 0 and concat(l) or "none"
+ r = nr > 0 and concat(r) or "none"
+ return format("%s seconds, %s processed, threshold %s seconds, loaded: %s, reused: %s",
+ statistics.elapsedtime(schemes), n, threshold, l, r)
+ else
+ return nil
+ end
+end)
+
+-- We provide a few more helpers:
+
+----- http = require("socket.http")
+local httprequest = http.request
+local toquery = url.toquery
+
+-- local function httprequest(url)
+-- return os.resultof(format("curl --silent %q", url))
+-- end
+
+local function fetchstring(url,data)
+ local q = data and toquery(data)
+ if q then
+ url = url .. "?" .. q
+ end
+ local reply = httprequest(url)
+ return reply -- just one argument
+end
+
+schemes.fetchstring = fetchstring
+
+function schemes.fetchtable(url,data)
+ local reply = fetchstring(url,data)
+ if reply then
+ local s = load("return " .. reply)
+ if s then
+ return s()
+ end
+ end
+end
diff --git a/tex/context/base/data-tex.lua b/tex/context/base/data-tex.lua
index 18e318f43..f5c986d77 100644
--- a/tex/context/base/data-tex.lua
+++ b/tex/context/base/data-tex.lua
@@ -1,183 +1,183 @@
-if not modules then modules = { } end modules ['data-tex'] = {
- 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 char = string.char
-local insert, remove = table.insert, table.remove
-
-local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
-
-local report_tex = logs.reporter("resolvers","tex")
-
-local resolvers = resolvers
-
-local sequencers = utilities.sequencers
-local methodhandler = resolvers.methodhandler
-local splitlines = string.splitlines
-local utffiletype = utf.filetype
-
--- local fileprocessor = nil
--- local lineprocessor = nil
-
-local textfileactions = sequencers.new {
- arguments = "str,filename,coding",
- returnvalues = "str",
- results = "str",
-}
-
-local textlineactions = sequencers.new {
- arguments = "str,filename,linenumber,noflines,coding",
- returnvalues = "str",
- results = "str",
-}
-
-local helpers = resolvers.openers.helpers
-local appendgroup = sequencers.appendgroup
-
-helpers.textfileactions = textfileactions
-helpers.textlineactions = textlineactions
-
-appendgroup(textfileactions,"before") -- user
-appendgroup(textfileactions,"system") -- private
-appendgroup(textfileactions,"after" ) -- user
-
-appendgroup(textlineactions,"before") -- user
-appendgroup(textlineactions,"system") -- private
-appendgroup(textlineactions,"after" ) -- user
-
-local ctrl_d = char( 4) -- unix
-local ctrl_z = char(26) -- windows
-
-resolvers.inputstack = resolvers.inputstack or { }
-
-local inputstack = resolvers.inputstack
-
-function helpers.textopener(tag,filename,filehandle,coding)
- local lines
- local t_filehandle = type(filehandle)
- if not filehandle then
- lines = io.loaddata(filename)
- elseif t_filehandle == "string" then
- lines = filehandle
- elseif t_filehandle == "table" then
- lines = filehandle
- else
- lines = filehandle:read("*a") -- io.readall(filehandle) ... but never that large files anyway
- -- lines = io.readall(filehandle)
- filehandle:close()
- end
- if type(lines) == "string" then
- local coding = coding or utffiletype(lines) -- so we can signal no regime
- if trace_locating then
- report_tex("%a opener: %a opened using method %a",tag,filename,coding)
- end
- if coding == "utf-16-be" then
- lines = utf.utf16_to_utf8_be(lines)
- elseif coding == "utf-16-le" then
- lines = utf.utf16_to_utf8_le(lines)
- elseif coding == "utf-32-be" then
- lines = utf.utf32_to_utf8_be(lines)
- elseif coding == "utf-32-le" then
- lines = utf.utf32_to_utf8_le(lines)
- else -- utf8 or unknown (could be a mkvi file)
- local runner = textfileactions.runner
- if runner then
- lines = runner(lines,filename,coding) or lines
- end
- lines = splitlines(lines)
- end
- elseif trace_locating then
- report_tex("%a opener: %a opened",tag,filename)
- end
- local noflines = #lines
- if lines[noflines] == "" then -- maybe some special check is needed
- lines[noflines] = nil
- end
- logs.show_open(filename)
- insert(inputstack,filename)
- return {
- filename = filename,
- noflines = noflines,
- currentline = 0,
- close = function()
- if trace_locating then
- report_tex("%a closer: %a closed",tag,filename)
- end
- logs.show_close(filename)
- remove(inputstack)
- t = nil
- end,
- reader = function(self)
- self = self or t
- local currentline, noflines = self.currentline, self.noflines
- if currentline >= noflines then
- return nil
- else
- currentline = currentline + 1
- self.currentline = currentline
- local content = lines[currentline]
- if not content then
- return nil
- elseif content == "" then
- return ""
- -- elseif content == ctrl_d or ctrl_z then
- -- return nil -- we need this as \endinput does not work in prints
- else
- local runner = textlineactions.runner
- if runner then
- return runner(content,filename,currentline,noflines,coding) or content
- else
- return content
- end
- end
- end
- end
- }
-end
-
-function resolvers.findtexfile(filename,filetype)
- return methodhandler('finders',filename,filetype)
-end
-
-function resolvers.opentexfile(filename)
- return methodhandler('openers',filename)
-end
-
-function resolvers.openfile(filename)
- local fullname = methodhandler('finders',filename)
- return fullname and fullname ~= "" and methodhandler('openers',fullname) or nil
-end
-
-function resolvers.loadtexfile(filename,filetype)
- -- todo: optionally apply filters
- local ok, data, size = resolvers.loadbinfile(filename, filetype)
- return data or ""
-end
-
-resolvers.texdatablob = resolvers.loadtexfile
-
-local function installhandler(namespace,what,where,func)
- if not func then
- where, func = "after", where
- end
- if where == "before" or where == "after" then
- sequencers.appendaction(namespace,where,func)
- else
- report_tex("installing input %a handlers in %a is not possible",what,tostring(where))
- end
-end
-
-function resolvers.installinputlinehandler(...) installhandler(helpers.textlineactions,"line",...) end
-function resolvers.installinputfilehandler(...) installhandler(helpers.textfileactions,"file",...) end
-
--- local basename = file.basename
--- resolvers.installinputlinehandler(function(str,filename,linenumber,noflines)
--- report_tex("[lc] file %a, line %a of %a, length %a",basename(filename),linenumber,noflines,#str)
--- end)
--- resolvers.installinputfilehandler(function(str,filename)
--- report_tex("[fc] file %a, length %a",basename(filename),#str)
--- end)
+if not modules then modules = { } end modules ['data-tex'] = {
+ 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 char = string.char
+local insert, remove = table.insert, table.remove
+
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+
+local report_tex = logs.reporter("resolvers","tex")
+
+local resolvers = resolvers
+
+local sequencers = utilities.sequencers
+local methodhandler = resolvers.methodhandler
+local splitlines = string.splitlines
+local utffiletype = utf.filetype
+
+-- local fileprocessor = nil
+-- local lineprocessor = nil
+
+local textfileactions = sequencers.new {
+ arguments = "str,filename,coding",
+ returnvalues = "str",
+ results = "str",
+}
+
+local textlineactions = sequencers.new {
+ arguments = "str,filename,linenumber,noflines,coding",
+ returnvalues = "str",
+ results = "str",
+}
+
+local helpers = resolvers.openers.helpers
+local appendgroup = sequencers.appendgroup
+
+helpers.textfileactions = textfileactions
+helpers.textlineactions = textlineactions
+
+appendgroup(textfileactions,"before") -- user
+appendgroup(textfileactions,"system") -- private
+appendgroup(textfileactions,"after" ) -- user
+
+appendgroup(textlineactions,"before") -- user
+appendgroup(textlineactions,"system") -- private
+appendgroup(textlineactions,"after" ) -- user
+
+local ctrl_d = char( 4) -- unix
+local ctrl_z = char(26) -- windows
+
+resolvers.inputstack = resolvers.inputstack or { }
+
+local inputstack = resolvers.inputstack
+
+function helpers.textopener(tag,filename,filehandle,coding)
+ local lines
+ local t_filehandle = type(filehandle)
+ if not filehandle then
+ lines = io.loaddata(filename)
+ elseif t_filehandle == "string" then
+ lines = filehandle
+ elseif t_filehandle == "table" then
+ lines = filehandle
+ else
+ lines = filehandle:read("*a") -- io.readall(filehandle) ... but never that large files anyway
+ -- lines = io.readall(filehandle)
+ filehandle:close()
+ end
+ if type(lines) == "string" then
+ local coding = coding or utffiletype(lines) -- so we can signal no regime
+ if trace_locating then
+ report_tex("%a opener: %a opened using method %a",tag,filename,coding)
+ end
+ if coding == "utf-16-be" then
+ lines = utf.utf16_to_utf8_be(lines)
+ elseif coding == "utf-16-le" then
+ lines = utf.utf16_to_utf8_le(lines)
+ elseif coding == "utf-32-be" then
+ lines = utf.utf32_to_utf8_be(lines)
+ elseif coding == "utf-32-le" then
+ lines = utf.utf32_to_utf8_le(lines)
+ else -- utf8 or unknown (could be a mkvi file)
+ local runner = textfileactions.runner
+ if runner then
+ lines = runner(lines,filename,coding) or lines
+ end
+ lines = splitlines(lines)
+ end
+ elseif trace_locating then
+ report_tex("%a opener: %a opened",tag,filename)
+ end
+ local noflines = #lines
+ if lines[noflines] == "" then -- maybe some special check is needed
+ lines[noflines] = nil
+ end
+ logs.show_open(filename)
+ insert(inputstack,filename)
+ return {
+ filename = filename,
+ noflines = noflines,
+ currentline = 0,
+ close = function()
+ if trace_locating then
+ report_tex("%a closer: %a closed",tag,filename)
+ end
+ logs.show_close(filename)
+ remove(inputstack)
+ t = nil
+ end,
+ reader = function(self)
+ self = self or t
+ local currentline, noflines = self.currentline, self.noflines
+ if currentline >= noflines then
+ return nil
+ else
+ currentline = currentline + 1
+ self.currentline = currentline
+ local content = lines[currentline]
+ if not content then
+ return nil
+ elseif content == "" then
+ return ""
+ -- elseif content == ctrl_d or ctrl_z then
+ -- return nil -- we need this as \endinput does not work in prints
+ else
+ local runner = textlineactions.runner
+ if runner then
+ return runner(content,filename,currentline,noflines,coding) or content
+ else
+ return content
+ end
+ end
+ end
+ end
+ }
+end
+
+function resolvers.findtexfile(filename,filetype)
+ return methodhandler('finders',filename,filetype)
+end
+
+function resolvers.opentexfile(filename)
+ return methodhandler('openers',filename)
+end
+
+function resolvers.openfile(filename)
+ local fullname = methodhandler('finders',filename)
+ return fullname and fullname ~= "" and methodhandler('openers',fullname) or nil
+end
+
+function resolvers.loadtexfile(filename,filetype)
+ -- todo: optionally apply filters
+ local ok, data, size = resolvers.loadbinfile(filename, filetype)
+ return data or ""
+end
+
+resolvers.texdatablob = resolvers.loadtexfile
+
+local function installhandler(namespace,what,where,func)
+ if not func then
+ where, func = "after", where
+ end
+ if where == "before" or where == "after" then
+ sequencers.appendaction(namespace,where,func)
+ else
+ report_tex("installing input %a handlers in %a is not possible",what,tostring(where))
+ end
+end
+
+function resolvers.installinputlinehandler(...) installhandler(helpers.textlineactions,"line",...) end
+function resolvers.installinputfilehandler(...) installhandler(helpers.textfileactions,"file",...) end
+
+-- local basename = file.basename
+-- resolvers.installinputlinehandler(function(str,filename,linenumber,noflines)
+-- report_tex("[lc] file %a, line %a of %a, length %a",basename(filename),linenumber,noflines,#str)
+-- end)
+-- resolvers.installinputfilehandler(function(str,filename)
+-- report_tex("[fc] file %a, length %a",basename(filename),#str)
+-- end)
diff --git a/tex/context/base/data-tmf.lua b/tex/context/base/data-tmf.lua
index 8300c3560..c52225193 100644
--- a/tex/context/base/data-tmf.lua
+++ b/tex/context/base/data-tmf.lua
@@ -1,73 +1,73 @@
-if not modules then modules = { } end modules ['data-tmf'] = {
- 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 resolvers = resolvers
-
-local report_tds = logs.reporter("resolvers","tds")
-
--- = <<
--- ? ??
--- < +=
--- > =+
-
-function resolvers.load_tree(tree,resolve)
- if type(tree) == "string" and tree ~= "" then
-
- local getenv, setenv = resolvers.getenv, resolvers.setenv
-
- -- later might listen to the raw osenv var as well
- local texos = "texmf-" .. os.platform
-
- local oldroot = environment.texroot
- local newroot = file.collapsepath(tree)
-
- local newtree = file.join(newroot,texos)
- local newpath = file.join(newtree,"bin")
-
- if not lfs.isdir(newtree) then
- report_tds("no %a under tree %a",texos,tree)
- os.exit()
- end
- if not lfs.isdir(newpath) then
- report_tds("no '%s/bin' under tree %a",texos,tree)
- os.exit()
- end
-
- local texmfos = newtree
-
- environment.texroot = newroot
- environment.texos = texos
- environment.texmfos = texmfos
-
- -- Beware, we need to obey the relocatable autoparent so we
- -- set TEXMFCNF to its raw value. This is somewhat tricky when
- -- we run a mkii job from within. Therefore, in mtxrun, there
- -- is a resolve applied when we're in mkii/kpse mode or when
- -- --resolve is passed to mtxrun. Maybe we should also set the
- -- local AUTOPARENT etc. although these are alwasy set new.
-
- if resolve then
- -- resolvers.luacnfspec = resolvers.joinpath(resolvers.resolve(resolvers.expandedpathfromlist(resolvers.splitpath(resolvers.luacnfspec))))
- resolvers.luacnfspec = resolvers.resolve(resolvers.luacnfspec)
- end
-
- setenv('SELFAUTOPARENT', newroot)
- setenv('SELFAUTODIR', newtree)
- setenv('SELFAUTOLOC', newpath)
- setenv('TEXROOT', newroot)
- setenv('TEXOS', texos)
- setenv('TEXMFOS', texmfos)
- setenv('TEXMFCNF', resolvers.luacnfspec,true) -- already resolved
- setenv('PATH', newpath .. io.pathseparator .. getenv('PATH'))
-
- report_tds("changing from root %a to %a",oldroot,newroot)
- report_tds("prepending %a to PATH",newpath)
- report_tds("setting TEXMFCNF to %a",resolvers.luacnfspec)
- report_tds()
- end
-end
+if not modules then modules = { } end modules ['data-tmf'] = {
+ 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 resolvers = resolvers
+
+local report_tds = logs.reporter("resolvers","tds")
+
+-- = <<
+-- ? ??
+-- < +=
+-- > =+
+
+function resolvers.load_tree(tree,resolve)
+ if type(tree) == "string" and tree ~= "" then
+
+ local getenv, setenv = resolvers.getenv, resolvers.setenv
+
+ -- later might listen to the raw osenv var as well
+ local texos = "texmf-" .. os.platform
+
+ local oldroot = environment.texroot
+ local newroot = file.collapsepath(tree)
+
+ local newtree = file.join(newroot,texos)
+ local newpath = file.join(newtree,"bin")
+
+ if not lfs.isdir(newtree) then
+ report_tds("no %a under tree %a",texos,tree)
+ os.exit()
+ end
+ if not lfs.isdir(newpath) then
+ report_tds("no '%s/bin' under tree %a",texos,tree)
+ os.exit()
+ end
+
+ local texmfos = newtree
+
+ environment.texroot = newroot
+ environment.texos = texos
+ environment.texmfos = texmfos
+
+ -- Beware, we need to obey the relocatable autoparent so we
+ -- set TEXMFCNF to its raw value. This is somewhat tricky when
+ -- we run a mkii job from within. Therefore, in mtxrun, there
+ -- is a resolve applied when we're in mkii/kpse mode or when
+ -- --resolve is passed to mtxrun. Maybe we should also set the
+ -- local AUTOPARENT etc. although these are alwasy set new.
+
+ if resolve then
+ -- resolvers.luacnfspec = resolvers.joinpath(resolvers.resolve(resolvers.expandedpathfromlist(resolvers.splitpath(resolvers.luacnfspec))))
+ resolvers.luacnfspec = resolvers.resolve(resolvers.luacnfspec)
+ end
+
+ setenv('SELFAUTOPARENT', newroot)
+ setenv('SELFAUTODIR', newtree)
+ setenv('SELFAUTOLOC', newpath)
+ setenv('TEXROOT', newroot)
+ setenv('TEXOS', texos)
+ setenv('TEXMFOS', texmfos)
+ setenv('TEXMFCNF', resolvers.luacnfspec,true) -- already resolved
+ setenv('PATH', newpath .. io.pathseparator .. getenv('PATH'))
+
+ report_tds("changing from root %a to %a",oldroot,newroot)
+ report_tds("prepending %a to PATH",newpath)
+ report_tds("setting TEXMFCNF to %a",resolvers.luacnfspec)
+ report_tds()
+ end
+end
diff --git a/tex/context/base/data-tmp.lua b/tex/context/base/data-tmp.lua
index 2f12ecfb9..5025a8a0a 100644
--- a/tex/context/base/data-tmp.lua
+++ b/tex/context/base/data-tmp.lua
@@ -1,420 +1,420 @@
-if not modules then modules = { } end modules ['data-tmp'] = {
- version = 1.100,
- 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>This module deals with caching data. It sets up the paths and
-implements loaders and savers for tables. Best is to set the
-following variable. When not set, the usual paths will be
-checked. Personally I prefer the (users) temporary path.</p>
-
-</code>
-TEXMFCACHE=$TMP;$TEMP;$TMPDIR;$TEMPDIR;$HOME;$TEXMFVAR;$VARTEXMF;.
-</code>
-
-<p>Currently we do no locking when we write files. This is no real
-problem because most caching involves fonts and the chance of them
-being written at the same time is small. We also need to extend
-luatools with a recache feature.</p>
---ldx]]--
-
-local format, lower, gsub, concat = string.format, string.lower, string.gsub, table.concat
-local concat, serialize, serializetofile = table.concat, table.serialize, table.tofile
-local mkdirs, isdir, isfile = dir.mkdirs, lfs.isdir, lfs.isfile
-local addsuffix, is_writable, is_readable = file.addsuffix, file.is_writable, file.is_readable
-local formatters = string.formatters
-
-local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
-local trace_cache = false trackers.register("resolvers.cache", function(v) trace_cache = v end)
-
-local report_caches = logs.reporter("resolvers","caches")
-local report_resolvers = logs.reporter("resolvers","caching")
-
-local resolvers = resolvers
-
--- intermezzo
-
-local directive_cleanup = false directives.register("system.compile.cleanup", function(v) directive_cleanup = v end)
-local directive_strip = false directives.register("system.compile.strip", function(v) directive_strip = v end)
-
-local compile = utilities.lua.compile
-
-function utilities.lua.compile(luafile,lucfile,cleanup,strip)
- if cleanup == nil then cleanup = directive_cleanup end
- if strip == nil then strip = directive_strip end
- return compile(luafile,lucfile,cleanup,strip)
-end
-
--- end of intermezzo
-
-caches = caches or { }
-local caches = caches
-
-local luasuffixes = utilities.lua.suffixes
-
-caches.base = caches.base or "luatex-cache"
-caches.more = caches.more or "context"
-caches.direct = false -- true is faster but may need huge amounts of memory
-caches.tree = false
-caches.force = true
-caches.ask = false
-caches.relocate = false
-caches.defaults = { "TMPDIR", "TEMPDIR", "TMP", "TEMP", "HOME", "HOMEPATH" }
-
-local writable, readables, usedreadables = nil, { }, { }
-
--- we could use a metatable for writable and readable but not yet
-
-local function identify()
- -- Combining the loops makes it messy. First we check the format cache path
- -- and when the last component is not present we try to create it.
- local texmfcaches = resolvers.cleanpathlist("TEXMFCACHE")
- if texmfcaches then
- for k=1,#texmfcaches do
- local cachepath = texmfcaches[k]
- if cachepath ~= "" then
- cachepath = resolvers.resolve(cachepath)
- cachepath = resolvers.cleanpath(cachepath)
- cachepath = file.collapsepath(cachepath)
- local valid = isdir(cachepath)
- if valid then
- if is_readable(cachepath) then
- readables[#readables+1] = cachepath
- if not writable and is_writable(cachepath) then
- writable = cachepath
- end
- end
- elseif not writable and caches.force then
- local cacheparent = file.dirname(cachepath)
- if is_writable(cacheparent) and true then -- we go on anyway (needed for mojca's kind of paths)
- if not caches.ask or io.ask(format("\nShould I create the cache path %s?",cachepath), "no", { "yes", "no" }) == "yes" then
- mkdirs(cachepath)
- if isdir(cachepath) and is_writable(cachepath) then
- report_caches("path %a created",cachepath)
- writable = cachepath
- readables[#readables+1] = cachepath
- end
- end
- end
- end
- end
- end
- end
- -- As a last resort we check some temporary paths but this time we don't
- -- create them.
- local texmfcaches = caches.defaults
- if texmfcaches then
- for k=1,#texmfcaches do
- local cachepath = texmfcaches[k]
- cachepath = resolvers.expansion(cachepath) -- was getenv
- if cachepath ~= "" then
- cachepath = resolvers.resolve(cachepath)
- cachepath = resolvers.cleanpath(cachepath)
- local valid = isdir(cachepath)
- if valid and is_readable(cachepath) then
- if not writable and is_writable(cachepath) then
- readables[#readables+1] = cachepath
- writable = cachepath
- break
- end
- end
- end
- end
- end
- -- Some extra checking. If we have no writable or readable path then we simply
- -- quit.
- if not writable then
- report_caches("fatal error: there is no valid writable cache path defined")
- os.exit()
- elseif #readables == 0 then
- report_caches("fatal error: there is no valid readable cache path defined")
- os.exit()
- end
- -- why here
- writable = dir.expandname(resolvers.cleanpath(writable)) -- just in case
- -- moved here
- local base, more, tree = caches.base, caches.more, caches.tree or caches.treehash() -- we have only one writable tree
- if tree then
- caches.tree = tree
- writable = mkdirs(writable,base,more,tree)
- for i=1,#readables do
- readables[i] = file.join(readables[i],base,more,tree)
- end
- else
- writable = mkdirs(writable,base,more)
- for i=1,#readables do
- readables[i] = file.join(readables[i],base,more)
- end
- end
- -- end
- if trace_cache then
- for i=1,#readables do
- report_caches("using readable path %a (order %s)",readables[i],i)
- end
- report_caches("using writable path %a",writable)
- end
- identify = function()
- return writable, readables
- end
- return writable, readables
-end
-
-function caches.usedpaths(separator)
- local writable, readables = identify()
- if #readables > 1 then
- local result = { }
- local done = { }
- for i=1,#readables do
- local readable = readables[i]
- if readable == writable then
- done[readable] = true
- result[#result+1] = formatters["readable+writable: %a"](readable)
- elseif usedreadables[i] then
- done[readable] = true
- result[#result+1] = formatters["readable: %a"](readable)
- end
- end
- if not done[writable] then
- result[#result+1] = formatters["writable: %a"](writable)
- end
- return concat(result,separator or " | ")
- else
- return writable or "?"
- end
-end
-
-function caches.configfiles()
- return concat(resolvers.instance.specification,";")
-end
-
-function caches.hashed(tree)
- tree = gsub(tree,"[\\/]+$","")
- tree = lower(tree)
- local hash = md5.hex(tree)
- if trace_cache or trace_locating then
- report_caches("hashing tree %a, hash %a",tree,hash)
- end
- return hash
-end
-
-function caches.treehash()
- local tree = caches.configfiles()
- if not tree or tree == "" then
- return false
- else
- return caches.hashed(tree)
- end
-end
-
-local r_cache, w_cache = { }, { } -- normally w in in r but who cares
-
-local function getreadablepaths(...)
- local tags = { ... }
- local hash = concat(tags,"/")
- local done = r_cache[hash]
- if not done then
- local writable, readables = identify() -- exit if not found
- if #tags > 0 then
- done = { }
- for i=1,#readables do
- done[i] = file.join(readables[i],...)
- end
- else
- done = readables
- end
- r_cache[hash] = done
- end
- return done
-end
-
-local function getwritablepath(...)
- local tags = { ... }
- local hash = concat(tags,"/")
- local done = w_cache[hash]
- if not done then
- local writable, readables = identify() -- exit if not found
- if #tags > 0 then
- done = mkdirs(writable,...)
- else
- done = writable
- end
- w_cache[hash] = done
- end
- return done
-end
-
-caches.getreadablepaths = getreadablepaths
-caches.getwritablepath = getwritablepath
-
-function caches.getfirstreadablefile(filename,...)
- local rd = getreadablepaths(...)
- for i=1,#rd do
- local path = rd[i]
- local fullname = file.join(path,filename)
- if is_readable(fullname) then
- usedreadables[i] = true
- return fullname, path
- end
- end
- return caches.setfirstwritablefile(filename,...)
-end
-
-function caches.setfirstwritablefile(filename,...)
- local wr = getwritablepath(...)
- local fullname = file.join(wr,filename)
- return fullname, wr
-end
-
-function caches.define(category,subcategory) -- for old times sake
- return function()
- return getwritablepath(category,subcategory)
- end
-end
-
-function caches.setluanames(path,name)
- return format("%s/%s.%s",path,name,luasuffixes.tma), format("%s/%s.%s",path,name,luasuffixes.tmc)
-end
-
-function caches.loaddata(readables,name)
- if type(readables) == "string" then
- readables = { readables }
- end
- for i=1,#readables do
- local path = readables[i]
- local tmaname, tmcname = caches.setluanames(path,name)
- local loader = false
- if isfile(tmcname) then
- loader = loadfile(tmcname)
- end
- if not loader and isfile(tmaname) then
- -- in case we have a different engine
- utilities.lua.compile(tmaname,tmcname)
- if isfile(tmcname) then
- loader = loadfile(tmcname)
- end
- if not loader then
- loader = loadfile(tmaname)
- end
- end
- if loader then
- loader = loader()
- collectgarbage("step")
- return loader
- end
- end
- return false
-end
-
-function caches.is_writable(filepath,filename)
- local tmaname, tmcname = caches.setluanames(filepath,filename)
- return is_writable(tmaname)
-end
-
-local saveoptions = { compact = true }
-
--- add some point we will only use the internal bytecode compiler and
--- then we can flag success in the tma so that it can trigger a compile
--- if the other engine
-
-function caches.savedata(filepath,filename,data,raw)
- local tmaname, tmcname = caches.setluanames(filepath,filename)
- local reduce, simplify = true, true
- if raw then
- reduce, simplify = false, false
- end
- data.cache_uuid = os.uuid()
- if caches.direct then
- file.savedata(tmaname,serialize(data,true,saveoptions))
- else
- serializetofile(tmaname,data,true,saveoptions)
- end
- utilities.lua.compile(tmaname,tmcname)
-end
-
--- moved from data-res:
-
-local content_state = { }
-
-function caches.contentstate()
- return content_state or { }
-end
-
-function caches.loadcontent(cachename,dataname)
- local name = caches.hashed(cachename)
- local full, path = caches.getfirstreadablefile(addsuffix(name,luasuffixes.lua),"trees")
- local filename = file.join(path,name)
- local blob = loadfile(addsuffix(filename,luasuffixes.luc)) or loadfile(addsuffix(filename,luasuffixes.lua))
- if blob then
- local data = blob()
- if data and data.content then
- if data.type == dataname then
- if data.version == resolvers.cacheversion then
- content_state[#content_state+1] = data.uuid
- if trace_locating then
- report_resolvers("loading %a for %a from %a",dataname,cachename,filename)
- end
- return data.content
- else
- report_resolvers("skipping %a for %a from %a (version mismatch)",dataname,cachename,filename)
- end
- else
- report_resolvers("skipping %a for %a from %a (datatype mismatch)",dataname,cachename,filename)
- end
- elseif trace_locating then
- report_resolvers("skipping %a for %a from %a (no content)",dataname,cachename,filename)
- end
- elseif trace_locating then
- report_resolvers("skipping %a for %a from %a (invalid file)",dataname,cachename,filename)
- end
-end
-
-function caches.collapsecontent(content)
- for k, v in next, content do
- if type(v) == "table" and #v == 1 then
- content[k] = v[1]
- end
- end
-end
-
-function caches.savecontent(cachename,dataname,content)
- local name = caches.hashed(cachename)
- local full, path = caches.setfirstwritablefile(addsuffix(name,luasuffixes.lua),"trees")
- local filename = file.join(path,name) -- is full
- local luaname = addsuffix(filename,luasuffixes.lua)
- local lucname = addsuffix(filename,luasuffixes.luc)
- if trace_locating then
- report_resolvers("preparing %a for %a",dataname,cachename)
- end
- local data = {
- type = dataname,
- root = cachename,
- version = resolvers.cacheversion,
- date = os.date("%Y-%m-%d"),
- time = os.date("%H:%M:%S"),
- content = content,
- uuid = os.uuid(),
- }
- local ok = io.savedata(luaname,serialize(data,true))
- if ok then
- if trace_locating then
- report_resolvers("category %a, cachename %a saved in %a",dataname,cachename,luaname)
- end
- if utilities.lua.compile(luaname,lucname) then
- if trace_locating then
- report_resolvers("%a compiled to %a",dataname,lucname)
- end
- return true
- else
- if trace_locating then
- report_resolvers("compiling failed for %a, deleting file %a",dataname,lucname)
- end
- os.remove(lucname)
- end
- elseif trace_locating then
- report_resolvers("unable to save %a in %a (access error)",dataname,luaname)
- end
-end
+if not modules then modules = { } end modules ['data-tmp'] = {
+ version = 1.100,
+ 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>This module deals with caching data. It sets up the paths and
+implements loaders and savers for tables. Best is to set the
+following variable. When not set, the usual paths will be
+checked. Personally I prefer the (users) temporary path.</p>
+
+</code>
+TEXMFCACHE=$TMP;$TEMP;$TMPDIR;$TEMPDIR;$HOME;$TEXMFVAR;$VARTEXMF;.
+</code>
+
+<p>Currently we do no locking when we write files. This is no real
+problem because most caching involves fonts and the chance of them
+being written at the same time is small. We also need to extend
+luatools with a recache feature.</p>
+--ldx]]--
+
+local format, lower, gsub, concat = string.format, string.lower, string.gsub, table.concat
+local concat, serialize, serializetofile = table.concat, table.serialize, table.tofile
+local mkdirs, isdir, isfile = dir.mkdirs, lfs.isdir, lfs.isfile
+local addsuffix, is_writable, is_readable = file.addsuffix, file.is_writable, file.is_readable
+local formatters = string.formatters
+
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+local trace_cache = false trackers.register("resolvers.cache", function(v) trace_cache = v end)
+
+local report_caches = logs.reporter("resolvers","caches")
+local report_resolvers = logs.reporter("resolvers","caching")
+
+local resolvers = resolvers
+
+-- intermezzo
+
+local directive_cleanup = false directives.register("system.compile.cleanup", function(v) directive_cleanup = v end)
+local directive_strip = false directives.register("system.compile.strip", function(v) directive_strip = v end)
+
+local compile = utilities.lua.compile
+
+function utilities.lua.compile(luafile,lucfile,cleanup,strip)
+ if cleanup == nil then cleanup = directive_cleanup end
+ if strip == nil then strip = directive_strip end
+ return compile(luafile,lucfile,cleanup,strip)
+end
+
+-- end of intermezzo
+
+caches = caches or { }
+local caches = caches
+
+local luasuffixes = utilities.lua.suffixes
+
+caches.base = caches.base or "luatex-cache"
+caches.more = caches.more or "context"
+caches.direct = false -- true is faster but may need huge amounts of memory
+caches.tree = false
+caches.force = true
+caches.ask = false
+caches.relocate = false
+caches.defaults = { "TMPDIR", "TEMPDIR", "TMP", "TEMP", "HOME", "HOMEPATH" }
+
+local writable, readables, usedreadables = nil, { }, { }
+
+-- we could use a metatable for writable and readable but not yet
+
+local function identify()
+ -- Combining the loops makes it messy. First we check the format cache path
+ -- and when the last component is not present we try to create it.
+ local texmfcaches = resolvers.cleanpathlist("TEXMFCACHE")
+ if texmfcaches then
+ for k=1,#texmfcaches do
+ local cachepath = texmfcaches[k]
+ if cachepath ~= "" then
+ cachepath = resolvers.resolve(cachepath)
+ cachepath = resolvers.cleanpath(cachepath)
+ cachepath = file.collapsepath(cachepath)
+ local valid = isdir(cachepath)
+ if valid then
+ if is_readable(cachepath) then
+ readables[#readables+1] = cachepath
+ if not writable and is_writable(cachepath) then
+ writable = cachepath
+ end
+ end
+ elseif not writable and caches.force then
+ local cacheparent = file.dirname(cachepath)
+ if is_writable(cacheparent) and true then -- we go on anyway (needed for mojca's kind of paths)
+ if not caches.ask or io.ask(format("\nShould I create the cache path %s?",cachepath), "no", { "yes", "no" }) == "yes" then
+ mkdirs(cachepath)
+ if isdir(cachepath) and is_writable(cachepath) then
+ report_caches("path %a created",cachepath)
+ writable = cachepath
+ readables[#readables+1] = cachepath
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ -- As a last resort we check some temporary paths but this time we don't
+ -- create them.
+ local texmfcaches = caches.defaults
+ if texmfcaches then
+ for k=1,#texmfcaches do
+ local cachepath = texmfcaches[k]
+ cachepath = resolvers.expansion(cachepath) -- was getenv
+ if cachepath ~= "" then
+ cachepath = resolvers.resolve(cachepath)
+ cachepath = resolvers.cleanpath(cachepath)
+ local valid = isdir(cachepath)
+ if valid and is_readable(cachepath) then
+ if not writable and is_writable(cachepath) then
+ readables[#readables+1] = cachepath
+ writable = cachepath
+ break
+ end
+ end
+ end
+ end
+ end
+ -- Some extra checking. If we have no writable or readable path then we simply
+ -- quit.
+ if not writable then
+ report_caches("fatal error: there is no valid writable cache path defined")
+ os.exit()
+ elseif #readables == 0 then
+ report_caches("fatal error: there is no valid readable cache path defined")
+ os.exit()
+ end
+ -- why here
+ writable = dir.expandname(resolvers.cleanpath(writable)) -- just in case
+ -- moved here
+ local base, more, tree = caches.base, caches.more, caches.tree or caches.treehash() -- we have only one writable tree
+ if tree then
+ caches.tree = tree
+ writable = mkdirs(writable,base,more,tree)
+ for i=1,#readables do
+ readables[i] = file.join(readables[i],base,more,tree)
+ end
+ else
+ writable = mkdirs(writable,base,more)
+ for i=1,#readables do
+ readables[i] = file.join(readables[i],base,more)
+ end
+ end
+ -- end
+ if trace_cache then
+ for i=1,#readables do
+ report_caches("using readable path %a (order %s)",readables[i],i)
+ end
+ report_caches("using writable path %a",writable)
+ end
+ identify = function()
+ return writable, readables
+ end
+ return writable, readables
+end
+
+function caches.usedpaths(separator)
+ local writable, readables = identify()
+ if #readables > 1 then
+ local result = { }
+ local done = { }
+ for i=1,#readables do
+ local readable = readables[i]
+ if readable == writable then
+ done[readable] = true
+ result[#result+1] = formatters["readable+writable: %a"](readable)
+ elseif usedreadables[i] then
+ done[readable] = true
+ result[#result+1] = formatters["readable: %a"](readable)
+ end
+ end
+ if not done[writable] then
+ result[#result+1] = formatters["writable: %a"](writable)
+ end
+ return concat(result,separator or " | ")
+ else
+ return writable or "?"
+ end
+end
+
+function caches.configfiles()
+ return concat(resolvers.instance.specification,";")
+end
+
+function caches.hashed(tree)
+ tree = gsub(tree,"[\\/]+$","")
+ tree = lower(tree)
+ local hash = md5.hex(tree)
+ if trace_cache or trace_locating then
+ report_caches("hashing tree %a, hash %a",tree,hash)
+ end
+ return hash
+end
+
+function caches.treehash()
+ local tree = caches.configfiles()
+ if not tree or tree == "" then
+ return false
+ else
+ return caches.hashed(tree)
+ end
+end
+
+local r_cache, w_cache = { }, { } -- normally w in in r but who cares
+
+local function getreadablepaths(...)
+ local tags = { ... }
+ local hash = concat(tags,"/")
+ local done = r_cache[hash]
+ if not done then
+ local writable, readables = identify() -- exit if not found
+ if #tags > 0 then
+ done = { }
+ for i=1,#readables do
+ done[i] = file.join(readables[i],...)
+ end
+ else
+ done = readables
+ end
+ r_cache[hash] = done
+ end
+ return done
+end
+
+local function getwritablepath(...)
+ local tags = { ... }
+ local hash = concat(tags,"/")
+ local done = w_cache[hash]
+ if not done then
+ local writable, readables = identify() -- exit if not found
+ if #tags > 0 then
+ done = mkdirs(writable,...)
+ else
+ done = writable
+ end
+ w_cache[hash] = done
+ end
+ return done
+end
+
+caches.getreadablepaths = getreadablepaths
+caches.getwritablepath = getwritablepath
+
+function caches.getfirstreadablefile(filename,...)
+ local rd = getreadablepaths(...)
+ for i=1,#rd do
+ local path = rd[i]
+ local fullname = file.join(path,filename)
+ if is_readable(fullname) then
+ usedreadables[i] = true
+ return fullname, path
+ end
+ end
+ return caches.setfirstwritablefile(filename,...)
+end
+
+function caches.setfirstwritablefile(filename,...)
+ local wr = getwritablepath(...)
+ local fullname = file.join(wr,filename)
+ return fullname, wr
+end
+
+function caches.define(category,subcategory) -- for old times sake
+ return function()
+ return getwritablepath(category,subcategory)
+ end
+end
+
+function caches.setluanames(path,name)
+ return format("%s/%s.%s",path,name,luasuffixes.tma), format("%s/%s.%s",path,name,luasuffixes.tmc)
+end
+
+function caches.loaddata(readables,name)
+ if type(readables) == "string" then
+ readables = { readables }
+ end
+ for i=1,#readables do
+ local path = readables[i]
+ local tmaname, tmcname = caches.setluanames(path,name)
+ local loader = false
+ if isfile(tmcname) then
+ loader = loadfile(tmcname)
+ end
+ if not loader and isfile(tmaname) then
+ -- in case we have a different engine
+ utilities.lua.compile(tmaname,tmcname)
+ if isfile(tmcname) then
+ loader = loadfile(tmcname)
+ end
+ if not loader then
+ loader = loadfile(tmaname)
+ end
+ end
+ if loader then
+ loader = loader()
+ collectgarbage("step")
+ return loader
+ end
+ end
+ return false
+end
+
+function caches.is_writable(filepath,filename)
+ local tmaname, tmcname = caches.setluanames(filepath,filename)
+ return is_writable(tmaname)
+end
+
+local saveoptions = { compact = true }
+
+-- add some point we will only use the internal bytecode compiler and
+-- then we can flag success in the tma so that it can trigger a compile
+-- if the other engine
+
+function caches.savedata(filepath,filename,data,raw)
+ local tmaname, tmcname = caches.setluanames(filepath,filename)
+ local reduce, simplify = true, true
+ if raw then
+ reduce, simplify = false, false
+ end
+ data.cache_uuid = os.uuid()
+ if caches.direct then
+ file.savedata(tmaname,serialize(data,true,saveoptions))
+ else
+ serializetofile(tmaname,data,true,saveoptions)
+ end
+ utilities.lua.compile(tmaname,tmcname)
+end
+
+-- moved from data-res:
+
+local content_state = { }
+
+function caches.contentstate()
+ return content_state or { }
+end
+
+function caches.loadcontent(cachename,dataname)
+ local name = caches.hashed(cachename)
+ local full, path = caches.getfirstreadablefile(addsuffix(name,luasuffixes.lua),"trees")
+ local filename = file.join(path,name)
+ local blob = loadfile(addsuffix(filename,luasuffixes.luc)) or loadfile(addsuffix(filename,luasuffixes.lua))
+ if blob then
+ local data = blob()
+ if data and data.content then
+ if data.type == dataname then
+ if data.version == resolvers.cacheversion then
+ content_state[#content_state+1] = data.uuid
+ if trace_locating then
+ report_resolvers("loading %a for %a from %a",dataname,cachename,filename)
+ end
+ return data.content
+ else
+ report_resolvers("skipping %a for %a from %a (version mismatch)",dataname,cachename,filename)
+ end
+ else
+ report_resolvers("skipping %a for %a from %a (datatype mismatch)",dataname,cachename,filename)
+ end
+ elseif trace_locating then
+ report_resolvers("skipping %a for %a from %a (no content)",dataname,cachename,filename)
+ end
+ elseif trace_locating then
+ report_resolvers("skipping %a for %a from %a (invalid file)",dataname,cachename,filename)
+ end
+end
+
+function caches.collapsecontent(content)
+ for k, v in next, content do
+ if type(v) == "table" and #v == 1 then
+ content[k] = v[1]
+ end
+ end
+end
+
+function caches.savecontent(cachename,dataname,content)
+ local name = caches.hashed(cachename)
+ local full, path = caches.setfirstwritablefile(addsuffix(name,luasuffixes.lua),"trees")
+ local filename = file.join(path,name) -- is full
+ local luaname = addsuffix(filename,luasuffixes.lua)
+ local lucname = addsuffix(filename,luasuffixes.luc)
+ if trace_locating then
+ report_resolvers("preparing %a for %a",dataname,cachename)
+ end
+ local data = {
+ type = dataname,
+ root = cachename,
+ version = resolvers.cacheversion,
+ date = os.date("%Y-%m-%d"),
+ time = os.date("%H:%M:%S"),
+ content = content,
+ uuid = os.uuid(),
+ }
+ local ok = io.savedata(luaname,serialize(data,true))
+ if ok then
+ if trace_locating then
+ report_resolvers("category %a, cachename %a saved in %a",dataname,cachename,luaname)
+ end
+ if utilities.lua.compile(luaname,lucname) then
+ if trace_locating then
+ report_resolvers("%a compiled to %a",dataname,lucname)
+ end
+ return true
+ else
+ if trace_locating then
+ report_resolvers("compiling failed for %a, deleting file %a",dataname,lucname)
+ end
+ os.remove(lucname)
+ end
+ elseif trace_locating then
+ report_resolvers("unable to save %a in %a (access error)",dataname,luaname)
+ end
+end
diff --git a/tex/context/base/data-tre.lua b/tex/context/base/data-tre.lua
index 5fe8fc4f2..0a8b00d9b 100644
--- a/tex/context/base/data-tre.lua
+++ b/tex/context/base/data-tre.lua
@@ -1,75 +1,75 @@
-if not modules then modules = { } end modules ['data-tre'] = {
- 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"
-}
-
--- \input tree://oeps1/**/oeps.tex
-
-local find, gsub, format = string.find, string.gsub, string.format
-
-local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
-
-local report_trees = logs.reporter("resolvers","trees")
-
-local resolvers = resolvers
-
-local done, found, notfound = { }, { }, resolvers.finders.notfound
-
-function resolvers.finders.tree(specification)
- local spec = specification.filename
- local fnd = found[spec]
- if fnd == nil then
- if spec ~= "" then
- local path, name = file.dirname(spec), file.basename(spec)
- if path == "" then path = "." end
- local hash = done[path]
- if not hash then
- local pattern = path .. "/*" -- we will use the proper splitter
- hash = dir.glob(pattern)
- done[path] = hash
- end
- local pattern = "/" .. gsub(name,"([%.%-%+])", "%%%1") .. "$"
- for k=1,#hash do
- local v = hash[k]
- if find(v,pattern) then
- found[spec] = v
- return v
- end
- end
- end
- fnd = notfound() -- false
- found[spec] = fnd
- end
- return fnd
-end
-
-function resolvers.locators.tree(specification)
- local name = specification.filename
- local realname = resolvers.resolve(name) -- no shortcut
- if realname and realname ~= '' and lfs.isdir(realname) then
- if trace_locating then
- report_trees("locator %a found",realname)
- end
- resolvers.appendhash('tree',name,false) -- don't cache
- elseif trace_locating then
- report_trees("locator %a not found",name)
- end
-end
-
-function resolvers.hashers.tree(specification)
- local name = specification.filename
- if trace_locating then
- report_trees("analysing %a",name)
- end
- resolvers.methodhandler("hashers",name)
-
- resolvers.generators.file(specification)
-end
-
-resolvers.concatinators.tree = resolvers.concatinators.file
-resolvers.generators.tree = resolvers.generators.file
-resolvers.openers.tree = resolvers.openers.file
-resolvers.loaders.tree = resolvers.loaders.file
+if not modules then modules = { } end modules ['data-tre'] = {
+ 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"
+}
+
+-- \input tree://oeps1/**/oeps.tex
+
+local find, gsub, format = string.find, string.gsub, string.format
+
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+
+local report_trees = logs.reporter("resolvers","trees")
+
+local resolvers = resolvers
+
+local done, found, notfound = { }, { }, resolvers.finders.notfound
+
+function resolvers.finders.tree(specification)
+ local spec = specification.filename
+ local fnd = found[spec]
+ if fnd == nil then
+ if spec ~= "" then
+ local path, name = file.dirname(spec), file.basename(spec)
+ if path == "" then path = "." end
+ local hash = done[path]
+ if not hash then
+ local pattern = path .. "/*" -- we will use the proper splitter
+ hash = dir.glob(pattern)
+ done[path] = hash
+ end
+ local pattern = "/" .. gsub(name,"([%.%-%+])", "%%%1") .. "$"
+ for k=1,#hash do
+ local v = hash[k]
+ if find(v,pattern) then
+ found[spec] = v
+ return v
+ end
+ end
+ end
+ fnd = notfound() -- false
+ found[spec] = fnd
+ end
+ return fnd
+end
+
+function resolvers.locators.tree(specification)
+ local name = specification.filename
+ local realname = resolvers.resolve(name) -- no shortcut
+ if realname and realname ~= '' and lfs.isdir(realname) then
+ if trace_locating then
+ report_trees("locator %a found",realname)
+ end
+ resolvers.appendhash('tree',name,false) -- don't cache
+ elseif trace_locating then
+ report_trees("locator %a not found",name)
+ end
+end
+
+function resolvers.hashers.tree(specification)
+ local name = specification.filename
+ if trace_locating then
+ report_trees("analysing %a",name)
+ end
+ resolvers.methodhandler("hashers",name)
+
+ resolvers.generators.file(specification)
+end
+
+resolvers.concatinators.tree = resolvers.concatinators.file
+resolvers.generators.tree = resolvers.generators.file
+resolvers.openers.tree = resolvers.openers.file
+resolvers.loaders.tree = resolvers.loaders.file
diff --git a/tex/context/base/data-use.lua b/tex/context/base/data-use.lua
index f03b19c7d..9c15263bb 100644
--- a/tex/context/base/data-use.lua
+++ b/tex/context/base/data-use.lua
@@ -1,101 +1,101 @@
-if not modules then modules = { } end modules ['data-use'] = {
- 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, lower, gsub, find = string.format, string.lower, string.gsub, string.find
-
-local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
-
-local report_mounts = logs.reporter("resolvers","mounts")
-
-local resolvers = resolvers
-
--- we will make a better format, maybe something xml or just text or lua
-
-resolvers.automounted = resolvers.automounted or { }
-
-function resolvers.automount(usecache)
- local mountpaths = resolvers.cleanpathlist(resolvers.expansion('TEXMFMOUNT'))
- if (not mountpaths or #mountpaths == 0) and usecache then
- mountpaths = caches.getreadablepaths("mount")
- end
- if mountpaths and #mountpaths > 0 then
- statistics.starttiming(resolvers.instance)
- for k=1,#mountpaths do
- local root = mountpaths[k]
- local f = io.open(root.."/url.tmi")
- if f then
- for line in f:lines() do
- if line then
- if find(line,"^[%%#%-]") then -- or %W
- -- skip
- elseif find(line,"^zip://") then
- if trace_locating then
- report_mounts("mounting %a",line)
- end
- table.insert(resolvers.automounted,line)
- resolvers.usezipfile(line)
- end
- end
- end
- f:close()
- end
- end
- statistics.stoptiming(resolvers.instance)
- end
-end
-
--- status info
-
-statistics.register("used config file", function() return caches.configfiles() end)
-statistics.register("used cache path", function() return caches.usedpaths() end)
-
--- experiment (code will move)
-
-function statistics.savefmtstatus(texname,formatbanner,sourcefile) -- texname == formatname
- local enginebanner = status.list().banner
- if formatbanner and enginebanner and sourcefile then
- local luvname = file.replacesuffix(texname,"luv") -- utilities.lua.suffixes.luv
- local luvdata = {
- enginebanner = enginebanner,
- formatbanner = formatbanner,
- sourcehash = md5.hex(io.loaddata(resolvers.findfile(sourcefile)) or "unknown"),
- sourcefile = sourcefile,
- }
- io.savedata(luvname,table.serialize(luvdata,true))
- end
-end
-
--- todo: check this at startup and return (say) 999 as signal that the run
--- was aborted due to a wrong format in which case mtx-context can trigger
--- a remake
-
-function statistics.checkfmtstatus(texname)
- local enginebanner = status.list().banner
- if enginebanner and texname then
- local luvname = file.replacesuffix(texname,"luv") -- utilities.lua.suffixes.luv
- if lfs.isfile(luvname) then
- local luv = dofile(luvname)
- if luv and luv.sourcefile then
- local sourcehash = md5.hex(io.loaddata(resolvers.findfile(luv.sourcefile)) or "unknown")
- local luvbanner = luv.enginebanner or "?"
- if luvbanner ~= enginebanner then
- return format("engine mismatch (luv: %s <> bin: %s)",luvbanner,enginebanner)
- end
- local luvhash = luv.sourcehash or "?"
- if luvhash ~= sourcehash then
- return format("source mismatch (luv: %s <> bin: %s)",luvhash,sourcehash)
- end
- else
- return "invalid status file"
- end
- else
- return "missing status file"
- end
- end
- return true
-end
+if not modules then modules = { } end modules ['data-use'] = {
+ 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, lower, gsub, find = string.format, string.lower, string.gsub, string.find
+
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+
+local report_mounts = logs.reporter("resolvers","mounts")
+
+local resolvers = resolvers
+
+-- we will make a better format, maybe something xml or just text or lua
+
+resolvers.automounted = resolvers.automounted or { }
+
+function resolvers.automount(usecache)
+ local mountpaths = resolvers.cleanpathlist(resolvers.expansion('TEXMFMOUNT'))
+ if (not mountpaths or #mountpaths == 0) and usecache then
+ mountpaths = caches.getreadablepaths("mount")
+ end
+ if mountpaths and #mountpaths > 0 then
+ statistics.starttiming(resolvers.instance)
+ for k=1,#mountpaths do
+ local root = mountpaths[k]
+ local f = io.open(root.."/url.tmi")
+ if f then
+ for line in f:lines() do
+ if line then
+ if find(line,"^[%%#%-]") then -- or %W
+ -- skip
+ elseif find(line,"^zip://") then
+ if trace_locating then
+ report_mounts("mounting %a",line)
+ end
+ table.insert(resolvers.automounted,line)
+ resolvers.usezipfile(line)
+ end
+ end
+ end
+ f:close()
+ end
+ end
+ statistics.stoptiming(resolvers.instance)
+ end
+end
+
+-- status info
+
+statistics.register("used config file", function() return caches.configfiles() end)
+statistics.register("used cache path", function() return caches.usedpaths() end)
+
+-- experiment (code will move)
+
+function statistics.savefmtstatus(texname,formatbanner,sourcefile) -- texname == formatname
+ local enginebanner = status.list().banner
+ if formatbanner and enginebanner and sourcefile then
+ local luvname = file.replacesuffix(texname,"luv") -- utilities.lua.suffixes.luv
+ local luvdata = {
+ enginebanner = enginebanner,
+ formatbanner = formatbanner,
+ sourcehash = md5.hex(io.loaddata(resolvers.findfile(sourcefile)) or "unknown"),
+ sourcefile = sourcefile,
+ }
+ io.savedata(luvname,table.serialize(luvdata,true))
+ end
+end
+
+-- todo: check this at startup and return (say) 999 as signal that the run
+-- was aborted due to a wrong format in which case mtx-context can trigger
+-- a remake
+
+function statistics.checkfmtstatus(texname)
+ local enginebanner = status.list().banner
+ if enginebanner and texname then
+ local luvname = file.replacesuffix(texname,"luv") -- utilities.lua.suffixes.luv
+ if lfs.isfile(luvname) then
+ local luv = dofile(luvname)
+ if luv and luv.sourcefile then
+ local sourcehash = md5.hex(io.loaddata(resolvers.findfile(luv.sourcefile)) or "unknown")
+ local luvbanner = luv.enginebanner or "?"
+ if luvbanner ~= enginebanner then
+ return format("engine mismatch (luv: %s <> bin: %s)",luvbanner,enginebanner)
+ end
+ local luvhash = luv.sourcehash or "?"
+ if luvhash ~= sourcehash then
+ return format("source mismatch (luv: %s <> bin: %s)",luvhash,sourcehash)
+ end
+ else
+ return "invalid status file"
+ end
+ else
+ return "missing status file"
+ end
+ end
+ return true
+end
diff --git a/tex/context/base/data-vir.lua b/tex/context/base/data-vir.lua
index fe8c30bfb..e5bf35fa7 100644
--- a/tex/context/base/data-vir.lua
+++ b/tex/context/base/data-vir.lua
@@ -1,84 +1,84 @@
-if not modules then modules = { } end modules ['data-vir'] = {
- 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, validstrings = string.format, string.valid
-
-local trace_virtual = false
-local report_virtual = logs.reporter("resolvers","virtual")
-
-trackers.register("resolvers.locating", function(v) trace_virtual = v end)
-trackers.register("resolvers.virtual", function(v) trace_virtual = v end)
-
-local resolvers = resolvers
-
-local finders, openers, loaders, savers = resolvers.finders, resolvers.openers, resolvers.loaders, resolvers.savers
-
-local data = { }
-local n = 0 -- hm, number can be query
-local template = "virtual://%s.%s"
-
-function savers.virtual(specification,content)
- n = n + 1 -- one number for all namespaces
- local path = specification.path
- local filename = format(template,path ~= "" and path or "virtualfile",n)
- if trace_virtual then
- report_virtual("saver: file %a saved",filename)
- end
- data[filename] = content
- return filename
-end
-
-function finders.virtual(specification)
- local original = specification.original
- local d = data[original]
- if d then
- if trace_virtual then
- report_virtual("finder: file %a found",original)
- end
- return original
- else
- if trace_virtual then
- report_virtual("finder: unknown file %a",original)
- end
- return finders.notfound()
- end
-end
-
-function openers.virtual(specification)
- local original = specification.original
- local d = data[original]
- if d then
- if trace_virtual then
- report_virtual("opener: file %a opened",original)
- end
- data[original] = nil -- when we comment this we can have error messages
- -- With utf-8 we signal that no regime is to be applied!
- return openers.helpers.textopener("virtual",original,d,"utf-8")
- else
- if trace_virtual then
- report_virtual("opener: file %a not found",original)
- end
- return openers.notfound()
- end
-end
-
-function loaders.virtual(specification)
- local original = specification.original
- local d = data[original]
- if d then
- if trace_virtual then
- report_virtual("loader: file %a loaded",original)
- end
- data[original] = nil
- return true, d, #d
- end
- if trace_virtual then
- report_virtual("loader: file %a not loaded",original)
- end
- return loaders.notfound()
-end
+if not modules then modules = { } end modules ['data-vir'] = {
+ 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, validstrings = string.format, string.valid
+
+local trace_virtual = false
+local report_virtual = logs.reporter("resolvers","virtual")
+
+trackers.register("resolvers.locating", function(v) trace_virtual = v end)
+trackers.register("resolvers.virtual", function(v) trace_virtual = v end)
+
+local resolvers = resolvers
+
+local finders, openers, loaders, savers = resolvers.finders, resolvers.openers, resolvers.loaders, resolvers.savers
+
+local data = { }
+local n = 0 -- hm, number can be query
+local template = "virtual://%s.%s"
+
+function savers.virtual(specification,content)
+ n = n + 1 -- one number for all namespaces
+ local path = specification.path
+ local filename = format(template,path ~= "" and path or "virtualfile",n)
+ if trace_virtual then
+ report_virtual("saver: file %a saved",filename)
+ end
+ data[filename] = content
+ return filename
+end
+
+function finders.virtual(specification)
+ local original = specification.original
+ local d = data[original]
+ if d then
+ if trace_virtual then
+ report_virtual("finder: file %a found",original)
+ end
+ return original
+ else
+ if trace_virtual then
+ report_virtual("finder: unknown file %a",original)
+ end
+ return finders.notfound()
+ end
+end
+
+function openers.virtual(specification)
+ local original = specification.original
+ local d = data[original]
+ if d then
+ if trace_virtual then
+ report_virtual("opener: file %a opened",original)
+ end
+ data[original] = nil -- when we comment this we can have error messages
+ -- With utf-8 we signal that no regime is to be applied!
+ return openers.helpers.textopener("virtual",original,d,"utf-8")
+ else
+ if trace_virtual then
+ report_virtual("opener: file %a not found",original)
+ end
+ return openers.notfound()
+ end
+end
+
+function loaders.virtual(specification)
+ local original = specification.original
+ local d = data[original]
+ if d then
+ if trace_virtual then
+ report_virtual("loader: file %a loaded",original)
+ end
+ data[original] = nil
+ return true, d, #d
+ end
+ if trace_virtual then
+ report_virtual("loader: file %a not loaded",original)
+ end
+ return loaders.notfound()
+end
diff --git a/tex/context/base/data-zip.lua b/tex/context/base/data-zip.lua
index 62eeb1f38..5db69670c 100644
--- a/tex/context/base/data-zip.lua
+++ b/tex/context/base/data-zip.lua
@@ -1,264 +1,264 @@
-if not modules then modules = { } end modules ['data-zip'] = {
- 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"
-}
-
--- partly redone .. needs testing
-
-local format, find, match = string.format, string.find, string.match
-
-local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
-
-local report_zip = logs.reporter("resolvers","zip")
-
---[[ldx--
-<p>We use a url syntax for accessing the zip file itself and file in it:</p>
-
-<typing>
-zip:///oeps.zip?name=bla/bla.tex
-zip:///oeps.zip?tree=tex/texmf-local
-zip:///texmf.zip?tree=/tex/texmf
-zip:///texmf.zip?tree=/tex/texmf-local
-zip:///texmf-mine.zip?tree=/tex/texmf-projects
-</typing>
---ldx]]--
-
-local resolvers = resolvers
-
-zip = zip or { }
-local zip = zip
-
-zip.archives = zip.archives or { }
-local archives = zip.archives
-
-zip.registeredfiles = zip.registeredfiles or { }
-local registeredfiles = zip.registeredfiles
-
-local limited = false
-
-directives.register("system.inputmode", function(v)
- if not limited then
- local i_limiter = io.i_limiter(v)
- if i_limiter then
- zip.open = i_limiter.protect(zip.open)
- limited = true
- end
- end
-end)
-
-local function validzip(str) -- todo: use url splitter
- if not find(str,"^zip://") then
- return "zip:///" .. str
- else
- return str
- end
-end
-
-function zip.openarchive(name)
- if not name or name == "" then
- return nil
- else
- local arch = archives[name]
- if not arch then
- local full = resolvers.findfile(name) or ""
- arch = (full ~= "" and zip.open(full)) or false
- archives[name] = arch
- end
- return arch
- end
-end
-
-function zip.closearchive(name)
- if not name or (name == "" and archives[name]) then
- zip.close(archives[name])
- archives[name] = nil
- end
-end
-
-function resolvers.locators.zip(specification)
- local archive = specification.filename
- local zipfile = archive and archive ~= "" and zip.openarchive(archive) -- tricky, could be in to be initialized tree
- if trace_locating then
- if zipfile then
- report_zip("locator: archive %a found",archive)
- else
- report_zip("locator: archive %a not found",archive)
- end
- end
-end
-
-function resolvers.hashers.zip(specification)
- local archive = specification.filename
- if trace_locating then
- report_zip("loading file %a",archive)
- end
- resolvers.usezipfile(specification.original)
-end
-
-function resolvers.concatinators.zip(zipfile,path,name) -- ok ?
- if not path or path == "" then
- return format('%s?name=%s',zipfile,name)
- else
- return format('%s?name=%s/%s',zipfile,path,name)
- end
-end
-
-function resolvers.finders.zip(specification)
- local original = specification.original
- local archive = specification.filename
- if archive then
- local query = url.query(specification.query)
- local queryname = query.name
- if queryname then
- local zfile = zip.openarchive(archive)
- if zfile then
- if trace_locating then
- report_zip("finder: archive %a found",archive)
- end
- local dfile = zfile:open(queryname)
- if dfile then
- dfile = zfile:close()
- if trace_locating then
- report_zip("finder: file %a found",queryname)
- end
- return specification.original
- elseif trace_locating then
- report_zip("finder: file %a not found",queryname)
- end
- elseif trace_locating then
- report_zip("finder: unknown archive %a",archive)
- end
- end
- end
- if trace_locating then
- report_zip("finder: %a not found",original)
- end
- return resolvers.finders.notfound()
-end
-
-function resolvers.openers.zip(specification)
- local original = specification.original
- local archive = specification.filename
- if archive then
- local query = url.query(specification.query)
- local queryname = query.name
- if queryname then
- local zfile = zip.openarchive(archive)
- if zfile then
- if trace_locating then
- report_zip("opener; archive %a opened",archive)
- end
- local dfile = zfile:open(queryname)
- if dfile then
- if trace_locating then
- report_zip("opener: file %a found",queryname)
- end
- return resolvers.openers.helpers.textopener('zip',original,dfile)
- elseif trace_locating then
- report_zip("opener: file %a not found",queryname)
- end
- elseif trace_locating then
- report_zip("opener: unknown archive %a",archive)
- end
- end
- end
- if trace_locating then
- report_zip("opener: %a not found",original)
- end
- return resolvers.openers.notfound()
-end
-
-function resolvers.loaders.zip(specification)
- local original = specification.original
- local archive = specification.filename
- if archive then
- local query = url.query(specification.query)
- local queryname = query.name
- if queryname then
- local zfile = zip.openarchive(archive)
- if zfile then
- if trace_locating then
- report_zip("loader: archive %a opened",archive)
- end
- local dfile = zfile:open(queryname)
- if dfile then
- logs.show_load(original)
- if trace_locating then
- report_zip("loader; file %a loaded",original)
- end
- local s = dfile:read("*all")
- dfile:close()
- return true, s, #s
- elseif trace_locating then
- report_zip("loader: file %a not found",queryname)
- end
- elseif trace_locating then
- report_zip("loader; unknown archive %a",archive)
- end
- end
- end
- if trace_locating then
- report_zip("loader: %a not found",original)
- end
- return resolvers.openers.notfound()
-end
-
--- zip:///somefile.zip
--- zip:///somefile.zip?tree=texmf-local -> mount
-
-function resolvers.usezipfile(archive)
- local specification = resolvers.splitmethod(archive) -- to be sure
- local archive = specification.filename
- if archive and not registeredfiles[archive] then
- local z = zip.openarchive(archive)
- if z then
- local instance = resolvers.instance
- local tree = url.query(specification.query).tree or ""
- if trace_locating then
- report_zip("registering: archive %a",archive)
- end
- statistics.starttiming(instance)
- resolvers.prependhash('zip',archive)
- resolvers.extendtexmfvariable(archive) -- resets hashes too
- registeredfiles[archive] = z
- instance.files[archive] = resolvers.registerzipfile(z,tree)
- statistics.stoptiming(instance)
- elseif trace_locating then
- report_zip("registering: unknown archive %a",archive)
- end
- elseif trace_locating then
- report_zip("registering: archive %a not found",archive)
- end
-end
-
-function resolvers.registerzipfile(z,tree)
- local files, filter = { }, ""
- if tree == "" then
- filter = "^(.+)/(.-)$"
- else
- filter = format("^%s/(.+)/(.-)$",tree)
- end
- if trace_locating then
- report_zip("registering: using filter %a",filter)
- end
- local register, n = resolvers.registerfile, 0
- for i in z:files() do
- local path, name = match(i.filename,filter)
- if path then
- if name and name ~= '' then
- register(files, name, path)
- n = n + 1
- else
- -- directory
- end
- else
- register(files, i.filename, '')
- n = n + 1
- end
- end
- report_zip("registering: %s files registered",n)
- return files
-end
+if not modules then modules = { } end modules ['data-zip'] = {
+ 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"
+}
+
+-- partly redone .. needs testing
+
+local format, find, match = string.format, string.find, string.match
+
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+
+local report_zip = logs.reporter("resolvers","zip")
+
+--[[ldx--
+<p>We use a url syntax for accessing the zip file itself and file in it:</p>
+
+<typing>
+zip:///oeps.zip?name=bla/bla.tex
+zip:///oeps.zip?tree=tex/texmf-local
+zip:///texmf.zip?tree=/tex/texmf
+zip:///texmf.zip?tree=/tex/texmf-local
+zip:///texmf-mine.zip?tree=/tex/texmf-projects
+</typing>
+--ldx]]--
+
+local resolvers = resolvers
+
+zip = zip or { }
+local zip = zip
+
+zip.archives = zip.archives or { }
+local archives = zip.archives
+
+zip.registeredfiles = zip.registeredfiles or { }
+local registeredfiles = zip.registeredfiles
+
+local limited = false
+
+directives.register("system.inputmode", function(v)
+ if not limited then
+ local i_limiter = io.i_limiter(v)
+ if i_limiter then
+ zip.open = i_limiter.protect(zip.open)
+ limited = true
+ end
+ end
+end)
+
+local function validzip(str) -- todo: use url splitter
+ if not find(str,"^zip://") then
+ return "zip:///" .. str
+ else
+ return str
+ end
+end
+
+function zip.openarchive(name)
+ if not name or name == "" then
+ return nil
+ else
+ local arch = archives[name]
+ if not arch then
+ local full = resolvers.findfile(name) or ""
+ arch = (full ~= "" and zip.open(full)) or false
+ archives[name] = arch
+ end
+ return arch
+ end
+end
+
+function zip.closearchive(name)
+ if not name or (name == "" and archives[name]) then
+ zip.close(archives[name])
+ archives[name] = nil
+ end
+end
+
+function resolvers.locators.zip(specification)
+ local archive = specification.filename
+ local zipfile = archive and archive ~= "" and zip.openarchive(archive) -- tricky, could be in to be initialized tree
+ if trace_locating then
+ if zipfile then
+ report_zip("locator: archive %a found",archive)
+ else
+ report_zip("locator: archive %a not found",archive)
+ end
+ end
+end
+
+function resolvers.hashers.zip(specification)
+ local archive = specification.filename
+ if trace_locating then
+ report_zip("loading file %a",archive)
+ end
+ resolvers.usezipfile(specification.original)
+end
+
+function resolvers.concatinators.zip(zipfile,path,name) -- ok ?
+ if not path or path == "" then
+ return format('%s?name=%s',zipfile,name)
+ else
+ return format('%s?name=%s/%s',zipfile,path,name)
+ end
+end
+
+function resolvers.finders.zip(specification)
+ local original = specification.original
+ local archive = specification.filename
+ if archive then
+ local query = url.query(specification.query)
+ local queryname = query.name
+ if queryname then
+ local zfile = zip.openarchive(archive)
+ if zfile then
+ if trace_locating then
+ report_zip("finder: archive %a found",archive)
+ end
+ local dfile = zfile:open(queryname)
+ if dfile then
+ dfile = zfile:close()
+ if trace_locating then
+ report_zip("finder: file %a found",queryname)
+ end
+ return specification.original
+ elseif trace_locating then
+ report_zip("finder: file %a not found",queryname)
+ end
+ elseif trace_locating then
+ report_zip("finder: unknown archive %a",archive)
+ end
+ end
+ end
+ if trace_locating then
+ report_zip("finder: %a not found",original)
+ end
+ return resolvers.finders.notfound()
+end
+
+function resolvers.openers.zip(specification)
+ local original = specification.original
+ local archive = specification.filename
+ if archive then
+ local query = url.query(specification.query)
+ local queryname = query.name
+ if queryname then
+ local zfile = zip.openarchive(archive)
+ if zfile then
+ if trace_locating then
+ report_zip("opener; archive %a opened",archive)
+ end
+ local dfile = zfile:open(queryname)
+ if dfile then
+ if trace_locating then
+ report_zip("opener: file %a found",queryname)
+ end
+ return resolvers.openers.helpers.textopener('zip',original,dfile)
+ elseif trace_locating then
+ report_zip("opener: file %a not found",queryname)
+ end
+ elseif trace_locating then
+ report_zip("opener: unknown archive %a",archive)
+ end
+ end
+ end
+ if trace_locating then
+ report_zip("opener: %a not found",original)
+ end
+ return resolvers.openers.notfound()
+end
+
+function resolvers.loaders.zip(specification)
+ local original = specification.original
+ local archive = specification.filename
+ if archive then
+ local query = url.query(specification.query)
+ local queryname = query.name
+ if queryname then
+ local zfile = zip.openarchive(archive)
+ if zfile then
+ if trace_locating then
+ report_zip("loader: archive %a opened",archive)
+ end
+ local dfile = zfile:open(queryname)
+ if dfile then
+ logs.show_load(original)
+ if trace_locating then
+ report_zip("loader; file %a loaded",original)
+ end
+ local s = dfile:read("*all")
+ dfile:close()
+ return true, s, #s
+ elseif trace_locating then
+ report_zip("loader: file %a not found",queryname)
+ end
+ elseif trace_locating then
+ report_zip("loader; unknown archive %a",archive)
+ end
+ end
+ end
+ if trace_locating then
+ report_zip("loader: %a not found",original)
+ end
+ return resolvers.openers.notfound()
+end
+
+-- zip:///somefile.zip
+-- zip:///somefile.zip?tree=texmf-local -> mount
+
+function resolvers.usezipfile(archive)
+ local specification = resolvers.splitmethod(archive) -- to be sure
+ local archive = specification.filename
+ if archive and not registeredfiles[archive] then
+ local z = zip.openarchive(archive)
+ if z then
+ local instance = resolvers.instance
+ local tree = url.query(specification.query).tree or ""
+ if trace_locating then
+ report_zip("registering: archive %a",archive)
+ end
+ statistics.starttiming(instance)
+ resolvers.prependhash('zip',archive)
+ resolvers.extendtexmfvariable(archive) -- resets hashes too
+ registeredfiles[archive] = z
+ instance.files[archive] = resolvers.registerzipfile(z,tree)
+ statistics.stoptiming(instance)
+ elseif trace_locating then
+ report_zip("registering: unknown archive %a",archive)
+ end
+ elseif trace_locating then
+ report_zip("registering: archive %a not found",archive)
+ end
+end
+
+function resolvers.registerzipfile(z,tree)
+ local files, filter = { }, ""
+ if tree == "" then
+ filter = "^(.+)/(.-)$"
+ else
+ filter = format("^%s/(.+)/(.-)$",tree)
+ end
+ if trace_locating then
+ report_zip("registering: using filter %a",filter)
+ end
+ local register, n = resolvers.registerfile, 0
+ for i in z:files() do
+ local path, name = match(i.filename,filter)
+ if path then
+ if name and name ~= '' then
+ register(files, name, path)
+ n = n + 1
+ else
+ -- directory
+ end
+ else
+ register(files, i.filename, '')
+ n = n + 1
+ end
+ end
+ report_zip("registering: %s files registered",n)
+ return files
+end
diff --git a/tex/context/base/file-ini.lua b/tex/context/base/file-ini.lua
index fe4515c84..1872ed3d3 100644
--- a/tex/context/base/file-ini.lua
+++ b/tex/context/base/file-ini.lua
@@ -1,37 +1,37 @@
-if not modules then modules = { } end modules ['file-ini'] = {
- version = 1.001,
- comment = "companion to file-ini.mkvi",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
---[[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'/> end.</p>
---ldx]]--
-
-resolvers.jobs = resolvers.jobs or { }
-
-local texcount = tex.count
-local setvalue = context.setvalue
-
-function commands.splitfilename(fullname)
- local t = file.nametotable(fullname)
- local path = t.path
- texcount.splitoffkind = (path == "" and 0) or (path == '.' and 1) or 2
- setvalue("splitofffull",fullname)
- setvalue("splitoffpath",path)
- setvalue("splitoffname",t.name)
- setvalue("splitoffbase",t.base)
- setvalue("splitofftype",t.suffix)
-end
-
-function commands.doifparentfileelse(n)
- commands.doifelse(n == environment.jobname or n == environment.jobname .. '.tex' or n == environment.outputfilename)
-end
-
-function commands.doiffileexistelse(name)
- local foundname = resolvers.findtexfile(name)
- commands.doifelse(foundname and foundname ~= "")
-end
+if not modules then modules = { } end modules ['file-ini'] = {
+ version = 1.001,
+ comment = "companion to file-ini.mkvi",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[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'/> end.</p>
+--ldx]]--
+
+resolvers.jobs = resolvers.jobs or { }
+
+local texcount = tex.count
+local setvalue = context.setvalue
+
+function commands.splitfilename(fullname)
+ local t = file.nametotable(fullname)
+ local path = t.path
+ texcount.splitoffkind = (path == "" and 0) or (path == '.' and 1) or 2
+ setvalue("splitofffull",fullname)
+ setvalue("splitoffpath",path)
+ setvalue("splitoffname",t.name)
+ setvalue("splitoffbase",t.base)
+ setvalue("splitofftype",t.suffix)
+end
+
+function commands.doifparentfileelse(n)
+ commands.doifelse(n == environment.jobname or n == environment.jobname .. '.tex' or n == environment.outputfilename)
+end
+
+function commands.doiffileexistelse(name)
+ local foundname = resolvers.findtexfile(name)
+ commands.doifelse(foundname and foundname ~= "")
+end
diff --git a/tex/context/base/file-job.lua b/tex/context/base/file-job.lua
index 9a88cefb4..288a690d2 100644
--- a/tex/context/base/file-job.lua
+++ b/tex/context/base/file-job.lua
@@ -1,1001 +1,1001 @@
-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 gsub, match, find = string.gsub, string.match, string.find
-local insert, remove, concat = table.insert, table.remove, table.concat
-local validstring = string.valid
-local sortedhash = table.sortedhash
-local formatters = string.formatters
-
-local commands, resolvers, context = commands, resolvers, context
-
-local trace_jobfiles = false trackers.register("system.jobfiles", function(v) trace_jobfiles = v end)
-
-local report_jobfiles = logs.reporter("system","jobfiles")
-
-local texsetcount = tex.setcount
-local elements = interfaces.elements
-local constants = interfaces.constants
-local variables = interfaces.variables
-local logsnewline = logs.newline
-local logspushtarget = logs.pushtarget
-local logspoptarget = logs.poptarget
-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 joinpath = file.join
-local is_qualified_path = file.is_qualified_path
-
-local cleanpath = resolvers.cleanpath
-local inputstack = resolvers.inputstack
-
-local 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 c_prefix = variables.prefix
-
--- 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 url.hasscheme(name) then
- return resolvers.finders.byscheme("loc",name) or ""
- else
- return resolvers.findtexfile(name) or ""
- end
-end
-
-resolvers.findctxfile = findctxfile
-
-function commands.processfile(name)
- name = findctxfile(name)
- if name ~= "" then
- context.input(name)
- end
-end
-
-function commands.doifinputfileelse(name)
- commands.doifelse(findctxfile(name) ~= "")
-end
-
-function commands.locatefilepath(name)
- context(dirname(findctxfile(name)))
-end
-
-function commands.usepath(paths)
- resolvers.registerextrapath(paths)
-end
-
-function commands.usesubpath(subpaths)
- resolvers.registerextrapath(nil,subpaths)
-end
-
-function commands.allinputpaths()
- context(concat(resolvers.instance.extra_paths or { },","))
-end
-
-function commands.setdocumentfilenames()
- environment.initializefilenames()
-end
-
-function commands.usezipfile(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
-
-local report_system = logs.reporter("system")
-
--- moved from tex to lua:
-
-local texpatterns = { "%s.mkvi", "%s.mkiv", "%s.tex" }
-local luapatterns = { "%s" .. utilities.lua.suffixes.luc, "%s.lua" }
-local cldpatterns = { "%s.cld" }
-local xmlpatterns = { "%s.xml" }
-
-local uselibrary = commands.uselibrary
-local input = context.input
-
--- status
---
--- these need to be synced with input stream:
-
-local processstack = { }
-local processedfile = ""
-local processedfiles = { }
-
-function commands.processedfile()
- context(processedfile)
-end
-
-function commands.processedfiles()
- context(concat(processedfiles,","))
-end
-
-function commands.dostarttextfile(name)
- insert(processstack,name)
- processedfile = name
- insert(processedfiles,name)
-end
-
-function commands.dostoptextfile()
- processedfile = remove(processstack) or ""
-end
-
-local function startprocessing(name,notext)
- if not notext then
- -- report_system("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_system("end file %a at line %a",name,status.linenumber or 0)
- end
-end
-
---
-
-local action = function(name,foundname) input(foundname) end
-local failure = function(name,foundname) report_jobfiles("unknown %s file %a","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 action = function(name,foundname) dofile(foundname) end
-local failure = function(name,foundname) report_jobfiles("unknown %s file %a","lua",name) end
-
-local function useluafile(name,onlyonce,notext)
- uselibrary {
- name = name,
- patterns = luapatterns,
- action = action,
- failure = failure,
- onlyonce = onlyonce,
- }
-end
-
-local action = function(name,foundname) dofile(foundname) end
-local failure = function(name,foundname) report_jobfiles("unknown %s file %a","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 action = function(name,foundname) context.xmlprocess(foundname,"main","") end
-local failure = function(name,foundname) report_jobfiles("unknown %s file %a","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
-
-commands.usetexfile = usetexfile
-commands.useluafile = useluafile
-commands.usecldfile = usecldfile
-commands.usexmlfile = usexmlfile
-
-local suffixes = {
- mkvi = usetexfile,
- mkiv = usetexfile,
- tex = usetexfile,
- luc = useluafile,
- lua = useluafile,
- cld = usecldfile,
- xml = usexmlfile,
- [""] = usetexfile,
-}
-
-local function useanyfile(name,onlyonce)
- local s = suffixes[file.suffix(name)]
- if s then
- s(removesuffix(name),onlyonce)
- else
- usetexfile(name,onlyonce) -- e.g. ctx file
---~ resolvers.readfilename(name)
- end
-end
-
-commands.useanyfile = useanyfile
-
-function resolvers.jobs.usefile(name,onlyonce,notext)
- local s = suffixes[file.suffix(name)]
- if s then
- s(removesuffix(name),onlyonce,notext)
- 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_system("invalid \\%s%s ... \\%s%s structure",elements.start,v_text,elements.stop,v_text)
- startstoperror = dummyfunction
-end
-
-local function starttext()
- if textlevel == 0 then
- if trace_jobfiles then
- report_jobfiles("starting text")
- end
- -- registerfileinfo[begin]jobfilename
- context.dostarttext()
- end
- textlevel = textlevel + 1
- texsetcount("global","textlevel",textlevel)
-end
-
-local function stoptext()
- 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()
- -- registerfileinfo[end]jobfilename
- context.finalend()
- commands.stoptext = dummyfunction
- end
-end
-
-commands.starttext = starttext
-commands.stoptext = stoptext
-
-function commands.forcequitjob(reason)
- if reason then
- report_system("forcing quit: %s",reason)
- else
- report_system("forcing quit")
- end
- context.batchmode()
- while textlevel >= 0 do
- context.stoptext()
- end
-end
-
-function commands.forceendjob()
- report_system([[don't use \end to finish a document]])
- context.stoptext()
-end
-
-function commands.autostarttext()
- if textlevel == 0 then
- report_system([[auto \starttext ... \stoptext]])
- end
- context.starttext()
-end
-
-commands.autostoptext = stoptext
-
--- project structure
-
-function commands.processfilemany(name)
- useanyfile(name,false)
-end
-
-function commands.processfileonce(name)
- useanyfile(name,true)
-end
-
-function commands.processfilenone(name)
- -- skip file
-end
-
---
-
-local typestack = { }
-local pathstack = { }
-
-local currenttype = v_text
-local currentpath = "."
-
-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 report_structures = logs.reporter("system","structure")
-local report_structure = logs.reporter("used structure")
-
-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
-
-local function log_tree(top,depth)
- report_structure("%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(branches[i],depth)
- end
- end
-end
-
-luatex.registerstopactions(function()
- logspushtarget("logfile")
- logsnewline()
- report_structures("start used structure")
- logsnewline()
- root.name = environment.jobname
- log_tree(root,"")
- logsnewline()
- report_structures("stop used structure")
- logsnewline()
- logspoptarget()
-end)
-
-job.structure = job.structure or { }
-job.structure.collected = job.structure.collected or { }
-job.structure.tobesaved = root
-job.structure.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
- job.structure.components = collect(job.structure.collected,{})
-end
-
-job.register('job.structure.collected',root,initialize)
-
--- component: small unit, either or not components itself
--- product : combination of components
-
-local context_processfilemany = context.processfilemany
-local context_processfileonce = context.processfileonce
-local context_processfilenone = context.processfilenone
-
-local processors = utilities.storage.allocate {
- -- [v_outer] = {
- -- [v_text] = { "many", context_processfilemany },
- -- [v_project] = { "once", context_processfileonce },
- -- [v_environment] = { "once", context_processfileonce },
- -- [v_product] = { "once", context_processfileonce },
- -- [v_component] = { "many", context_processfilemany },
- -- },
- [v_text] = {
- [v_text] = { "many", context_processfilemany },
- [v_project] = { "once", context_processfileonce }, -- dubious
- [v_environment] = { "once", context_processfileonce },
- [v_product] = { "many", context_processfilemany }, -- dubious
- [v_component] = { "many", context_processfilemany },
- },
- [v_project] = {
- [v_text] = { "many", context_processfilemany },
- [v_project] = { "none", context_processfilenone },
- [v_environment] = { "once", context_processfileonce },
- [v_product] = { "none", context_processfilenone },
- [v_component] = { "none", context_processfilenone },
- },
- [v_environment] = {
- [v_text] = { "many", context_processfilemany },
- [v_project] = { "none", context_processfilenone },
- [v_environment] = { "once", context_processfileonce },
- [v_product] = { "none", context_processfilenone },
- [v_component] = { "none", context_processfilenone },
- },
- [v_product] = {
- [v_text] = { "many", context_processfilemany },
- [v_project] = { "once", context_processfileonce },
- [v_environment] = { "once", context_processfileonce },
- [v_product] = { "many", context_processfilemany },
- [v_component] = { "many", context_processfilemany },
- },
- [v_component] = {
- [v_text] = { "many", context_processfilemany },
- [v_project] = { "once", context_processfileonce },
- [v_environment] = { "once", context_processfileonce },
- [v_product] = { "none", context_processfilenone },
- [v_component] = { "many", context_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,
-}
-
-resolvers.jobs.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
-
-resolvers.jobs.productcomponent = productcomponent
-resolvers.jobs.justacomponent = justacomponent
-
-function resolvers.jobs.currentproject () return topofstack(v_project ) end
-function resolvers.jobs.currentproduct () return topofstack(v_product ) end
-function resolvers.jobs.currentcomponent () return topofstack(v_component ) end
-function resolvers.jobs.currentenvironment() return topofstack(v_environment) end
-
-local done = { }
-local tolerant = false -- too messy, mkii user with the wrong sructure should adapt
-
-local function process(what,name)
- local depth = #typestack
- local process
- --
- name = resolvers.resolve(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
-
-function commands.useproject (name) process(v_project, name) end
-function commands.useenvironment(name) process(v_environment,name) end
-function commands.useproduct (name) process(v_product, name) end
-function commands.usecomponent (name) process(v_component, name) end
-
--- 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)
- insert(pathstack,currentpath)
- currenttype = what
- currentpath = dirname(name)
- pushtree(what,name)
- if start[what] then
- start[what]()
- end
-end
-
-local function gotopreviouslevel(what)
- if stop[what] then
- stop[what]()
- end
- poptree()
- currentpath = remove(pathstack) or "."
- 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(name)
- if name == "*" then
- name = nameonly(inputstack[#inputstack] or name)
- end
- return name
-end
-
-function commands.startproject (name) gotonextlevel(v_project, autoname(name)) end
-function commands.startproduct (name) gotonextlevel(v_product, autoname(name)) end
-function commands.startcomponent (name) gotonextlevel(v_component, autoname(name)) end
-function commands.startenvironment(name) gotonextlevel(v_environment,autoname(name)) end
-
-function commands.stopproject () gotopreviouslevel(v_project ) end
-function commands.stopproduct () gotopreviouslevel(v_product ) end
-function commands.stopcomponent () gotopreviouslevel(v_component ) end
-function commands.stopenvironment() gotopreviouslevel(v_environment) end
-
-function commands.currentproject () context(topofstack(v_project )) end
-function commands.currentproduct () context(topofstack(v_product )) end
-function commands.currentcomponent () context(topofstack(v_component )) end
-function commands.currentenvironment() 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 commands.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
-
--- 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(),
- },
- },
-}
-
-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) -- commands
- local v = document.arguments[key]
- if type(v) == "boolean" then
- v = (v and "yes") or "no"
- document.arguments[key] = v
- end
- context(v or default or "")
-end
-
-function document.getfilename(i) -- commands
- context(document.files[tonumber(i)] or "")
-end
-
-function commands.getcommandline() -- 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.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
-
- local inputfile = validstring(arguments.input)
-
- if inputfile and dirname(inputfile) == "." and lfs.isfile(inputfile) then
- -- nicer in checks
- inputfile = basename(inputfile)
- end
-
- local kindofrun = arguments.kindofrun
- local currentrun = arguments.maxnofruns
- local maxnofruns = arguments.currentrun
-
- 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),
- }
-
- environment.kindofrun = tonumber(kindofrun) or 0
- environment.maxnofruns = tonumber(maxnofruns) or 0
- environment.currentrun = tonumber(currentrun) or 0
-
- 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 commands.setdocumentmodes() -- was setup: *runtime:modes
- apply(document.options.ctxfile .modes,context.enablemode)
- apply(document.options.commandline.modes,context.enablemode)
-end
-
-function commands.setdocumentmodules() -- was setup: *runtime:modules
- apply(document.options.ctxfile .modules,context.usemodule)
- apply(document.options.commandline.modules,context.usemodule)
-end
-
-function commands.setdocumentenvironments() -- was setup: *runtime:environments
- apply(document.options.ctxfile .environments,context.environment)
- apply(document.options.commandline.environments,context.environment)
-end
-
-local report_files = logs.reporter("system","files")
-local report_options = logs.reporter("system","options")
-local report_file = logs.reporter("used file")
-local report_option = logs.reporter("used option")
-
-luatex.registerstopactions(function()
- local foundintrees = resolvers.instance.foundintrees
- if #foundintrees > 0 then
- logspushtarget("logfile")
- logsnewline()
- report_files("start used files")
- logsnewline()
- for i=1,#foundintrees do
- report_file("%4i: % T",i,foundintrees[i])
- end
- logsnewline()
- report_files("stop used files")
- logsnewline()
- logspoptarget()
- end
-end)
-
-luatex.registerstopactions(function()
- local files = document.files -- or environment.files
- local arguments = document.arguments -- or environment.arguments
- --
- logspushtarget("logfile")
- logsnewline()
- report_options("start commandline options")
- logsnewline()
- for argument, value in sortedhash(arguments) do
- report_option("%s=%A",argument,value)
- end
- logsnewline()
- report_options("stop commandline options")
- logsnewline()
- report_options("start commandline files")
- logsnewline()
- for i=1,#files do
- report_file("% 4i: %s",i,files[i])
- end
- logsnewline()
- report_options("stop commandline files")
- logsnewline()
- logspoptarget()
-end)
-
-if environment.initex then
-
- local report_storage = logs.reporter("system","storage")
- local report_table = logs.reporter("stored table")
- local report_module = logs.reporter("stored module")
- local report_attribute = logs.reporter("stored attribute")
- local report_catcodetable = logs.reporter("stored catcodetable")
- local report_corenamespace = logs.reporter("stored corenamespace")
-
- luatex.registerstopactions(function()
- logspushtarget("logfile")
- logsnewline()
- report_storage("start stored tables")
- logsnewline()
- for k,v in sortedhash(storage.data) do
- report_table("%03i %s",k,v[1])
- end
- logsnewline()
- report_storage("stop stored tables")
- logsnewline()
- report_storage("start stored modules")
- logsnewline()
- for k,v in sortedhash(lua.bytedata) do
- report_module("%03i %s %s",k,v[2],v[1])
- end
- logsnewline()
- report_storage("stop stored modules")
- logsnewline()
- report_storage("start stored attributes")
- logsnewline()
- for k,v in sortedhash(attributes.names) do
- report_attribute("%03i %s",k,v)
- end
- logsnewline()
- report_storage("stop stored attributes")
- logsnewline()
- report_storage("start stored catcodetables")
- logsnewline()
- for k,v in sortedhash(catcodes.names) do
- report_catcodetable("%03i % t",k,v)
- end
- logsnewline()
- report_storage("stop stored catcodetables")
- logsnewline()
- report_storage("start stored corenamespaces")
- for k,v in sortedhash(interfaces.corenamespaces) do
- report_corenamespace("%03i %s",k,v)
- end
- logsnewline()
- report_storage("stop stored corenamespaces")
- logsnewline()
- logspoptarget()
- end)
-
-end
-
-function commands.doifelsecontinuewithfile(inpname,basetoo)
- local inpnamefull = addsuffix(inpname,"tex")
- local inpfilefull = addsuffix(environment.inputfilename,"tex")
- local continue = inpnamefull == inpfilefull
- if basetoo and not continue then
- continue = inpnamefull == basename(inpfilefull)
- end
- if continue then
- report_system("continuing input file %a",inpname)
- end
- commands.doifelse(continue)
-end
+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 gsub, match, find = string.gsub, string.match, string.find
+local insert, remove, concat = table.insert, table.remove, table.concat
+local validstring = string.valid
+local sortedhash = table.sortedhash
+local formatters = string.formatters
+
+local commands, resolvers, context = commands, resolvers, context
+
+local trace_jobfiles = false trackers.register("system.jobfiles", function(v) trace_jobfiles = v end)
+
+local report_jobfiles = logs.reporter("system","jobfiles")
+
+local texsetcount = tex.setcount
+local elements = interfaces.elements
+local constants = interfaces.constants
+local variables = interfaces.variables
+local logsnewline = logs.newline
+local logspushtarget = logs.pushtarget
+local logspoptarget = logs.poptarget
+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 joinpath = file.join
+local is_qualified_path = file.is_qualified_path
+
+local cleanpath = resolvers.cleanpath
+local inputstack = resolvers.inputstack
+
+local 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 c_prefix = variables.prefix
+
+-- 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 url.hasscheme(name) then
+ return resolvers.finders.byscheme("loc",name) or ""
+ else
+ return resolvers.findtexfile(name) or ""
+ end
+end
+
+resolvers.findctxfile = findctxfile
+
+function commands.processfile(name)
+ name = findctxfile(name)
+ if name ~= "" then
+ context.input(name)
+ end
+end
+
+function commands.doifinputfileelse(name)
+ commands.doifelse(findctxfile(name) ~= "")
+end
+
+function commands.locatefilepath(name)
+ context(dirname(findctxfile(name)))
+end
+
+function commands.usepath(paths)
+ resolvers.registerextrapath(paths)
+end
+
+function commands.usesubpath(subpaths)
+ resolvers.registerextrapath(nil,subpaths)
+end
+
+function commands.allinputpaths()
+ context(concat(resolvers.instance.extra_paths or { },","))
+end
+
+function commands.setdocumentfilenames()
+ environment.initializefilenames()
+end
+
+function commands.usezipfile(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
+
+local report_system = logs.reporter("system")
+
+-- moved from tex to lua:
+
+local texpatterns = { "%s.mkvi", "%s.mkiv", "%s.tex" }
+local luapatterns = { "%s" .. utilities.lua.suffixes.luc, "%s.lua" }
+local cldpatterns = { "%s.cld" }
+local xmlpatterns = { "%s.xml" }
+
+local uselibrary = commands.uselibrary
+local input = context.input
+
+-- status
+--
+-- these need to be synced with input stream:
+
+local processstack = { }
+local processedfile = ""
+local processedfiles = { }
+
+function commands.processedfile()
+ context(processedfile)
+end
+
+function commands.processedfiles()
+ context(concat(processedfiles,","))
+end
+
+function commands.dostarttextfile(name)
+ insert(processstack,name)
+ processedfile = name
+ insert(processedfiles,name)
+end
+
+function commands.dostoptextfile()
+ processedfile = remove(processstack) or ""
+end
+
+local function startprocessing(name,notext)
+ if not notext then
+ -- report_system("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_system("end file %a at line %a",name,status.linenumber or 0)
+ end
+end
+
+--
+
+local action = function(name,foundname) input(foundname) end
+local failure = function(name,foundname) report_jobfiles("unknown %s file %a","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 action = function(name,foundname) dofile(foundname) end
+local failure = function(name,foundname) report_jobfiles("unknown %s file %a","lua",name) end
+
+local function useluafile(name,onlyonce,notext)
+ uselibrary {
+ name = name,
+ patterns = luapatterns,
+ action = action,
+ failure = failure,
+ onlyonce = onlyonce,
+ }
+end
+
+local action = function(name,foundname) dofile(foundname) end
+local failure = function(name,foundname) report_jobfiles("unknown %s file %a","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 action = function(name,foundname) context.xmlprocess(foundname,"main","") end
+local failure = function(name,foundname) report_jobfiles("unknown %s file %a","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
+
+commands.usetexfile = usetexfile
+commands.useluafile = useluafile
+commands.usecldfile = usecldfile
+commands.usexmlfile = usexmlfile
+
+local suffixes = {
+ mkvi = usetexfile,
+ mkiv = usetexfile,
+ tex = usetexfile,
+ luc = useluafile,
+ lua = useluafile,
+ cld = usecldfile,
+ xml = usexmlfile,
+ [""] = usetexfile,
+}
+
+local function useanyfile(name,onlyonce)
+ local s = suffixes[file.suffix(name)]
+ if s then
+ s(removesuffix(name),onlyonce)
+ else
+ usetexfile(name,onlyonce) -- e.g. ctx file
+--~ resolvers.readfilename(name)
+ end
+end
+
+commands.useanyfile = useanyfile
+
+function resolvers.jobs.usefile(name,onlyonce,notext)
+ local s = suffixes[file.suffix(name)]
+ if s then
+ s(removesuffix(name),onlyonce,notext)
+ 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_system("invalid \\%s%s ... \\%s%s structure",elements.start,v_text,elements.stop,v_text)
+ startstoperror = dummyfunction
+end
+
+local function starttext()
+ if textlevel == 0 then
+ if trace_jobfiles then
+ report_jobfiles("starting text")
+ end
+ -- registerfileinfo[begin]jobfilename
+ context.dostarttext()
+ end
+ textlevel = textlevel + 1
+ texsetcount("global","textlevel",textlevel)
+end
+
+local function stoptext()
+ 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()
+ -- registerfileinfo[end]jobfilename
+ context.finalend()
+ commands.stoptext = dummyfunction
+ end
+end
+
+commands.starttext = starttext
+commands.stoptext = stoptext
+
+function commands.forcequitjob(reason)
+ if reason then
+ report_system("forcing quit: %s",reason)
+ else
+ report_system("forcing quit")
+ end
+ context.batchmode()
+ while textlevel >= 0 do
+ context.stoptext()
+ end
+end
+
+function commands.forceendjob()
+ report_system([[don't use \end to finish a document]])
+ context.stoptext()
+end
+
+function commands.autostarttext()
+ if textlevel == 0 then
+ report_system([[auto \starttext ... \stoptext]])
+ end
+ context.starttext()
+end
+
+commands.autostoptext = stoptext
+
+-- project structure
+
+function commands.processfilemany(name)
+ useanyfile(name,false)
+end
+
+function commands.processfileonce(name)
+ useanyfile(name,true)
+end
+
+function commands.processfilenone(name)
+ -- skip file
+end
+
+--
+
+local typestack = { }
+local pathstack = { }
+
+local currenttype = v_text
+local currentpath = "."
+
+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 report_structures = logs.reporter("system","structure")
+local report_structure = logs.reporter("used structure")
+
+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
+
+local function log_tree(top,depth)
+ report_structure("%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(branches[i],depth)
+ end
+ end
+end
+
+luatex.registerstopactions(function()
+ logspushtarget("logfile")
+ logsnewline()
+ report_structures("start used structure")
+ logsnewline()
+ root.name = environment.jobname
+ log_tree(root,"")
+ logsnewline()
+ report_structures("stop used structure")
+ logsnewline()
+ logspoptarget()
+end)
+
+job.structure = job.structure or { }
+job.structure.collected = job.structure.collected or { }
+job.structure.tobesaved = root
+job.structure.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
+ job.structure.components = collect(job.structure.collected,{})
+end
+
+job.register('job.structure.collected',root,initialize)
+
+-- component: small unit, either or not components itself
+-- product : combination of components
+
+local context_processfilemany = context.processfilemany
+local context_processfileonce = context.processfileonce
+local context_processfilenone = context.processfilenone
+
+local processors = utilities.storage.allocate {
+ -- [v_outer] = {
+ -- [v_text] = { "many", context_processfilemany },
+ -- [v_project] = { "once", context_processfileonce },
+ -- [v_environment] = { "once", context_processfileonce },
+ -- [v_product] = { "once", context_processfileonce },
+ -- [v_component] = { "many", context_processfilemany },
+ -- },
+ [v_text] = {
+ [v_text] = { "many", context_processfilemany },
+ [v_project] = { "once", context_processfileonce }, -- dubious
+ [v_environment] = { "once", context_processfileonce },
+ [v_product] = { "many", context_processfilemany }, -- dubious
+ [v_component] = { "many", context_processfilemany },
+ },
+ [v_project] = {
+ [v_text] = { "many", context_processfilemany },
+ [v_project] = { "none", context_processfilenone },
+ [v_environment] = { "once", context_processfileonce },
+ [v_product] = { "none", context_processfilenone },
+ [v_component] = { "none", context_processfilenone },
+ },
+ [v_environment] = {
+ [v_text] = { "many", context_processfilemany },
+ [v_project] = { "none", context_processfilenone },
+ [v_environment] = { "once", context_processfileonce },
+ [v_product] = { "none", context_processfilenone },
+ [v_component] = { "none", context_processfilenone },
+ },
+ [v_product] = {
+ [v_text] = { "many", context_processfilemany },
+ [v_project] = { "once", context_processfileonce },
+ [v_environment] = { "once", context_processfileonce },
+ [v_product] = { "many", context_processfilemany },
+ [v_component] = { "many", context_processfilemany },
+ },
+ [v_component] = {
+ [v_text] = { "many", context_processfilemany },
+ [v_project] = { "once", context_processfileonce },
+ [v_environment] = { "once", context_processfileonce },
+ [v_product] = { "none", context_processfilenone },
+ [v_component] = { "many", context_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,
+}
+
+resolvers.jobs.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
+
+resolvers.jobs.productcomponent = productcomponent
+resolvers.jobs.justacomponent = justacomponent
+
+function resolvers.jobs.currentproject () return topofstack(v_project ) end
+function resolvers.jobs.currentproduct () return topofstack(v_product ) end
+function resolvers.jobs.currentcomponent () return topofstack(v_component ) end
+function resolvers.jobs.currentenvironment() return topofstack(v_environment) end
+
+local done = { }
+local tolerant = false -- too messy, mkii user with the wrong sructure should adapt
+
+local function process(what,name)
+ local depth = #typestack
+ local process
+ --
+ name = resolvers.resolve(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
+
+function commands.useproject (name) process(v_project, name) end
+function commands.useenvironment(name) process(v_environment,name) end
+function commands.useproduct (name) process(v_product, name) end
+function commands.usecomponent (name) process(v_component, name) end
+
+-- 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)
+ insert(pathstack,currentpath)
+ currenttype = what
+ currentpath = dirname(name)
+ pushtree(what,name)
+ if start[what] then
+ start[what]()
+ end
+end
+
+local function gotopreviouslevel(what)
+ if stop[what] then
+ stop[what]()
+ end
+ poptree()
+ currentpath = remove(pathstack) or "."
+ 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(name)
+ if name == "*" then
+ name = nameonly(inputstack[#inputstack] or name)
+ end
+ return name
+end
+
+function commands.startproject (name) gotonextlevel(v_project, autoname(name)) end
+function commands.startproduct (name) gotonextlevel(v_product, autoname(name)) end
+function commands.startcomponent (name) gotonextlevel(v_component, autoname(name)) end
+function commands.startenvironment(name) gotonextlevel(v_environment,autoname(name)) end
+
+function commands.stopproject () gotopreviouslevel(v_project ) end
+function commands.stopproduct () gotopreviouslevel(v_product ) end
+function commands.stopcomponent () gotopreviouslevel(v_component ) end
+function commands.stopenvironment() gotopreviouslevel(v_environment) end
+
+function commands.currentproject () context(topofstack(v_project )) end
+function commands.currentproduct () context(topofstack(v_product )) end
+function commands.currentcomponent () context(topofstack(v_component )) end
+function commands.currentenvironment() 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 commands.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
+
+-- 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(),
+ },
+ },
+}
+
+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) -- commands
+ local v = document.arguments[key]
+ if type(v) == "boolean" then
+ v = (v and "yes") or "no"
+ document.arguments[key] = v
+ end
+ context(v or default or "")
+end
+
+function document.getfilename(i) -- commands
+ context(document.files[tonumber(i)] or "")
+end
+
+function commands.getcommandline() -- 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.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
+
+ local inputfile = validstring(arguments.input)
+
+ if inputfile and dirname(inputfile) == "." and lfs.isfile(inputfile) then
+ -- nicer in checks
+ inputfile = basename(inputfile)
+ end
+
+ local kindofrun = arguments.kindofrun
+ local currentrun = arguments.maxnofruns
+ local maxnofruns = arguments.currentrun
+
+ 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),
+ }
+
+ environment.kindofrun = tonumber(kindofrun) or 0
+ environment.maxnofruns = tonumber(maxnofruns) or 0
+ environment.currentrun = tonumber(currentrun) or 0
+
+ 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 commands.setdocumentmodes() -- was setup: *runtime:modes
+ apply(document.options.ctxfile .modes,context.enablemode)
+ apply(document.options.commandline.modes,context.enablemode)
+end
+
+function commands.setdocumentmodules() -- was setup: *runtime:modules
+ apply(document.options.ctxfile .modules,context.usemodule)
+ apply(document.options.commandline.modules,context.usemodule)
+end
+
+function commands.setdocumentenvironments() -- was setup: *runtime:environments
+ apply(document.options.ctxfile .environments,context.environment)
+ apply(document.options.commandline.environments,context.environment)
+end
+
+local report_files = logs.reporter("system","files")
+local report_options = logs.reporter("system","options")
+local report_file = logs.reporter("used file")
+local report_option = logs.reporter("used option")
+
+luatex.registerstopactions(function()
+ local foundintrees = resolvers.instance.foundintrees
+ if #foundintrees > 0 then
+ logspushtarget("logfile")
+ logsnewline()
+ report_files("start used files")
+ logsnewline()
+ for i=1,#foundintrees do
+ report_file("%4i: % T",i,foundintrees[i])
+ end
+ logsnewline()
+ report_files("stop used files")
+ logsnewline()
+ logspoptarget()
+ end
+end)
+
+luatex.registerstopactions(function()
+ local files = document.files -- or environment.files
+ local arguments = document.arguments -- or environment.arguments
+ --
+ logspushtarget("logfile")
+ logsnewline()
+ report_options("start commandline options")
+ logsnewline()
+ for argument, value in sortedhash(arguments) do
+ report_option("%s=%A",argument,value)
+ end
+ logsnewline()
+ report_options("stop commandline options")
+ logsnewline()
+ report_options("start commandline files")
+ logsnewline()
+ for i=1,#files do
+ report_file("% 4i: %s",i,files[i])
+ end
+ logsnewline()
+ report_options("stop commandline files")
+ logsnewline()
+ logspoptarget()
+end)
+
+if environment.initex then
+
+ local report_storage = logs.reporter("system","storage")
+ local report_table = logs.reporter("stored table")
+ local report_module = logs.reporter("stored module")
+ local report_attribute = logs.reporter("stored attribute")
+ local report_catcodetable = logs.reporter("stored catcodetable")
+ local report_corenamespace = logs.reporter("stored corenamespace")
+
+ luatex.registerstopactions(function()
+ logspushtarget("logfile")
+ logsnewline()
+ report_storage("start stored tables")
+ logsnewline()
+ for k,v in sortedhash(storage.data) do
+ report_table("%03i %s",k,v[1])
+ end
+ logsnewline()
+ report_storage("stop stored tables")
+ logsnewline()
+ report_storage("start stored modules")
+ logsnewline()
+ for k,v in sortedhash(lua.bytedata) do
+ report_module("%03i %s %s",k,v[2],v[1])
+ end
+ logsnewline()
+ report_storage("stop stored modules")
+ logsnewline()
+ report_storage("start stored attributes")
+ logsnewline()
+ for k,v in sortedhash(attributes.names) do
+ report_attribute("%03i %s",k,v)
+ end
+ logsnewline()
+ report_storage("stop stored attributes")
+ logsnewline()
+ report_storage("start stored catcodetables")
+ logsnewline()
+ for k,v in sortedhash(catcodes.names) do
+ report_catcodetable("%03i % t",k,v)
+ end
+ logsnewline()
+ report_storage("stop stored catcodetables")
+ logsnewline()
+ report_storage("start stored corenamespaces")
+ for k,v in sortedhash(interfaces.corenamespaces) do
+ report_corenamespace("%03i %s",k,v)
+ end
+ logsnewline()
+ report_storage("stop stored corenamespaces")
+ logsnewline()
+ logspoptarget()
+ end)
+
+end
+
+function commands.doifelsecontinuewithfile(inpname,basetoo)
+ local inpnamefull = addsuffix(inpname,"tex")
+ local inpfilefull = addsuffix(environment.inputfilename,"tex")
+ local continue = inpnamefull == inpfilefull
+ if basetoo and not continue then
+ continue = inpnamefull == basename(inpfilefull)
+ end
+ if continue then
+ report_system("continuing input file %a",inpname)
+ end
+ commands.doifelse(continue)
+end
diff --git a/tex/context/base/file-lib.lua b/tex/context/base/file-lib.lua
index 5f1a3a1bb..3311321c5 100644
--- a/tex/context/base/file-lib.lua
+++ b/tex/context/base/file-lib.lua
@@ -1,65 +1,65 @@
-if not modules then modules = { } end modules ['file-lib'] = {
- version = 1.001,
- comment = "companion to file-lib.mkvi",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- todo: check all usage of truefilename at the tex end and remove
--- files there (and replace definitions by full names)
-
-local format = string.format
-
-local trace_files = false trackers.register("resolvers.readfile", function(v) trace_files = v end)
-local report_files = logs.reporter("files","readfile")
-
-local loaded = { }
-local defaultpatterns = { "%s" }
-
-local function defaultaction(name,foundname)
- report_files("asked name %a, found name %a",name,foundname)
-end
-
-local function defaultfailure(name)
- report_files("asked name %a, not found",name)
-end
-
-function commands.uselibrary(specification) -- todo; reporter
- local name = specification.name
- if name and name ~= "" then
- local patterns = specification.patterns or defaultpatterns
- local action = specification.action or defaultaction
- local failure = specification.failure or defaultfailure
- local onlyonce = specification.onlyonce
- local files = utilities.parsers.settings_to_array(name)
- local truename = environment.truefilename
- local done = false
- for i=1,#files do
- local filename = files[i]
- if not loaded[filename] then
- if onlyonce then
- loaded[filename] = true -- todo: base this on return value
- end
- for i=1,#patterns do
- local somename = format(patterns[i],filename)
- if truename then
- somename = truename(somename)
- end
- local foundname = resolvers.getreadfilename("any",".",somename) or ""
- if foundname ~= "" then
- action(name,foundname)
- done = true
- break
- end
- end
- if done then
- break
- end
- end
- end
- if failure and not done then
- failure(name)
- end
- end
-end
+if not modules then modules = { } end modules ['file-lib'] = {
+ version = 1.001,
+ comment = "companion to file-lib.mkvi",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo: check all usage of truefilename at the tex end and remove
+-- files there (and replace definitions by full names)
+
+local format = string.format
+
+local trace_files = false trackers.register("resolvers.readfile", function(v) trace_files = v end)
+local report_files = logs.reporter("files","readfile")
+
+local loaded = { }
+local defaultpatterns = { "%s" }
+
+local function defaultaction(name,foundname)
+ report_files("asked name %a, found name %a",name,foundname)
+end
+
+local function defaultfailure(name)
+ report_files("asked name %a, not found",name)
+end
+
+function commands.uselibrary(specification) -- todo; reporter
+ local name = specification.name
+ if name and name ~= "" then
+ local patterns = specification.patterns or defaultpatterns
+ local action = specification.action or defaultaction
+ local failure = specification.failure or defaultfailure
+ local onlyonce = specification.onlyonce
+ local files = utilities.parsers.settings_to_array(name)
+ local truename = environment.truefilename
+ local done = false
+ for i=1,#files do
+ local filename = files[i]
+ if not loaded[filename] then
+ if onlyonce then
+ loaded[filename] = true -- todo: base this on return value
+ end
+ for i=1,#patterns do
+ local somename = format(patterns[i],filename)
+ if truename then
+ somename = truename(somename)
+ end
+ local foundname = resolvers.getreadfilename("any",".",somename) or ""
+ if foundname ~= "" then
+ action(name,foundname)
+ done = true
+ break
+ end
+ end
+ if done then
+ break
+ end
+ end
+ end
+ if failure and not done then
+ failure(name)
+ end
+ end
+end
diff --git a/tex/context/base/file-mod.lua b/tex/context/base/file-mod.lua
index fd31955f8..3659d3089 100644
--- a/tex/context/base/file-mod.lua
+++ b/tex/context/base/file-mod.lua
@@ -1,181 +1,181 @@
-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, concat, tonumber = string.format, table.concat, tonumber
-
-local trace_modules = false trackers.register("modules.loading", function(v) trace_modules = v end)
-
-local report_modules = logs.reporter("resolvers","modules")
-
-commands = commands or { }
-local commands = commands
-
-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", "p", "s", "x", "v", "t" }
-local suffixes = { "mkvi", "mkiv", "tex", "cld", "lua" } -- order might change and how about cld
-local modstatus = { }
-
-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_modules("checking url %a",fullname)
- end
- foundname = resolvers.findtexfile(fullname) or ""
- elseif file.suffix(name) ~= "" then
- if trace_modules then
- report_modules("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_modules("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_modules("loading file %a",foundname)
- end
- context.startreadingfile()
- resolvers.jobs.usefile(foundname,true) -- once, notext
- -- context.input(foundname)
- context.stopreadingfile()
- return true
- else
- return false
- end
-end
-
-function commands.usemodules(prefix,askedname,truename)
- local truename = truename or environment.truefilename(askedname)
- local hasprefix = prefix and prefix ~= ""
- local hashname = ((hasprefix and prefix) or "*") .. "-" .. truename
- local status = modstatus[hashname]
- if status == 0 then
- -- not found
- elseif status == 1 then
- status = status + 1
- else
- if trace_modules then
- report_modules("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 usemodule(truename) then
- status = 1
- else
- status = 0
- end
- end
- end
- if status == 0 then
- report_modules("%a is not found",askedname)
- elseif status == 1 then
- report_modules("%a is loaded",trace_modules and truename or askedname)
- else
- report_modules("%a is already loaded",trace_modules and truename or askedname)
- end
- modstatus[hashname] = status
-end
-
-statistics.register("loaded tex modules", function()
- if next(modstatus) then
- local t, f, nt, nf = { }, { }, 0, 0
- for k, v in table.sortedhash(modstatus) do
- k = file.basename(k)
- if v == 0 then
- nf = nf + 1
- f[nf] = k
- else
- nt = nt + 1
- t[nt] = k
- 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)
-
--- moved from syst-lua.lua:
-
-local splitter = lpeg.tsplitter(lpeg.S(". "),tonumber)
-
-function commands.doifolderversionelse(one,two) -- one >= two
- if not two then
- one, two = environment.version, one
- elseif one == "" then
- one = environment.version
- end
- one = lpeg.match(splitter,one)
- two = lpeg.match(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)
- commands.doifelse(one>=two)
-end
-
-function commands.useluamodule(list)
- for filename in iterator(list) do
- environment.loadluafile(filename)
- end
-end
+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, concat, tonumber = string.format, table.concat, tonumber
+
+local trace_modules = false trackers.register("modules.loading", function(v) trace_modules = v end)
+
+local report_modules = logs.reporter("resolvers","modules")
+
+commands = commands or { }
+local commands = commands
+
+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", "p", "s", "x", "v", "t" }
+local suffixes = { "mkvi", "mkiv", "tex", "cld", "lua" } -- order might change and how about cld
+local modstatus = { }
+
+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_modules("checking url %a",fullname)
+ end
+ foundname = resolvers.findtexfile(fullname) or ""
+ elseif file.suffix(name) ~= "" then
+ if trace_modules then
+ report_modules("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_modules("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_modules("loading file %a",foundname)
+ end
+ context.startreadingfile()
+ resolvers.jobs.usefile(foundname,true) -- once, notext
+ -- context.input(foundname)
+ context.stopreadingfile()
+ return true
+ else
+ return false
+ end
+end
+
+function commands.usemodules(prefix,askedname,truename)
+ local truename = truename or environment.truefilename(askedname)
+ local hasprefix = prefix and prefix ~= ""
+ local hashname = ((hasprefix and prefix) or "*") .. "-" .. truename
+ local status = modstatus[hashname]
+ if status == 0 then
+ -- not found
+ elseif status == 1 then
+ status = status + 1
+ else
+ if trace_modules then
+ report_modules("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 usemodule(truename) then
+ status = 1
+ else
+ status = 0
+ end
+ end
+ end
+ if status == 0 then
+ report_modules("%a is not found",askedname)
+ elseif status == 1 then
+ report_modules("%a is loaded",trace_modules and truename or askedname)
+ else
+ report_modules("%a is already loaded",trace_modules and truename or askedname)
+ end
+ modstatus[hashname] = status
+end
+
+statistics.register("loaded tex modules", function()
+ if next(modstatus) then
+ local t, f, nt, nf = { }, { }, 0, 0
+ for k, v in table.sortedhash(modstatus) do
+ k = file.basename(k)
+ if v == 0 then
+ nf = nf + 1
+ f[nf] = k
+ else
+ nt = nt + 1
+ t[nt] = k
+ 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)
+
+-- moved from syst-lua.lua:
+
+local splitter = lpeg.tsplitter(lpeg.S(". "),tonumber)
+
+function commands.doifolderversionelse(one,two) -- one >= two
+ if not two then
+ one, two = environment.version, one
+ elseif one == "" then
+ one = environment.version
+ end
+ one = lpeg.match(splitter,one)
+ two = lpeg.match(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)
+ commands.doifelse(one>=two)
+end
+
+function commands.useluamodule(list)
+ for filename in iterator(list) do
+ environment.loadluafile(filename)
+ end
+end
diff --git a/tex/context/base/file-res.lua b/tex/context/base/file-res.lua
index 6eb8667d6..8e65ba4c7 100644
--- a/tex/context/base/file-res.lua
+++ b/tex/context/base/file-res.lua
@@ -1,155 +1,155 @@
-if not modules then modules = { } end modules ['file-res'] = {
- version = 1.001,
- comment = "companion to supp-fil.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 isfile = lfs.isfile
-local is_qualified_path = file.is_qualified_path
-local hasscheme = url.hasscheme
-
-local trace_files = false trackers.register("resolvers.readfile", function(v) trace_files = v end)
-local report_files = logs.reporter("files","readfile")
-
-resolvers.maxreadlevel = 2
-
-directives.register("resolvers.maxreadlevel", function(v) resolvers.maxreadlevel = tonumber(v) or resolvers.maxreadlevel end)
-
-local finders, loaders, openers = resolvers.finders, resolvers.loaders, resolvers.openers
-
-local found = { } -- can best be done in the resolver itself
-
-local function readfilename(specification,backtrack,treetoo)
- local name = specification.filename
- local fnd = name and found[name]
- if not fnd then
- local names
- local suffix = file.suffix(name)
- if suffix ~= "" then
- names = { name }
- else
- local defaultsuffixes = resolvers.defaultsuffixes
- names = { }
- for i=1,#defaultsuffixes do
- names[i] = name .. "." .. defaultsuffixes[i]
- end
- if trace_files then
- report_files("locating: %s, using default suffixes: %a",name,defaultsuffixes)
- end
- end
- for i=1,#names do
- local fname = names[i]
- if isfile(fname) then
- if trace_files then
- report_files("found local: %s",name)
- end
- fnd = fname
- break
- end
- end
- if not fnd and backtrack then
- for i=1,#names do
- local fname = names[i]
- for i=1,backtrack,1 do
- fname = "../" .. fname
- if isfile(fname) then
- if trace_files then
- report_files("found by backtracking: %s",fname)
- end
- fnd = fname
- break
- elseif trace_files then
- report_files("not found by backtracking: %s",fname)
- end
- end
- if fnd then
- break
- end
- end
- end
- if not fnd then
- local paths = resolvers.instance.extra_paths
- if paths then
- for i=1,#paths do
- for i=1,#names do
- local fname = paths[i] .. "/" .. names[i]
- if isfile(fname) then
- if trace_files then
- report_files("found on extra path: %s",fname)
- end
- fnd = fname
- break
- end
- end
- if fnd then
- break
- end
- end
- end
- end
- if not fnd and treetoo then
- fnd = resolvers.findtexfile(name) or ""
- if trace_files then
- if fnd ~= "" then
- report_files("found by tree lookup: %s",fnd)
- else
- report_files("not found by tree lookup: %s",name)
- end
- end
- end
- found[name] = fnd
- elseif trace_files then
- if fnd ~= "" then
- report_files("already found: %s",fnd)
- else
- report_files("already not found: %s",name)
- end
- end
- return fnd or ""
-end
-
---~ resolvers.readfilename = readfilename -- bonus use getreadfilename instead
-
-function finders.job(specification) return readfilename(specification,false, false) end -- current path, no backtracking
-function finders.loc(specification) return readfilename(specification,resolvers.maxreadlevel,false) end -- current path, backtracking
-function finders.sys(specification) return readfilename(specification,false, true ) end -- current path, obeys tex search
-function finders.fix(specification) return readfilename(specification,resolvers.maxreadlevel,false) end -- specified path, backtracking
-function finders.set(specification) return readfilename(specification,false, false) end -- specified path, no backtracking
-function finders.any(specification) return readfilename(specification,resolvers.maxreadlevel,true ) end -- loc job sys
-
-openers.job = openers.file loaders.job = loaders.file -- default anyway
-openers.loc = openers.file loaders.loc = loaders.file
-openers.sys = openers.file loaders.sys = loaders.file
-openers.fix = openers.file loaders.fix = loaders.file
-openers.set = openers.file loaders.set = loaders.file
-openers.any = openers.file loaders.any = loaders.file
-
-function getreadfilename(scheme,path,name) -- better do a split and then pass table
- local fullname
- if hasscheme(name) or is_qualified_path(name) then
- fullname = name
- else
- fullname = ((path == "") and format("%s:///%s",scheme,name)) or format("%s:///%s/%s",scheme,path,name)
- end
---~ print(">>>",fullname)
- return resolvers.findtexfile(fullname) or "" -- can be more direct
-end
-
-resolvers.getreadfilename = getreadfilename
-
-function commands.getreadfilename(scheme,path,name)
- context(getreadfilename(scheme,path,name))
-end
-
--- a name belonging to the run but also honoring qualified
-
-function commands.locfilename(name)
- context(getreadfilename("loc",".",name))
-end
-
-function commands.doiflocfileelse(name)
- commands.doifelse(isfile(getreadfilename("loc",".",name)))
-end
+if not modules then modules = { } end modules ['file-res'] = {
+ version = 1.001,
+ comment = "companion to supp-fil.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 isfile = lfs.isfile
+local is_qualified_path = file.is_qualified_path
+local hasscheme = url.hasscheme
+
+local trace_files = false trackers.register("resolvers.readfile", function(v) trace_files = v end)
+local report_files = logs.reporter("files","readfile")
+
+resolvers.maxreadlevel = 2
+
+directives.register("resolvers.maxreadlevel", function(v) resolvers.maxreadlevel = tonumber(v) or resolvers.maxreadlevel end)
+
+local finders, loaders, openers = resolvers.finders, resolvers.loaders, resolvers.openers
+
+local found = { } -- can best be done in the resolver itself
+
+local function readfilename(specification,backtrack,treetoo)
+ local name = specification.filename
+ local fnd = name and found[name]
+ if not fnd then
+ local names
+ local suffix = file.suffix(name)
+ if suffix ~= "" then
+ names = { name }
+ else
+ local defaultsuffixes = resolvers.defaultsuffixes
+ names = { }
+ for i=1,#defaultsuffixes do
+ names[i] = name .. "." .. defaultsuffixes[i]
+ end
+ if trace_files then
+ report_files("locating: %s, using default suffixes: %a",name,defaultsuffixes)
+ end
+ end
+ for i=1,#names do
+ local fname = names[i]
+ if isfile(fname) then
+ if trace_files then
+ report_files("found local: %s",name)
+ end
+ fnd = fname
+ break
+ end
+ end
+ if not fnd and backtrack then
+ for i=1,#names do
+ local fname = names[i]
+ for i=1,backtrack,1 do
+ fname = "../" .. fname
+ if isfile(fname) then
+ if trace_files then
+ report_files("found by backtracking: %s",fname)
+ end
+ fnd = fname
+ break
+ elseif trace_files then
+ report_files("not found by backtracking: %s",fname)
+ end
+ end
+ if fnd then
+ break
+ end
+ end
+ end
+ if not fnd then
+ local paths = resolvers.instance.extra_paths
+ if paths then
+ for i=1,#paths do
+ for i=1,#names do
+ local fname = paths[i] .. "/" .. names[i]
+ if isfile(fname) then
+ if trace_files then
+ report_files("found on extra path: %s",fname)
+ end
+ fnd = fname
+ break
+ end
+ end
+ if fnd then
+ break
+ end
+ end
+ end
+ end
+ if not fnd and treetoo then
+ fnd = resolvers.findtexfile(name) or ""
+ if trace_files then
+ if fnd ~= "" then
+ report_files("found by tree lookup: %s",fnd)
+ else
+ report_files("not found by tree lookup: %s",name)
+ end
+ end
+ end
+ found[name] = fnd
+ elseif trace_files then
+ if fnd ~= "" then
+ report_files("already found: %s",fnd)
+ else
+ report_files("already not found: %s",name)
+ end
+ end
+ return fnd or ""
+end
+
+--~ resolvers.readfilename = readfilename -- bonus use getreadfilename instead
+
+function finders.job(specification) return readfilename(specification,false, false) end -- current path, no backtracking
+function finders.loc(specification) return readfilename(specification,resolvers.maxreadlevel,false) end -- current path, backtracking
+function finders.sys(specification) return readfilename(specification,false, true ) end -- current path, obeys tex search
+function finders.fix(specification) return readfilename(specification,resolvers.maxreadlevel,false) end -- specified path, backtracking
+function finders.set(specification) return readfilename(specification,false, false) end -- specified path, no backtracking
+function finders.any(specification) return readfilename(specification,resolvers.maxreadlevel,true ) end -- loc job sys
+
+openers.job = openers.file loaders.job = loaders.file -- default anyway
+openers.loc = openers.file loaders.loc = loaders.file
+openers.sys = openers.file loaders.sys = loaders.file
+openers.fix = openers.file loaders.fix = loaders.file
+openers.set = openers.file loaders.set = loaders.file
+openers.any = openers.file loaders.any = loaders.file
+
+function getreadfilename(scheme,path,name) -- better do a split and then pass table
+ local fullname
+ if hasscheme(name) or is_qualified_path(name) then
+ fullname = name
+ else
+ fullname = ((path == "") and format("%s:///%s",scheme,name)) or format("%s:///%s/%s",scheme,path,name)
+ end
+--~ print(">>>",fullname)
+ return resolvers.findtexfile(fullname) or "" -- can be more direct
+end
+
+resolvers.getreadfilename = getreadfilename
+
+function commands.getreadfilename(scheme,path,name)
+ context(getreadfilename(scheme,path,name))
+end
+
+-- a name belonging to the run but also honoring qualified
+
+function commands.locfilename(name)
+ context(getreadfilename("loc",".",name))
+end
+
+function commands.doiflocfileelse(name)
+ commands.doifelse(isfile(getreadfilename("loc",".",name)))
+end
diff --git a/tex/context/base/file-syn.lua b/tex/context/base/file-syn.lua
index 60dcb462d..8d913bb37 100644
--- a/tex/context/base/file-syn.lua
+++ b/tex/context/base/file-syn.lua
@@ -1,51 +1,51 @@
-if not modules then modules = { } end modules ['file-syn'] = {
- version = 1.001,
- comment = "companion to file-syn.mkvi",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local report_files = logs.reporter("files")
-
-environment.filesynonyms = environment.filesynonyms or { }
-local filesynonyms = environment.filesynonyms
-
-local settings_to_array = utilities.parsers.settings_to_array
-local findfile = resolvers.findfile
-
-storage.register("environment/filesynonyms", filesynonyms, "environment.filesynonyms")
-
-local function truefilename(name)
- local realname = filesynonyms[name] or name
- if realname ~= name then
- return truefilename(realname)
- else
- return realname
- end
-end
-
-environment.truefilename = truefilename
-
-function commands.truefilename(name)
- context(truefilename(name))
-end
-
-function commands.definefilesynonym(name,realname)
- local synonym = filesynonyms[name]
- if synonym then
- interfaces.showmessage("files",1,{ name or "?", realname or "?", synonym or "?" })
- end
- filesynonyms[name] = realname
-end
-
-function commands.definefilefallback(name,alternatives)
- local names = settings_to_array(alternatives)
- for i=1,#names do
- local realname = findfile(names[i])
- if realname ~= "" then
- filesynonyms[name] = realname
- break
- end
- end
-end
+if not modules then modules = { } end modules ['file-syn'] = {
+ version = 1.001,
+ comment = "companion to file-syn.mkvi",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local report_files = logs.reporter("files")
+
+environment.filesynonyms = environment.filesynonyms or { }
+local filesynonyms = environment.filesynonyms
+
+local settings_to_array = utilities.parsers.settings_to_array
+local findfile = resolvers.findfile
+
+storage.register("environment/filesynonyms", filesynonyms, "environment.filesynonyms")
+
+local function truefilename(name)
+ local realname = filesynonyms[name] or name
+ if realname ~= name then
+ return truefilename(realname)
+ else
+ return realname
+ end
+end
+
+environment.truefilename = truefilename
+
+function commands.truefilename(name)
+ context(truefilename(name))
+end
+
+function commands.definefilesynonym(name,realname)
+ local synonym = filesynonyms[name]
+ if synonym then
+ interfaces.showmessage("files",1,{ name or "?", realname or "?", synonym or "?" })
+ end
+ filesynonyms[name] = realname
+end
+
+function commands.definefilefallback(name,alternatives)
+ local names = settings_to_array(alternatives)
+ for i=1,#names do
+ local realname = findfile(names[i])
+ if realname ~= "" then
+ filesynonyms[name] = realname
+ break
+ end
+ end
+end
diff --git a/tex/context/base/font-afk.lua b/tex/context/base/font-afk.lua
index bd8d3276b..8b65b0631 100644
--- a/tex/context/base/font-afk.lua
+++ b/tex/context/base/font-afk.lua
@@ -1,200 +1,200 @@
-if not modules then modules = { } end modules ['font-afk'] = {
- version = 1.001,
- comment = "companion to font-afm.lua",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files",
- dataonly = true,
-}
-
---[[ldx--
-<p>For ligatures, only characters with a code smaller than 128 make sense,
-anything larger is encoding dependent. An interesting complication is that a
-character can be in an encoding twice but is hashed once.</p>
---ldx]]--
-
-local allocate = utilities.storage.allocate
-
-fonts.handlers.afm.helpdata = {
- ligatures = allocate { -- okay, nowadays we could parse the name but type 1 fonts
- ['f'] = { -- don't have that many ligatures anyway
- { 'f', 'ff' },
- { 'i', 'fi' },
- { 'l', 'fl' },
- },
- ['ff'] = {
- { 'i', 'ffi' }
- },
- ['fi'] = {
- { 'i', 'fii' }
- },
- ['fl'] = {
- { 'i', 'fli' }
- },
- ['s'] = {
- { 't', 'st' }
- },
- ['i'] = {
- { 'j', 'ij' }
- },
- },
- texligatures = allocate {
- -- ['space'] = {
- -- { 'L', 'Lslash' },
- -- { 'l', 'lslash' }
- -- },
- -- ['question'] = {
- -- { 'quoteleft', 'questiondown' }
- -- },
- -- ['exclam'] = {
- -- { 'quoteleft', 'exclamdown' }
- -- },
- ['quoteleft'] = {
- { 'quoteleft', 'quotedblleft' }
- },
- ['quoteright'] = {
- { 'quoteright', 'quotedblright' }
- },
- ['hyphen'] = {
- { 'hyphen', 'endash' }
- },
- ['endash'] = {
- { 'hyphen', 'emdash' }
- }
- },
- leftkerned = allocate {
- AEligature = "A", aeligature = "a",
- OEligature = "O", oeligature = "o",
- IJligature = "I", ijligature = "i",
- AE = "A", ae = "a",
- OE = "O", oe = "o",
- IJ = "I", ij = "i",
- Ssharp = "S", ssharp = "s",
- },
- rightkerned = allocate {
- AEligature = "E", aeligature = "e",
- OEligature = "E", oeligature = "e",
- IJligature = "J", ijligature = "j",
- AE = "E", ae = "e",
- OE = "E", oe = "e",
- IJ = "J", ij = "j",
- Ssharp = "S", ssharp = "s",
- },
- bothkerned = allocate {
- Acircumflex = "A", acircumflex = "a",
- Ccircumflex = "C", ccircumflex = "c",
- Ecircumflex = "E", ecircumflex = "e",
- Gcircumflex = "G", gcircumflex = "g",
- Hcircumflex = "H", hcircumflex = "h",
- Icircumflex = "I", icircumflex = "i",
- Jcircumflex = "J", jcircumflex = "j",
- Ocircumflex = "O", ocircumflex = "o",
- Scircumflex = "S", scircumflex = "s",
- Ucircumflex = "U", ucircumflex = "u",
- Wcircumflex = "W", wcircumflex = "w",
- Ycircumflex = "Y", ycircumflex = "y",
-
- Agrave = "A", agrave = "a",
- Egrave = "E", egrave = "e",
- Igrave = "I", igrave = "i",
- Ograve = "O", ograve = "o",
- Ugrave = "U", ugrave = "u",
- Ygrave = "Y", ygrave = "y",
-
- Atilde = "A", atilde = "a",
- Itilde = "I", itilde = "i",
- Otilde = "O", otilde = "o",
- Utilde = "U", utilde = "u",
- Ntilde = "N", ntilde = "n",
-
- Adiaeresis = "A", adiaeresis = "a", Adieresis = "A", adieresis = "a",
- Ediaeresis = "E", ediaeresis = "e", Edieresis = "E", edieresis = "e",
- Idiaeresis = "I", idiaeresis = "i", Idieresis = "I", idieresis = "i",
- Odiaeresis = "O", odiaeresis = "o", Odieresis = "O", odieresis = "o",
- Udiaeresis = "U", udiaeresis = "u", Udieresis = "U", udieresis = "u",
- Ydiaeresis = "Y", ydiaeresis = "y", Ydieresis = "Y", ydieresis = "y",
-
- Aacute = "A", aacute = "a",
- Cacute = "C", cacute = "c",
- Eacute = "E", eacute = "e",
- Iacute = "I", iacute = "i",
- Lacute = "L", lacute = "l",
- Nacute = "N", nacute = "n",
- Oacute = "O", oacute = "o",
- Racute = "R", racute = "r",
- Sacute = "S", sacute = "s",
- Uacute = "U", uacute = "u",
- Yacute = "Y", yacute = "y",
- Zacute = "Z", zacute = "z",
-
- Dstroke = "D", dstroke = "d",
- Hstroke = "H", hstroke = "h",
- Tstroke = "T", tstroke = "t",
-
- Cdotaccent = "C", cdotaccent = "c",
- Edotaccent = "E", edotaccent = "e",
- Gdotaccent = "G", gdotaccent = "g",
- Idotaccent = "I", idotaccent = "i",
- Zdotaccent = "Z", zdotaccent = "z",
-
- Amacron = "A", amacron = "a",
- Emacron = "E", emacron = "e",
- Imacron = "I", imacron = "i",
- Omacron = "O", omacron = "o",
- Umacron = "U", umacron = "u",
-
- Ccedilla = "C", ccedilla = "c",
- Kcedilla = "K", kcedilla = "k",
- Lcedilla = "L", lcedilla = "l",
- Ncedilla = "N", ncedilla = "n",
- Rcedilla = "R", rcedilla = "r",
- Scedilla = "S", scedilla = "s",
- Tcedilla = "T", tcedilla = "t",
-
- Ohungarumlaut = "O", ohungarumlaut = "o",
- Uhungarumlaut = "U", uhungarumlaut = "u",
-
- Aogonek = "A", aogonek = "a",
- Eogonek = "E", eogonek = "e",
- Iogonek = "I", iogonek = "i",
- Uogonek = "U", uogonek = "u",
-
- Aring = "A", aring = "a",
- Uring = "U", uring = "u",
-
- Abreve = "A", abreve = "a",
- Ebreve = "E", ebreve = "e",
- Gbreve = "G", gbreve = "g",
- Ibreve = "I", ibreve = "i",
- Obreve = "O", obreve = "o",
- Ubreve = "U", ubreve = "u",
-
- Ccaron = "C", ccaron = "c",
- Dcaron = "D", dcaron = "d",
- Ecaron = "E", ecaron = "e",
- Lcaron = "L", lcaron = "l",
- Ncaron = "N", ncaron = "n",
- Rcaron = "R", rcaron = "r",
- Scaron = "S", scaron = "s",
- Tcaron = "T", tcaron = "t",
- Zcaron = "Z", zcaron = "z",
-
- dotlessI = "I", dotlessi = "i",
- dotlessJ = "J", dotlessj = "j",
-
- AEligature = "AE", aeligature = "ae", AE = "AE", ae = "ae",
- OEligature = "OE", oeligature = "oe", OE = "OE", oe = "oe",
- IJligature = "IJ", ijligature = "ij", IJ = "IJ", ij = "ij",
-
- Lstroke = "L", lstroke = "l", Lslash = "L", lslash = "l",
- Ostroke = "O", ostroke = "o", Oslash = "O", oslash = "o",
-
- Ssharp = "SS", ssharp = "ss",
-
- Aumlaut = "A", aumlaut = "a",
- Eumlaut = "E", eumlaut = "e",
- Iumlaut = "I", iumlaut = "i",
- Oumlaut = "O", oumlaut = "o",
- Uumlaut = "U", uumlaut = "u",
- }
-}
+if not modules then modules = { } end modules ['font-afk'] = {
+ version = 1.001,
+ comment = "companion to font-afm.lua",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+ dataonly = true,
+}
+
+--[[ldx--
+<p>For ligatures, only characters with a code smaller than 128 make sense,
+anything larger is encoding dependent. An interesting complication is that a
+character can be in an encoding twice but is hashed once.</p>
+--ldx]]--
+
+local allocate = utilities.storage.allocate
+
+fonts.handlers.afm.helpdata = {
+ ligatures = allocate { -- okay, nowadays we could parse the name but type 1 fonts
+ ['f'] = { -- don't have that many ligatures anyway
+ { 'f', 'ff' },
+ { 'i', 'fi' },
+ { 'l', 'fl' },
+ },
+ ['ff'] = {
+ { 'i', 'ffi' }
+ },
+ ['fi'] = {
+ { 'i', 'fii' }
+ },
+ ['fl'] = {
+ { 'i', 'fli' }
+ },
+ ['s'] = {
+ { 't', 'st' }
+ },
+ ['i'] = {
+ { 'j', 'ij' }
+ },
+ },
+ texligatures = allocate {
+ -- ['space'] = {
+ -- { 'L', 'Lslash' },
+ -- { 'l', 'lslash' }
+ -- },
+ -- ['question'] = {
+ -- { 'quoteleft', 'questiondown' }
+ -- },
+ -- ['exclam'] = {
+ -- { 'quoteleft', 'exclamdown' }
+ -- },
+ ['quoteleft'] = {
+ { 'quoteleft', 'quotedblleft' }
+ },
+ ['quoteright'] = {
+ { 'quoteright', 'quotedblright' }
+ },
+ ['hyphen'] = {
+ { 'hyphen', 'endash' }
+ },
+ ['endash'] = {
+ { 'hyphen', 'emdash' }
+ }
+ },
+ leftkerned = allocate {
+ AEligature = "A", aeligature = "a",
+ OEligature = "O", oeligature = "o",
+ IJligature = "I", ijligature = "i",
+ AE = "A", ae = "a",
+ OE = "O", oe = "o",
+ IJ = "I", ij = "i",
+ Ssharp = "S", ssharp = "s",
+ },
+ rightkerned = allocate {
+ AEligature = "E", aeligature = "e",
+ OEligature = "E", oeligature = "e",
+ IJligature = "J", ijligature = "j",
+ AE = "E", ae = "e",
+ OE = "E", oe = "e",
+ IJ = "J", ij = "j",
+ Ssharp = "S", ssharp = "s",
+ },
+ bothkerned = allocate {
+ Acircumflex = "A", acircumflex = "a",
+ Ccircumflex = "C", ccircumflex = "c",
+ Ecircumflex = "E", ecircumflex = "e",
+ Gcircumflex = "G", gcircumflex = "g",
+ Hcircumflex = "H", hcircumflex = "h",
+ Icircumflex = "I", icircumflex = "i",
+ Jcircumflex = "J", jcircumflex = "j",
+ Ocircumflex = "O", ocircumflex = "o",
+ Scircumflex = "S", scircumflex = "s",
+ Ucircumflex = "U", ucircumflex = "u",
+ Wcircumflex = "W", wcircumflex = "w",
+ Ycircumflex = "Y", ycircumflex = "y",
+
+ Agrave = "A", agrave = "a",
+ Egrave = "E", egrave = "e",
+ Igrave = "I", igrave = "i",
+ Ograve = "O", ograve = "o",
+ Ugrave = "U", ugrave = "u",
+ Ygrave = "Y", ygrave = "y",
+
+ Atilde = "A", atilde = "a",
+ Itilde = "I", itilde = "i",
+ Otilde = "O", otilde = "o",
+ Utilde = "U", utilde = "u",
+ Ntilde = "N", ntilde = "n",
+
+ Adiaeresis = "A", adiaeresis = "a", Adieresis = "A", adieresis = "a",
+ Ediaeresis = "E", ediaeresis = "e", Edieresis = "E", edieresis = "e",
+ Idiaeresis = "I", idiaeresis = "i", Idieresis = "I", idieresis = "i",
+ Odiaeresis = "O", odiaeresis = "o", Odieresis = "O", odieresis = "o",
+ Udiaeresis = "U", udiaeresis = "u", Udieresis = "U", udieresis = "u",
+ Ydiaeresis = "Y", ydiaeresis = "y", Ydieresis = "Y", ydieresis = "y",
+
+ Aacute = "A", aacute = "a",
+ Cacute = "C", cacute = "c",
+ Eacute = "E", eacute = "e",
+ Iacute = "I", iacute = "i",
+ Lacute = "L", lacute = "l",
+ Nacute = "N", nacute = "n",
+ Oacute = "O", oacute = "o",
+ Racute = "R", racute = "r",
+ Sacute = "S", sacute = "s",
+ Uacute = "U", uacute = "u",
+ Yacute = "Y", yacute = "y",
+ Zacute = "Z", zacute = "z",
+
+ Dstroke = "D", dstroke = "d",
+ Hstroke = "H", hstroke = "h",
+ Tstroke = "T", tstroke = "t",
+
+ Cdotaccent = "C", cdotaccent = "c",
+ Edotaccent = "E", edotaccent = "e",
+ Gdotaccent = "G", gdotaccent = "g",
+ Idotaccent = "I", idotaccent = "i",
+ Zdotaccent = "Z", zdotaccent = "z",
+
+ Amacron = "A", amacron = "a",
+ Emacron = "E", emacron = "e",
+ Imacron = "I", imacron = "i",
+ Omacron = "O", omacron = "o",
+ Umacron = "U", umacron = "u",
+
+ Ccedilla = "C", ccedilla = "c",
+ Kcedilla = "K", kcedilla = "k",
+ Lcedilla = "L", lcedilla = "l",
+ Ncedilla = "N", ncedilla = "n",
+ Rcedilla = "R", rcedilla = "r",
+ Scedilla = "S", scedilla = "s",
+ Tcedilla = "T", tcedilla = "t",
+
+ Ohungarumlaut = "O", ohungarumlaut = "o",
+ Uhungarumlaut = "U", uhungarumlaut = "u",
+
+ Aogonek = "A", aogonek = "a",
+ Eogonek = "E", eogonek = "e",
+ Iogonek = "I", iogonek = "i",
+ Uogonek = "U", uogonek = "u",
+
+ Aring = "A", aring = "a",
+ Uring = "U", uring = "u",
+
+ Abreve = "A", abreve = "a",
+ Ebreve = "E", ebreve = "e",
+ Gbreve = "G", gbreve = "g",
+ Ibreve = "I", ibreve = "i",
+ Obreve = "O", obreve = "o",
+ Ubreve = "U", ubreve = "u",
+
+ Ccaron = "C", ccaron = "c",
+ Dcaron = "D", dcaron = "d",
+ Ecaron = "E", ecaron = "e",
+ Lcaron = "L", lcaron = "l",
+ Ncaron = "N", ncaron = "n",
+ Rcaron = "R", rcaron = "r",
+ Scaron = "S", scaron = "s",
+ Tcaron = "T", tcaron = "t",
+ Zcaron = "Z", zcaron = "z",
+
+ dotlessI = "I", dotlessi = "i",
+ dotlessJ = "J", dotlessj = "j",
+
+ AEligature = "AE", aeligature = "ae", AE = "AE", ae = "ae",
+ OEligature = "OE", oeligature = "oe", OE = "OE", oe = "oe",
+ IJligature = "IJ", ijligature = "ij", IJ = "IJ", ij = "ij",
+
+ Lstroke = "L", lstroke = "l", Lslash = "L", lslash = "l",
+ Ostroke = "O", ostroke = "o", Oslash = "O", oslash = "o",
+
+ Ssharp = "SS", ssharp = "ss",
+
+ Aumlaut = "A", aumlaut = "a",
+ Eumlaut = "E", eumlaut = "e",
+ Iumlaut = "I", iumlaut = "i",
+ Oumlaut = "O", oumlaut = "o",
+ Uumlaut = "U", uumlaut = "u",
+ }
+}
diff --git a/tex/context/base/font-afm.lua b/tex/context/base/font-afm.lua
index 58408457e..cb0c2438f 100644
--- a/tex/context/base/font-afm.lua
+++ b/tex/context/base/font-afm.lua
@@ -1,971 +1,971 @@
-if not modules then modules = { } end modules ['font-afm'] = {
- 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>Some code may look a bit obscure but this has to do with the
-fact that we also use this code for testing and much code evolved
-in the transition from <l n='tfm'/> to <l n='afm'/> to <l
-n='otf'/>.</p>
-
-<p>The following code still has traces of intermediate font support
-where we handles font encodings. Eventually font encoding goes
-away.</p>
---ldx]]--
-
-local fonts, logs, trackers, containers, resolvers = fonts, logs, trackers, containers, resolvers
-
-local next, type, tonumber = next, type, tonumber
-local format, match, gmatch, lower, gsub, strip = string.format, string.match, string.gmatch, string.lower, string.gsub, string.strip
-local abs = math.abs
-local P, S, C, R, lpegmatch, patterns = lpeg.P, lpeg.S, lpeg.C, lpeg.R, lpeg.match, lpeg.patterns
-local derivetable = table.derive
-
-local trace_features = false trackers.register("afm.features", function(v) trace_features = v end)
-local trace_indexing = false trackers.register("afm.indexing", function(v) trace_indexing = v end)
-local trace_loading = false trackers.register("afm.loading", function(v) trace_loading = v end)
-local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end)
-
-local report_afm = logs.reporter("fonts","afm loading")
-
-local findbinfile = resolvers.findbinfile
-
-local definers = fonts.definers
-local readers = fonts.readers
-local constructors = fonts.constructors
-
-local afm = constructors.newhandler("afm")
-local pfb = constructors.newhandler("pfb")
-
-local afmfeatures = constructors.newfeatures("afm")
-local registerafmfeature = afmfeatures.register
-
-afm.version = 1.410 -- incrementing this number one up will force a re-cache
-afm.cache = containers.define("fonts", "afm", afm.version, true)
-afm.autoprefixed = true -- this will become false some day (catches texnansi-blabla.*)
-
-afm.helpdata = { } -- set later on so no local for this
-afm.syncspace = true -- when true, nicer stretch values
-afm.addligatures = true -- best leave this set to true
-afm.addtexligatures = true -- best leave this set to true
-afm.addkerns = true -- best leave this set to true
-
-local function setmode(tfmdata,value)
- if value then
- tfmdata.properties.mode = lower(value)
- end
-end
-
-registerafmfeature {
- name = "mode",
- description = "mode",
- initializers = {
- base = setmode,
- node = setmode,
- }
-}
-
---[[ldx--
-<p>We start with the basic reader which we give a name similar to the
-built in <l n='tfm'/> and <l n='otf'/> reader.</p>
---ldx]]--
-
---~ Comment FONTIDENTIFIER LMMATHSYMBOLS10
---~ Comment CODINGSCHEME TEX MATH SYMBOLS
---~ Comment DESIGNSIZE 10.0 pt
---~ Comment CHECKSUM O 4261307036
---~ Comment SPACE 0 plus 0 minus 0
---~ Comment QUAD 1000
---~ Comment EXTRASPACE 0
---~ Comment NUM 676.508 393.732 443.731
---~ Comment DENOM 685.951 344.841
---~ Comment SUP 412.892 362.892 288.889
---~ Comment SUB 150 247.217
---~ Comment SUPDROP 386.108
---~ Comment SUBDROP 50
---~ Comment DELIM 2390 1010
---~ Comment AXISHEIGHT 250
-
-local comment = P("Comment")
-local spacing = patterns.spacer -- S(" \t")^1
-local lineend = patterns.newline -- S("\n\r")
-local words = C((1 - lineend)^1)
-local number = C((R("09") + S("."))^1) / tonumber * spacing^0
-local data = lpeg.Carg(1)
-
-local pattern = ( -- needs testing ... not used anyway as we no longer need math afm's
- comment * spacing *
- (
- data * (
- ("CODINGSCHEME" * spacing * words ) / function(fd,a) end +
- ("DESIGNSIZE" * spacing * number * words ) / function(fd,a) fd[ 1] = a end +
- ("CHECKSUM" * spacing * number * words ) / function(fd,a) fd[ 2] = a end +
- ("SPACE" * spacing * number * "plus" * number * "minus" * number) / function(fd,a,b,c) fd[ 3], fd[ 4], fd[ 5] = a, b, c end +
- ("QUAD" * spacing * number ) / function(fd,a) fd[ 6] = a end +
- ("EXTRASPACE" * spacing * number ) / function(fd,a) fd[ 7] = a end +
- ("NUM" * spacing * number * number * number ) / function(fd,a,b,c) fd[ 8], fd[ 9], fd[10] = a, b, c end +
- ("DENOM" * spacing * number * number ) / function(fd,a,b ) fd[11], fd[12] = a, b end +
- ("SUP" * spacing * number * number * number ) / function(fd,a,b,c) fd[13], fd[14], fd[15] = a, b, c end +
- ("SUB" * spacing * number * number ) / function(fd,a,b) fd[16], fd[17] = a, b end +
- ("SUPDROP" * spacing * number ) / function(fd,a) fd[18] = a end +
- ("SUBDROP" * spacing * number ) / function(fd,a) fd[19] = a end +
- ("DELIM" * spacing * number * number ) / function(fd,a,b) fd[20], fd[21] = a, b end +
- ("AXISHEIGHT" * spacing * number ) / function(fd,a) fd[22] = a end
- )
- + (1-lineend)^0
- )
- + (1-comment)^1
-)^0
-
-local function scan_comment(str)
- local fd = { }
- lpegmatch(pattern,str,1,fd)
- return fd
-end
-
--- On a rainy day I will rewrite this in lpeg ... or we can use the (slower) fontloader
--- as in now supports afm/pfb loading but it's not too bad to have different methods
--- for testing approaches.
-
-local keys = { }
-
-function keys.FontName (data,line) data.metadata.fontname = strip (line) -- get rid of spaces
- data.metadata.fullname = strip (line) end
-function keys.ItalicAngle (data,line) data.metadata.italicangle = tonumber (line) end
-function keys.IsFixedPitch(data,line) data.metadata.isfixedpitch = toboolean(line,true) end
-function keys.CharWidth (data,line) data.metadata.charwidth = tonumber (line) end
-function keys.XHeight (data,line) data.metadata.xheight = tonumber (line) end
-function keys.Descender (data,line) data.metadata.descender = tonumber (line) end
-function keys.Ascender (data,line) data.metadata.ascender = tonumber (line) end
-function keys.Comment (data,line)
- -- Comment DesignSize 12 (pts)
- -- Comment TFM designsize: 12 (in points)
- line = lower(line)
- local designsize = match(line,"designsize[^%d]*(%d+)")
- if designsize then data.metadata.designsize = tonumber(designsize) end
-end
-
-local function get_charmetrics(data,charmetrics,vector)
- local characters = data.characters
- local chr, ind = { }, 0
- for k,v in gmatch(charmetrics,"([%a]+) +(.-) *;") do
- if k == 'C' then
- v = tonumber(v)
- if v < 0 then
- ind = ind + 1 -- ?
- else
- ind = v
- end
- chr = {
- index = ind
- }
- elseif k == 'WX' then
- chr.width = tonumber(v)
- elseif k == 'N' then
- characters[v] = chr
- elseif k == 'B' then
- local llx, lly, urx, ury = match(v,"^ *(.-) +(.-) +(.-) +(.-)$")
- chr.boundingbox = { tonumber(llx), tonumber(lly), tonumber(urx), tonumber(ury) }
- elseif k == 'L' then
- local plus, becomes = match(v,"^(.-) +(.-)$")
- local ligatures = chr.ligatures
- if ligatures then
- ligatures[plus] = becomes
- else
- chr.ligatures = { [plus] = becomes }
- end
- end
- end
-end
-
-local function get_kernpairs(data,kernpairs)
- local characters = data.characters
- for one, two, value in gmatch(kernpairs,"KPX +(.-) +(.-) +(.-)\n") do
- local chr = characters[one]
- if chr then
- local kerns = chr.kerns
- if kerns then
- kerns[two] = tonumber(value)
- else
- chr.kerns = { [two] = tonumber(value) }
- end
- end
- end
-end
-
-local function get_variables(data,fontmetrics)
- for key, rest in gmatch(fontmetrics,"(%a+) *(.-)[\n\r]") do
- local keyhandler = keys[key]
- if keyhandler then
- keyhandler(data,rest)
- end
- end
-end
-
-local function get_indexes(data,pfbname)
- data.resources.filename = resolvers.unresolve(pfbname) -- no shortcut
- local pfbblob = fontloader.open(pfbname)
- if pfbblob then
- local characters = data.characters
- local pfbdata = fontloader.to_table(pfbblob)
- if pfbdata then
- local glyphs = pfbdata.glyphs
- if glyphs then
- if trace_loading then
- report_afm("getting index data from %a",pfbname)
- end
- for index, glyph in next, glyphs do
- local name = glyph.name
- if name then
- local char = characters[name]
- if char then
- if trace_indexing then
- report_afm("glyph %a has index %a",name,index)
- end
- char.index = index
- end
- end
- end
- elseif trace_loading then
- report_afm("no glyph data in pfb file %a",pfbname)
- end
- elseif trace_loading then
- report_afm("no data in pfb file %a",pfbname)
- end
- fontloader.close(pfbblob)
- elseif trace_loading then
- report_afm("invalid pfb file %a",pfbname)
- end
-end
-
-local function readafm(filename)
- local ok, afmblob, size = resolvers.loadbinfile(filename) -- has logging
- if ok and afmblob then
- local data = {
- resources = {
- filename = resolvers.unresolve(filename),
- version = afm.version,
- creator = "context mkiv",
- },
- properties = {
- hasitalics = false,
- },
- goodies = {
- },
- metadata = {
- filename = file.removesuffix(file.basename(filename))
- },
- characters = {
- -- a temporary store
- },
- descriptions = {
- -- the final store
- },
- }
- afmblob = gsub(afmblob,"StartCharMetrics(.-)EndCharMetrics", function(charmetrics)
- if trace_loading then
- report_afm("loading char metrics")
- end
- get_charmetrics(data,charmetrics,vector)
- return ""
- end)
- afmblob = gsub(afmblob,"StartKernPairs(.-)EndKernPairs", function(kernpairs)
- if trace_loading then
- report_afm("loading kern pairs")
- end
- get_kernpairs(data,kernpairs)
- return ""
- end)
- afmblob = gsub(afmblob,"StartFontMetrics%s+([%d%.]+)(.-)EndFontMetrics", function(version,fontmetrics)
- if trace_loading then
- report_afm("loading variables")
- end
- data.afmversion = version
- get_variables(data,fontmetrics)
- data.fontdimens = scan_comment(fontmetrics) -- todo: all lpeg, no time now
- return ""
- end)
- return data
- else
- if trace_loading then
- report_afm("no valid afm file %a",filename)
- end
- return nil
- end
-end
-
---[[ldx--
-<p>We cache files. Caching is taken care of in the loader. We cheat a bit
-by adding ligatures and kern information to the afm derived data. That
-way we can set them faster when defining a font.</p>
---ldx]]--
-
-local addkerns, addligatures, addtexligatures, unify, normalize -- we will implement these later
-
-function afm.load(filename)
- -- hm, for some reasons not resolved yet
- filename = resolvers.findfile(filename,'afm') or ""
- if filename ~= "" then
- local name = file.removesuffix(file.basename(filename))
- local data = containers.read(afm.cache,name)
- local attr = lfs.attributes(filename)
- local size, time = attr.size or 0, attr.modification or 0
- --
- local pfbfile = file.replacesuffix(name,"pfb")
- local pfbname = resolvers.findfile(pfbfile,"pfb") or ""
- if pfbname == "" then
- pfbname = resolvers.findfile(file.basename(pfbfile),"pfb") or ""
- end
- local pfbsize, pfbtime = 0, 0
- if pfbname ~= "" then
- local attr = lfs.attributes(pfbname)
- pfbsize = attr.size or 0
- pfbtime = attr.modification or 0
- end
- if not data or data.size ~= size or data.time ~= time or data.pfbsize ~= pfbsize or data.pfbtime ~= pfbtime then
- report_afm("reading %a",filename)
- data = readafm(filename)
- if data then
- if pfbname ~= "" then
- get_indexes(data,pfbname)
- elseif trace_loading then
- report_afm("no pfb file for %a",filename)
- end
- report_afm("unifying %a",filename)
- unify(data,filename)
- if afm.addligatures then
- report_afm("add ligatures")
- addligatures(data)
- end
- if afm.addtexligatures then
- report_afm("add tex ligatures")
- addtexligatures(data)
- end
- if afm.addkerns then
- report_afm("add extra kerns")
- addkerns(data)
- end
- normalize(data)
- report_afm("add tounicode data")
- fonts.mappings.addtounicode(data,filename)
- data.size = size
- data.time = time
- data.pfbsize = pfbsize
- data.pfbtime = pfbtime
- report_afm("saving %a in cache",name)
- data = containers.write(afm.cache, name, data)
- data = containers.read(afm.cache,name)
- end
- end
- return data
- else
- return nil
- end
-end
-
-local uparser = fonts.mappings.makenameparser()
-
-unify = function(data, filename)
- local unicodevector = fonts.encodings.agl.unicodes -- loaded runtime in context
- local unicodes, names = { }, { }
- local private = constructors.privateoffset
- local descriptions = data.descriptions
- for name, blob in next, data.characters do
- local code = unicodevector[name] -- or characters.name_to_unicode[name]
- if not code then
- code = lpegmatch(uparser,name)
- if not code then
- code = private
- private = private + 1
- report_afm("assigning private slot %U for unknown glyph name %a",code,name)
- end
- end
- local index = blob.index
- unicodes[name] = code
- names[name] = index
- blob.name = name
- descriptions[code] = {
- boundingbox = blob.boundingbox,
- width = blob.width,
- kerns = blob.kerns,
- index = index,
- name = name,
- }
- end
- for unicode, description in next, descriptions do
- local kerns = description.kerns
- if kerns then
- local krn = { }
- for name, kern in next, kerns do
- local unicode = unicodes[name]
- if unicode then
- krn[unicode] = kern
- else
- print(unicode,name)
- end
- end
- description.kerns = krn
- end
- end
- data.characters = nil
- local resources = data.resources
- local filename = resources.filename or file.removesuffix(file.basename(filename))
- resources.filename = resolvers.unresolve(filename) -- no shortcut
- resources.unicodes = unicodes -- name to unicode
- resources.marks = { } -- todo
- resources.names = names -- name to index
- resources.private = private
-end
-
-normalize = function(data)
-end
-
---[[ldx--
-<p>These helpers extend the basic table with extra ligatures, texligatures
-and extra kerns. This saves quite some lookups later.</p>
---ldx]]--
-
-local addthem = function(rawdata,ligatures)
- if ligatures then
- local descriptions = rawdata.descriptions
- local resources = rawdata.resources
- local unicodes = resources.unicodes
- local names = resources.names
- for ligname, ligdata in next, ligatures do
- local one = descriptions[unicodes[ligname]]
- if one then
- for _, pair in next, ligdata do
- local two, three = unicodes[pair[1]], unicodes[pair[2]]
- if two and three then
- local ol = one.ligatures
- if ol then
- if not ol[two] then
- ol[two] = three
- end
- else
- one.ligatures = { [two] = three }
- end
- end
- end
- end
- end
- end
-end
-
-addligatures = function(rawdata) addthem(rawdata,afm.helpdata.ligatures ) end
-addtexligatures = function(rawdata) addthem(rawdata,afm.helpdata.texligatures) end
-
---[[ldx--
-<p>We keep the extra kerns in separate kerning tables so that we can use
-them selectively.</p>
---ldx]]--
-
--- This is rather old code (from the beginning when we had only tfm). If
--- we unify the afm data (now we have names all over the place) then
--- we can use shcodes but there will be many more looping then. But we
--- could get rid of the tables in char-cmp then. Als, in the generic version
--- we don't use the character database. (Ok, we can have a context specific
--- variant).
-
-addkerns = function(rawdata) -- using shcodes is not robust here
- local descriptions = rawdata.descriptions
- local resources = rawdata.resources
- local unicodes = resources.unicodes
- local function do_it_left(what)
- if what then
- for unicode, description in next, descriptions do
- local kerns = description.kerns
- if kerns then
- local extrakerns
- for complex, simple in next, what do
- complex = unicodes[complex]
- simple = unicodes[simple]
- if complex and simple then
- local ks = kerns[simple]
- if ks and not kerns[complex] then
- if extrakerns then
- extrakerns[complex] = ks
- else
- extrakerns = { [complex] = ks }
- end
- end
- end
- end
- if extrakerns then
- description.extrakerns = extrakerns
- end
- end
- end
- end
- end
- local function do_it_copy(what)
- if what then
- for complex, simple in next, what do
- complex = unicodes[complex]
- simple = unicodes[simple]
- if complex and simple then
- local complexdescription = descriptions[complex]
- if complexdescription then -- optional
- local simpledescription = descriptions[complex]
- if simpledescription then
- local extrakerns
- local kerns = simpledescription.kerns
- if kerns then
- for unicode, kern in next, kerns do
- if extrakerns then
- extrakerns[unicode] = kern
- else
- extrakerns = { [unicode] = kern }
- end
- end
- end
- local extrakerns = simpledescription.extrakerns
- if extrakerns then
- for unicode, kern in next, extrakerns do
- if extrakerns then
- extrakerns[unicode] = kern
- else
- extrakerns = { [unicode] = kern }
- end
- end
- end
- if extrakerns then
- complexdescription.extrakerns = extrakerns
- end
- end
- end
- end
- end
- end
- end
- -- add complex with values of simplified when present
- do_it_left(afm.helpdata.leftkerned)
- do_it_left(afm.helpdata.bothkerned)
- -- copy kerns from simple char to complex char unless set
- do_it_copy(afm.helpdata.bothkerned)
- do_it_copy(afm.helpdata.rightkerned)
-end
-
---[[ldx--
-<p>The copying routine looks messy (and is indeed a bit messy).</p>
---ldx]]--
-
-local function adddimensions(data) -- we need to normalize afm to otf i.e. indexed table instead of name
- if data then
- for unicode, description in next, data.descriptions do
- local bb = description.boundingbox
- if bb then
- local ht, dp = bb[4], -bb[2]
- if ht == 0 or ht < 0 then
- -- no need to set it and no negative heights, nil == 0
- else
- description.height = ht
- end
- if dp == 0 or dp < 0 then
- -- no negative depths and no negative depths, nil == 0
- else
- description.depth = dp
- end
- end
- end
- end
-end
-
-local function copytotfm(data)
- if data and data.descriptions then
- local metadata = data.metadata
- local resources = data.resources
- local properties = derivetable(data.properties)
- local descriptions = derivetable(data.descriptions)
- local goodies = derivetable(data.goodies)
- local characters = { }
- local parameters = { }
- local unicodes = resources.unicodes
- --
- for unicode, description in next, data.descriptions do -- use parent table
- characters[unicode] = { }
- end
- --
- local filename = constructors.checkedfilename(resources)
- local fontname = metadata.fontname or metadata.fullname
- local fullname = metadata.fullname or metadata.fontname
- local endash = unicodes['space']
- local emdash = unicodes['emdash']
- local spacer = "space"
- local spaceunits = 500
- --
- local monospaced = metadata.isfixedpitch
- local charwidth = metadata.charwidth
- local italicangle = metadata.italicangle
- local charxheight = metadata.xheight and metadata.xheight > 0 and metadata.xheight
- properties.monospaced = monospaced
- parameters.italicangle = italicangle
- parameters.charwidth = charwidth
- parameters.charxheight = charxheight
- -- same as otf
- if properties.monospaced then
- if descriptions[endash] then
- spaceunits, spacer = descriptions[endash].width, "space"
- end
- if not spaceunits and descriptions[emdash] then
- spaceunits, spacer = descriptions[emdash].width, "emdash"
- end
- if not spaceunits and charwidth then
- spaceunits, spacer = charwidth, "charwidth"
- end
- else
- if descriptions[endash] then
- spaceunits, spacer = descriptions[endash].width, "space"
- end
- if not spaceunits and charwidth then
- spaceunits, spacer = charwidth, "charwidth"
- end
- end
- spaceunits = tonumber(spaceunits)
- if spaceunits < 200 then
- -- todo: warning
- end
- --
- parameters.slant = 0
- parameters.space = spaceunits
- parameters.space_stretch = 500
- parameters.space_shrink = 333
- parameters.x_height = 400
- parameters.quad = 1000
- --
- if italicangle then
- parameters.italicangle = italicangle
- parameters.italicfactor = math.cos(math.rad(90+italicangle))
- parameters.slant = - math.round(math.tan(italicangle*math.pi/180))
- end
- if monospaced then
- parameters.space_stretch = 0
- parameters.space_shrink = 0
- elseif afm.syncspace then
- parameters.space_stretch = spaceunits/2
- parameters.space_shrink = spaceunits/3
- end
- parameters.extra_space = parameters.space_shrink
- if charxheight then
- parameters.x_height = charxheight
- else
- -- same as otf
- local x = unicodes['x']
- if x then
- local x = descriptions[x]
- if x then
- parameters.x_height = x.height
- end
- end
- --
- end
- local fd = data.fontdimens
- if fd and fd[8] and fd[9] and fd[10] then -- math
- for k,v in next, fd do
- parameters[k] = v
- end
- end
- --
- parameters.designsize = (metadata.designsize or 10)*65536
- parameters.ascender = abs(metadata.ascender or 0)
- parameters.descender = abs(metadata.descender or 0)
- parameters.units = 1000
- --
- properties.spacer = spacer
- properties.encodingbytes = 2
- properties.format = fonts.formats[filename] or "type1"
- properties.filename = filename
- properties.fontname = fontname
- properties.fullname = fullname
- properties.psname = fullname
- properties.name = filename or fullname or fontname
- --
- if next(characters) then
- return {
- characters = characters,
- descriptions = descriptions,
- parameters = parameters,
- resources = resources,
- properties = properties,
- goodies = goodies,
- }
- end
- end
- return nil
-end
-
---[[ldx--
-<p>Originally we had features kind of hard coded for <l n='afm'/>
-files but since I expect to support more font formats, I decided
-to treat this fontformat like any other and handle features in a
-more configurable way.</p>
---ldx]]--
-
-function afm.setfeatures(tfmdata,features)
- local okay = constructors.initializefeatures("afm",tfmdata,features,trace_features,report_afm)
- if okay then
- return constructors.collectprocessors("afm",tfmdata,features,trace_features,report_afm)
- else
- return { } -- will become false
- end
-end
-
-local function checkfeatures(specification)
-end
-
-local function afmtotfm(specification)
- local afmname = specification.filename or specification.name
- if specification.forced == "afm" or specification.format == "afm" then -- move this one up
- if trace_loading then
- report_afm("forcing afm format for %a",afmname)
- end
- else
- local tfmname = findbinfile(afmname,"ofm") or ""
- if tfmname ~= "" then
- if trace_loading then
- report_afm("fallback from afm to tfm for %a",afmname)
- end
- return -- just that
- end
- end
- if afmname ~= "" then
- -- weird, isn't this already done then?
- local features = constructors.checkedfeatures("afm",specification.features.normal)
- specification.features.normal = features
- constructors.hashinstance(specification,true) -- also weird here
- --
- specification = definers.resolve(specification) -- new, was forgotten
- local cache_id = specification.hash
- local tfmdata = containers.read(constructors.cache, cache_id) -- cache with features applied
- if not tfmdata then
- local rawdata = afm.load(afmname)
- if rawdata and next(rawdata) then
- adddimensions(rawdata)
- tfmdata = copytotfm(rawdata)
- if tfmdata and next(tfmdata) then
- local shared = tfmdata.shared
- if not shared then
- shared = { }
- tfmdata.shared = shared
- end
- shared.rawdata = rawdata
- shared.features = features
- shared.processes = afm.setfeatures(tfmdata,features)
- end
- elseif trace_loading then
- report_afm("no (valid) afm file found with name %a",afmname)
- end
- tfmdata = containers.write(constructors.cache,cache_id,tfmdata)
- end
- return tfmdata
- end
-end
-
---[[ldx--
-<p>As soon as we could intercept the <l n='tfm'/> reader, I implemented an
-<l n='afm'/> reader. Since traditional <l n='pdftex'/> could use <l n='opentype'/>
-fonts with <l n='afm'/> companions, the following method also could handle
-those cases, but now that we can handle <l n='opentype'/> directly we no longer
-need this features.</p>
---ldx]]--
-
-local function read_from_afm(specification)
- local tfmdata = afmtotfm(specification)
- if tfmdata then
- tfmdata.properties.name = specification.name
- tfmdata = constructors.scale(tfmdata, specification)
- local allfeatures = tfmdata.shared.features or specification.features.normal
- constructors.applymanipulators("afm",tfmdata,allfeatures,trace_features,report_afm)
- fonts.loggers.register(tfmdata,'afm',specification)
- end
- return tfmdata
-end
-
---[[ldx--
-<p>Here comes the implementation of a few features. We only implement
-those that make sense for this format.</p>
---ldx]]--
-
-local function prepareligatures(tfmdata,ligatures,value)
- if value then
- local descriptions = tfmdata.descriptions
- for unicode, character in next, tfmdata.characters do
- local description = descriptions[unicode]
- local dligatures = description.ligatures
- if dligatures then
- local cligatures = character.ligatures
- if not cligatures then
- cligatures = { }
- character.ligatures = cligatures
- end
- for unicode, ligature in next, dligatures do
- cligatures[unicode] = {
- char = ligature,
- type = 0
- }
- end
- end
- end
- end
-end
-
-local function preparekerns(tfmdata,kerns,value)
- if value then
- local rawdata = tfmdata.shared.rawdata
- local resources = rawdata.resources
- local unicodes = resources.unicodes
- local descriptions = tfmdata.descriptions
- for u, chr in next, tfmdata.characters do
- local d = descriptions[u]
- local newkerns = d[kerns]
- if newkerns then
- local kerns = chr.kerns
- if not kerns then
- kerns = { }
- chr.kerns = kerns
- end
- for k,v in next, newkerns do
- local uk = unicodes[k]
- if uk then
- kerns[uk] = v
- end
- end
- end
- end
- end
-end
-
-local list = {
- -- [0x0022] = 0x201D,
- [0x0027] = 0x2019,
- -- [0x0060] = 0x2018,
-}
-
-local function texreplacements(tfmdata,value)
- local descriptions = tfmdata.descriptions
- local characters = tfmdata.characters
- for k, v in next, list do
- characters [k] = characters [v] -- we forget about kerns
- descriptions[k] = descriptions[v] -- we forget about kerns
- end
-end
-
-local function ligatures (tfmdata,value) prepareligatures(tfmdata,'ligatures', value) end
-local function texligatures(tfmdata,value) prepareligatures(tfmdata,'texligatures',value) end
-local function kerns (tfmdata,value) preparekerns (tfmdata,'kerns', value) end
-local function extrakerns (tfmdata,value) preparekerns (tfmdata,'extrakerns', value) end
-
-registerafmfeature {
- name = "liga",
- description = "traditional ligatures",
- initializers = {
- base = ligatures,
- node = ligatures,
- }
-}
-
-registerafmfeature {
- name = "kern",
- description = "intercharacter kerning",
- initializers = {
- base = kerns,
- node = kerns,
- }
-}
-
-registerafmfeature {
- name = "extrakerns",
- description = "additional intercharacter kerning",
- initializers = {
- base = extrakerns,
- node = extrakerns,
- }
-}
-
-registerafmfeature {
- name = 'tlig',
- description = 'tex ligatures',
- initializers = {
- base = texligatures,
- node = texligatures,
- }
-}
-
-registerafmfeature {
- name = 'trep',
- description = 'tex replacements',
- initializers = {
- base = texreplacements,
- node = texreplacements,
- }
-}
-
--- readers
-
-local check_tfm = readers.check_tfm
-
-fonts.formats.afm = "type1"
-fonts.formats.pfb = "type1"
-
-local function check_afm(specification,fullname)
- local foundname = findbinfile(fullname, 'afm') or "" -- just to be sure
- if foundname == "" then
- foundname = fonts.names.getfilename(fullname,"afm") or ""
- end
- if foundname == "" and afm.autoprefixed then
- local encoding, shortname = match(fullname,"^(.-)%-(.*)$") -- context: encoding-name.*
- if encoding and shortname and fonts.encodings.known[encoding] then
- shortname = findbinfile(shortname,'afm') or "" -- just to be sure
- if shortname ~= "" then
- foundname = shortname
- if trace_defining then
- report_afm("stripping encoding prefix from filename %a",afmname)
- end
- end
- end
- end
- if foundname ~= "" then
- specification.filename = foundname
- specification.format = "afm"
- return read_from_afm(specification)
- end
-end
-
-function readers.afm(specification,method)
- local fullname, tfmdata = specification.filename or "", nil
- if fullname == "" then
- local forced = specification.forced or ""
- if forced ~= "" then
- tfmdata = check_afm(specification,specification.name .. "." .. forced)
- end
- if not tfmdata then
- method = method or definers.method or "afm or tfm"
- if method == "tfm" then
- tfmdata = check_tfm(specification,specification.name)
- elseif method == "afm" then
- tfmdata = check_afm(specification,specification.name)
- elseif method == "tfm or afm" then
- tfmdata = check_tfm(specification,specification.name) or check_afm(specification,specification.name)
- else -- method == "afm or tfm" or method == "" then
- tfmdata = check_afm(specification,specification.name) or check_tfm(specification,specification.name)
- end
- end
- else
- tfmdata = check_afm(specification,fullname)
- end
- return tfmdata
-end
-
-function readers.pfb(specification,method) -- only called when forced
- local original = specification.specification
- if trace_defining then
- report_afm("using afm reader for %a",original)
- end
- specification.specification = gsub(original,"%.pfb",".afm")
- specification.forced = "afm"
- return readers.afm(specification,method)
-end
+if not modules then modules = { } end modules ['font-afm'] = {
+ 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>Some code may look a bit obscure but this has to do with the
+fact that we also use this code for testing and much code evolved
+in the transition from <l n='tfm'/> to <l n='afm'/> to <l
+n='otf'/>.</p>
+
+<p>The following code still has traces of intermediate font support
+where we handles font encodings. Eventually font encoding goes
+away.</p>
+--ldx]]--
+
+local fonts, logs, trackers, containers, resolvers = fonts, logs, trackers, containers, resolvers
+
+local next, type, tonumber = next, type, tonumber
+local format, match, gmatch, lower, gsub, strip = string.format, string.match, string.gmatch, string.lower, string.gsub, string.strip
+local abs = math.abs
+local P, S, C, R, lpegmatch, patterns = lpeg.P, lpeg.S, lpeg.C, lpeg.R, lpeg.match, lpeg.patterns
+local derivetable = table.derive
+
+local trace_features = false trackers.register("afm.features", function(v) trace_features = v end)
+local trace_indexing = false trackers.register("afm.indexing", function(v) trace_indexing = v end)
+local trace_loading = false trackers.register("afm.loading", function(v) trace_loading = v end)
+local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end)
+
+local report_afm = logs.reporter("fonts","afm loading")
+
+local findbinfile = resolvers.findbinfile
+
+local definers = fonts.definers
+local readers = fonts.readers
+local constructors = fonts.constructors
+
+local afm = constructors.newhandler("afm")
+local pfb = constructors.newhandler("pfb")
+
+local afmfeatures = constructors.newfeatures("afm")
+local registerafmfeature = afmfeatures.register
+
+afm.version = 1.410 -- incrementing this number one up will force a re-cache
+afm.cache = containers.define("fonts", "afm", afm.version, true)
+afm.autoprefixed = true -- this will become false some day (catches texnansi-blabla.*)
+
+afm.helpdata = { } -- set later on so no local for this
+afm.syncspace = true -- when true, nicer stretch values
+afm.addligatures = true -- best leave this set to true
+afm.addtexligatures = true -- best leave this set to true
+afm.addkerns = true -- best leave this set to true
+
+local function setmode(tfmdata,value)
+ if value then
+ tfmdata.properties.mode = lower(value)
+ end
+end
+
+registerafmfeature {
+ name = "mode",
+ description = "mode",
+ initializers = {
+ base = setmode,
+ node = setmode,
+ }
+}
+
+--[[ldx--
+<p>We start with the basic reader which we give a name similar to the
+built in <l n='tfm'/> and <l n='otf'/> reader.</p>
+--ldx]]--
+
+--~ Comment FONTIDENTIFIER LMMATHSYMBOLS10
+--~ Comment CODINGSCHEME TEX MATH SYMBOLS
+--~ Comment DESIGNSIZE 10.0 pt
+--~ Comment CHECKSUM O 4261307036
+--~ Comment SPACE 0 plus 0 minus 0
+--~ Comment QUAD 1000
+--~ Comment EXTRASPACE 0
+--~ Comment NUM 676.508 393.732 443.731
+--~ Comment DENOM 685.951 344.841
+--~ Comment SUP 412.892 362.892 288.889
+--~ Comment SUB 150 247.217
+--~ Comment SUPDROP 386.108
+--~ Comment SUBDROP 50
+--~ Comment DELIM 2390 1010
+--~ Comment AXISHEIGHT 250
+
+local comment = P("Comment")
+local spacing = patterns.spacer -- S(" \t")^1
+local lineend = patterns.newline -- S("\n\r")
+local words = C((1 - lineend)^1)
+local number = C((R("09") + S("."))^1) / tonumber * spacing^0
+local data = lpeg.Carg(1)
+
+local pattern = ( -- needs testing ... not used anyway as we no longer need math afm's
+ comment * spacing *
+ (
+ data * (
+ ("CODINGSCHEME" * spacing * words ) / function(fd,a) end +
+ ("DESIGNSIZE" * spacing * number * words ) / function(fd,a) fd[ 1] = a end +
+ ("CHECKSUM" * spacing * number * words ) / function(fd,a) fd[ 2] = a end +
+ ("SPACE" * spacing * number * "plus" * number * "minus" * number) / function(fd,a,b,c) fd[ 3], fd[ 4], fd[ 5] = a, b, c end +
+ ("QUAD" * spacing * number ) / function(fd,a) fd[ 6] = a end +
+ ("EXTRASPACE" * spacing * number ) / function(fd,a) fd[ 7] = a end +
+ ("NUM" * spacing * number * number * number ) / function(fd,a,b,c) fd[ 8], fd[ 9], fd[10] = a, b, c end +
+ ("DENOM" * spacing * number * number ) / function(fd,a,b ) fd[11], fd[12] = a, b end +
+ ("SUP" * spacing * number * number * number ) / function(fd,a,b,c) fd[13], fd[14], fd[15] = a, b, c end +
+ ("SUB" * spacing * number * number ) / function(fd,a,b) fd[16], fd[17] = a, b end +
+ ("SUPDROP" * spacing * number ) / function(fd,a) fd[18] = a end +
+ ("SUBDROP" * spacing * number ) / function(fd,a) fd[19] = a end +
+ ("DELIM" * spacing * number * number ) / function(fd,a,b) fd[20], fd[21] = a, b end +
+ ("AXISHEIGHT" * spacing * number ) / function(fd,a) fd[22] = a end
+ )
+ + (1-lineend)^0
+ )
+ + (1-comment)^1
+)^0
+
+local function scan_comment(str)
+ local fd = { }
+ lpegmatch(pattern,str,1,fd)
+ return fd
+end
+
+-- On a rainy day I will rewrite this in lpeg ... or we can use the (slower) fontloader
+-- as in now supports afm/pfb loading but it's not too bad to have different methods
+-- for testing approaches.
+
+local keys = { }
+
+function keys.FontName (data,line) data.metadata.fontname = strip (line) -- get rid of spaces
+ data.metadata.fullname = strip (line) end
+function keys.ItalicAngle (data,line) data.metadata.italicangle = tonumber (line) end
+function keys.IsFixedPitch(data,line) data.metadata.isfixedpitch = toboolean(line,true) end
+function keys.CharWidth (data,line) data.metadata.charwidth = tonumber (line) end
+function keys.XHeight (data,line) data.metadata.xheight = tonumber (line) end
+function keys.Descender (data,line) data.metadata.descender = tonumber (line) end
+function keys.Ascender (data,line) data.metadata.ascender = tonumber (line) end
+function keys.Comment (data,line)
+ -- Comment DesignSize 12 (pts)
+ -- Comment TFM designsize: 12 (in points)
+ line = lower(line)
+ local designsize = match(line,"designsize[^%d]*(%d+)")
+ if designsize then data.metadata.designsize = tonumber(designsize) end
+end
+
+local function get_charmetrics(data,charmetrics,vector)
+ local characters = data.characters
+ local chr, ind = { }, 0
+ for k,v in gmatch(charmetrics,"([%a]+) +(.-) *;") do
+ if k == 'C' then
+ v = tonumber(v)
+ if v < 0 then
+ ind = ind + 1 -- ?
+ else
+ ind = v
+ end
+ chr = {
+ index = ind
+ }
+ elseif k == 'WX' then
+ chr.width = tonumber(v)
+ elseif k == 'N' then
+ characters[v] = chr
+ elseif k == 'B' then
+ local llx, lly, urx, ury = match(v,"^ *(.-) +(.-) +(.-) +(.-)$")
+ chr.boundingbox = { tonumber(llx), tonumber(lly), tonumber(urx), tonumber(ury) }
+ elseif k == 'L' then
+ local plus, becomes = match(v,"^(.-) +(.-)$")
+ local ligatures = chr.ligatures
+ if ligatures then
+ ligatures[plus] = becomes
+ else
+ chr.ligatures = { [plus] = becomes }
+ end
+ end
+ end
+end
+
+local function get_kernpairs(data,kernpairs)
+ local characters = data.characters
+ for one, two, value in gmatch(kernpairs,"KPX +(.-) +(.-) +(.-)\n") do
+ local chr = characters[one]
+ if chr then
+ local kerns = chr.kerns
+ if kerns then
+ kerns[two] = tonumber(value)
+ else
+ chr.kerns = { [two] = tonumber(value) }
+ end
+ end
+ end
+end
+
+local function get_variables(data,fontmetrics)
+ for key, rest in gmatch(fontmetrics,"(%a+) *(.-)[\n\r]") do
+ local keyhandler = keys[key]
+ if keyhandler then
+ keyhandler(data,rest)
+ end
+ end
+end
+
+local function get_indexes(data,pfbname)
+ data.resources.filename = resolvers.unresolve(pfbname) -- no shortcut
+ local pfbblob = fontloader.open(pfbname)
+ if pfbblob then
+ local characters = data.characters
+ local pfbdata = fontloader.to_table(pfbblob)
+ if pfbdata then
+ local glyphs = pfbdata.glyphs
+ if glyphs then
+ if trace_loading then
+ report_afm("getting index data from %a",pfbname)
+ end
+ for index, glyph in next, glyphs do
+ local name = glyph.name
+ if name then
+ local char = characters[name]
+ if char then
+ if trace_indexing then
+ report_afm("glyph %a has index %a",name,index)
+ end
+ char.index = index
+ end
+ end
+ end
+ elseif trace_loading then
+ report_afm("no glyph data in pfb file %a",pfbname)
+ end
+ elseif trace_loading then
+ report_afm("no data in pfb file %a",pfbname)
+ end
+ fontloader.close(pfbblob)
+ elseif trace_loading then
+ report_afm("invalid pfb file %a",pfbname)
+ end
+end
+
+local function readafm(filename)
+ local ok, afmblob, size = resolvers.loadbinfile(filename) -- has logging
+ if ok and afmblob then
+ local data = {
+ resources = {
+ filename = resolvers.unresolve(filename),
+ version = afm.version,
+ creator = "context mkiv",
+ },
+ properties = {
+ hasitalics = false,
+ },
+ goodies = {
+ },
+ metadata = {
+ filename = file.removesuffix(file.basename(filename))
+ },
+ characters = {
+ -- a temporary store
+ },
+ descriptions = {
+ -- the final store
+ },
+ }
+ afmblob = gsub(afmblob,"StartCharMetrics(.-)EndCharMetrics", function(charmetrics)
+ if trace_loading then
+ report_afm("loading char metrics")
+ end
+ get_charmetrics(data,charmetrics,vector)
+ return ""
+ end)
+ afmblob = gsub(afmblob,"StartKernPairs(.-)EndKernPairs", function(kernpairs)
+ if trace_loading then
+ report_afm("loading kern pairs")
+ end
+ get_kernpairs(data,kernpairs)
+ return ""
+ end)
+ afmblob = gsub(afmblob,"StartFontMetrics%s+([%d%.]+)(.-)EndFontMetrics", function(version,fontmetrics)
+ if trace_loading then
+ report_afm("loading variables")
+ end
+ data.afmversion = version
+ get_variables(data,fontmetrics)
+ data.fontdimens = scan_comment(fontmetrics) -- todo: all lpeg, no time now
+ return ""
+ end)
+ return data
+ else
+ if trace_loading then
+ report_afm("no valid afm file %a",filename)
+ end
+ return nil
+ end
+end
+
+--[[ldx--
+<p>We cache files. Caching is taken care of in the loader. We cheat a bit
+by adding ligatures and kern information to the afm derived data. That
+way we can set them faster when defining a font.</p>
+--ldx]]--
+
+local addkerns, addligatures, addtexligatures, unify, normalize -- we will implement these later
+
+function afm.load(filename)
+ -- hm, for some reasons not resolved yet
+ filename = resolvers.findfile(filename,'afm') or ""
+ if filename ~= "" then
+ local name = file.removesuffix(file.basename(filename))
+ local data = containers.read(afm.cache,name)
+ local attr = lfs.attributes(filename)
+ local size, time = attr.size or 0, attr.modification or 0
+ --
+ local pfbfile = file.replacesuffix(name,"pfb")
+ local pfbname = resolvers.findfile(pfbfile,"pfb") or ""
+ if pfbname == "" then
+ pfbname = resolvers.findfile(file.basename(pfbfile),"pfb") or ""
+ end
+ local pfbsize, pfbtime = 0, 0
+ if pfbname ~= "" then
+ local attr = lfs.attributes(pfbname)
+ pfbsize = attr.size or 0
+ pfbtime = attr.modification or 0
+ end
+ if not data or data.size ~= size or data.time ~= time or data.pfbsize ~= pfbsize or data.pfbtime ~= pfbtime then
+ report_afm("reading %a",filename)
+ data = readafm(filename)
+ if data then
+ if pfbname ~= "" then
+ get_indexes(data,pfbname)
+ elseif trace_loading then
+ report_afm("no pfb file for %a",filename)
+ end
+ report_afm("unifying %a",filename)
+ unify(data,filename)
+ if afm.addligatures then
+ report_afm("add ligatures")
+ addligatures(data)
+ end
+ if afm.addtexligatures then
+ report_afm("add tex ligatures")
+ addtexligatures(data)
+ end
+ if afm.addkerns then
+ report_afm("add extra kerns")
+ addkerns(data)
+ end
+ normalize(data)
+ report_afm("add tounicode data")
+ fonts.mappings.addtounicode(data,filename)
+ data.size = size
+ data.time = time
+ data.pfbsize = pfbsize
+ data.pfbtime = pfbtime
+ report_afm("saving %a in cache",name)
+ data = containers.write(afm.cache, name, data)
+ data = containers.read(afm.cache,name)
+ end
+ end
+ return data
+ else
+ return nil
+ end
+end
+
+local uparser = fonts.mappings.makenameparser()
+
+unify = function(data, filename)
+ local unicodevector = fonts.encodings.agl.unicodes -- loaded runtime in context
+ local unicodes, names = { }, { }
+ local private = constructors.privateoffset
+ local descriptions = data.descriptions
+ for name, blob in next, data.characters do
+ local code = unicodevector[name] -- or characters.name_to_unicode[name]
+ if not code then
+ code = lpegmatch(uparser,name)
+ if not code then
+ code = private
+ private = private + 1
+ report_afm("assigning private slot %U for unknown glyph name %a",code,name)
+ end
+ end
+ local index = blob.index
+ unicodes[name] = code
+ names[name] = index
+ blob.name = name
+ descriptions[code] = {
+ boundingbox = blob.boundingbox,
+ width = blob.width,
+ kerns = blob.kerns,
+ index = index,
+ name = name,
+ }
+ end
+ for unicode, description in next, descriptions do
+ local kerns = description.kerns
+ if kerns then
+ local krn = { }
+ for name, kern in next, kerns do
+ local unicode = unicodes[name]
+ if unicode then
+ krn[unicode] = kern
+ else
+ print(unicode,name)
+ end
+ end
+ description.kerns = krn
+ end
+ end
+ data.characters = nil
+ local resources = data.resources
+ local filename = resources.filename or file.removesuffix(file.basename(filename))
+ resources.filename = resolvers.unresolve(filename) -- no shortcut
+ resources.unicodes = unicodes -- name to unicode
+ resources.marks = { } -- todo
+ resources.names = names -- name to index
+ resources.private = private
+end
+
+normalize = function(data)
+end
+
+--[[ldx--
+<p>These helpers extend the basic table with extra ligatures, texligatures
+and extra kerns. This saves quite some lookups later.</p>
+--ldx]]--
+
+local addthem = function(rawdata,ligatures)
+ if ligatures then
+ local descriptions = rawdata.descriptions
+ local resources = rawdata.resources
+ local unicodes = resources.unicodes
+ local names = resources.names
+ for ligname, ligdata in next, ligatures do
+ local one = descriptions[unicodes[ligname]]
+ if one then
+ for _, pair in next, ligdata do
+ local two, three = unicodes[pair[1]], unicodes[pair[2]]
+ if two and three then
+ local ol = one.ligatures
+ if ol then
+ if not ol[two] then
+ ol[two] = three
+ end
+ else
+ one.ligatures = { [two] = three }
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+addligatures = function(rawdata) addthem(rawdata,afm.helpdata.ligatures ) end
+addtexligatures = function(rawdata) addthem(rawdata,afm.helpdata.texligatures) end
+
+--[[ldx--
+<p>We keep the extra kerns in separate kerning tables so that we can use
+them selectively.</p>
+--ldx]]--
+
+-- This is rather old code (from the beginning when we had only tfm). If
+-- we unify the afm data (now we have names all over the place) then
+-- we can use shcodes but there will be many more looping then. But we
+-- could get rid of the tables in char-cmp then. Als, in the generic version
+-- we don't use the character database. (Ok, we can have a context specific
+-- variant).
+
+addkerns = function(rawdata) -- using shcodes is not robust here
+ local descriptions = rawdata.descriptions
+ local resources = rawdata.resources
+ local unicodes = resources.unicodes
+ local function do_it_left(what)
+ if what then
+ for unicode, description in next, descriptions do
+ local kerns = description.kerns
+ if kerns then
+ local extrakerns
+ for complex, simple in next, what do
+ complex = unicodes[complex]
+ simple = unicodes[simple]
+ if complex and simple then
+ local ks = kerns[simple]
+ if ks and not kerns[complex] then
+ if extrakerns then
+ extrakerns[complex] = ks
+ else
+ extrakerns = { [complex] = ks }
+ end
+ end
+ end
+ end
+ if extrakerns then
+ description.extrakerns = extrakerns
+ end
+ end
+ end
+ end
+ end
+ local function do_it_copy(what)
+ if what then
+ for complex, simple in next, what do
+ complex = unicodes[complex]
+ simple = unicodes[simple]
+ if complex and simple then
+ local complexdescription = descriptions[complex]
+ if complexdescription then -- optional
+ local simpledescription = descriptions[complex]
+ if simpledescription then
+ local extrakerns
+ local kerns = simpledescription.kerns
+ if kerns then
+ for unicode, kern in next, kerns do
+ if extrakerns then
+ extrakerns[unicode] = kern
+ else
+ extrakerns = { [unicode] = kern }
+ end
+ end
+ end
+ local extrakerns = simpledescription.extrakerns
+ if extrakerns then
+ for unicode, kern in next, extrakerns do
+ if extrakerns then
+ extrakerns[unicode] = kern
+ else
+ extrakerns = { [unicode] = kern }
+ end
+ end
+ end
+ if extrakerns then
+ complexdescription.extrakerns = extrakerns
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ -- add complex with values of simplified when present
+ do_it_left(afm.helpdata.leftkerned)
+ do_it_left(afm.helpdata.bothkerned)
+ -- copy kerns from simple char to complex char unless set
+ do_it_copy(afm.helpdata.bothkerned)
+ do_it_copy(afm.helpdata.rightkerned)
+end
+
+--[[ldx--
+<p>The copying routine looks messy (and is indeed a bit messy).</p>
+--ldx]]--
+
+local function adddimensions(data) -- we need to normalize afm to otf i.e. indexed table instead of name
+ if data then
+ for unicode, description in next, data.descriptions do
+ local bb = description.boundingbox
+ if bb then
+ local ht, dp = bb[4], -bb[2]
+ if ht == 0 or ht < 0 then
+ -- no need to set it and no negative heights, nil == 0
+ else
+ description.height = ht
+ end
+ if dp == 0 or dp < 0 then
+ -- no negative depths and no negative depths, nil == 0
+ else
+ description.depth = dp
+ end
+ end
+ end
+ end
+end
+
+local function copytotfm(data)
+ if data and data.descriptions then
+ local metadata = data.metadata
+ local resources = data.resources
+ local properties = derivetable(data.properties)
+ local descriptions = derivetable(data.descriptions)
+ local goodies = derivetable(data.goodies)
+ local characters = { }
+ local parameters = { }
+ local unicodes = resources.unicodes
+ --
+ for unicode, description in next, data.descriptions do -- use parent table
+ characters[unicode] = { }
+ end
+ --
+ local filename = constructors.checkedfilename(resources)
+ local fontname = metadata.fontname or metadata.fullname
+ local fullname = metadata.fullname or metadata.fontname
+ local endash = unicodes['space']
+ local emdash = unicodes['emdash']
+ local spacer = "space"
+ local spaceunits = 500
+ --
+ local monospaced = metadata.isfixedpitch
+ local charwidth = metadata.charwidth
+ local italicangle = metadata.italicangle
+ local charxheight = metadata.xheight and metadata.xheight > 0 and metadata.xheight
+ properties.monospaced = monospaced
+ parameters.italicangle = italicangle
+ parameters.charwidth = charwidth
+ parameters.charxheight = charxheight
+ -- same as otf
+ if properties.monospaced then
+ if descriptions[endash] then
+ spaceunits, spacer = descriptions[endash].width, "space"
+ end
+ if not spaceunits and descriptions[emdash] then
+ spaceunits, spacer = descriptions[emdash].width, "emdash"
+ end
+ if not spaceunits and charwidth then
+ spaceunits, spacer = charwidth, "charwidth"
+ end
+ else
+ if descriptions[endash] then
+ spaceunits, spacer = descriptions[endash].width, "space"
+ end
+ if not spaceunits and charwidth then
+ spaceunits, spacer = charwidth, "charwidth"
+ end
+ end
+ spaceunits = tonumber(spaceunits)
+ if spaceunits < 200 then
+ -- todo: warning
+ end
+ --
+ parameters.slant = 0
+ parameters.space = spaceunits
+ parameters.space_stretch = 500
+ parameters.space_shrink = 333
+ parameters.x_height = 400
+ parameters.quad = 1000
+ --
+ if italicangle then
+ parameters.italicangle = italicangle
+ parameters.italicfactor = math.cos(math.rad(90+italicangle))
+ parameters.slant = - math.round(math.tan(italicangle*math.pi/180))
+ end
+ if monospaced then
+ parameters.space_stretch = 0
+ parameters.space_shrink = 0
+ elseif afm.syncspace then
+ parameters.space_stretch = spaceunits/2
+ parameters.space_shrink = spaceunits/3
+ end
+ parameters.extra_space = parameters.space_shrink
+ if charxheight then
+ parameters.x_height = charxheight
+ else
+ -- same as otf
+ local x = unicodes['x']
+ if x then
+ local x = descriptions[x]
+ if x then
+ parameters.x_height = x.height
+ end
+ end
+ --
+ end
+ local fd = data.fontdimens
+ if fd and fd[8] and fd[9] and fd[10] then -- math
+ for k,v in next, fd do
+ parameters[k] = v
+ end
+ end
+ --
+ parameters.designsize = (metadata.designsize or 10)*65536
+ parameters.ascender = abs(metadata.ascender or 0)
+ parameters.descender = abs(metadata.descender or 0)
+ parameters.units = 1000
+ --
+ properties.spacer = spacer
+ properties.encodingbytes = 2
+ properties.format = fonts.formats[filename] or "type1"
+ properties.filename = filename
+ properties.fontname = fontname
+ properties.fullname = fullname
+ properties.psname = fullname
+ properties.name = filename or fullname or fontname
+ --
+ if next(characters) then
+ return {
+ characters = characters,
+ descriptions = descriptions,
+ parameters = parameters,
+ resources = resources,
+ properties = properties,
+ goodies = goodies,
+ }
+ end
+ end
+ return nil
+end
+
+--[[ldx--
+<p>Originally we had features kind of hard coded for <l n='afm'/>
+files but since I expect to support more font formats, I decided
+to treat this fontformat like any other and handle features in a
+more configurable way.</p>
+--ldx]]--
+
+function afm.setfeatures(tfmdata,features)
+ local okay = constructors.initializefeatures("afm",tfmdata,features,trace_features,report_afm)
+ if okay then
+ return constructors.collectprocessors("afm",tfmdata,features,trace_features,report_afm)
+ else
+ return { } -- will become false
+ end
+end
+
+local function checkfeatures(specification)
+end
+
+local function afmtotfm(specification)
+ local afmname = specification.filename or specification.name
+ if specification.forced == "afm" or specification.format == "afm" then -- move this one up
+ if trace_loading then
+ report_afm("forcing afm format for %a",afmname)
+ end
+ else
+ local tfmname = findbinfile(afmname,"ofm") or ""
+ if tfmname ~= "" then
+ if trace_loading then
+ report_afm("fallback from afm to tfm for %a",afmname)
+ end
+ return -- just that
+ end
+ end
+ if afmname ~= "" then
+ -- weird, isn't this already done then?
+ local features = constructors.checkedfeatures("afm",specification.features.normal)
+ specification.features.normal = features
+ constructors.hashinstance(specification,true) -- also weird here
+ --
+ specification = definers.resolve(specification) -- new, was forgotten
+ local cache_id = specification.hash
+ local tfmdata = containers.read(constructors.cache, cache_id) -- cache with features applied
+ if not tfmdata then
+ local rawdata = afm.load(afmname)
+ if rawdata and next(rawdata) then
+ adddimensions(rawdata)
+ tfmdata = copytotfm(rawdata)
+ if tfmdata and next(tfmdata) then
+ local shared = tfmdata.shared
+ if not shared then
+ shared = { }
+ tfmdata.shared = shared
+ end
+ shared.rawdata = rawdata
+ shared.features = features
+ shared.processes = afm.setfeatures(tfmdata,features)
+ end
+ elseif trace_loading then
+ report_afm("no (valid) afm file found with name %a",afmname)
+ end
+ tfmdata = containers.write(constructors.cache,cache_id,tfmdata)
+ end
+ return tfmdata
+ end
+end
+
+--[[ldx--
+<p>As soon as we could intercept the <l n='tfm'/> reader, I implemented an
+<l n='afm'/> reader. Since traditional <l n='pdftex'/> could use <l n='opentype'/>
+fonts with <l n='afm'/> companions, the following method also could handle
+those cases, but now that we can handle <l n='opentype'/> directly we no longer
+need this features.</p>
+--ldx]]--
+
+local function read_from_afm(specification)
+ local tfmdata = afmtotfm(specification)
+ if tfmdata then
+ tfmdata.properties.name = specification.name
+ tfmdata = constructors.scale(tfmdata, specification)
+ local allfeatures = tfmdata.shared.features or specification.features.normal
+ constructors.applymanipulators("afm",tfmdata,allfeatures,trace_features,report_afm)
+ fonts.loggers.register(tfmdata,'afm',specification)
+ end
+ return tfmdata
+end
+
+--[[ldx--
+<p>Here comes the implementation of a few features. We only implement
+those that make sense for this format.</p>
+--ldx]]--
+
+local function prepareligatures(tfmdata,ligatures,value)
+ if value then
+ local descriptions = tfmdata.descriptions
+ for unicode, character in next, tfmdata.characters do
+ local description = descriptions[unicode]
+ local dligatures = description.ligatures
+ if dligatures then
+ local cligatures = character.ligatures
+ if not cligatures then
+ cligatures = { }
+ character.ligatures = cligatures
+ end
+ for unicode, ligature in next, dligatures do
+ cligatures[unicode] = {
+ char = ligature,
+ type = 0
+ }
+ end
+ end
+ end
+ end
+end
+
+local function preparekerns(tfmdata,kerns,value)
+ if value then
+ local rawdata = tfmdata.shared.rawdata
+ local resources = rawdata.resources
+ local unicodes = resources.unicodes
+ local descriptions = tfmdata.descriptions
+ for u, chr in next, tfmdata.characters do
+ local d = descriptions[u]
+ local newkerns = d[kerns]
+ if newkerns then
+ local kerns = chr.kerns
+ if not kerns then
+ kerns = { }
+ chr.kerns = kerns
+ end
+ for k,v in next, newkerns do
+ local uk = unicodes[k]
+ if uk then
+ kerns[uk] = v
+ end
+ end
+ end
+ end
+ end
+end
+
+local list = {
+ -- [0x0022] = 0x201D,
+ [0x0027] = 0x2019,
+ -- [0x0060] = 0x2018,
+}
+
+local function texreplacements(tfmdata,value)
+ local descriptions = tfmdata.descriptions
+ local characters = tfmdata.characters
+ for k, v in next, list do
+ characters [k] = characters [v] -- we forget about kerns
+ descriptions[k] = descriptions[v] -- we forget about kerns
+ end
+end
+
+local function ligatures (tfmdata,value) prepareligatures(tfmdata,'ligatures', value) end
+local function texligatures(tfmdata,value) prepareligatures(tfmdata,'texligatures',value) end
+local function kerns (tfmdata,value) preparekerns (tfmdata,'kerns', value) end
+local function extrakerns (tfmdata,value) preparekerns (tfmdata,'extrakerns', value) end
+
+registerafmfeature {
+ name = "liga",
+ description = "traditional ligatures",
+ initializers = {
+ base = ligatures,
+ node = ligatures,
+ }
+}
+
+registerafmfeature {
+ name = "kern",
+ description = "intercharacter kerning",
+ initializers = {
+ base = kerns,
+ node = kerns,
+ }
+}
+
+registerafmfeature {
+ name = "extrakerns",
+ description = "additional intercharacter kerning",
+ initializers = {
+ base = extrakerns,
+ node = extrakerns,
+ }
+}
+
+registerafmfeature {
+ name = 'tlig',
+ description = 'tex ligatures',
+ initializers = {
+ base = texligatures,
+ node = texligatures,
+ }
+}
+
+registerafmfeature {
+ name = 'trep',
+ description = 'tex replacements',
+ initializers = {
+ base = texreplacements,
+ node = texreplacements,
+ }
+}
+
+-- readers
+
+local check_tfm = readers.check_tfm
+
+fonts.formats.afm = "type1"
+fonts.formats.pfb = "type1"
+
+local function check_afm(specification,fullname)
+ local foundname = findbinfile(fullname, 'afm') or "" -- just to be sure
+ if foundname == "" then
+ foundname = fonts.names.getfilename(fullname,"afm") or ""
+ end
+ if foundname == "" and afm.autoprefixed then
+ local encoding, shortname = match(fullname,"^(.-)%-(.*)$") -- context: encoding-name.*
+ if encoding and shortname and fonts.encodings.known[encoding] then
+ shortname = findbinfile(shortname,'afm') or "" -- just to be sure
+ if shortname ~= "" then
+ foundname = shortname
+ if trace_defining then
+ report_afm("stripping encoding prefix from filename %a",afmname)
+ end
+ end
+ end
+ end
+ if foundname ~= "" then
+ specification.filename = foundname
+ specification.format = "afm"
+ return read_from_afm(specification)
+ end
+end
+
+function readers.afm(specification,method)
+ local fullname, tfmdata = specification.filename or "", nil
+ if fullname == "" then
+ local forced = specification.forced or ""
+ if forced ~= "" then
+ tfmdata = check_afm(specification,specification.name .. "." .. forced)
+ end
+ if not tfmdata then
+ method = method or definers.method or "afm or tfm"
+ if method == "tfm" then
+ tfmdata = check_tfm(specification,specification.name)
+ elseif method == "afm" then
+ tfmdata = check_afm(specification,specification.name)
+ elseif method == "tfm or afm" then
+ tfmdata = check_tfm(specification,specification.name) or check_afm(specification,specification.name)
+ else -- method == "afm or tfm" or method == "" then
+ tfmdata = check_afm(specification,specification.name) or check_tfm(specification,specification.name)
+ end
+ end
+ else
+ tfmdata = check_afm(specification,fullname)
+ end
+ return tfmdata
+end
+
+function readers.pfb(specification,method) -- only called when forced
+ local original = specification.specification
+ if trace_defining then
+ report_afm("using afm reader for %a",original)
+ end
+ specification.specification = gsub(original,"%.pfb",".afm")
+ specification.forced = "afm"
+ return readers.afm(specification,method)
+end
diff --git a/tex/context/base/font-age.lua b/tex/context/base/font-age.lua
index b7632b55f..bb6883a74 100644
--- a/tex/context/base/font-age.lua
+++ b/tex/context/base/font-age.lua
@@ -1,4115 +1,4115 @@
-if not modules then modules = { } end modules ['font-age'] = {
- version = 1.001,
- comment = "companion to luatex-fonts.lua",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "derived from http://www.adobe.com/devnet/opentype/archives/glyphlist.txt",
- original = "Adobe Glyph List, version 2.0, September 20, 2002",
- dataonly = true,
-}
-
-if context then
- logs.report("fatal error","this module is not for context")
- os.exit()
-end
-
-return { -- generated: inspect(fonts.encodings.agl.unicodes)
- ["A"]=65,
- ["AE"]=198,
- ["AEacute"]=508,
- ["AEmacron"]=482,
- ["Aacute"]=193,
- ["Abreve"]=258,
- ["Abreveacute"]=7854,
- ["Abrevecyrillic"]=1232,
- ["Abrevedotbelow"]=7862,
- ["Abrevegrave"]=7856,
- ["Abrevehookabove"]=7858,
- ["Abrevetilde"]=7860,
- ["Acaron"]=461,
- ["Acircle"]=9398,
- ["Acircumflex"]=194,
- ["Acircumflexacute"]=7844,
- ["Acircumflexdotbelow"]=7852,
- ["Acircumflexgrave"]=7846,
- ["Acircumflexhookabove"]=7848,
- ["Acircumflextilde"]=7850,
- ["Acyrillic"]=1040,
- ["Adblgrave"]=512,
- ["Adieresis"]=196,
- ["Adieresiscyrillic"]=1234,
- ["Adieresismacron"]=478,
- ["Adotbelow"]=7840,
- ["Adotmacron"]=480,
- ["Agrave"]=192,
- ["Ahookabove"]=7842,
- ["Aiecyrillic"]=1236,
- ["Ainvertedbreve"]=514,
- ["Alpha"]=913,
- ["Alphatonos"]=902,
- ["Amacron"]=256,
- ["Amonospace"]=65313,
- ["Aogonek"]=260,
- ["Aring"]=197,
- ["Aringacute"]=506,
- ["Aringbelow"]=7680,
- ["Atilde"]=195,
- ["Aybarmenian"]=1329,
- ["B"]=66,
- ["Bcircle"]=9399,
- ["Bdotaccent"]=7682,
- ["Bdotbelow"]=7684,
- ["Becyrillic"]=1041,
- ["Benarmenian"]=1330,
- ["Beta"]=914,
- ["Bhook"]=385,
- ["Blinebelow"]=7686,
- ["Bmonospace"]=65314,
- ["Btopbar"]=386,
- ["C"]=67,
- ["Caarmenian"]=1342,
- ["Cacute"]=262,
- ["Ccaron"]=268,
- ["Ccedilla"]=199,
- ["Ccedillaacute"]=7688,
- ["Ccircle"]=9400,
- ["Ccircumflex"]=264,
- ["Cdot"]=266,
- ["Cdotaccent"]=266,
- ["Chaarmenian"]=1353,
- ["Cheabkhasiancyrillic"]=1212,
- ["Checyrillic"]=1063,
- ["Chedescenderabkhasiancyrillic"]=1214,
- ["Chedescendercyrillic"]=1206,
- ["Chedieresiscyrillic"]=1268,
- ["Cheharmenian"]=1347,
- ["Chekhakassiancyrillic"]=1227,
- ["Cheverticalstrokecyrillic"]=1208,
- ["Chi"]=935,
- ["Chook"]=391,
- ["Cmonospace"]=65315,
- ["Coarmenian"]=1361,
- ["D"]=68,
- ["DZ"]=497,
- ["DZcaron"]=452,
- ["Daarmenian"]=1332,
- ["Dafrican"]=393,
- ["Dcaron"]=270,
- ["Dcedilla"]=7696,
- ["Dcircle"]=9401,
- ["Dcircumflexbelow"]=7698,
- ["Dcroat"]=272,
- ["Ddotaccent"]=7690,
- ["Ddotbelow"]=7692,
- ["Decyrillic"]=1044,
- ["Deicoptic"]=1006,
- ["Delta"]=8710,
- ["Deltagreek"]=916,
- ["Dhook"]=394,
- ["Digammagreek"]=988,
- ["Djecyrillic"]=1026,
- ["Dlinebelow"]=7694,
- ["Dmonospace"]=65316,
- ["Dslash"]=272,
- ["Dtopbar"]=395,
- ["Dz"]=498,
- ["Dzcaron"]=453,
- ["Dzeabkhasiancyrillic"]=1248,
- ["Dzecyrillic"]=1029,
- ["Dzhecyrillic"]=1039,
- ["E"]=69,
- ["Eacute"]=201,
- ["Ebreve"]=276,
- ["Ecaron"]=282,
- ["Ecedillabreve"]=7708,
- ["Echarmenian"]=1333,
- ["Ecircle"]=9402,
- ["Ecircumflex"]=202,
- ["Ecircumflexacute"]=7870,
- ["Ecircumflexbelow"]=7704,
- ["Ecircumflexdotbelow"]=7878,
- ["Ecircumflexgrave"]=7872,
- ["Ecircumflexhookabove"]=7874,
- ["Ecircumflextilde"]=7876,
- ["Ecyrillic"]=1028,
- ["Edblgrave"]=516,
- ["Edieresis"]=203,
- ["Edot"]=278,
- ["Edotaccent"]=278,
- ["Edotbelow"]=7864,
- ["Efcyrillic"]=1060,
- ["Egrave"]=200,
- ["Eharmenian"]=1335,
- ["Ehookabove"]=7866,
- ["Eightroman"]=8551,
- ["Einvertedbreve"]=518,
- ["Eiotifiedcyrillic"]=1124,
- ["Elcyrillic"]=1051,
- ["Elevenroman"]=8554,
- ["Emacron"]=274,
- ["Emacronacute"]=7702,
- ["Emacrongrave"]=7700,
- ["Emcyrillic"]=1052,
- ["Emonospace"]=65317,
- ["Encyrillic"]=1053,
- ["Endescendercyrillic"]=1186,
- ["Eng"]=330,
- ["Enghecyrillic"]=1188,
- ["Enhookcyrillic"]=1223,
- ["Eogonek"]=280,
- ["Eopen"]=400,
- ["Epsilon"]=917,
- ["Epsilontonos"]=904,
- ["Ercyrillic"]=1056,
- ["Ereversed"]=398,
- ["Ereversedcyrillic"]=1069,
- ["Escyrillic"]=1057,
- ["Esdescendercyrillic"]=1194,
- ["Esh"]=425,
- ["Eta"]=919,
- ["Etarmenian"]=1336,
- ["Etatonos"]=905,
- ["Eth"]=208,
- ["Etilde"]=7868,
- ["Etildebelow"]=7706,
- ["Euro"]=8364,
- ["Ezh"]=439,
- ["Ezhcaron"]=494,
- ["Ezhreversed"]=440,
- ["F"]=70,
- ["Fcircle"]=9403,
- ["Fdotaccent"]=7710,
- ["Feharmenian"]=1366,
- ["Feicoptic"]=996,
- ["Fhook"]=401,
- ["Fitacyrillic"]=1138,
- ["Fiveroman"]=8548,
- ["Fmonospace"]=65318,
- ["Fourroman"]=8547,
- ["G"]=71,
- ["GBsquare"]=13191,
- ["Gacute"]=500,
- ["Gamma"]=915,
- ["Gammaafrican"]=404,
- ["Gangiacoptic"]=1002,
- ["Gbreve"]=286,
- ["Gcaron"]=486,
- ["Gcedilla"]=290,
- ["Gcircle"]=9404,
- ["Gcircumflex"]=284,
- ["Gcommaaccent"]=290,
- ["Gdot"]=288,
- ["Gdotaccent"]=288,
- ["Gecyrillic"]=1043,
- ["Ghadarmenian"]=1346,
- ["Ghemiddlehookcyrillic"]=1172,
- ["Ghestrokecyrillic"]=1170,
- ["Gheupturncyrillic"]=1168,
- ["Ghook"]=403,
- ["Gimarmenian"]=1331,
- ["Gjecyrillic"]=1027,
- ["Gmacron"]=7712,
- ["Gmonospace"]=65319,
- ["Gsmallhook"]=667,
- ["Gstroke"]=484,
- ["H"]=72,
- ["H18533"]=9679,
- ["H18543"]=9642,
- ["H18551"]=9643,
- ["H22073"]=9633,
- ["HPsquare"]=13259,
- ["Haabkhasiancyrillic"]=1192,
- ["Hadescendercyrillic"]=1202,
- ["Hardsigncyrillic"]=1066,
- ["Hbar"]=294,
- ["Hbrevebelow"]=7722,
- ["Hcedilla"]=7720,
- ["Hcircle"]=9405,
- ["Hcircumflex"]=292,
- ["Hdieresis"]=7718,
- ["Hdotaccent"]=7714,
- ["Hdotbelow"]=7716,
- ["Hmonospace"]=65320,
- ["Hoarmenian"]=1344,
- ["Horicoptic"]=1000,
- ["Hzsquare"]=13200,
- ["I"]=73,
- ["IAcyrillic"]=1071,
- ["IJ"]=306,
- ["IUcyrillic"]=1070,
- ["Iacute"]=205,
- ["Ibreve"]=300,
- ["Icaron"]=463,
- ["Icircle"]=9406,
- ["Icircumflex"]=206,
- ["Icyrillic"]=1030,
- ["Idblgrave"]=520,
- ["Idieresis"]=207,
- ["Idieresisacute"]=7726,
- ["Idieresiscyrillic"]=1252,
- ["Idot"]=304,
- ["Idotaccent"]=304,
- ["Idotbelow"]=7882,
- ["Iebrevecyrillic"]=1238,
- ["Iecyrillic"]=1045,
- ["Ifraktur"]=8465,
- ["Igrave"]=204,
- ["Ihookabove"]=7880,
- ["Iicyrillic"]=1048,
- ["Iinvertedbreve"]=522,
- ["Iishortcyrillic"]=1049,
- ["Imacron"]=298,
- ["Imacroncyrillic"]=1250,
- ["Imonospace"]=65321,
- ["Iniarmenian"]=1339,
- ["Iocyrillic"]=1025,
- ["Iogonek"]=302,
- ["Iota"]=921,
- ["Iotaafrican"]=406,
- ["Iotadieresis"]=938,
- ["Iotatonos"]=906,
- ["Istroke"]=407,
- ["Itilde"]=296,
- ["Itildebelow"]=7724,
- ["Izhitsacyrillic"]=1140,
- ["Izhitsadblgravecyrillic"]=1142,
- ["J"]=74,
- ["Jaarmenian"]=1345,
- ["Jcircle"]=9407,
- ["Jcircumflex"]=308,
- ["Jecyrillic"]=1032,
- ["Jheharmenian"]=1355,
- ["Jmonospace"]=65322,
- ["K"]=75,
- ["KBsquare"]=13189,
- ["KKsquare"]=13261,
- ["Kabashkircyrillic"]=1184,
- ["Kacute"]=7728,
- ["Kacyrillic"]=1050,
- ["Kadescendercyrillic"]=1178,
- ["Kahookcyrillic"]=1219,
- ["Kappa"]=922,
- ["Kastrokecyrillic"]=1182,
- ["Kaverticalstrokecyrillic"]=1180,
- ["Kcaron"]=488,
- ["Kcedilla"]=310,
- ["Kcircle"]=9408,
- ["Kcommaaccent"]=310,
- ["Kdotbelow"]=7730,
- ["Keharmenian"]=1364,
- ["Kenarmenian"]=1343,
- ["Khacyrillic"]=1061,
- ["Kheicoptic"]=998,
- ["Khook"]=408,
- ["Kjecyrillic"]=1036,
- ["Klinebelow"]=7732,
- ["Kmonospace"]=65323,
- ["Koppacyrillic"]=1152,
- ["Koppagreek"]=990,
- ["Ksicyrillic"]=1134,
- ["L"]=76,
- ["LJ"]=455,
- ["Lacute"]=313,
- ["Lambda"]=923,
- ["Lcaron"]=317,
- ["Lcedilla"]=315,
- ["Lcircle"]=9409,
- ["Lcircumflexbelow"]=7740,
- ["Lcommaaccent"]=315,
- ["Ldot"]=319,
- ["Ldotaccent"]=319,
- ["Ldotbelow"]=7734,
- ["Ldotbelowmacron"]=7736,
- ["Liwnarmenian"]=1340,
- ["Lj"]=456,
- ["Ljecyrillic"]=1033,
- ["Llinebelow"]=7738,
- ["Lmonospace"]=65324,
- ["Lslash"]=321,
- ["M"]=77,
- ["MBsquare"]=13190,
- ["Macute"]=7742,
- ["Mcircle"]=9410,
- ["Mdotaccent"]=7744,
- ["Mdotbelow"]=7746,
- ["Menarmenian"]=1348,
- ["Mmonospace"]=65325,
- ["Mturned"]=412,
- ["Mu"]=924,
- ["N"]=78,
- ["NJ"]=458,
- ["Nacute"]=323,
- ["Ncaron"]=327,
- ["Ncedilla"]=325,
- ["Ncircle"]=9411,
- ["Ncircumflexbelow"]=7754,
- ["Ncommaaccent"]=325,
- ["Ndotaccent"]=7748,
- ["Ndotbelow"]=7750,
- ["Nhookleft"]=413,
- ["Nineroman"]=8552,
- ["Nj"]=459,
- ["Njecyrillic"]=1034,
- ["Nlinebelow"]=7752,
- ["Nmonospace"]=65326,
- ["Nowarmenian"]=1350,
- ["Ntilde"]=209,
- ["Nu"]=925,
- ["O"]=79,
- ["OE"]=338,
- ["Oacute"]=211,
- ["Obarredcyrillic"]=1256,
- ["Obarreddieresiscyrillic"]=1258,
- ["Obreve"]=334,
- ["Ocaron"]=465,
- ["Ocenteredtilde"]=415,
- ["Ocircle"]=9412,
- ["Ocircumflex"]=212,
- ["Ocircumflexacute"]=7888,
- ["Ocircumflexdotbelow"]=7896,
- ["Ocircumflexgrave"]=7890,
- ["Ocircumflexhookabove"]=7892,
- ["Ocircumflextilde"]=7894,
- ["Ocyrillic"]=1054,
- ["Odblacute"]=336,
- ["Odblgrave"]=524,
- ["Odieresis"]=214,
- ["Odieresiscyrillic"]=1254,
- ["Odotbelow"]=7884,
- ["Ograve"]=210,
- ["Oharmenian"]=1365,
- ["Ohm"]=8486,
- ["Ohookabove"]=7886,
- ["Ohorn"]=416,
- ["Ohornacute"]=7898,
- ["Ohorndotbelow"]=7906,
- ["Ohorngrave"]=7900,
- ["Ohornhookabove"]=7902,
- ["Ohorntilde"]=7904,
- ["Ohungarumlaut"]=336,
- ["Oi"]=418,
- ["Oinvertedbreve"]=526,
- ["Omacron"]=332,
- ["Omacronacute"]=7762,
- ["Omacrongrave"]=7760,
- ["Omega"]=8486,
- ["Omegacyrillic"]=1120,
- ["Omegagreek"]=937,
- ["Omegaroundcyrillic"]=1146,
- ["Omegatitlocyrillic"]=1148,
- ["Omegatonos"]=911,
- ["Omicron"]=927,
- ["Omicrontonos"]=908,
- ["Omonospace"]=65327,
- ["Oneroman"]=8544,
- ["Oogonek"]=490,
- ["Oogonekmacron"]=492,
- ["Oopen"]=390,
- ["Oslash"]=216,
- ["Oslashacute"]=510,
- ["Ostrokeacute"]=510,
- ["Otcyrillic"]=1150,
- ["Otilde"]=213,
- ["Otildeacute"]=7756,
- ["Otildedieresis"]=7758,
- ["P"]=80,
- ["Pacute"]=7764,
- ["Pcircle"]=9413,
- ["Pdotaccent"]=7766,
- ["Pecyrillic"]=1055,
- ["Peharmenian"]=1354,
- ["Pemiddlehookcyrillic"]=1190,
- ["Phi"]=934,
- ["Phook"]=420,
- ["Pi"]=928,
- ["Piwrarmenian"]=1363,
- ["Pmonospace"]=65328,
- ["Psi"]=936,
- ["Psicyrillic"]=1136,
- ["Q"]=81,
- ["Qcircle"]=9414,
- ["Qmonospace"]=65329,
- ["R"]=82,
- ["Raarmenian"]=1356,
- ["Racute"]=340,
- ["Rcaron"]=344,
- ["Rcedilla"]=342,
- ["Rcircle"]=9415,
- ["Rcommaaccent"]=342,
- ["Rdblgrave"]=528,
- ["Rdotaccent"]=7768,
- ["Rdotbelow"]=7770,
- ["Rdotbelowmacron"]=7772,
- ["Reharmenian"]=1360,
- ["Rfraktur"]=8476,
- ["Rho"]=929,
- ["Rinvertedbreve"]=530,
- ["Rlinebelow"]=7774,
- ["Rmonospace"]=65330,
- ["Rsmallinverted"]=641,
- ["Rsmallinvertedsuperior"]=694,
- ["S"]=83,
- ["SF010000"]=9484,
- ["SF020000"]=9492,
- ["SF030000"]=9488,
- ["SF040000"]=9496,
- ["SF050000"]=9532,
- ["SF060000"]=9516,
- ["SF070000"]=9524,
- ["SF080000"]=9500,
- ["SF090000"]=9508,
- ["SF10000"]=9484,
- ["SF100000"]=9472,
- ["SF110000"]=9474,
- ["SF190000"]=9569,
- ["SF20000"]=9492,
- ["SF200000"]=9570,
- ["SF210000"]=9558,
- ["SF220000"]=9557,
- ["SF230000"]=9571,
- ["SF240000"]=9553,
- ["SF250000"]=9559,
- ["SF260000"]=9565,
- ["SF270000"]=9564,
- ["SF280000"]=9563,
- ["SF30000"]=9488,
- ["SF360000"]=9566,
- ["SF370000"]=9567,
- ["SF380000"]=9562,
- ["SF390000"]=9556,
- ["SF40000"]=9496,
- ["SF400000"]=9577,
- ["SF410000"]=9574,
- ["SF420000"]=9568,
- ["SF430000"]=9552,
- ["SF440000"]=9580,
- ["SF450000"]=9575,
- ["SF460000"]=9576,
- ["SF470000"]=9572,
- ["SF480000"]=9573,
- ["SF490000"]=9561,
- ["SF50000"]=9532,
- ["SF500000"]=9560,
- ["SF510000"]=9554,
- ["SF520000"]=9555,
- ["SF530000"]=9579,
- ["SF540000"]=9578,
- ["SF60000"]=9516,
- ["SF70000"]=9524,
- ["SF80000"]=9500,
- ["SF90000"]=9508,
- ["Sacute"]=346,
- ["Sacutedotaccent"]=7780,
- ["Sampigreek"]=992,
- ["Scaron"]=352,
- ["Scarondotaccent"]=7782,
- ["Scedilla"]=350,
- ["Schwa"]=399,
- ["Schwacyrillic"]=1240,
- ["Schwadieresiscyrillic"]=1242,
- ["Scircle"]=9416,
- ["Scircumflex"]=348,
- ["Scommaaccent"]=536,
- ["Sdotaccent"]=7776,
- ["Sdotbelow"]=7778,
- ["Sdotbelowdotaccent"]=7784,
- ["Seharmenian"]=1357,
- ["Sevenroman"]=8550,
- ["Shaarmenian"]=1351,
- ["Shacyrillic"]=1064,
- ["Shchacyrillic"]=1065,
- ["Sheicoptic"]=994,
- ["Shhacyrillic"]=1210,
- ["Shimacoptic"]=1004,
- ["Sigma"]=931,
- ["Sixroman"]=8549,
- ["Smonospace"]=65331,
- ["Softsigncyrillic"]=1068,
- ["Stigmagreek"]=986,
- ["T"]=84,
- ["Tau"]=932,
- ["Tbar"]=358,
- ["Tcaron"]=356,
- ["Tcedilla"]=354,
- ["Tcircle"]=9417,
- ["Tcircumflexbelow"]=7792,
- ["Tcommaaccent"]=354,
- ["Tdotaccent"]=7786,
- ["Tdotbelow"]=7788,
- ["Tecyrillic"]=1058,
- ["Tedescendercyrillic"]=1196,
- ["Tenroman"]=8553,
- ["Tetsecyrillic"]=1204,
- ["Theta"]=920,
- ["Thook"]=428,
- ["Thorn"]=222,
- ["Threeroman"]=8546,
- ["Tiwnarmenian"]=1359,
- ["Tlinebelow"]=7790,
- ["Tmonospace"]=65332,
- ["Toarmenian"]=1337,
- ["Tonefive"]=444,
- ["Tonesix"]=388,
- ["Tonetwo"]=423,
- ["Tretroflexhook"]=430,
- ["Tsecyrillic"]=1062,
- ["Tshecyrillic"]=1035,
- ["Twelveroman"]=8555,
- ["Tworoman"]=8545,
- ["U"]=85,
- ["Uacute"]=218,
- ["Ubreve"]=364,
- ["Ucaron"]=467,
- ["Ucircle"]=9418,
- ["Ucircumflex"]=219,
- ["Ucircumflexbelow"]=7798,
- ["Ucyrillic"]=1059,
- ["Udblacute"]=368,
- ["Udblgrave"]=532,
- ["Udieresis"]=220,
- ["Udieresisacute"]=471,
- ["Udieresisbelow"]=7794,
- ["Udieresiscaron"]=473,
- ["Udieresiscyrillic"]=1264,
- ["Udieresisgrave"]=475,
- ["Udieresismacron"]=469,
- ["Udotbelow"]=7908,
- ["Ugrave"]=217,
- ["Uhookabove"]=7910,
- ["Uhorn"]=431,
- ["Uhornacute"]=7912,
- ["Uhorndotbelow"]=7920,
- ["Uhorngrave"]=7914,
- ["Uhornhookabove"]=7916,
- ["Uhorntilde"]=7918,
- ["Uhungarumlaut"]=368,
- ["Uhungarumlautcyrillic"]=1266,
- ["Uinvertedbreve"]=534,
- ["Ukcyrillic"]=1144,
- ["Umacron"]=362,
- ["Umacroncyrillic"]=1262,
- ["Umacrondieresis"]=7802,
- ["Umonospace"]=65333,
- ["Uogonek"]=370,
- ["Upsilon"]=933,
- ["Upsilon1"]=978,
- ["Upsilonacutehooksymbolgreek"]=979,
- ["Upsilonafrican"]=433,
- ["Upsilondieresis"]=939,
- ["Upsilondieresishooksymbolgreek"]=980,
- ["Upsilonhooksymbol"]=978,
- ["Upsilontonos"]=910,
- ["Uring"]=366,
- ["Ushortcyrillic"]=1038,
- ["Ustraightcyrillic"]=1198,
- ["Ustraightstrokecyrillic"]=1200,
- ["Utilde"]=360,
- ["Utildeacute"]=7800,
- ["Utildebelow"]=7796,
- ["V"]=86,
- ["Vcircle"]=9419,
- ["Vdotbelow"]=7806,
- ["Vecyrillic"]=1042,
- ["Vewarmenian"]=1358,
- ["Vhook"]=434,
- ["Vmonospace"]=65334,
- ["Voarmenian"]=1352,
- ["Vtilde"]=7804,
- ["W"]=87,
- ["Wacute"]=7810,
- ["Wcircle"]=9420,
- ["Wcircumflex"]=372,
- ["Wdieresis"]=7812,
- ["Wdotaccent"]=7814,
- ["Wdotbelow"]=7816,
- ["Wgrave"]=7808,
- ["Wmonospace"]=65335,
- ["X"]=88,
- ["Xcircle"]=9421,
- ["Xdieresis"]=7820,
- ["Xdotaccent"]=7818,
- ["Xeharmenian"]=1341,
- ["Xi"]=926,
- ["Xmonospace"]=65336,
- ["Y"]=89,
- ["Yacute"]=221,
- ["Yatcyrillic"]=1122,
- ["Ycircle"]=9422,
- ["Ycircumflex"]=374,
- ["Ydieresis"]=376,
- ["Ydotaccent"]=7822,
- ["Ydotbelow"]=7924,
- ["Yericyrillic"]=1067,
- ["Yerudieresiscyrillic"]=1272,
- ["Ygrave"]=7922,
- ["Yhook"]=435,
- ["Yhookabove"]=7926,
- ["Yiarmenian"]=1349,
- ["Yicyrillic"]=1031,
- ["Yiwnarmenian"]=1362,
- ["Ymonospace"]=65337,
- ["Ytilde"]=7928,
- ["Yusbigcyrillic"]=1130,
- ["Yusbigiotifiedcyrillic"]=1132,
- ["Yuslittlecyrillic"]=1126,
- ["Yuslittleiotifiedcyrillic"]=1128,
- ["Z"]=90,
- ["Zaarmenian"]=1334,
- ["Zacute"]=377,
- ["Zcaron"]=381,
- ["Zcircle"]=9423,
- ["Zcircumflex"]=7824,
- ["Zdot"]=379,
- ["Zdotaccent"]=379,
- ["Zdotbelow"]=7826,
- ["Zecyrillic"]=1047,
- ["Zedescendercyrillic"]=1176,
- ["Zedieresiscyrillic"]=1246,
- ["Zeta"]=918,
- ["Zhearmenian"]=1338,
- ["Zhebrevecyrillic"]=1217,
- ["Zhecyrillic"]=1046,
- ["Zhedescendercyrillic"]=1174,
- ["Zhedieresiscyrillic"]=1244,
- ["Zlinebelow"]=7828,
- ["Zmonospace"]=65338,
- ["Zstroke"]=437,
- ["a"]=97,
- ["aabengali"]=2438,
- ["aacute"]=225,
- ["aadeva"]=2310,
- ["aagujarati"]=2694,
- ["aagurmukhi"]=2566,
- ["aamatragurmukhi"]=2622,
- ["aarusquare"]=13059,
- ["aavowelsignbengali"]=2494,
- ["aavowelsigndeva"]=2366,
- ["aavowelsigngujarati"]=2750,
- ["abbreviationmarkarmenian"]=1375,
- ["abbreviationsigndeva"]=2416,
- ["abengali"]=2437,
- ["abopomofo"]=12570,
- ["abreve"]=259,
- ["abreveacute"]=7855,
- ["abrevecyrillic"]=1233,
- ["abrevedotbelow"]=7863,
- ["abrevegrave"]=7857,
- ["abrevehookabove"]=7859,
- ["abrevetilde"]=7861,
- ["acaron"]=462,
- ["acircle"]=9424,
- ["acircumflex"]=226,
- ["acircumflexacute"]=7845,
- ["acircumflexdotbelow"]=7853,
- ["acircumflexgrave"]=7847,
- ["acircumflexhookabove"]=7849,
- ["acircumflextilde"]=7851,
- ["acute"]=180,
- ["acutebelowcmb"]=791,
- ["acutecmb"]=769,
- ["acutecomb"]=769,
- ["acutedeva"]=2388,
- ["acutelowmod"]=719,
- ["acutetonecmb"]=833,
- ["acyrillic"]=1072,
- ["adblgrave"]=513,
- ["addakgurmukhi"]=2673,
- ["adeva"]=2309,
- ["adieresis"]=228,
- ["adieresiscyrillic"]=1235,
- ["adieresismacron"]=479,
- ["adotbelow"]=7841,
- ["adotmacron"]=481,
- ["ae"]=230,
- ["aeacute"]=509,
- ["aekorean"]=12624,
- ["aemacron"]=483,
- ["afii00208"]=8213,
- ["afii08941"]=8356,
- ["afii10017"]=1040,
- ["afii10018"]=1041,
- ["afii10019"]=1042,
- ["afii10020"]=1043,
- ["afii10021"]=1044,
- ["afii10022"]=1045,
- ["afii10023"]=1025,
- ["afii10024"]=1046,
- ["afii10025"]=1047,
- ["afii10026"]=1048,
- ["afii10027"]=1049,
- ["afii10028"]=1050,
- ["afii10029"]=1051,
- ["afii10030"]=1052,
- ["afii10031"]=1053,
- ["afii10032"]=1054,
- ["afii10033"]=1055,
- ["afii10034"]=1056,
- ["afii10035"]=1057,
- ["afii10036"]=1058,
- ["afii10037"]=1059,
- ["afii10038"]=1060,
- ["afii10039"]=1061,
- ["afii10040"]=1062,
- ["afii10041"]=1063,
- ["afii10042"]=1064,
- ["afii10043"]=1065,
- ["afii10044"]=1066,
- ["afii10045"]=1067,
- ["afii10046"]=1068,
- ["afii10047"]=1069,
- ["afii10048"]=1070,
- ["afii10049"]=1071,
- ["afii10050"]=1168,
- ["afii10051"]=1026,
- ["afii10052"]=1027,
- ["afii10053"]=1028,
- ["afii10054"]=1029,
- ["afii10055"]=1030,
- ["afii10056"]=1031,
- ["afii10057"]=1032,
- ["afii10058"]=1033,
- ["afii10059"]=1034,
- ["afii10060"]=1035,
- ["afii10061"]=1036,
- ["afii10062"]=1038,
- ["afii10065"]=1072,
- ["afii10066"]=1073,
- ["afii10067"]=1074,
- ["afii10068"]=1075,
- ["afii10069"]=1076,
- ["afii10070"]=1077,
- ["afii10071"]=1105,
- ["afii10072"]=1078,
- ["afii10073"]=1079,
- ["afii10074"]=1080,
- ["afii10075"]=1081,
- ["afii10076"]=1082,
- ["afii10077"]=1083,
- ["afii10078"]=1084,
- ["afii10079"]=1085,
- ["afii10080"]=1086,
- ["afii10081"]=1087,
- ["afii10082"]=1088,
- ["afii10083"]=1089,
- ["afii10084"]=1090,
- ["afii10085"]=1091,
- ["afii10086"]=1092,
- ["afii10087"]=1093,
- ["afii10088"]=1094,
- ["afii10089"]=1095,
- ["afii10090"]=1096,
- ["afii10091"]=1097,
- ["afii10092"]=1098,
- ["afii10093"]=1099,
- ["afii10094"]=1100,
- ["afii10095"]=1101,
- ["afii10096"]=1102,
- ["afii10097"]=1103,
- ["afii10098"]=1169,
- ["afii10099"]=1106,
- ["afii10100"]=1107,
- ["afii10101"]=1108,
- ["afii10102"]=1109,
- ["afii10103"]=1110,
- ["afii10104"]=1111,
- ["afii10105"]=1112,
- ["afii10106"]=1113,
- ["afii10107"]=1114,
- ["afii10108"]=1115,
- ["afii10109"]=1116,
- ["afii10110"]=1118,
- ["afii10145"]=1039,
- ["afii10146"]=1122,
- ["afii10147"]=1138,
- ["afii10148"]=1140,
- ["afii10193"]=1119,
- ["afii10194"]=1123,
- ["afii10195"]=1139,
- ["afii10196"]=1141,
- ["afii10846"]=1241,
- ["afii208"]=8213,
- ["afii299"]=8206,
- ["afii300"]=8207,
- ["afii301"]=8205,
- ["afii57381"]=1642,
- ["afii57388"]=1548,
- ["afii57392"]=1632,
- ["afii57393"]=1633,
- ["afii57394"]=1634,
- ["afii57395"]=1635,
- ["afii57396"]=1636,
- ["afii57397"]=1637,
- ["afii57398"]=1638,
- ["afii57399"]=1639,
- ["afii57400"]=1640,
- ["afii57401"]=1641,
- ["afii57403"]=1563,
- ["afii57407"]=1567,
- ["afii57409"]=1569,
- ["afii57410"]=1570,
- ["afii57411"]=1571,
- ["afii57412"]=1572,
- ["afii57413"]=1573,
- ["afii57414"]=1574,
- ["afii57415"]=1575,
- ["afii57416"]=1576,
- ["afii57417"]=1577,
- ["afii57418"]=1578,
- ["afii57419"]=1579,
- ["afii57420"]=1580,
- ["afii57421"]=1581,
- ["afii57422"]=1582,
- ["afii57423"]=1583,
- ["afii57424"]=1584,
- ["afii57425"]=1585,
- ["afii57426"]=1586,
- ["afii57427"]=1587,
- ["afii57428"]=1588,
- ["afii57429"]=1589,
- ["afii57430"]=1590,
- ["afii57431"]=1591,
- ["afii57432"]=1592,
- ["afii57433"]=1593,
- ["afii57434"]=1594,
- ["afii57440"]=1600,
- ["afii57441"]=1601,
- ["afii57442"]=1602,
- ["afii57443"]=1603,
- ["afii57444"]=1604,
- ["afii57445"]=1605,
- ["afii57446"]=1606,
- ["afii57448"]=1608,
- ["afii57449"]=1609,
- ["afii57450"]=1610,
- ["afii57451"]=1611,
- ["afii57452"]=1612,
- ["afii57453"]=1613,
- ["afii57454"]=1614,
- ["afii57455"]=1615,
- ["afii57456"]=1616,
- ["afii57457"]=1617,
- ["afii57458"]=1618,
- ["afii57470"]=1607,
- ["afii57505"]=1700,
- ["afii57506"]=1662,
- ["afii57507"]=1670,
- ["afii57508"]=1688,
- ["afii57509"]=1711,
- ["afii57511"]=1657,
- ["afii57512"]=1672,
- ["afii57513"]=1681,
- ["afii57514"]=1722,
- ["afii57519"]=1746,
- ["afii57534"]=1749,
- ["afii57636"]=8362,
- ["afii57645"]=1470,
- ["afii57658"]=1475,
- ["afii57664"]=1488,
- ["afii57665"]=1489,
- ["afii57666"]=1490,
- ["afii57667"]=1491,
- ["afii57668"]=1492,
- ["afii57669"]=1493,
- ["afii57670"]=1494,
- ["afii57671"]=1495,
- ["afii57672"]=1496,
- ["afii57673"]=1497,
- ["afii57674"]=1498,
- ["afii57675"]=1499,
- ["afii57676"]=1500,
- ["afii57677"]=1501,
- ["afii57678"]=1502,
- ["afii57679"]=1503,
- ["afii57680"]=1504,
- ["afii57681"]=1505,
- ["afii57682"]=1506,
- ["afii57683"]=1507,
- ["afii57684"]=1508,
- ["afii57685"]=1509,
- ["afii57686"]=1510,
- ["afii57687"]=1511,
- ["afii57688"]=1512,
- ["afii57689"]=1513,
- ["afii57690"]=1514,
- ["afii57694"]=64298,
- ["afii57695"]=64299,
- ["afii57700"]=64331,
- ["afii57705"]=64287,
- ["afii57716"]=1520,
- ["afii57717"]=1521,
- ["afii57718"]=1522,
- ["afii57723"]=64309,
- ["afii57793"]=1460,
- ["afii57794"]=1461,
- ["afii57795"]=1462,
- ["afii57796"]=1467,
- ["afii57797"]=1464,
- ["afii57798"]=1463,
- ["afii57799"]=1456,
- ["afii57800"]=1458,
- ["afii57801"]=1457,
- ["afii57802"]=1459,
- ["afii57803"]=1474,
- ["afii57804"]=1473,
- ["afii57806"]=1465,
- ["afii57807"]=1468,
- ["afii57839"]=1469,
- ["afii57841"]=1471,
- ["afii57842"]=1472,
- ["afii57929"]=700,
- ["afii61248"]=8453,
- ["afii61289"]=8467,
- ["afii61352"]=8470,
- ["afii61573"]=8236,
- ["afii61574"]=8237,
- ["afii61575"]=8238,
- ["afii61664"]=8204,
- ["afii63167"]=1645,
- ["afii64937"]=701,
- ["agrave"]=224,
- ["agujarati"]=2693,
- ["agurmukhi"]=2565,
- ["ahiragana"]=12354,
- ["ahookabove"]=7843,
- ["aibengali"]=2448,
- ["aibopomofo"]=12574,
- ["aideva"]=2320,
- ["aiecyrillic"]=1237,
- ["aigujarati"]=2704,
- ["aigurmukhi"]=2576,
- ["aimatragurmukhi"]=2632,
- ["ainarabic"]=1593,
- ["ainfinalarabic"]=65226,
- ["aininitialarabic"]=65227,
- ["ainmedialarabic"]=65228,
- ["ainvertedbreve"]=515,
- ["aivowelsignbengali"]=2504,
- ["aivowelsigndeva"]=2376,
- ["aivowelsigngujarati"]=2760,
- ["akatakana"]=12450,
- ["akatakanahalfwidth"]=65393,
- ["akorean"]=12623,
- ["alef"]=1488,
- ["alefarabic"]=1575,
- ["alefdageshhebrew"]=64304,
- ["aleffinalarabic"]=65166,
- ["alefhamzaabovearabic"]=1571,
- ["alefhamzaabovefinalarabic"]=65156,
- ["alefhamzabelowarabic"]=1573,
- ["alefhamzabelowfinalarabic"]=65160,
- ["alefhebrew"]=1488,
- ["aleflamedhebrew"]=64335,
- ["alefmaddaabovearabic"]=1570,
- ["alefmaddaabovefinalarabic"]=65154,
- ["alefmaksuraarabic"]=1609,
- ["alefmaksurafinalarabic"]=65264,
- ["alefmaksurainitialarabic"]=65267,
- ["alefmaksuramedialarabic"]=65268,
- ["alefpatahhebrew"]=64302,
- ["alefqamatshebrew"]=64303,
- ["aleph"]=8501,
- ["allequal"]=8780,
- ["alpha"]=945,
- ["alphatonos"]=940,
- ["amacron"]=257,
- ["amonospace"]=65345,
- ["ampersand"]=38,
- ["ampersandmonospace"]=65286,
- ["amsquare"]=13250,
- ["anbopomofo"]=12578,
- ["angbopomofo"]=12580,
- ["angkhankhuthai"]=3674,
- ["angle"]=8736,
- ["anglebracketleft"]=12296,
- ["anglebracketleftvertical"]=65087,
- ["anglebracketright"]=12297,
- ["anglebracketrightvertical"]=65088,
- ["angleleft"]=9001,
- ["angleright"]=9002,
- ["angstrom"]=8491,
- ["anoteleia"]=903,
- ["anudattadeva"]=2386,
- ["anusvarabengali"]=2434,
- ["anusvaradeva"]=2306,
- ["anusvaragujarati"]=2690,
- ["aogonek"]=261,
- ["apaatosquare"]=13056,
- ["aparen"]=9372,
- ["apostrophearmenian"]=1370,
- ["apostrophemod"]=700,
- ["apple"]=63743,
- ["approaches"]=8784,
- ["approxequal"]=8776,
- ["approxequalorimage"]=8786,
- ["approximatelyequal"]=8773,
- ["araeaekorean"]=12686,
- ["araeakorean"]=12685,
- ["arc"]=8978,
- ["arighthalfring"]=7834,
- ["aring"]=229,
- ["aringacute"]=507,
- ["aringbelow"]=7681,
- ["arrowboth"]=8596,
- ["arrowdashdown"]=8675,
- ["arrowdashleft"]=8672,
- ["arrowdashright"]=8674,
- ["arrowdashup"]=8673,
- ["arrowdblboth"]=8660,
- ["arrowdbldown"]=8659,
- ["arrowdblleft"]=8656,
- ["arrowdblright"]=8658,
- ["arrowdblup"]=8657,
- ["arrowdown"]=8595,
- ["arrowdownleft"]=8601,
- ["arrowdownright"]=8600,
- ["arrowdownwhite"]=8681,
- ["arrowheaddownmod"]=709,
- ["arrowheadleftmod"]=706,
- ["arrowheadrightmod"]=707,
- ["arrowheadupmod"]=708,
- ["arrowleft"]=8592,
- ["arrowleftdbl"]=8656,
- ["arrowleftdblstroke"]=8653,
- ["arrowleftoverright"]=8646,
- ["arrowleftwhite"]=8678,
- ["arrowright"]=8594,
- ["arrowrightdblstroke"]=8655,
- ["arrowrightheavy"]=10142,
- ["arrowrightoverleft"]=8644,
- ["arrowrightwhite"]=8680,
- ["arrowtableft"]=8676,
- ["arrowtabright"]=8677,
- ["arrowup"]=8593,
- ["arrowupdn"]=8597,
- ["arrowupdnbse"]=8616,
- ["arrowupdownbase"]=8616,
- ["arrowupleft"]=8598,
- ["arrowupleftofdown"]=8645,
- ["arrowupright"]=8599,
- ["arrowupwhite"]=8679,
- ["asciicircum"]=94,
- ["asciicircummonospace"]=65342,
- ["asciitilde"]=126,
- ["asciitildemonospace"]=65374,
- ["ascript"]=593,
- ["ascriptturned"]=594,
- ["asmallhiragana"]=12353,
- ["asmallkatakana"]=12449,
- ["asmallkatakanahalfwidth"]=65383,
- ["asterisk"]=42,
- ["asteriskaltonearabic"]=1645,
- ["asteriskarabic"]=1645,
- ["asteriskmath"]=8727,
- ["asteriskmonospace"]=65290,
- ["asterisksmall"]=65121,
- ["asterism"]=8258,
- ["asymptoticallyequal"]=8771,
- ["at"]=64,
- ["atilde"]=227,
- ["atmonospace"]=65312,
- ["atsmall"]=65131,
- ["aturned"]=592,
- ["aubengali"]=2452,
- ["aubopomofo"]=12576,
- ["audeva"]=2324,
- ["augujarati"]=2708,
- ["augurmukhi"]=2580,
- ["aulengthmarkbengali"]=2519,
- ["aumatragurmukhi"]=2636,
- ["auvowelsignbengali"]=2508,
- ["auvowelsigndeva"]=2380,
- ["auvowelsigngujarati"]=2764,
- ["avagrahadeva"]=2365,
- ["aybarmenian"]=1377,
- ["ayin"]=1506,
- ["ayinaltonehebrew"]=64288,
- ["ayinhebrew"]=1506,
- ["b"]=98,
- ["babengali"]=2476,
- ["backslash"]=92,
- ["backslashmonospace"]=65340,
- ["badeva"]=2348,
- ["bagujarati"]=2732,
- ["bagurmukhi"]=2604,
- ["bahiragana"]=12400,
- ["bahtthai"]=3647,
- ["bakatakana"]=12496,
- ["bar"]=124,
- ["barmonospace"]=65372,
- ["bbopomofo"]=12549,
- ["bcircle"]=9425,
- ["bdotaccent"]=7683,
- ["bdotbelow"]=7685,
- ["beamedsixteenthnotes"]=9836,
- ["because"]=8757,
- ["becyrillic"]=1073,
- ["beharabic"]=1576,
- ["behfinalarabic"]=65168,
- ["behinitialarabic"]=65169,
- ["behiragana"]=12409,
- ["behmedialarabic"]=65170,
- ["behmeeminitialarabic"]=64671,
- ["behmeemisolatedarabic"]=64520,
- ["behnoonfinalarabic"]=64621,
- ["bekatakana"]=12505,
- ["benarmenian"]=1378,
- ["bet"]=1489,
- ["beta"]=946,
- ["betasymbolgreek"]=976,
- ["betdagesh"]=64305,
- ["betdageshhebrew"]=64305,
- ["bethebrew"]=1489,
- ["betrafehebrew"]=64332,
- ["bhabengali"]=2477,
- ["bhadeva"]=2349,
- ["bhagujarati"]=2733,
- ["bhagurmukhi"]=2605,
- ["bhook"]=595,
- ["bihiragana"]=12403,
- ["bikatakana"]=12499,
- ["bilabialclick"]=664,
- ["bindigurmukhi"]=2562,
- ["birusquare"]=13105,
- ["blackcircle"]=9679,
- ["blackdiamond"]=9670,
- ["blackdownpointingtriangle"]=9660,
- ["blackleftpointingpointer"]=9668,
- ["blackleftpointingtriangle"]=9664,
- ["blacklenticularbracketleft"]=12304,
- ["blacklenticularbracketleftvertical"]=65083,
- ["blacklenticularbracketright"]=12305,
- ["blacklenticularbracketrightvertical"]=65084,
- ["blacklowerlefttriangle"]=9699,
- ["blacklowerrighttriangle"]=9698,
- ["blackrectangle"]=9644,
- ["blackrightpointingpointer"]=9658,
- ["blackrightpointingtriangle"]=9654,
- ["blacksmallsquare"]=9642,
- ["blacksmilingface"]=9787,
- ["blacksquare"]=9632,
- ["blackstar"]=9733,
- ["blackupperlefttriangle"]=9700,
- ["blackupperrighttriangle"]=9701,
- ["blackuppointingsmalltriangle"]=9652,
- ["blackuppointingtriangle"]=9650,
- ["blank"]=9251,
- ["blinebelow"]=7687,
- ["block"]=9608,
- ["bmonospace"]=65346,
- ["bobaimaithai"]=3610,
- ["bohiragana"]=12412,
- ["bokatakana"]=12508,
- ["bparen"]=9373,
- ["bqsquare"]=13251,
- ["braceleft"]=123,
- ["braceleftmonospace"]=65371,
- ["braceleftsmall"]=65115,
- ["braceleftvertical"]=65079,
- ["braceright"]=125,
- ["bracerightmonospace"]=65373,
- ["bracerightsmall"]=65116,
- ["bracerightvertical"]=65080,
- ["bracketleft"]=91,
- ["bracketleftmonospace"]=65339,
- ["bracketright"]=93,
- ["bracketrightmonospace"]=65341,
- ["breve"]=728,
- ["brevebelowcmb"]=814,
- ["brevecmb"]=774,
- ["breveinvertedbelowcmb"]=815,
- ["breveinvertedcmb"]=785,
- ["breveinverteddoublecmb"]=865,
- ["bridgebelowcmb"]=810,
- ["bridgeinvertedbelowcmb"]=826,
- ["brokenbar"]=166,
- ["bstroke"]=384,
- ["btopbar"]=387,
- ["buhiragana"]=12406,
- ["bukatakana"]=12502,
- ["bullet"]=8226,
- ["bulletinverse"]=9688,
- ["bulletoperator"]=8729,
- ["bullseye"]=9678,
- ["c"]=99,
- ["caarmenian"]=1390,
- ["cabengali"]=2458,
- ["cacute"]=263,
- ["cadeva"]=2330,
- ["cagujarati"]=2714,
- ["cagurmukhi"]=2586,
- ["calsquare"]=13192,
- ["candrabindubengali"]=2433,
- ["candrabinducmb"]=784,
- ["candrabindudeva"]=2305,
- ["candrabindugujarati"]=2689,
- ["capslock"]=8682,
- ["careof"]=8453,
- ["caron"]=711,
- ["caronbelowcmb"]=812,
- ["caroncmb"]=780,
- ["carriagereturn"]=8629,
- ["cbopomofo"]=12568,
- ["ccaron"]=269,
- ["ccedilla"]=231,
- ["ccedillaacute"]=7689,
- ["ccircle"]=9426,
- ["ccircumflex"]=265,
- ["ccurl"]=597,
- ["cdot"]=267,
- ["cdotaccent"]=267,
- ["cdsquare"]=13253,
- ["cedilla"]=184,
- ["cedillacmb"]=807,
- ["cent"]=162,
- ["centigrade"]=8451,
- ["centmonospace"]=65504,
- ["chaarmenian"]=1401,
- ["chabengali"]=2459,
- ["chadeva"]=2331,
- ["chagujarati"]=2715,
- ["chagurmukhi"]=2587,
- ["chbopomofo"]=12564,
- ["cheabkhasiancyrillic"]=1213,
- ["checkmark"]=10003,
- ["checyrillic"]=1095,
- ["chedescenderabkhasiancyrillic"]=1215,
- ["chedescendercyrillic"]=1207,
- ["chedieresiscyrillic"]=1269,
- ["cheharmenian"]=1395,
- ["chekhakassiancyrillic"]=1228,
- ["cheverticalstrokecyrillic"]=1209,
- ["chi"]=967,
- ["chieuchacirclekorean"]=12919,
- ["chieuchaparenkorean"]=12823,
- ["chieuchcirclekorean"]=12905,
- ["chieuchkorean"]=12618,
- ["chieuchparenkorean"]=12809,
- ["chochangthai"]=3594,
- ["chochanthai"]=3592,
- ["chochingthai"]=3593,
- ["chochoethai"]=3596,
- ["chook"]=392,
- ["cieucacirclekorean"]=12918,
- ["cieucaparenkorean"]=12822,
- ["cieuccirclekorean"]=12904,
- ["cieuckorean"]=12616,
- ["cieucparenkorean"]=12808,
- ["cieucuparenkorean"]=12828,
- ["circle"]=9675,
- ["circlemultiply"]=8855,
- ["circleot"]=8857,
- ["circleplus"]=8853,
- ["circlepostalmark"]=12342,
- ["circlewithlefthalfblack"]=9680,
- ["circlewithrighthalfblack"]=9681,
- ["circumflex"]=710,
- ["circumflexbelowcmb"]=813,
- ["circumflexcmb"]=770,
- ["clear"]=8999,
- ["clickalveolar"]=450,
- ["clickdental"]=448,
- ["clicklateral"]=449,
- ["clickretroflex"]=451,
- ["club"]=9827,
- ["clubsuitblack"]=9827,
- ["clubsuitwhite"]=9831,
- ["cmcubedsquare"]=13220,
- ["cmonospace"]=65347,
- ["cmsquaredsquare"]=13216,
- ["coarmenian"]=1409,
- ["colon"]=58,
- ["colonmonetary"]=8353,
- ["colonmonospace"]=65306,
- ["colonsign"]=8353,
- ["colonsmall"]=65109,
- ["colontriangularhalfmod"]=721,
- ["colontriangularmod"]=720,
- ["comma"]=44,
- ["commaabovecmb"]=787,
- ["commaaboverightcmb"]=789,
- ["commaarabic"]=1548,
- ["commaarmenian"]=1373,
- ["commamonospace"]=65292,
- ["commareversedabovecmb"]=788,
- ["commareversedmod"]=701,
- ["commasmall"]=65104,
- ["commaturnedabovecmb"]=786,
- ["commaturnedmod"]=699,
- ["compass"]=9788,
- ["congruent"]=8773,
- ["contourintegral"]=8750,
- ["control"]=8963,
- ["controlACK"]=6,
- ["controlBEL"]=7,
- ["controlBS"]=8,
- ["controlCAN"]=24,
- ["controlCR"]=13,
- ["controlDC1"]=17,
- ["controlDC2"]=18,
- ["controlDC3"]=19,
- ["controlDC4"]=20,
- ["controlDEL"]=127,
- ["controlDLE"]=16,
- ["controlEM"]=25,
- ["controlENQ"]=5,
- ["controlEOT"]=4,
- ["controlESC"]=27,
- ["controlETB"]=23,
- ["controlETX"]=3,
- ["controlFF"]=12,
- ["controlFS"]=28,
- ["controlGS"]=29,
- ["controlHT"]=9,
- ["controlLF"]=10,
- ["controlNAK"]=21,
- ["controlRS"]=30,
- ["controlSI"]=15,
- ["controlSO"]=14,
- ["controlSOT"]=2,
- ["controlSTX"]=1,
- ["controlSUB"]=26,
- ["controlSYN"]=22,
- ["controlUS"]=31,
- ["controlVT"]=11,
- ["copyright"]=169,
- ["cornerbracketleft"]=12300,
- ["cornerbracketlefthalfwidth"]=65378,
- ["cornerbracketleftvertical"]=65089,
- ["cornerbracketright"]=12301,
- ["cornerbracketrighthalfwidth"]=65379,
- ["cornerbracketrightvertical"]=65090,
- ["corporationsquare"]=13183,
- ["cosquare"]=13255,
- ["coverkgsquare"]=13254,
- ["cparen"]=9374,
- ["cruzeiro"]=8354,
- ["cstretched"]=663,
- ["curlyand"]=8911,
- ["curlyor"]=8910,
- ["currency"]=164,
- ["d"]=100,
- ["daarmenian"]=1380,
- ["dabengali"]=2470,
- ["dadarabic"]=1590,
- ["dadeva"]=2342,
- ["dadfinalarabic"]=65214,
- ["dadinitialarabic"]=65215,
- ["dadmedialarabic"]=65216,
- ["dagesh"]=1468,
- ["dageshhebrew"]=1468,
- ["dagger"]=8224,
- ["daggerdbl"]=8225,
- ["dagujarati"]=2726,
- ["dagurmukhi"]=2598,
- ["dahiragana"]=12384,
- ["dakatakana"]=12480,
- ["dalarabic"]=1583,
- ["dalet"]=1491,
- ["daletdagesh"]=64307,
- ["daletdageshhebrew"]=64307,
- ["dalethatafpatah"]=1491,
- ["dalethatafpatahhebrew"]=1491,
- ["dalethatafsegol"]=1491,
- ["dalethatafsegolhebrew"]=1491,
- ["dalethebrew"]=1491,
- ["dalethiriq"]=1491,
- ["dalethiriqhebrew"]=1491,
- ["daletholam"]=1491,
- ["daletholamhebrew"]=1491,
- ["daletpatah"]=1491,
- ["daletpatahhebrew"]=1491,
- ["daletqamats"]=1491,
- ["daletqamatshebrew"]=1491,
- ["daletqubuts"]=1491,
- ["daletqubutshebrew"]=1491,
- ["daletsegol"]=1491,
- ["daletsegolhebrew"]=1491,
- ["daletsheva"]=1491,
- ["daletshevahebrew"]=1491,
- ["dalettsere"]=1491,
- ["dalettserehebrew"]=1491,
- ["dalfinalarabic"]=65194,
- ["dammaarabic"]=1615,
- ["dammalowarabic"]=1615,
- ["dammatanaltonearabic"]=1612,
- ["dammatanarabic"]=1612,
- ["danda"]=2404,
- ["dargahebrew"]=1447,
- ["dargalefthebrew"]=1447,
- ["dasiapneumatacyrilliccmb"]=1157,
- ["dblanglebracketleft"]=12298,
- ["dblanglebracketleftvertical"]=65085,
- ["dblanglebracketright"]=12299,
- ["dblanglebracketrightvertical"]=65086,
- ["dblarchinvertedbelowcmb"]=811,
- ["dblarrowleft"]=8660,
- ["dblarrowright"]=8658,
- ["dbldanda"]=2405,
- ["dblgravecmb"]=783,
- ["dblintegral"]=8748,
- ["dbllowline"]=8215,
- ["dbllowlinecmb"]=819,
- ["dbloverlinecmb"]=831,
- ["dblprimemod"]=698,
- ["dblverticalbar"]=8214,
- ["dblverticallineabovecmb"]=782,
- ["dbopomofo"]=12553,
- ["dbsquare"]=13256,
- ["dcaron"]=271,
- ["dcedilla"]=7697,
- ["dcircle"]=9427,
- ["dcircumflexbelow"]=7699,
- ["dcroat"]=273,
- ["ddabengali"]=2465,
- ["ddadeva"]=2337,
- ["ddagujarati"]=2721,
- ["ddagurmukhi"]=2593,
- ["ddalarabic"]=1672,
- ["ddalfinalarabic"]=64393,
- ["dddhadeva"]=2396,
- ["ddhabengali"]=2466,
- ["ddhadeva"]=2338,
- ["ddhagujarati"]=2722,
- ["ddhagurmukhi"]=2594,
- ["ddotaccent"]=7691,
- ["ddotbelow"]=7693,
- ["decimalseparatorarabic"]=1643,
- ["decimalseparatorpersian"]=1643,
- ["decyrillic"]=1076,
- ["degree"]=176,
- ["dehihebrew"]=1453,
- ["dehiragana"]=12391,
- ["deicoptic"]=1007,
- ["dekatakana"]=12487,
- ["deleteleft"]=9003,
- ["deleteright"]=8998,
- ["delta"]=948,
- ["deltaturned"]=397,
- ["denominatorminusonenumeratorbengali"]=2552,
- ["dezh"]=676,
- ["dhabengali"]=2471,
- ["dhadeva"]=2343,
- ["dhagujarati"]=2727,
- ["dhagurmukhi"]=2599,
- ["dhook"]=599,
- ["dialytikatonos"]=901,
- ["dialytikatonoscmb"]=836,
- ["diamond"]=9830,
- ["diamondsuitwhite"]=9826,
- ["dieresis"]=168,
- ["dieresisbelowcmb"]=804,
- ["dieresiscmb"]=776,
- ["dieresistonos"]=901,
- ["dihiragana"]=12386,
- ["dikatakana"]=12482,
- ["dittomark"]=12291,
- ["divide"]=247,
- ["divides"]=8739,
- ["divisionslash"]=8725,
- ["djecyrillic"]=1106,
- ["dkshade"]=9619,
- ["dlinebelow"]=7695,
- ["dlsquare"]=13207,
- ["dmacron"]=273,
- ["dmonospace"]=65348,
- ["dnblock"]=9604,
- ["dochadathai"]=3598,
- ["dodekthai"]=3604,
- ["dohiragana"]=12393,
- ["dokatakana"]=12489,
- ["dollar"]=36,
- ["dollarmonospace"]=65284,
- ["dollarsmall"]=65129,
- ["dong"]=8363,
- ["dorusquare"]=13094,
- ["dotaccent"]=729,
- ["dotaccentcmb"]=775,
- ["dotbelowcmb"]=803,
- ["dotbelowcomb"]=803,
- ["dotkatakana"]=12539,
- ["dotlessi"]=305,
- ["dotlessjstrokehook"]=644,
- ["dotmath"]=8901,
- ["dottedcircle"]=9676,
- ["doubleyodpatah"]=64287,
- ["doubleyodpatahhebrew"]=64287,
- ["downtackbelowcmb"]=798,
- ["downtackmod"]=725,
- ["dparen"]=9375,
- ["dtail"]=598,
- ["dtopbar"]=396,
- ["duhiragana"]=12389,
- ["dukatakana"]=12485,
- ["dz"]=499,
- ["dzaltone"]=675,
- ["dzcaron"]=454,
- ["dzcurl"]=677,
- ["dzeabkhasiancyrillic"]=1249,
- ["dzecyrillic"]=1109,
- ["dzhecyrillic"]=1119,
- ["e"]=101,
- ["eacute"]=233,
- ["earth"]=9793,
- ["ebengali"]=2447,
- ["ebopomofo"]=12572,
- ["ebreve"]=277,
- ["ecandradeva"]=2317,
- ["ecandragujarati"]=2701,
- ["ecandravowelsigndeva"]=2373,
- ["ecandravowelsigngujarati"]=2757,
- ["ecaron"]=283,
- ["ecedillabreve"]=7709,
- ["echarmenian"]=1381,
- ["echyiwnarmenian"]=1415,
- ["ecircle"]=9428,
- ["ecircumflex"]=234,
- ["ecircumflexacute"]=7871,
- ["ecircumflexbelow"]=7705,
- ["ecircumflexdotbelow"]=7879,
- ["ecircumflexgrave"]=7873,
- ["ecircumflexhookabove"]=7875,
- ["ecircumflextilde"]=7877,
- ["ecyrillic"]=1108,
- ["edblgrave"]=517,
- ["edeva"]=2319,
- ["edieresis"]=235,
- ["edot"]=279,
- ["edotaccent"]=279,
- ["edotbelow"]=7865,
- ["eegurmukhi"]=2575,
- ["eematragurmukhi"]=2631,
- ["efcyrillic"]=1092,
- ["egrave"]=232,
- ["egujarati"]=2703,
- ["eharmenian"]=1383,
- ["ehbopomofo"]=12573,
- ["ehiragana"]=12360,
- ["ehookabove"]=7867,
- ["eibopomofo"]=12575,
- ["eight"]=56,
- ["eightarabic"]=1640,
- ["eightbengali"]=2542,
- ["eightcircle"]=9319,
- ["eightcircleinversesansserif"]=10129,
- ["eightdeva"]=2414,
- ["eighteencircle"]=9329,
- ["eighteenparen"]=9349,
- ["eighteenperiod"]=9369,
- ["eightgujarati"]=2798,
- ["eightgurmukhi"]=2670,
- ["eighthackarabic"]=1640,
- ["eighthangzhou"]=12328,
- ["eighthnotebeamed"]=9835,
- ["eightideographicparen"]=12839,
- ["eightinferior"]=8328,
- ["eightmonospace"]=65304,
- ["eightparen"]=9339,
- ["eightperiod"]=9359,
- ["eightpersian"]=1784,
- ["eightroman"]=8567,
- ["eightsuperior"]=8312,
- ["eightthai"]=3672,
- ["einvertedbreve"]=519,
- ["eiotifiedcyrillic"]=1125,
- ["ekatakana"]=12456,
- ["ekatakanahalfwidth"]=65396,
- ["ekonkargurmukhi"]=2676,
- ["ekorean"]=12628,
- ["elcyrillic"]=1083,
- ["element"]=8712,
- ["elevencircle"]=9322,
- ["elevenparen"]=9342,
- ["elevenperiod"]=9362,
- ["elevenroman"]=8570,
- ["ellipsis"]=8230,
- ["ellipsisvertical"]=8942,
- ["emacron"]=275,
- ["emacronacute"]=7703,
- ["emacrongrave"]=7701,
- ["emcyrillic"]=1084,
- ["emdash"]=8212,
- ["emdashvertical"]=65073,
- ["emonospace"]=65349,
- ["emphasismarkarmenian"]=1371,
- ["emptyset"]=8709,
- ["enbopomofo"]=12579,
- ["encyrillic"]=1085,
- ["endash"]=8211,
- ["endashvertical"]=65074,
- ["endescendercyrillic"]=1187,
- ["eng"]=331,
- ["engbopomofo"]=12581,
- ["enghecyrillic"]=1189,
- ["enhookcyrillic"]=1224,
- ["enspace"]=8194,
- ["eogonek"]=281,
- ["eokorean"]=12627,
- ["eopen"]=603,
- ["eopenclosed"]=666,
- ["eopenreversed"]=604,
- ["eopenreversedclosed"]=606,
- ["eopenreversedhook"]=605,
- ["eparen"]=9376,
- ["epsilon"]=949,
- ["epsilontonos"]=941,
- ["equal"]=61,
- ["equalmonospace"]=65309,
- ["equalsmall"]=65126,
- ["equalsuperior"]=8316,
- ["equivalence"]=8801,
- ["erbopomofo"]=12582,
- ["ercyrillic"]=1088,
- ["ereversed"]=600,
- ["ereversedcyrillic"]=1101,
- ["escyrillic"]=1089,
- ["esdescendercyrillic"]=1195,
- ["esh"]=643,
- ["eshcurl"]=646,
- ["eshortdeva"]=2318,
- ["eshortvowelsigndeva"]=2374,
- ["eshreversedloop"]=426,
- ["eshsquatreversed"]=645,
- ["esmallhiragana"]=12359,
- ["esmallkatakana"]=12455,
- ["esmallkatakanahalfwidth"]=65386,
- ["estimated"]=8494,
- ["eta"]=951,
- ["etarmenian"]=1384,
- ["etatonos"]=942,
- ["eth"]=240,
- ["etilde"]=7869,
- ["etildebelow"]=7707,
- ["etnahtafoukhhebrew"]=1425,
- ["etnahtafoukhlefthebrew"]=1425,
- ["etnahtahebrew"]=1425,
- ["etnahtalefthebrew"]=1425,
- ["eturned"]=477,
- ["eukorean"]=12641,
- ["euro"]=8364,
- ["evowelsignbengali"]=2503,
- ["evowelsigndeva"]=2375,
- ["evowelsigngujarati"]=2759,
- ["exclam"]=33,
- ["exclamarmenian"]=1372,
- ["exclamdbl"]=8252,
- ["exclamdown"]=161,
- ["exclammonospace"]=65281,
- ["existential"]=8707,
- ["ezh"]=658,
- ["ezhcaron"]=495,
- ["ezhcurl"]=659,
- ["ezhreversed"]=441,
- ["ezhtail"]=442,
- ["f"]=102,
- ["fadeva"]=2398,
- ["fagurmukhi"]=2654,
- ["fahrenheit"]=8457,
- ["fathaarabic"]=1614,
- ["fathalowarabic"]=1614,
- ["fathatanarabic"]=1611,
- ["fbopomofo"]=12552,
- ["fcircle"]=9429,
- ["fdotaccent"]=7711,
- ["feharabic"]=1601,
- ["feharmenian"]=1414,
- ["fehfinalarabic"]=65234,
- ["fehinitialarabic"]=65235,
- ["fehmedialarabic"]=65236,
- ["feicoptic"]=997,
- ["female"]=9792,
- ["ff"]=64256,
- ["ffi"]=64259,
- ["ffl"]=64260,
- ["fi"]=64257,
- ["fifteencircle"]=9326,
- ["fifteenparen"]=9346,
- ["fifteenperiod"]=9366,
- ["figuredash"]=8210,
- ["filledbox"]=9632,
- ["filledrect"]=9644,
- ["finalkaf"]=1498,
- ["finalkafdagesh"]=64314,
- ["finalkafdageshhebrew"]=64314,
- ["finalkafhebrew"]=1498,
- ["finalkafqamats"]=1498,
- ["finalkafqamatshebrew"]=1498,
- ["finalkafsheva"]=1498,
- ["finalkafshevahebrew"]=1498,
- ["finalmem"]=1501,
- ["finalmemhebrew"]=1501,
- ["finalnun"]=1503,
- ["finalnunhebrew"]=1503,
- ["finalpe"]=1507,
- ["finalpehebrew"]=1507,
- ["finaltsadi"]=1509,
- ["finaltsadihebrew"]=1509,
- ["firsttonechinese"]=713,
- ["fisheye"]=9673,
- ["fitacyrillic"]=1139,
- ["five"]=53,
- ["fivearabic"]=1637,
- ["fivebengali"]=2539,
- ["fivecircle"]=9316,
- ["fivecircleinversesansserif"]=10126,
- ["fivedeva"]=2411,
- ["fiveeighths"]=8541,
- ["fivegujarati"]=2795,
- ["fivegurmukhi"]=2667,
- ["fivehackarabic"]=1637,
- ["fivehangzhou"]=12325,
- ["fiveideographicparen"]=12836,
- ["fiveinferior"]=8325,
- ["fivemonospace"]=65301,
- ["fiveparen"]=9336,
- ["fiveperiod"]=9356,
- ["fivepersian"]=1781,
- ["fiveroman"]=8564,
- ["fivesuperior"]=8309,
- ["fivethai"]=3669,
- ["fl"]=64258,
- ["florin"]=402,
- ["fmonospace"]=65350,
- ["fmsquare"]=13209,
- ["fofanthai"]=3615,
- ["fofathai"]=3613,
- ["fongmanthai"]=3663,
- ["forall"]=8704,
- ["four"]=52,
- ["fourarabic"]=1636,
- ["fourbengali"]=2538,
- ["fourcircle"]=9315,
- ["fourcircleinversesansserif"]=10125,
- ["fourdeva"]=2410,
- ["fourgujarati"]=2794,
- ["fourgurmukhi"]=2666,
- ["fourhackarabic"]=1636,
- ["fourhangzhou"]=12324,
- ["fourideographicparen"]=12835,
- ["fourinferior"]=8324,
- ["fourmonospace"]=65300,
- ["fournumeratorbengali"]=2551,
- ["fourparen"]=9335,
- ["fourperiod"]=9355,
- ["fourpersian"]=1780,
- ["fourroman"]=8563,
- ["foursuperior"]=8308,
- ["fourteencircle"]=9325,
- ["fourteenparen"]=9345,
- ["fourteenperiod"]=9365,
- ["fourthai"]=3668,
- ["fourthtonechinese"]=715,
- ["fparen"]=9377,
- ["fraction"]=8260,
- ["franc"]=8355,
- ["g"]=103,
- ["gabengali"]=2455,
- ["gacute"]=501,
- ["gadeva"]=2327,
- ["gafarabic"]=1711,
- ["gaffinalarabic"]=64403,
- ["gafinitialarabic"]=64404,
- ["gafmedialarabic"]=64405,
- ["gagujarati"]=2711,
- ["gagurmukhi"]=2583,
- ["gahiragana"]=12364,
- ["gakatakana"]=12460,
- ["gamma"]=947,
- ["gammalatinsmall"]=611,
- ["gammasuperior"]=736,
- ["gangiacoptic"]=1003,
- ["gbopomofo"]=12557,
- ["gbreve"]=287,
- ["gcaron"]=487,
- ["gcedilla"]=291,
- ["gcircle"]=9430,
- ["gcircumflex"]=285,
- ["gcommaaccent"]=291,
- ["gdot"]=289,
- ["gdotaccent"]=289,
- ["gecyrillic"]=1075,
- ["gehiragana"]=12370,
- ["gekatakana"]=12466,
- ["geometricallyequal"]=8785,
- ["gereshaccenthebrew"]=1436,
- ["gereshhebrew"]=1523,
- ["gereshmuqdamhebrew"]=1437,
- ["germandbls"]=223,
- ["gershayimaccenthebrew"]=1438,
- ["gershayimhebrew"]=1524,
- ["getamark"]=12307,
- ["ghabengali"]=2456,
- ["ghadarmenian"]=1394,
- ["ghadeva"]=2328,
- ["ghagujarati"]=2712,
- ["ghagurmukhi"]=2584,
- ["ghainarabic"]=1594,
- ["ghainfinalarabic"]=65230,
- ["ghaininitialarabic"]=65231,
- ["ghainmedialarabic"]=65232,
- ["ghemiddlehookcyrillic"]=1173,
- ["ghestrokecyrillic"]=1171,
- ["gheupturncyrillic"]=1169,
- ["ghhadeva"]=2394,
- ["ghhagurmukhi"]=2650,
- ["ghook"]=608,
- ["ghzsquare"]=13203,
- ["gihiragana"]=12366,
- ["gikatakana"]=12462,
- ["gimarmenian"]=1379,
- ["gimel"]=1490,
- ["gimeldagesh"]=64306,
- ["gimeldageshhebrew"]=64306,
- ["gimelhebrew"]=1490,
- ["gjecyrillic"]=1107,
- ["glottalinvertedstroke"]=446,
- ["glottalstop"]=660,
- ["glottalstopinverted"]=662,
- ["glottalstopmod"]=704,
- ["glottalstopreversed"]=661,
- ["glottalstopreversedmod"]=705,
- ["glottalstopreversedsuperior"]=740,
- ["glottalstopstroke"]=673,
- ["glottalstopstrokereversed"]=674,
- ["gmacron"]=7713,
- ["gmonospace"]=65351,
- ["gohiragana"]=12372,
- ["gokatakana"]=12468,
- ["gparen"]=9378,
- ["gpasquare"]=13228,
- ["gradient"]=8711,
- ["grave"]=96,
- ["gravebelowcmb"]=790,
- ["gravecmb"]=768,
- ["gravecomb"]=768,
- ["gravedeva"]=2387,
- ["gravelowmod"]=718,
- ["gravemonospace"]=65344,
- ["gravetonecmb"]=832,
- ["greater"]=62,
- ["greaterequal"]=8805,
- ["greaterequalorless"]=8923,
- ["greatermonospace"]=65310,
- ["greaterorequivalent"]=8819,
- ["greaterorless"]=8823,
- ["greateroverequal"]=8807,
- ["greatersmall"]=65125,
- ["gscript"]=609,
- ["gstroke"]=485,
- ["guhiragana"]=12368,
- ["guillemotleft"]=171,
- ["guillemotright"]=187,
- ["guilsinglleft"]=8249,
- ["guilsinglright"]=8250,
- ["gukatakana"]=12464,
- ["guramusquare"]=13080,
- ["gysquare"]=13257,
- ["h"]=104,
- ["haabkhasiancyrillic"]=1193,
- ["haaltonearabic"]=1729,
- ["habengali"]=2489,
- ["hadescendercyrillic"]=1203,
- ["hadeva"]=2361,
- ["hagujarati"]=2745,
- ["hagurmukhi"]=2617,
- ["haharabic"]=1581,
- ["hahfinalarabic"]=65186,
- ["hahinitialarabic"]=65187,
- ["hahiragana"]=12399,
- ["hahmedialarabic"]=65188,
- ["haitusquare"]=13098,
- ["hakatakana"]=12495,
- ["hakatakanahalfwidth"]=65418,
- ["halantgurmukhi"]=2637,
- ["hamzaarabic"]=1569,
- ["hamzadammaarabic"]=1569,
- ["hamzadammatanarabic"]=1569,
- ["hamzafathaarabic"]=1569,
- ["hamzafathatanarabic"]=1569,
- ["hamzalowarabic"]=1569,
- ["hamzalowkasraarabic"]=1569,
- ["hamzalowkasratanarabic"]=1569,
- ["hamzasukunarabic"]=1569,
- ["hangulfiller"]=12644,
- ["hardsigncyrillic"]=1098,
- ["harpoonleftbarbup"]=8636,
- ["harpoonrightbarbup"]=8640,
- ["hasquare"]=13258,
- ["hatafpatah"]=1458,
- ["hatafpatah16"]=1458,
- ["hatafpatah23"]=1458,
- ["hatafpatah2f"]=1458,
- ["hatafpatahhebrew"]=1458,
- ["hatafpatahnarrowhebrew"]=1458,
- ["hatafpatahquarterhebrew"]=1458,
- ["hatafpatahwidehebrew"]=1458,
- ["hatafqamats"]=1459,
- ["hatafqamats1b"]=1459,
- ["hatafqamats28"]=1459,
- ["hatafqamats34"]=1459,
- ["hatafqamatshebrew"]=1459,
- ["hatafqamatsnarrowhebrew"]=1459,
- ["hatafqamatsquarterhebrew"]=1459,
- ["hatafqamatswidehebrew"]=1459,
- ["hatafsegol"]=1457,
- ["hatafsegol17"]=1457,
- ["hatafsegol24"]=1457,
- ["hatafsegol30"]=1457,
- ["hatafsegolhebrew"]=1457,
- ["hatafsegolnarrowhebrew"]=1457,
- ["hatafsegolquarterhebrew"]=1457,
- ["hatafsegolwidehebrew"]=1457,
- ["hbar"]=295,
- ["hbopomofo"]=12559,
- ["hbrevebelow"]=7723,
- ["hcedilla"]=7721,
- ["hcircle"]=9431,
- ["hcircumflex"]=293,
- ["hdieresis"]=7719,
- ["hdotaccent"]=7715,
- ["hdotbelow"]=7717,
- ["he"]=1492,
- ["heart"]=9829,
- ["heartsuitblack"]=9829,
- ["heartsuitwhite"]=9825,
- ["hedagesh"]=64308,
- ["hedageshhebrew"]=64308,
- ["hehaltonearabic"]=1729,
- ["heharabic"]=1607,
- ["hehebrew"]=1492,
- ["hehfinalaltonearabic"]=64423,
- ["hehfinalalttwoarabic"]=65258,
- ["hehfinalarabic"]=65258,
- ["hehhamzaabovefinalarabic"]=64421,
- ["hehhamzaaboveisolatedarabic"]=64420,
- ["hehinitialaltonearabic"]=64424,
- ["hehinitialarabic"]=65259,
- ["hehiragana"]=12408,
- ["hehmedialaltonearabic"]=64425,
- ["hehmedialarabic"]=65260,
- ["heiseierasquare"]=13179,
- ["hekatakana"]=12504,
- ["hekatakanahalfwidth"]=65421,
- ["hekutaarusquare"]=13110,
- ["henghook"]=615,
- ["herutusquare"]=13113,
- ["het"]=1495,
- ["hethebrew"]=1495,
- ["hhook"]=614,
- ["hhooksuperior"]=689,
- ["hieuhacirclekorean"]=12923,
- ["hieuhaparenkorean"]=12827,
- ["hieuhcirclekorean"]=12909,
- ["hieuhkorean"]=12622,
- ["hieuhparenkorean"]=12813,
- ["hihiragana"]=12402,
- ["hikatakana"]=12498,
- ["hikatakanahalfwidth"]=65419,
- ["hiriq"]=1460,
- ["hiriq14"]=1460,
- ["hiriq21"]=1460,
- ["hiriq2d"]=1460,
- ["hiriqhebrew"]=1460,
- ["hiriqnarrowhebrew"]=1460,
- ["hiriqquarterhebrew"]=1460,
- ["hiriqwidehebrew"]=1460,
- ["hlinebelow"]=7830,
- ["hmonospace"]=65352,
- ["hoarmenian"]=1392,
- ["hohipthai"]=3627,
- ["hohiragana"]=12411,
- ["hokatakana"]=12507,
- ["hokatakanahalfwidth"]=65422,
- ["holam"]=1465,
- ["holam19"]=1465,
- ["holam26"]=1465,
- ["holam32"]=1465,
- ["holamhebrew"]=1465,
- ["holamnarrowhebrew"]=1465,
- ["holamquarterhebrew"]=1465,
- ["holamwidehebrew"]=1465,
- ["honokhukthai"]=3630,
- ["hookabovecomb"]=777,
- ["hookcmb"]=777,
- ["hookpalatalizedbelowcmb"]=801,
- ["hookretroflexbelowcmb"]=802,
- ["hoonsquare"]=13122,
- ["horicoptic"]=1001,
- ["horizontalbar"]=8213,
- ["horncmb"]=795,
- ["hotsprings"]=9832,
- ["house"]=8962,
- ["hparen"]=9379,
- ["hsuperior"]=688,
- ["hturned"]=613,
- ["huhiragana"]=12405,
- ["huiitosquare"]=13107,
- ["hukatakana"]=12501,
- ["hukatakanahalfwidth"]=65420,
- ["hungarumlaut"]=733,
- ["hungarumlautcmb"]=779,
- ["hv"]=405,
- ["hyphen"]=45,
- ["hyphenmonospace"]=65293,
- ["hyphensmall"]=65123,
- ["hyphentwo"]=8208,
- ["i"]=105,
- ["iacute"]=237,
- ["iacyrillic"]=1103,
- ["ibengali"]=2439,
- ["ibopomofo"]=12583,
- ["ibreve"]=301,
- ["icaron"]=464,
- ["icircle"]=9432,
- ["icircumflex"]=238,
- ["icyrillic"]=1110,
- ["idblgrave"]=521,
- ["ideographearthcircle"]=12943,
- ["ideographfirecircle"]=12939,
- ["ideographicallianceparen"]=12863,
- ["ideographiccallparen"]=12858,
- ["ideographiccentrecircle"]=12965,
- ["ideographicclose"]=12294,
- ["ideographiccomma"]=12289,
- ["ideographiccommaleft"]=65380,
- ["ideographiccongratulationparen"]=12855,
- ["ideographiccorrectcircle"]=12963,
- ["ideographicearthparen"]=12847,
- ["ideographicenterpriseparen"]=12861,
- ["ideographicexcellentcircle"]=12957,
- ["ideographicfestivalparen"]=12864,
- ["ideographicfinancialcircle"]=12950,
- ["ideographicfinancialparen"]=12854,
- ["ideographicfireparen"]=12843,
- ["ideographichaveparen"]=12850,
- ["ideographichighcircle"]=12964,
- ["ideographiciterationmark"]=12293,
- ["ideographiclaborcircle"]=12952,
- ["ideographiclaborparen"]=12856,
- ["ideographicleftcircle"]=12967,
- ["ideographiclowcircle"]=12966,
- ["ideographicmedicinecircle"]=12969,
- ["ideographicmetalparen"]=12846,
- ["ideographicmoonparen"]=12842,
- ["ideographicnameparen"]=12852,
- ["ideographicperiod"]=12290,
- ["ideographicprintcircle"]=12958,
- ["ideographicreachparen"]=12867,
- ["ideographicrepresentparen"]=12857,
- ["ideographicresourceparen"]=12862,
- ["ideographicrightcircle"]=12968,
- ["ideographicsecretcircle"]=12953,
- ["ideographicselfparen"]=12866,
- ["ideographicsocietyparen"]=12851,
- ["ideographicspace"]=12288,
- ["ideographicspecialparen"]=12853,
- ["ideographicstockparen"]=12849,
- ["ideographicstudyparen"]=12859,
- ["ideographicsunparen"]=12848,
- ["ideographicsuperviseparen"]=12860,
- ["ideographicwaterparen"]=12844,
- ["ideographicwoodparen"]=12845,
- ["ideographiczero"]=12295,
- ["ideographmetalcircle"]=12942,
- ["ideographmooncircle"]=12938,
- ["ideographnamecircle"]=12948,
- ["ideographsuncircle"]=12944,
- ["ideographwatercircle"]=12940,
- ["ideographwoodcircle"]=12941,
- ["ideva"]=2311,
- ["idieresis"]=239,
- ["idieresisacute"]=7727,
- ["idieresiscyrillic"]=1253,
- ["idotbelow"]=7883,
- ["iebrevecyrillic"]=1239,
- ["iecyrillic"]=1077,
- ["ieungacirclekorean"]=12917,
- ["ieungaparenkorean"]=12821,
- ["ieungcirclekorean"]=12903,
- ["ieungkorean"]=12615,
- ["ieungparenkorean"]=12807,
- ["igrave"]=236,
- ["igujarati"]=2695,
- ["igurmukhi"]=2567,
- ["ihiragana"]=12356,
- ["ihookabove"]=7881,
- ["iibengali"]=2440,
- ["iicyrillic"]=1080,
- ["iideva"]=2312,
- ["iigujarati"]=2696,
- ["iigurmukhi"]=2568,
- ["iimatragurmukhi"]=2624,
- ["iinvertedbreve"]=523,
- ["iishortcyrillic"]=1081,
- ["iivowelsignbengali"]=2496,
- ["iivowelsigndeva"]=2368,
- ["iivowelsigngujarati"]=2752,
- ["ij"]=307,
- ["ikatakana"]=12452,
- ["ikatakanahalfwidth"]=65394,
- ["ikorean"]=12643,
- ["ilde"]=732,
- ["iluyhebrew"]=1452,
- ["imacron"]=299,
- ["imacroncyrillic"]=1251,
- ["imageorapproximatelyequal"]=8787,
- ["imatragurmukhi"]=2623,
- ["imonospace"]=65353,
- ["increment"]=8710,
- ["infinity"]=8734,
- ["iniarmenian"]=1387,
- ["integral"]=8747,
- ["integralbottom"]=8993,
- ["integralbt"]=8993,
- ["integraltop"]=8992,
- ["integraltp"]=8992,
- ["intersection"]=8745,
- ["intisquare"]=13061,
- ["invbullet"]=9688,
- ["invcircle"]=9689,
- ["invsmileface"]=9787,
- ["iocyrillic"]=1105,
- ["iogonek"]=303,
- ["iota"]=953,
- ["iotadieresis"]=970,
- ["iotadieresistonos"]=912,
- ["iotalatin"]=617,
- ["iotatonos"]=943,
- ["iparen"]=9380,
- ["irigurmukhi"]=2674,
- ["ismallhiragana"]=12355,
- ["ismallkatakana"]=12451,
- ["ismallkatakanahalfwidth"]=65384,
- ["issharbengali"]=2554,
- ["istroke"]=616,
- ["iterationhiragana"]=12445,
- ["iterationkatakana"]=12541,
- ["itilde"]=297,
- ["itildebelow"]=7725,
- ["iubopomofo"]=12585,
- ["iucyrillic"]=1102,
- ["ivowelsignbengali"]=2495,
- ["ivowelsigndeva"]=2367,
- ["ivowelsigngujarati"]=2751,
- ["izhitsacyrillic"]=1141,
- ["izhitsadblgravecyrillic"]=1143,
- ["j"]=106,
- ["jaarmenian"]=1393,
- ["jabengali"]=2460,
- ["jadeva"]=2332,
- ["jagujarati"]=2716,
- ["jagurmukhi"]=2588,
- ["jbopomofo"]=12560,
- ["jcaron"]=496,
- ["jcircle"]=9433,
- ["jcircumflex"]=309,
- ["jcrossedtail"]=669,
- ["jdotlessstroke"]=607,
- ["jecyrillic"]=1112,
- ["jeemarabic"]=1580,
- ["jeemfinalarabic"]=65182,
- ["jeeminitialarabic"]=65183,
- ["jeemmedialarabic"]=65184,
- ["jeharabic"]=1688,
- ["jehfinalarabic"]=64395,
- ["jhabengali"]=2461,
- ["jhadeva"]=2333,
- ["jhagujarati"]=2717,
- ["jhagurmukhi"]=2589,
- ["jheharmenian"]=1403,
- ["jis"]=12292,
- ["jmonospace"]=65354,
- ["jparen"]=9381,
- ["jsuperior"]=690,
- ["k"]=107,
- ["kabashkircyrillic"]=1185,
- ["kabengali"]=2453,
- ["kacute"]=7729,
- ["kacyrillic"]=1082,
- ["kadescendercyrillic"]=1179,
- ["kadeva"]=2325,
- ["kaf"]=1499,
- ["kafarabic"]=1603,
- ["kafdagesh"]=64315,
- ["kafdageshhebrew"]=64315,
- ["kaffinalarabic"]=65242,
- ["kafhebrew"]=1499,
- ["kafinitialarabic"]=65243,
- ["kafmedialarabic"]=65244,
- ["kafrafehebrew"]=64333,
- ["kagujarati"]=2709,
- ["kagurmukhi"]=2581,
- ["kahiragana"]=12363,
- ["kahookcyrillic"]=1220,
- ["kakatakana"]=12459,
- ["kakatakanahalfwidth"]=65398,
- ["kappa"]=954,
- ["kappasymbolgreek"]=1008,
- ["kapyeounmieumkorean"]=12657,
- ["kapyeounphieuphkorean"]=12676,
- ["kapyeounpieupkorean"]=12664,
- ["kapyeounssangpieupkorean"]=12665,
- ["karoriisquare"]=13069,
- ["kashidaautoarabic"]=1600,
- ["kashidaautonosidebearingarabic"]=1600,
- ["kasmallkatakana"]=12533,
- ["kasquare"]=13188,
- ["kasraarabic"]=1616,
- ["kasratanarabic"]=1613,
- ["kastrokecyrillic"]=1183,
- ["katahiraprolongmarkhalfwidth"]=65392,
- ["kaverticalstrokecyrillic"]=1181,
- ["kbopomofo"]=12558,
- ["kcalsquare"]=13193,
- ["kcaron"]=489,
- ["kcedilla"]=311,
- ["kcircle"]=9434,
- ["kcommaaccent"]=311,
- ["kdotbelow"]=7731,
- ["keharmenian"]=1412,
- ["kehiragana"]=12369,
- ["kekatakana"]=12465,
- ["kekatakanahalfwidth"]=65401,
- ["kenarmenian"]=1391,
- ["kesmallkatakana"]=12534,
- ["kgreenlandic"]=312,
- ["khabengali"]=2454,
- ["khacyrillic"]=1093,
- ["khadeva"]=2326,
- ["khagujarati"]=2710,
- ["khagurmukhi"]=2582,
- ["khaharabic"]=1582,
- ["khahfinalarabic"]=65190,
- ["khahinitialarabic"]=65191,
- ["khahmedialarabic"]=65192,
- ["kheicoptic"]=999,
- ["khhadeva"]=2393,
- ["khhagurmukhi"]=2649,
- ["khieukhacirclekorean"]=12920,
- ["khieukhaparenkorean"]=12824,
- ["khieukhcirclekorean"]=12906,
- ["khieukhkorean"]=12619,
- ["khieukhparenkorean"]=12810,
- ["khokhaithai"]=3586,
- ["khokhonthai"]=3589,
- ["khokhuatthai"]=3587,
- ["khokhwaithai"]=3588,
- ["khomutthai"]=3675,
- ["khook"]=409,
- ["khorakhangthai"]=3590,
- ["khzsquare"]=13201,
- ["kihiragana"]=12365,
- ["kikatakana"]=12461,
- ["kikatakanahalfwidth"]=65399,
- ["kiroguramusquare"]=13077,
- ["kiromeetorusquare"]=13078,
- ["kirosquare"]=13076,
- ["kiyeokacirclekorean"]=12910,
- ["kiyeokaparenkorean"]=12814,
- ["kiyeokcirclekorean"]=12896,
- ["kiyeokkorean"]=12593,
- ["kiyeokparenkorean"]=12800,
- ["kiyeoksioskorean"]=12595,
- ["kjecyrillic"]=1116,
- ["klinebelow"]=7733,
- ["klsquare"]=13208,
- ["kmcubedsquare"]=13222,
- ["kmonospace"]=65355,
- ["kmsquaredsquare"]=13218,
- ["kohiragana"]=12371,
- ["kohmsquare"]=13248,
- ["kokaithai"]=3585,
- ["kokatakana"]=12467,
- ["kokatakanahalfwidth"]=65402,
- ["kooposquare"]=13086,
- ["koppacyrillic"]=1153,
- ["koreanstandardsymbol"]=12927,
- ["koroniscmb"]=835,
- ["kparen"]=9382,
- ["kpasquare"]=13226,
- ["ksicyrillic"]=1135,
- ["ktsquare"]=13263,
- ["kturned"]=670,
- ["kuhiragana"]=12367,
- ["kukatakana"]=12463,
- ["kukatakanahalfwidth"]=65400,
- ["kvsquare"]=13240,
- ["kwsquare"]=13246,
- ["l"]=108,
- ["labengali"]=2482,
- ["lacute"]=314,
- ["ladeva"]=2354,
- ["lagujarati"]=2738,
- ["lagurmukhi"]=2610,
- ["lakkhangyaothai"]=3653,
- ["lamaleffinalarabic"]=65276,
- ["lamalefhamzaabovefinalarabic"]=65272,
- ["lamalefhamzaaboveisolatedarabic"]=65271,
- ["lamalefhamzabelowfinalarabic"]=65274,
- ["lamalefhamzabelowisolatedarabic"]=65273,
- ["lamalefisolatedarabic"]=65275,
- ["lamalefmaddaabovefinalarabic"]=65270,
- ["lamalefmaddaaboveisolatedarabic"]=65269,
- ["lamarabic"]=1604,
- ["lambda"]=955,
- ["lambdastroke"]=411,
- ["lamed"]=1500,
- ["lameddagesh"]=64316,
- ["lameddageshhebrew"]=64316,
- ["lamedhebrew"]=1500,
- ["lamedholam"]=1500,
- ["lamedholamdagesh"]=1500,
- ["lamedholamdageshhebrew"]=1500,
- ["lamedholamhebrew"]=1500,
- ["lamfinalarabic"]=65246,
- ["lamhahinitialarabic"]=64714,
- ["laminitialarabic"]=65247,
- ["lamjeeminitialarabic"]=64713,
- ["lamkhahinitialarabic"]=64715,
- ["lamlamhehisolatedarabic"]=65010,
- ["lammedialarabic"]=65248,
- ["lammeemhahinitialarabic"]=64904,
- ["lammeeminitialarabic"]=64716,
- ["lammeemjeeminitialarabic"]=65247,
- ["lammeemkhahinitialarabic"]=65247,
- ["largecircle"]=9711,
- ["lbar"]=410,
- ["lbelt"]=620,
- ["lbopomofo"]=12556,
- ["lcaron"]=318,
- ["lcedilla"]=316,
- ["lcircle"]=9435,
- ["lcircumflexbelow"]=7741,
- ["lcommaaccent"]=316,
- ["ldot"]=320,
- ["ldotaccent"]=320,
- ["ldotbelow"]=7735,
- ["ldotbelowmacron"]=7737,
- ["leftangleabovecmb"]=794,
- ["lefttackbelowcmb"]=792,
- ["less"]=60,
- ["lessequal"]=8804,
- ["lessequalorgreater"]=8922,
- ["lessmonospace"]=65308,
- ["lessorequivalent"]=8818,
- ["lessorgreater"]=8822,
- ["lessoverequal"]=8806,
- ["lesssmall"]=65124,
- ["lezh"]=622,
- ["lfblock"]=9612,
- ["lhookretroflex"]=621,
- ["lira"]=8356,
- ["liwnarmenian"]=1388,
- ["lj"]=457,
- ["ljecyrillic"]=1113,
- ["lladeva"]=2355,
- ["llagujarati"]=2739,
- ["llinebelow"]=7739,
- ["llladeva"]=2356,
- ["llvocalicbengali"]=2529,
- ["llvocalicdeva"]=2401,
- ["llvocalicvowelsignbengali"]=2531,
- ["llvocalicvowelsigndeva"]=2403,
- ["lmiddletilde"]=619,
- ["lmonospace"]=65356,
- ["lmsquare"]=13264,
- ["lochulathai"]=3628,
- ["logicaland"]=8743,
- ["logicalnot"]=172,
- ["logicalnotreversed"]=8976,
- ["logicalor"]=8744,
- ["lolingthai"]=3621,
- ["longs"]=383,
- ["lowlinecenterline"]=65102,
- ["lowlinecmb"]=818,
- ["lowlinedashed"]=65101,
- ["lozenge"]=9674,
- ["lparen"]=9383,
- ["lslash"]=322,
- ["lsquare"]=8467,
- ["ltshade"]=9617,
- ["luthai"]=3622,
- ["lvocalicbengali"]=2444,
- ["lvocalicdeva"]=2316,
- ["lvocalicvowelsignbengali"]=2530,
- ["lvocalicvowelsigndeva"]=2402,
- ["lxsquare"]=13267,
- ["m"]=109,
- ["mabengali"]=2478,
- ["macron"]=175,
- ["macronbelowcmb"]=817,
- ["macroncmb"]=772,
- ["macronlowmod"]=717,
- ["macronmonospace"]=65507,
- ["macute"]=7743,
- ["madeva"]=2350,
- ["magujarati"]=2734,
- ["magurmukhi"]=2606,
- ["mahapakhhebrew"]=1444,
- ["mahapakhlefthebrew"]=1444,
- ["mahiragana"]=12414,
- ["maichattawathai"]=3659,
- ["maiekthai"]=3656,
- ["maihanakatthai"]=3633,
- ["maitaikhuthai"]=3655,
- ["maithothai"]=3657,
- ["maitrithai"]=3658,
- ["maiyamokthai"]=3654,
- ["makatakana"]=12510,
- ["makatakanahalfwidth"]=65423,
- ["male"]=9794,
- ["mansyonsquare"]=13127,
- ["maqafhebrew"]=1470,
- ["mars"]=9794,
- ["masoracirclehebrew"]=1455,
- ["masquare"]=13187,
- ["mbopomofo"]=12551,
- ["mbsquare"]=13268,
- ["mcircle"]=9436,
- ["mcubedsquare"]=13221,
- ["mdotaccent"]=7745,
- ["mdotbelow"]=7747,
- ["meemarabic"]=1605,
- ["meemfinalarabic"]=65250,
- ["meeminitialarabic"]=65251,
- ["meemmedialarabic"]=65252,
- ["meemmeeminitialarabic"]=64721,
- ["meemmeemisolatedarabic"]=64584,
- ["meetorusquare"]=13133,
- ["mehiragana"]=12417,
- ["meizierasquare"]=13182,
- ["mekatakana"]=12513,
- ["mekatakanahalfwidth"]=65426,
- ["mem"]=1502,
- ["memdagesh"]=64318,
- ["memdageshhebrew"]=64318,
- ["memhebrew"]=1502,
- ["menarmenian"]=1396,
- ["merkhahebrew"]=1445,
- ["merkhakefulahebrew"]=1446,
- ["merkhakefulalefthebrew"]=1446,
- ["merkhalefthebrew"]=1445,
- ["mhook"]=625,
- ["mhzsquare"]=13202,
- ["middledotkatakanahalfwidth"]=65381,
- ["middot"]=183,
- ["mieumacirclekorean"]=12914,
- ["mieumaparenkorean"]=12818,
- ["mieumcirclekorean"]=12900,
- ["mieumkorean"]=12609,
- ["mieumpansioskorean"]=12656,
- ["mieumparenkorean"]=12804,
- ["mieumpieupkorean"]=12654,
- ["mieumsioskorean"]=12655,
- ["mihiragana"]=12415,
- ["mikatakana"]=12511,
- ["mikatakanahalfwidth"]=65424,
- ["minus"]=8722,
- ["minusbelowcmb"]=800,
- ["minuscircle"]=8854,
- ["minusmod"]=727,
- ["minusplus"]=8723,
- ["minute"]=8242,
- ["miribaarusquare"]=13130,
- ["mirisquare"]=13129,
- ["mlonglegturned"]=624,
- ["mlsquare"]=13206,
- ["mmcubedsquare"]=13219,
- ["mmonospace"]=65357,
- ["mmsquaredsquare"]=13215,
- ["mohiragana"]=12418,
- ["mohmsquare"]=13249,
- ["mokatakana"]=12514,
- ["mokatakanahalfwidth"]=65427,
- ["molsquare"]=13270,
- ["momathai"]=3617,
- ["moverssquare"]=13223,
- ["moverssquaredsquare"]=13224,
- ["mparen"]=9384,
- ["mpasquare"]=13227,
- ["mssquare"]=13235,
- ["mturned"]=623,
- ["mu"]=181,
- ["mu1"]=181,
- ["muasquare"]=13186,
- ["muchgreater"]=8811,
- ["muchless"]=8810,
- ["mufsquare"]=13196,
- ["mugreek"]=956,
- ["mugsquare"]=13197,
- ["muhiragana"]=12416,
- ["mukatakana"]=12512,
- ["mukatakanahalfwidth"]=65425,
- ["mulsquare"]=13205,
- ["multiply"]=215,
- ["mumsquare"]=13211,
- ["munahhebrew"]=1443,
- ["munahlefthebrew"]=1443,
- ["musicalnote"]=9834,
- ["musicalnotedbl"]=9835,
- ["musicflatsign"]=9837,
- ["musicsharpsign"]=9839,
- ["mussquare"]=13234,
- ["muvsquare"]=13238,
- ["muwsquare"]=13244,
- ["mvmegasquare"]=13241,
- ["mvsquare"]=13239,
- ["mwmegasquare"]=13247,
- ["mwsquare"]=13245,
- ["n"]=110,
- ["nabengali"]=2472,
- ["nabla"]=8711,
- ["nacute"]=324,
- ["nadeva"]=2344,
- ["nagujarati"]=2728,
- ["nagurmukhi"]=2600,
- ["nahiragana"]=12394,
- ["nakatakana"]=12490,
- ["nakatakanahalfwidth"]=65413,
- ["napostrophe"]=329,
- ["nasquare"]=13185,
- ["nbopomofo"]=12555,
- ["nbspace"]=160,
- ["ncaron"]=328,
- ["ncedilla"]=326,
- ["ncircle"]=9437,
- ["ncircumflexbelow"]=7755,
- ["ncommaaccent"]=326,
- ["ndotaccent"]=7749,
- ["ndotbelow"]=7751,
- ["nehiragana"]=12397,
- ["nekatakana"]=12493,
- ["nekatakanahalfwidth"]=65416,
- ["newsheqelsign"]=8362,
- ["nfsquare"]=13195,
- ["ngabengali"]=2457,
- ["ngadeva"]=2329,
- ["ngagujarati"]=2713,
- ["ngagurmukhi"]=2585,
- ["ngonguthai"]=3591,
- ["nhiragana"]=12435,
- ["nhookleft"]=626,
- ["nhookretroflex"]=627,
- ["nieunacirclekorean"]=12911,
- ["nieunaparenkorean"]=12815,
- ["nieuncieuckorean"]=12597,
- ["nieuncirclekorean"]=12897,
- ["nieunhieuhkorean"]=12598,
- ["nieunkorean"]=12596,
- ["nieunpansioskorean"]=12648,
- ["nieunparenkorean"]=12801,
- ["nieunsioskorean"]=12647,
- ["nieuntikeutkorean"]=12646,
- ["nihiragana"]=12395,
- ["nikatakana"]=12491,
- ["nikatakanahalfwidth"]=65414,
- ["nikhahitthai"]=3661,
- ["nine"]=57,
- ["ninearabic"]=1641,
- ["ninebengali"]=2543,
- ["ninecircle"]=9320,
- ["ninecircleinversesansserif"]=10130,
- ["ninedeva"]=2415,
- ["ninegujarati"]=2799,
- ["ninegurmukhi"]=2671,
- ["ninehackarabic"]=1641,
- ["ninehangzhou"]=12329,
- ["nineideographicparen"]=12840,
- ["nineinferior"]=8329,
- ["ninemonospace"]=65305,
- ["nineparen"]=9340,
- ["nineperiod"]=9360,
- ["ninepersian"]=1785,
- ["nineroman"]=8568,
- ["ninesuperior"]=8313,
- ["nineteencircle"]=9330,
- ["nineteenparen"]=9350,
- ["nineteenperiod"]=9370,
- ["ninethai"]=3673,
- ["nj"]=460,
- ["njecyrillic"]=1114,
- ["nkatakana"]=12531,
- ["nkatakanahalfwidth"]=65437,
- ["nlegrightlong"]=414,
- ["nlinebelow"]=7753,
- ["nmonospace"]=65358,
- ["nmsquare"]=13210,
- ["nnabengali"]=2467,
- ["nnadeva"]=2339,
- ["nnagujarati"]=2723,
- ["nnagurmukhi"]=2595,
- ["nnnadeva"]=2345,
- ["nohiragana"]=12398,
- ["nokatakana"]=12494,
- ["nokatakanahalfwidth"]=65417,
- ["nonbreakingspace"]=160,
- ["nonenthai"]=3603,
- ["nonuthai"]=3609,
- ["noonarabic"]=1606,
- ["noonfinalarabic"]=65254,
- ["noonghunnaarabic"]=1722,
- ["noonghunnafinalarabic"]=64415,
- ["noonhehinitialarabic"]=65255,
- ["nooninitialarabic"]=65255,
- ["noonjeeminitialarabic"]=64722,
- ["noonjeemisolatedarabic"]=64587,
- ["noonmedialarabic"]=65256,
- ["noonmeeminitialarabic"]=64725,
- ["noonmeemisolatedarabic"]=64590,
- ["noonnoonfinalarabic"]=64653,
- ["notcontains"]=8716,
- ["notelement"]=8713,
- ["notelementof"]=8713,
- ["notequal"]=8800,
- ["notgreater"]=8815,
- ["notgreaternorequal"]=8817,
- ["notgreaternorless"]=8825,
- ["notidentical"]=8802,
- ["notless"]=8814,
- ["notlessnorequal"]=8816,
- ["notparallel"]=8742,
- ["notprecedes"]=8832,
- ["notsubset"]=8836,
- ["notsucceeds"]=8833,
- ["notsuperset"]=8837,
- ["nowarmenian"]=1398,
- ["nparen"]=9385,
- ["nssquare"]=13233,
- ["nsuperior"]=8319,
- ["ntilde"]=241,
- ["nu"]=957,
- ["nuhiragana"]=12396,
- ["nukatakana"]=12492,
- ["nukatakanahalfwidth"]=65415,
- ["nuktabengali"]=2492,
- ["nuktadeva"]=2364,
- ["nuktagujarati"]=2748,
- ["nuktagurmukhi"]=2620,
- ["numbersign"]=35,
- ["numbersignmonospace"]=65283,
- ["numbersignsmall"]=65119,
- ["numeralsigngreek"]=884,
- ["numeralsignlowergreek"]=885,
- ["numero"]=8470,
- ["nun"]=1504,
- ["nundagesh"]=64320,
- ["nundageshhebrew"]=64320,
- ["nunhebrew"]=1504,
- ["nvsquare"]=13237,
- ["nwsquare"]=13243,
- ["nyabengali"]=2462,
- ["nyadeva"]=2334,
- ["nyagujarati"]=2718,
- ["nyagurmukhi"]=2590,
- ["o"]=111,
- ["oacute"]=243,
- ["oangthai"]=3629,
- ["obarred"]=629,
- ["obarredcyrillic"]=1257,
- ["obarreddieresiscyrillic"]=1259,
- ["obengali"]=2451,
- ["obopomofo"]=12571,
- ["obreve"]=335,
- ["ocandradeva"]=2321,
- ["ocandragujarati"]=2705,
- ["ocandravowelsigndeva"]=2377,
- ["ocandravowelsigngujarati"]=2761,
- ["ocaron"]=466,
- ["ocircle"]=9438,
- ["ocircumflex"]=244,
- ["ocircumflexacute"]=7889,
- ["ocircumflexdotbelow"]=7897,
- ["ocircumflexgrave"]=7891,
- ["ocircumflexhookabove"]=7893,
- ["ocircumflextilde"]=7895,
- ["ocyrillic"]=1086,
- ["odblacute"]=337,
- ["odblgrave"]=525,
- ["odeva"]=2323,
- ["odieresis"]=246,
- ["odieresiscyrillic"]=1255,
- ["odotbelow"]=7885,
- ["oe"]=339,
- ["oekorean"]=12634,
- ["ogonek"]=731,
- ["ogonekcmb"]=808,
- ["ograve"]=242,
- ["ogujarati"]=2707,
- ["oharmenian"]=1413,
- ["ohiragana"]=12362,
- ["ohookabove"]=7887,
- ["ohorn"]=417,
- ["ohornacute"]=7899,
- ["ohorndotbelow"]=7907,
- ["ohorngrave"]=7901,
- ["ohornhookabove"]=7903,
- ["ohorntilde"]=7905,
- ["ohungarumlaut"]=337,
- ["oi"]=419,
- ["oinvertedbreve"]=527,
- ["okatakana"]=12458,
- ["okatakanahalfwidth"]=65397,
- ["okorean"]=12631,
- ["olehebrew"]=1451,
- ["omacron"]=333,
- ["omacronacute"]=7763,
- ["omacrongrave"]=7761,
- ["omdeva"]=2384,
- ["omega"]=969,
- ["omega1"]=982,
- ["omegacyrillic"]=1121,
- ["omegalatinclosed"]=631,
- ["omegaroundcyrillic"]=1147,
- ["omegatitlocyrillic"]=1149,
- ["omegatonos"]=974,
- ["omgujarati"]=2768,
- ["omicron"]=959,
- ["omicrontonos"]=972,
- ["omonospace"]=65359,
- ["one"]=49,
- ["onearabic"]=1633,
- ["onebengali"]=2535,
- ["onecircle"]=9312,
- ["onecircleinversesansserif"]=10122,
- ["onedeva"]=2407,
- ["onedotenleader"]=8228,
- ["oneeighth"]=8539,
- ["onegujarati"]=2791,
- ["onegurmukhi"]=2663,
- ["onehackarabic"]=1633,
- ["onehalf"]=189,
- ["onehangzhou"]=12321,
- ["oneideographicparen"]=12832,
- ["oneinferior"]=8321,
- ["onemonospace"]=65297,
- ["onenumeratorbengali"]=2548,
- ["oneparen"]=9332,
- ["oneperiod"]=9352,
- ["onepersian"]=1777,
- ["onequarter"]=188,
- ["oneroman"]=8560,
- ["onesuperior"]=185,
- ["onethai"]=3665,
- ["onethird"]=8531,
- ["oogonek"]=491,
- ["oogonekmacron"]=493,
- ["oogurmukhi"]=2579,
- ["oomatragurmukhi"]=2635,
- ["oopen"]=596,
- ["oparen"]=9386,
- ["openbullet"]=9702,
- ["option"]=8997,
- ["ordfeminine"]=170,
- ["ordmasculine"]=186,
- ["orthogonal"]=8735,
- ["oshortdeva"]=2322,
- ["oshortvowelsigndeva"]=2378,
- ["oslash"]=248,
- ["oslashacute"]=511,
- ["osmallhiragana"]=12361,
- ["osmallkatakana"]=12457,
- ["osmallkatakanahalfwidth"]=65387,
- ["ostrokeacute"]=511,
- ["otcyrillic"]=1151,
- ["otilde"]=245,
- ["otildeacute"]=7757,
- ["otildedieresis"]=7759,
- ["oubopomofo"]=12577,
- ["overline"]=8254,
- ["overlinecenterline"]=65098,
- ["overlinecmb"]=773,
- ["overlinedashed"]=65097,
- ["overlinedblwavy"]=65100,
- ["overlinewavy"]=65099,
- ["overscore"]=175,
- ["ovowelsignbengali"]=2507,
- ["ovowelsigndeva"]=2379,
- ["ovowelsigngujarati"]=2763,
- ["p"]=112,
- ["paampssquare"]=13184,
- ["paasentosquare"]=13099,
- ["pabengali"]=2474,
- ["pacute"]=7765,
- ["padeva"]=2346,
- ["pagedown"]=8671,
- ["pageup"]=8670,
- ["pagujarati"]=2730,
- ["pagurmukhi"]=2602,
- ["pahiragana"]=12401,
- ["paiyannoithai"]=3631,
- ["pakatakana"]=12497,
- ["palatalizationcyrilliccmb"]=1156,
- ["palochkacyrillic"]=1216,
- ["pansioskorean"]=12671,
- ["paragraph"]=182,
- ["parallel"]=8741,
- ["parenleft"]=40,
- ["parenleftaltonearabic"]=64830,
- ["parenleftinferior"]=8333,
- ["parenleftmonospace"]=65288,
- ["parenleftsmall"]=65113,
- ["parenleftsuperior"]=8317,
- ["parenleftvertical"]=65077,
- ["parenright"]=41,
- ["parenrightaltonearabic"]=64831,
- ["parenrightinferior"]=8334,
- ["parenrightmonospace"]=65289,
- ["parenrightsmall"]=65114,
- ["parenrightsuperior"]=8318,
- ["parenrightvertical"]=65078,
- ["partialdiff"]=8706,
- ["paseqhebrew"]=1472,
- ["pashtahebrew"]=1433,
- ["pasquare"]=13225,
- ["patah"]=1463,
- ["patah11"]=1463,
- ["patah1d"]=1463,
- ["patah2a"]=1463,
- ["patahhebrew"]=1463,
- ["patahnarrowhebrew"]=1463,
- ["patahquarterhebrew"]=1463,
- ["patahwidehebrew"]=1463,
- ["pazerhebrew"]=1441,
- ["pbopomofo"]=12550,
- ["pcircle"]=9439,
- ["pdotaccent"]=7767,
- ["pe"]=1508,
- ["pecyrillic"]=1087,
- ["pedagesh"]=64324,
- ["pedageshhebrew"]=64324,
- ["peezisquare"]=13115,
- ["pefinaldageshhebrew"]=64323,
- ["peharabic"]=1662,
- ["peharmenian"]=1402,
- ["pehebrew"]=1508,
- ["pehfinalarabic"]=64343,
- ["pehinitialarabic"]=64344,
- ["pehiragana"]=12410,
- ["pehmedialarabic"]=64345,
- ["pekatakana"]=12506,
- ["pemiddlehookcyrillic"]=1191,
- ["perafehebrew"]=64334,
- ["percent"]=37,
- ["percentarabic"]=1642,
- ["percentmonospace"]=65285,
- ["percentsmall"]=65130,
- ["period"]=46,
- ["periodarmenian"]=1417,
- ["periodcentered"]=183,
- ["periodhalfwidth"]=65377,
- ["periodmonospace"]=65294,
- ["periodsmall"]=65106,
- ["perispomenigreekcmb"]=834,
- ["perpendicular"]=8869,
- ["perthousand"]=8240,
- ["peseta"]=8359,
- ["pfsquare"]=13194,
- ["phabengali"]=2475,
- ["phadeva"]=2347,
- ["phagujarati"]=2731,
- ["phagurmukhi"]=2603,
- ["phi"]=966,
- ["phi1"]=981,
- ["phieuphacirclekorean"]=12922,
- ["phieuphaparenkorean"]=12826,
- ["phieuphcirclekorean"]=12908,
- ["phieuphkorean"]=12621,
- ["phieuphparenkorean"]=12812,
- ["philatin"]=632,
- ["phinthuthai"]=3642,
- ["phisymbolgreek"]=981,
- ["phook"]=421,
- ["phophanthai"]=3614,
- ["phophungthai"]=3612,
- ["phosamphaothai"]=3616,
- ["pi"]=960,
- ["pieupacirclekorean"]=12915,
- ["pieupaparenkorean"]=12819,
- ["pieupcieuckorean"]=12662,
- ["pieupcirclekorean"]=12901,
- ["pieupkiyeokkorean"]=12658,
- ["pieupkorean"]=12610,
- ["pieupparenkorean"]=12805,
- ["pieupsioskiyeokkorean"]=12660,
- ["pieupsioskorean"]=12612,
- ["pieupsiostikeutkorean"]=12661,
- ["pieupthieuthkorean"]=12663,
- ["pieuptikeutkorean"]=12659,
- ["pihiragana"]=12404,
- ["pikatakana"]=12500,
- ["pisymbolgreek"]=982,
- ["piwrarmenian"]=1411,
- ["plus"]=43,
- ["plusbelowcmb"]=799,
- ["pluscircle"]=8853,
- ["plusminus"]=177,
- ["plusmod"]=726,
- ["plusmonospace"]=65291,
- ["plussmall"]=65122,
- ["plussuperior"]=8314,
- ["pmonospace"]=65360,
- ["pmsquare"]=13272,
- ["pohiragana"]=12413,
- ["pointingindexdownwhite"]=9759,
- ["pointingindexleftwhite"]=9756,
- ["pointingindexrightwhite"]=9758,
- ["pointingindexupwhite"]=9757,
- ["pokatakana"]=12509,
- ["poplathai"]=3611,
- ["postalmark"]=12306,
- ["postalmarkface"]=12320,
- ["pparen"]=9387,
- ["precedes"]=8826,
- ["prescription"]=8478,
- ["primemod"]=697,
- ["primereversed"]=8245,
- ["product"]=8719,
- ["projective"]=8965,
- ["prolongedkana"]=12540,
- ["propellor"]=8984,
- ["propersubset"]=8834,
- ["propersuperset"]=8835,
- ["proportion"]=8759,
- ["proportional"]=8733,
- ["psi"]=968,
- ["psicyrillic"]=1137,
- ["psilipneumatacyrilliccmb"]=1158,
- ["pssquare"]=13232,
- ["puhiragana"]=12407,
- ["pukatakana"]=12503,
- ["pvsquare"]=13236,
- ["pwsquare"]=13242,
- ["q"]=113,
- ["qadeva"]=2392,
- ["qadmahebrew"]=1448,
- ["qafarabic"]=1602,
- ["qaffinalarabic"]=65238,
- ["qafinitialarabic"]=65239,
- ["qafmedialarabic"]=65240,
- ["qamats"]=1464,
- ["qamats10"]=1464,
- ["qamats1a"]=1464,
- ["qamats1c"]=1464,
- ["qamats27"]=1464,
- ["qamats29"]=1464,
- ["qamats33"]=1464,
- ["qamatsde"]=1464,
- ["qamatshebrew"]=1464,
- ["qamatsnarrowhebrew"]=1464,
- ["qamatsqatanhebrew"]=1464,
- ["qamatsqatannarrowhebrew"]=1464,
- ["qamatsqatanquarterhebrew"]=1464,
- ["qamatsqatanwidehebrew"]=1464,
- ["qamatsquarterhebrew"]=1464,
- ["qamatswidehebrew"]=1464,
- ["qarneyparahebrew"]=1439,
- ["qbopomofo"]=12561,
- ["qcircle"]=9440,
- ["qhook"]=672,
- ["qmonospace"]=65361,
- ["qof"]=1511,
- ["qofdagesh"]=64327,
- ["qofdageshhebrew"]=64327,
- ["qofhatafpatah"]=1511,
- ["qofhatafpatahhebrew"]=1511,
- ["qofhatafsegol"]=1511,
- ["qofhatafsegolhebrew"]=1511,
- ["qofhebrew"]=1511,
- ["qofhiriq"]=1511,
- ["qofhiriqhebrew"]=1511,
- ["qofholam"]=1511,
- ["qofholamhebrew"]=1511,
- ["qofpatah"]=1511,
- ["qofpatahhebrew"]=1511,
- ["qofqamats"]=1511,
- ["qofqamatshebrew"]=1511,
- ["qofqubuts"]=1511,
- ["qofqubutshebrew"]=1511,
- ["qofsegol"]=1511,
- ["qofsegolhebrew"]=1511,
- ["qofsheva"]=1511,
- ["qofshevahebrew"]=1511,
- ["qoftsere"]=1511,
- ["qoftserehebrew"]=1511,
- ["qparen"]=9388,
- ["quarternote"]=9833,
- ["qubuts"]=1467,
- ["qubuts18"]=1467,
- ["qubuts25"]=1467,
- ["qubuts31"]=1467,
- ["qubutshebrew"]=1467,
- ["qubutsnarrowhebrew"]=1467,
- ["qubutsquarterhebrew"]=1467,
- ["qubutswidehebrew"]=1467,
- ["question"]=63,
- ["questionarabic"]=1567,
- ["questionarmenian"]=1374,
- ["questiondown"]=191,
- ["questiongreek"]=894,
- ["questionmonospace"]=65311,
- ["quotedbl"]=34,
- ["quotedblbase"]=8222,
- ["quotedblleft"]=8220,
- ["quotedblmonospace"]=65282,
- ["quotedblprime"]=12318,
- ["quotedblprimereversed"]=12317,
- ["quotedblright"]=8221,
- ["quoteleft"]=8216,
- ["quoteleftreversed"]=8219,
- ["quotereversed"]=8219,
- ["quoteright"]=8217,
- ["quoterightn"]=329,
- ["quotesinglbase"]=8218,
- ["quotesingle"]=39,
- ["quotesinglemonospace"]=65287,
- ["r"]=114,
- ["raarmenian"]=1404,
- ["rabengali"]=2480,
- ["racute"]=341,
- ["radeva"]=2352,
- ["radical"]=8730,
- ["radoverssquare"]=13230,
- ["radoverssquaredsquare"]=13231,
- ["radsquare"]=13229,
- ["rafe"]=1471,
- ["rafehebrew"]=1471,
- ["ragujarati"]=2736,
- ["ragurmukhi"]=2608,
- ["rahiragana"]=12425,
- ["rakatakana"]=12521,
- ["rakatakanahalfwidth"]=65431,
- ["ralowerdiagonalbengali"]=2545,
- ["ramiddlediagonalbengali"]=2544,
- ["ramshorn"]=612,
- ["ratio"]=8758,
- ["rbopomofo"]=12566,
- ["rcaron"]=345,
- ["rcedilla"]=343,
- ["rcircle"]=9441,
- ["rcommaaccent"]=343,
- ["rdblgrave"]=529,
- ["rdotaccent"]=7769,
- ["rdotbelow"]=7771,
- ["rdotbelowmacron"]=7773,
- ["referencemark"]=8251,
- ["reflexsubset"]=8838,
- ["reflexsuperset"]=8839,
- ["registered"]=174,
- ["reharabic"]=1585,
- ["reharmenian"]=1408,
- ["rehfinalarabic"]=65198,
- ["rehiragana"]=12428,
- ["rehyehaleflamarabic"]=1585,
- ["rekatakana"]=12524,
- ["rekatakanahalfwidth"]=65434,
- ["resh"]=1512,
- ["reshdageshhebrew"]=64328,
- ["reshhatafpatah"]=1512,
- ["reshhatafpatahhebrew"]=1512,
- ["reshhatafsegol"]=1512,
- ["reshhatafsegolhebrew"]=1512,
- ["reshhebrew"]=1512,
- ["reshhiriq"]=1512,
- ["reshhiriqhebrew"]=1512,
- ["reshholam"]=1512,
- ["reshholamhebrew"]=1512,
- ["reshpatah"]=1512,
- ["reshpatahhebrew"]=1512,
- ["reshqamats"]=1512,
- ["reshqamatshebrew"]=1512,
- ["reshqubuts"]=1512,
- ["reshqubutshebrew"]=1512,
- ["reshsegol"]=1512,
- ["reshsegolhebrew"]=1512,
- ["reshsheva"]=1512,
- ["reshshevahebrew"]=1512,
- ["reshtsere"]=1512,
- ["reshtserehebrew"]=1512,
- ["reversedtilde"]=8765,
- ["reviahebrew"]=1431,
- ["reviamugrashhebrew"]=1431,
- ["revlogicalnot"]=8976,
- ["rfishhook"]=638,
- ["rfishhookreversed"]=639,
- ["rhabengali"]=2525,
- ["rhadeva"]=2397,
- ["rho"]=961,
- ["rhook"]=637,
- ["rhookturned"]=635,
- ["rhookturnedsuperior"]=693,
- ["rhosymbolgreek"]=1009,
- ["rhotichookmod"]=734,
- ["rieulacirclekorean"]=12913,
- ["rieulaparenkorean"]=12817,
- ["rieulcirclekorean"]=12899,
- ["rieulhieuhkorean"]=12608,
- ["rieulkiyeokkorean"]=12602,
- ["rieulkiyeoksioskorean"]=12649,
- ["rieulkorean"]=12601,
- ["rieulmieumkorean"]=12603,
- ["rieulpansioskorean"]=12652,
- ["rieulparenkorean"]=12803,
- ["rieulphieuphkorean"]=12607,
- ["rieulpieupkorean"]=12604,
- ["rieulpieupsioskorean"]=12651,
- ["rieulsioskorean"]=12605,
- ["rieulthieuthkorean"]=12606,
- ["rieultikeutkorean"]=12650,
- ["rieulyeorinhieuhkorean"]=12653,
- ["rightangle"]=8735,
- ["righttackbelowcmb"]=793,
- ["righttriangle"]=8895,
- ["rihiragana"]=12426,
- ["rikatakana"]=12522,
- ["rikatakanahalfwidth"]=65432,
- ["ring"]=730,
- ["ringbelowcmb"]=805,
- ["ringcmb"]=778,
- ["ringhalfleft"]=703,
- ["ringhalfleftarmenian"]=1369,
- ["ringhalfleftbelowcmb"]=796,
- ["ringhalfleftcentered"]=723,
- ["ringhalfright"]=702,
- ["ringhalfrightbelowcmb"]=825,
- ["ringhalfrightcentered"]=722,
- ["rinvertedbreve"]=531,
- ["rittorusquare"]=13137,
- ["rlinebelow"]=7775,
- ["rlongleg"]=636,
- ["rlonglegturned"]=634,
- ["rmonospace"]=65362,
- ["rohiragana"]=12429,
- ["rokatakana"]=12525,
- ["rokatakanahalfwidth"]=65435,
- ["roruathai"]=3619,
- ["rparen"]=9389,
- ["rrabengali"]=2524,
- ["rradeva"]=2353,
- ["rragurmukhi"]=2652,
- ["rreharabic"]=1681,
- ["rrehfinalarabic"]=64397,
- ["rrvocalicbengali"]=2528,
- ["rrvocalicdeva"]=2400,
- ["rrvocalicgujarati"]=2784,
- ["rrvocalicvowelsignbengali"]=2500,
- ["rrvocalicvowelsigndeva"]=2372,
- ["rrvocalicvowelsigngujarati"]=2756,
- ["rtblock"]=9616,
- ["rturned"]=633,
- ["rturnedsuperior"]=692,
- ["ruhiragana"]=12427,
- ["rukatakana"]=12523,
- ["rukatakanahalfwidth"]=65433,
- ["rupeemarkbengali"]=2546,
- ["rupeesignbengali"]=2547,
- ["ruthai"]=3620,
- ["rvocalicbengali"]=2443,
- ["rvocalicdeva"]=2315,
- ["rvocalicgujarati"]=2699,
- ["rvocalicvowelsignbengali"]=2499,
- ["rvocalicvowelsigndeva"]=2371,
- ["rvocalicvowelsigngujarati"]=2755,
- ["s"]=115,
- ["sabengali"]=2488,
- ["sacute"]=347,
- ["sacutedotaccent"]=7781,
- ["sadarabic"]=1589,
- ["sadeva"]=2360,
- ["sadfinalarabic"]=65210,
- ["sadinitialarabic"]=65211,
- ["sadmedialarabic"]=65212,
- ["sagujarati"]=2744,
- ["sagurmukhi"]=2616,
- ["sahiragana"]=12373,
- ["sakatakana"]=12469,
- ["sakatakanahalfwidth"]=65403,
- ["sallallahoualayhewasallamarabic"]=65018,
- ["samekh"]=1505,
- ["samekhdagesh"]=64321,
- ["samekhdageshhebrew"]=64321,
- ["samekhhebrew"]=1505,
- ["saraaathai"]=3634,
- ["saraaethai"]=3649,
- ["saraaimaimalaithai"]=3652,
- ["saraaimaimuanthai"]=3651,
- ["saraamthai"]=3635,
- ["saraathai"]=3632,
- ["saraethai"]=3648,
- ["saraiithai"]=3637,
- ["saraithai"]=3636,
- ["saraothai"]=3650,
- ["saraueethai"]=3639,
- ["sarauethai"]=3638,
- ["sarauthai"]=3640,
- ["sarauuthai"]=3641,
- ["sbopomofo"]=12569,
- ["scaron"]=353,
- ["scarondotaccent"]=7783,
- ["scedilla"]=351,
- ["schwa"]=601,
- ["schwacyrillic"]=1241,
- ["schwadieresiscyrillic"]=1243,
- ["schwahook"]=602,
- ["scircle"]=9442,
- ["scircumflex"]=349,
- ["scommaaccent"]=537,
- ["sdotaccent"]=7777,
- ["sdotbelow"]=7779,
- ["sdotbelowdotaccent"]=7785,
- ["seagullbelowcmb"]=828,
- ["second"]=8243,
- ["secondtonechinese"]=714,
- ["section"]=167,
- ["seenarabic"]=1587,
- ["seenfinalarabic"]=65202,
- ["seeninitialarabic"]=65203,
- ["seenmedialarabic"]=65204,
- ["segol"]=1462,
- ["segol13"]=1462,
- ["segol1f"]=1462,
- ["segol2c"]=1462,
- ["segolhebrew"]=1462,
- ["segolnarrowhebrew"]=1462,
- ["segolquarterhebrew"]=1462,
- ["segoltahebrew"]=1426,
- ["segolwidehebrew"]=1462,
- ["seharmenian"]=1405,
- ["sehiragana"]=12379,
- ["sekatakana"]=12475,
- ["sekatakanahalfwidth"]=65406,
- ["semicolon"]=59,
- ["semicolonarabic"]=1563,
- ["semicolonmonospace"]=65307,
- ["semicolonsmall"]=65108,
- ["semivoicedmarkkana"]=12444,
- ["semivoicedmarkkanahalfwidth"]=65439,
- ["sentisquare"]=13090,
- ["sentosquare"]=13091,
- ["seven"]=55,
- ["sevenarabic"]=1639,
- ["sevenbengali"]=2541,
- ["sevencircle"]=9318,
- ["sevencircleinversesansserif"]=10128,
- ["sevendeva"]=2413,
- ["seveneighths"]=8542,
- ["sevengujarati"]=2797,
- ["sevengurmukhi"]=2669,
- ["sevenhackarabic"]=1639,
- ["sevenhangzhou"]=12327,
- ["sevenideographicparen"]=12838,
- ["seveninferior"]=8327,
- ["sevenmonospace"]=65303,
- ["sevenparen"]=9338,
- ["sevenperiod"]=9358,
- ["sevenpersian"]=1783,
- ["sevenroman"]=8566,
- ["sevensuperior"]=8311,
- ["seventeencircle"]=9328,
- ["seventeenparen"]=9348,
- ["seventeenperiod"]=9368,
- ["seventhai"]=3671,
- ["sfthyphen"]=173,
- ["shaarmenian"]=1399,
- ["shabengali"]=2486,
- ["shacyrillic"]=1096,
- ["shaddaarabic"]=1617,
- ["shaddadammaarabic"]=64609,
- ["shaddadammatanarabic"]=64606,
- ["shaddafathaarabic"]=64608,
- ["shaddafathatanarabic"]=1617,
- ["shaddakasraarabic"]=64610,
- ["shaddakasratanarabic"]=64607,
- ["shade"]=9618,
- ["shadedark"]=9619,
- ["shadelight"]=9617,
- ["shademedium"]=9618,
- ["shadeva"]=2358,
- ["shagujarati"]=2742,
- ["shagurmukhi"]=2614,
- ["shalshelethebrew"]=1427,
- ["shbopomofo"]=12565,
- ["shchacyrillic"]=1097,
- ["sheenarabic"]=1588,
- ["sheenfinalarabic"]=65206,
- ["sheeninitialarabic"]=65207,
- ["sheenmedialarabic"]=65208,
- ["sheicoptic"]=995,
- ["sheqel"]=8362,
- ["sheqelhebrew"]=8362,
- ["sheva"]=1456,
- ["sheva115"]=1456,
- ["sheva15"]=1456,
- ["sheva22"]=1456,
- ["sheva2e"]=1456,
- ["shevahebrew"]=1456,
- ["shevanarrowhebrew"]=1456,
- ["shevaquarterhebrew"]=1456,
- ["shevawidehebrew"]=1456,
- ["shhacyrillic"]=1211,
- ["shimacoptic"]=1005,
- ["shin"]=1513,
- ["shindagesh"]=64329,
- ["shindageshhebrew"]=64329,
- ["shindageshshindot"]=64300,
- ["shindageshshindothebrew"]=64300,
- ["shindageshsindot"]=64301,
- ["shindageshsindothebrew"]=64301,
- ["shindothebrew"]=1473,
- ["shinhebrew"]=1513,
- ["shinshindot"]=64298,
- ["shinshindothebrew"]=64298,
- ["shinsindot"]=64299,
- ["shinsindothebrew"]=64299,
- ["shook"]=642,
- ["sigma"]=963,
- ["sigma1"]=962,
- ["sigmafinal"]=962,
- ["sigmalunatesymbolgreek"]=1010,
- ["sihiragana"]=12375,
- ["sikatakana"]=12471,
- ["sikatakanahalfwidth"]=65404,
- ["siluqhebrew"]=1469,
- ["siluqlefthebrew"]=1469,
- ["similar"]=8764,
- ["sindothebrew"]=1474,
- ["siosacirclekorean"]=12916,
- ["siosaparenkorean"]=12820,
- ["sioscieuckorean"]=12670,
- ["sioscirclekorean"]=12902,
- ["sioskiyeokkorean"]=12666,
- ["sioskorean"]=12613,
- ["siosnieunkorean"]=12667,
- ["siosparenkorean"]=12806,
- ["siospieupkorean"]=12669,
- ["siostikeutkorean"]=12668,
- ["six"]=54,
- ["sixarabic"]=1638,
- ["sixbengali"]=2540,
- ["sixcircle"]=9317,
- ["sixcircleinversesansserif"]=10127,
- ["sixdeva"]=2412,
- ["sixgujarati"]=2796,
- ["sixgurmukhi"]=2668,
- ["sixhackarabic"]=1638,
- ["sixhangzhou"]=12326,
- ["sixideographicparen"]=12837,
- ["sixinferior"]=8326,
- ["sixmonospace"]=65302,
- ["sixparen"]=9337,
- ["sixperiod"]=9357,
- ["sixpersian"]=1782,
- ["sixroman"]=8565,
- ["sixsuperior"]=8310,
- ["sixteencircle"]=9327,
- ["sixteencurrencydenominatorbengali"]=2553,
- ["sixteenparen"]=9347,
- ["sixteenperiod"]=9367,
- ["sixthai"]=3670,
- ["slash"]=47,
- ["slashmonospace"]=65295,
- ["slong"]=383,
- ["slongdotaccent"]=7835,
- ["smileface"]=9786,
- ["smonospace"]=65363,
- ["sofpasuqhebrew"]=1475,
- ["softhyphen"]=173,
- ["softsigncyrillic"]=1100,
- ["sohiragana"]=12381,
- ["sokatakana"]=12477,
- ["sokatakanahalfwidth"]=65407,
- ["soliduslongoverlaycmb"]=824,
- ["solidusshortoverlaycmb"]=823,
- ["sorusithai"]=3625,
- ["sosalathai"]=3624,
- ["sosothai"]=3595,
- ["sosuathai"]=3626,
- ["space"]=32,
- ["spacehackarabic"]=32,
- ["spade"]=9824,
- ["spadesuitblack"]=9824,
- ["spadesuitwhite"]=9828,
- ["sparen"]=9390,
- ["squarebelowcmb"]=827,
- ["squarecc"]=13252,
- ["squarecm"]=13213,
- ["squarediagonalcrosshatchfill"]=9641,
- ["squarehorizontalfill"]=9636,
- ["squarekg"]=13199,
- ["squarekm"]=13214,
- ["squarekmcapital"]=13262,
- ["squareln"]=13265,
- ["squarelog"]=13266,
- ["squaremg"]=13198,
- ["squaremil"]=13269,
- ["squaremm"]=13212,
- ["squaremsquared"]=13217,
- ["squareorthogonalcrosshatchfill"]=9638,
- ["squareupperlefttolowerrightfill"]=9639,
- ["squareupperrighttolowerleftfill"]=9640,
- ["squareverticalfill"]=9637,
- ["squarewhitewithsmallblack"]=9635,
- ["srsquare"]=13275,
- ["ssabengali"]=2487,
- ["ssadeva"]=2359,
- ["ssagujarati"]=2743,
- ["ssangcieuckorean"]=12617,
- ["ssanghieuhkorean"]=12677,
- ["ssangieungkorean"]=12672,
- ["ssangkiyeokkorean"]=12594,
- ["ssangnieunkorean"]=12645,
- ["ssangpieupkorean"]=12611,
- ["ssangsioskorean"]=12614,
- ["ssangtikeutkorean"]=12600,
- ["sterling"]=163,
- ["sterlingmonospace"]=65505,
- ["strokelongoverlaycmb"]=822,
- ["strokeshortoverlaycmb"]=821,
- ["subset"]=8834,
- ["subsetnotequal"]=8842,
- ["subsetorequal"]=8838,
- ["succeeds"]=8827,
- ["suchthat"]=8715,
- ["suhiragana"]=12377,
- ["sukatakana"]=12473,
- ["sukatakanahalfwidth"]=65405,
- ["sukunarabic"]=1618,
- ["summation"]=8721,
- ["sun"]=9788,
- ["superset"]=8835,
- ["supersetnotequal"]=8843,
- ["supersetorequal"]=8839,
- ["svsquare"]=13276,
- ["syouwaerasquare"]=13180,
- ["t"]=116,
- ["tabengali"]=2468,
- ["tackdown"]=8868,
- ["tackleft"]=8867,
- ["tadeva"]=2340,
- ["tagujarati"]=2724,
- ["tagurmukhi"]=2596,
- ["taharabic"]=1591,
- ["tahfinalarabic"]=65218,
- ["tahinitialarabic"]=65219,
- ["tahiragana"]=12383,
- ["tahmedialarabic"]=65220,
- ["taisyouerasquare"]=13181,
- ["takatakana"]=12479,
- ["takatakanahalfwidth"]=65408,
- ["tatweelarabic"]=1600,
- ["tau"]=964,
- ["tav"]=1514,
- ["tavdages"]=64330,
- ["tavdagesh"]=64330,
- ["tavdageshhebrew"]=64330,
- ["tavhebrew"]=1514,
- ["tbar"]=359,
- ["tbopomofo"]=12554,
- ["tcaron"]=357,
- ["tccurl"]=680,
- ["tcedilla"]=355,
- ["tcheharabic"]=1670,
- ["tchehfinalarabic"]=64379,
- ["tchehinitialarabic"]=64380,
- ["tchehmedialarabic"]=64381,
- ["tchehmeeminitialarabic"]=64380,
- ["tcircle"]=9443,
- ["tcircumflexbelow"]=7793,
- ["tcommaaccent"]=355,
- ["tdieresis"]=7831,
- ["tdotaccent"]=7787,
- ["tdotbelow"]=7789,
- ["tecyrillic"]=1090,
- ["tedescendercyrillic"]=1197,
- ["teharabic"]=1578,
- ["tehfinalarabic"]=65174,
- ["tehhahinitialarabic"]=64674,
- ["tehhahisolatedarabic"]=64524,
- ["tehinitialarabic"]=65175,
- ["tehiragana"]=12390,
- ["tehjeeminitialarabic"]=64673,
- ["tehjeemisolatedarabic"]=64523,
- ["tehmarbutaarabic"]=1577,
- ["tehmarbutafinalarabic"]=65172,
- ["tehmedialarabic"]=65176,
- ["tehmeeminitialarabic"]=64676,
- ["tehmeemisolatedarabic"]=64526,
- ["tehnoonfinalarabic"]=64627,
- ["tekatakana"]=12486,
- ["tekatakanahalfwidth"]=65411,
- ["telephone"]=8481,
- ["telephoneblack"]=9742,
- ["telishagedolahebrew"]=1440,
- ["telishaqetanahebrew"]=1449,
- ["tencircle"]=9321,
- ["tenideographicparen"]=12841,
- ["tenparen"]=9341,
- ["tenperiod"]=9361,
- ["tenroman"]=8569,
- ["tesh"]=679,
- ["tet"]=1496,
- ["tetdagesh"]=64312,
- ["tetdageshhebrew"]=64312,
- ["tethebrew"]=1496,
- ["tetsecyrillic"]=1205,
- ["tevirhebrew"]=1435,
- ["tevirlefthebrew"]=1435,
- ["thabengali"]=2469,
- ["thadeva"]=2341,
- ["thagujarati"]=2725,
- ["thagurmukhi"]=2597,
- ["thalarabic"]=1584,
- ["thalfinalarabic"]=65196,
- ["thanthakhatthai"]=3660,
- ["theharabic"]=1579,
- ["thehfinalarabic"]=65178,
- ["thehinitialarabic"]=65179,
- ["thehmedialarabic"]=65180,
- ["thereexists"]=8707,
- ["therefore"]=8756,
- ["theta"]=952,
- ["theta1"]=977,
- ["thetasymbolgreek"]=977,
- ["thieuthacirclekorean"]=12921,
- ["thieuthaparenkorean"]=12825,
- ["thieuthcirclekorean"]=12907,
- ["thieuthkorean"]=12620,
- ["thieuthparenkorean"]=12811,
- ["thirteencircle"]=9324,
- ["thirteenparen"]=9344,
- ["thirteenperiod"]=9364,
- ["thonangmonthothai"]=3601,
- ["thook"]=429,
- ["thophuthaothai"]=3602,
- ["thorn"]=254,
- ["thothahanthai"]=3607,
- ["thothanthai"]=3600,
- ["thothongthai"]=3608,
- ["thothungthai"]=3606,
- ["thousandcyrillic"]=1154,
- ["thousandsseparatorarabic"]=1644,
- ["thousandsseparatorpersian"]=1644,
- ["three"]=51,
- ["threearabic"]=1635,
- ["threebengali"]=2537,
- ["threecircle"]=9314,
- ["threecircleinversesansserif"]=10124,
- ["threedeva"]=2409,
- ["threeeighths"]=8540,
- ["threegujarati"]=2793,
- ["threegurmukhi"]=2665,
- ["threehackarabic"]=1635,
- ["threehangzhou"]=12323,
- ["threeideographicparen"]=12834,
- ["threeinferior"]=8323,
- ["threemonospace"]=65299,
- ["threenumeratorbengali"]=2550,
- ["threeparen"]=9334,
- ["threeperiod"]=9354,
- ["threepersian"]=1779,
- ["threequarters"]=190,
- ["threeroman"]=8562,
- ["threesuperior"]=179,
- ["threethai"]=3667,
- ["thzsquare"]=13204,
- ["tihiragana"]=12385,
- ["tikatakana"]=12481,
- ["tikatakanahalfwidth"]=65409,
- ["tikeutacirclekorean"]=12912,
- ["tikeutaparenkorean"]=12816,
- ["tikeutcirclekorean"]=12898,
- ["tikeutkorean"]=12599,
- ["tikeutparenkorean"]=12802,
- ["tilde"]=732,
- ["tildebelowcmb"]=816,
- ["tildecmb"]=771,
- ["tildecomb"]=771,
- ["tildedoublecmb"]=864,
- ["tildeoperator"]=8764,
- ["tildeoverlaycmb"]=820,
- ["tildeverticalcmb"]=830,
- ["timescircle"]=8855,
- ["tipehahebrew"]=1430,
- ["tipehalefthebrew"]=1430,
- ["tippigurmukhi"]=2672,
- ["titlocyrilliccmb"]=1155,
- ["tiwnarmenian"]=1407,
- ["tlinebelow"]=7791,
- ["tmonospace"]=65364,
- ["toarmenian"]=1385,
- ["tohiragana"]=12392,
- ["tokatakana"]=12488,
- ["tokatakanahalfwidth"]=65412,
- ["tonebarextrahighmod"]=741,
- ["tonebarextralowmod"]=745,
- ["tonebarhighmod"]=742,
- ["tonebarlowmod"]=744,
- ["tonebarmidmod"]=743,
- ["tonefive"]=445,
- ["tonesix"]=389,
- ["tonetwo"]=424,
- ["tonos"]=900,
- ["tonsquare"]=13095,
- ["topatakthai"]=3599,
- ["tortoiseshellbracketleft"]=12308,
- ["tortoiseshellbracketleftsmall"]=65117,
- ["tortoiseshellbracketleftvertical"]=65081,
- ["tortoiseshellbracketright"]=12309,
- ["tortoiseshellbracketrightsmall"]=65118,
- ["tortoiseshellbracketrightvertical"]=65082,
- ["totaothai"]=3605,
- ["tpalatalhook"]=427,
- ["tparen"]=9391,
- ["trademark"]=8482,
- ["tretroflexhook"]=648,
- ["triagdn"]=9660,
- ["triaglf"]=9668,
- ["triagrt"]=9658,
- ["triagup"]=9650,
- ["ts"]=678,
- ["tsadi"]=1510,
- ["tsadidagesh"]=64326,
- ["tsadidageshhebrew"]=64326,
- ["tsadihebrew"]=1510,
- ["tsecyrillic"]=1094,
- ["tsere"]=1461,
- ["tsere12"]=1461,
- ["tsere1e"]=1461,
- ["tsere2b"]=1461,
- ["tserehebrew"]=1461,
- ["tserenarrowhebrew"]=1461,
- ["tserequarterhebrew"]=1461,
- ["tserewidehebrew"]=1461,
- ["tshecyrillic"]=1115,
- ["ttabengali"]=2463,
- ["ttadeva"]=2335,
- ["ttagujarati"]=2719,
- ["ttagurmukhi"]=2591,
- ["tteharabic"]=1657,
- ["ttehfinalarabic"]=64359,
- ["ttehinitialarabic"]=64360,
- ["ttehmedialarabic"]=64361,
- ["tthabengali"]=2464,
- ["tthadeva"]=2336,
- ["tthagujarati"]=2720,
- ["tthagurmukhi"]=2592,
- ["tturned"]=647,
- ["tuhiragana"]=12388,
- ["tukatakana"]=12484,
- ["tukatakanahalfwidth"]=65410,
- ["tusmallhiragana"]=12387,
- ["tusmallkatakana"]=12483,
- ["tusmallkatakanahalfwidth"]=65391,
- ["twelvecircle"]=9323,
- ["twelveparen"]=9343,
- ["twelveperiod"]=9363,
- ["twelveroman"]=8571,
- ["twentycircle"]=9331,
- ["twentyparen"]=9351,
- ["twentyperiod"]=9371,
- ["two"]=50,
- ["twoarabic"]=1634,
- ["twobengali"]=2536,
- ["twocircle"]=9313,
- ["twocircleinversesansserif"]=10123,
- ["twodeva"]=2408,
- ["twodotenleader"]=8229,
- ["twodotleader"]=8229,
- ["twodotleadervertical"]=65072,
- ["twogujarati"]=2792,
- ["twogurmukhi"]=2664,
- ["twohackarabic"]=1634,
- ["twohangzhou"]=12322,
- ["twoideographicparen"]=12833,
- ["twoinferior"]=8322,
- ["twomonospace"]=65298,
- ["twonumeratorbengali"]=2549,
- ["twoparen"]=9333,
- ["twoperiod"]=9353,
- ["twopersian"]=1778,
- ["tworoman"]=8561,
- ["twostroke"]=443,
- ["twosuperior"]=178,
- ["twothai"]=3666,
- ["twothirds"]=8532,
- ["u"]=117,
- ["uacute"]=250,
- ["ubar"]=649,
- ["ubengali"]=2441,
- ["ubopomofo"]=12584,
- ["ubreve"]=365,
- ["ucaron"]=468,
- ["ucircle"]=9444,
- ["ucircumflex"]=251,
- ["ucircumflexbelow"]=7799,
- ["ucyrillic"]=1091,
- ["udattadeva"]=2385,
- ["udblacute"]=369,
- ["udblgrave"]=533,
- ["udeva"]=2313,
- ["udieresis"]=252,
- ["udieresisacute"]=472,
- ["udieresisbelow"]=7795,
- ["udieresiscaron"]=474,
- ["udieresiscyrillic"]=1265,
- ["udieresisgrave"]=476,
- ["udieresismacron"]=470,
- ["udotbelow"]=7909,
- ["ugrave"]=249,
- ["ugujarati"]=2697,
- ["ugurmukhi"]=2569,
- ["uhiragana"]=12358,
- ["uhookabove"]=7911,
- ["uhorn"]=432,
- ["uhornacute"]=7913,
- ["uhorndotbelow"]=7921,
- ["uhorngrave"]=7915,
- ["uhornhookabove"]=7917,
- ["uhorntilde"]=7919,
- ["uhungarumlaut"]=369,
- ["uhungarumlautcyrillic"]=1267,
- ["uinvertedbreve"]=535,
- ["ukatakana"]=12454,
- ["ukatakanahalfwidth"]=65395,
- ["ukcyrillic"]=1145,
- ["ukorean"]=12636,
- ["umacron"]=363,
- ["umacroncyrillic"]=1263,
- ["umacrondieresis"]=7803,
- ["umatragurmukhi"]=2625,
- ["umonospace"]=65365,
- ["underscore"]=95,
- ["underscoredbl"]=8215,
- ["underscoremonospace"]=65343,
- ["underscorevertical"]=65075,
- ["underscorewavy"]=65103,
- ["union"]=8746,
- ["universal"]=8704,
- ["uogonek"]=371,
- ["uparen"]=9392,
- ["upblock"]=9600,
- ["upperdothebrew"]=1476,
- ["upsilon"]=965,
- ["upsilondieresis"]=971,
- ["upsilondieresistonos"]=944,
- ["upsilonlatin"]=650,
- ["upsilontonos"]=973,
- ["uptackbelowcmb"]=797,
- ["uptackmod"]=724,
- ["uragurmukhi"]=2675,
- ["uring"]=367,
- ["ushortcyrillic"]=1118,
- ["usmallhiragana"]=12357,
- ["usmallkatakana"]=12453,
- ["usmallkatakanahalfwidth"]=65385,
- ["ustraightcyrillic"]=1199,
- ["ustraightstrokecyrillic"]=1201,
- ["utilde"]=361,
- ["utildeacute"]=7801,
- ["utildebelow"]=7797,
- ["uubengali"]=2442,
- ["uudeva"]=2314,
- ["uugujarati"]=2698,
- ["uugurmukhi"]=2570,
- ["uumatragurmukhi"]=2626,
- ["uuvowelsignbengali"]=2498,
- ["uuvowelsigndeva"]=2370,
- ["uuvowelsigngujarati"]=2754,
- ["uvowelsignbengali"]=2497,
- ["uvowelsigndeva"]=2369,
- ["uvowelsigngujarati"]=2753,
- ["v"]=118,
- ["vadeva"]=2357,
- ["vagujarati"]=2741,
- ["vagurmukhi"]=2613,
- ["vakatakana"]=12535,
- ["vav"]=1493,
- ["vavdagesh"]=64309,
- ["vavdagesh65"]=64309,
- ["vavdageshhebrew"]=64309,
- ["vavhebrew"]=1493,
- ["vavholam"]=64331,
- ["vavholamhebrew"]=64331,
- ["vavvavhebrew"]=1520,
- ["vavyodhebrew"]=1521,
- ["vcircle"]=9445,
- ["vdotbelow"]=7807,
- ["vecyrillic"]=1074,
- ["veharabic"]=1700,
- ["vehfinalarabic"]=64363,
- ["vehinitialarabic"]=64364,
- ["vehmedialarabic"]=64365,
- ["vekatakana"]=12537,
- ["venus"]=9792,
- ["verticalbar"]=124,
- ["verticallineabovecmb"]=781,
- ["verticallinebelowcmb"]=809,
- ["verticallinelowmod"]=716,
- ["verticallinemod"]=712,
- ["vewarmenian"]=1406,
- ["vhook"]=651,
- ["vikatakana"]=12536,
- ["viramabengali"]=2509,
- ["viramadeva"]=2381,
- ["viramagujarati"]=2765,
- ["visargabengali"]=2435,
- ["visargadeva"]=2307,
- ["visargagujarati"]=2691,
- ["vmonospace"]=65366,
- ["voarmenian"]=1400,
- ["voicediterationhiragana"]=12446,
- ["voicediterationkatakana"]=12542,
- ["voicedmarkkana"]=12443,
- ["voicedmarkkanahalfwidth"]=65438,
- ["vokatakana"]=12538,
- ["vparen"]=9393,
- ["vtilde"]=7805,
- ["vturned"]=652,
- ["vuhiragana"]=12436,
- ["vukatakana"]=12532,
- ["w"]=119,
- ["wacute"]=7811,
- ["waekorean"]=12633,
- ["wahiragana"]=12431,
- ["wakatakana"]=12527,
- ["wakatakanahalfwidth"]=65436,
- ["wakorean"]=12632,
- ["wasmallhiragana"]=12430,
- ["wasmallkatakana"]=12526,
- ["wattosquare"]=13143,
- ["wavedash"]=12316,
- ["wavyunderscorevertical"]=65076,
- ["wawarabic"]=1608,
- ["wawfinalarabic"]=65262,
- ["wawhamzaabovearabic"]=1572,
- ["wawhamzaabovefinalarabic"]=65158,
- ["wbsquare"]=13277,
- ["wcircle"]=9446,
- ["wcircumflex"]=373,
- ["wdieresis"]=7813,
- ["wdotaccent"]=7815,
- ["wdotbelow"]=7817,
- ["wehiragana"]=12433,
- ["weierstrass"]=8472,
- ["wekatakana"]=12529,
- ["wekorean"]=12638,
- ["weokorean"]=12637,
- ["wgrave"]=7809,
- ["whitebullet"]=9702,
- ["whitecircle"]=9675,
- ["whitecircleinverse"]=9689,
- ["whitecornerbracketleft"]=12302,
- ["whitecornerbracketleftvertical"]=65091,
- ["whitecornerbracketright"]=12303,
- ["whitecornerbracketrightvertical"]=65092,
- ["whitediamond"]=9671,
- ["whitediamondcontainingblacksmalldiamond"]=9672,
- ["whitedownpointingsmalltriangle"]=9663,
- ["whitedownpointingtriangle"]=9661,
- ["whiteleftpointingsmalltriangle"]=9667,
- ["whiteleftpointingtriangle"]=9665,
- ["whitelenticularbracketleft"]=12310,
- ["whitelenticularbracketright"]=12311,
- ["whiterightpointingsmalltriangle"]=9657,
- ["whiterightpointingtriangle"]=9655,
- ["whitesmallsquare"]=9643,
- ["whitesmilingface"]=9786,
- ["whitesquare"]=9633,
- ["whitestar"]=9734,
- ["whitetelephone"]=9743,
- ["whitetortoiseshellbracketleft"]=12312,
- ["whitetortoiseshellbracketright"]=12313,
- ["whiteuppointingsmalltriangle"]=9653,
- ["whiteuppointingtriangle"]=9651,
- ["wihiragana"]=12432,
- ["wikatakana"]=12528,
- ["wikorean"]=12639,
- ["wmonospace"]=65367,
- ["wohiragana"]=12434,
- ["wokatakana"]=12530,
- ["wokatakanahalfwidth"]=65382,
- ["won"]=8361,
- ["wonmonospace"]=65510,
- ["wowaenthai"]=3623,
- ["wparen"]=9394,
- ["wring"]=7832,
- ["wsuperior"]=695,
- ["wturned"]=653,
- ["wynn"]=447,
- ["x"]=120,
- ["xabovecmb"]=829,
- ["xbopomofo"]=12562,
- ["xcircle"]=9447,
- ["xdieresis"]=7821,
- ["xdotaccent"]=7819,
- ["xeharmenian"]=1389,
- ["xi"]=958,
- ["xmonospace"]=65368,
- ["xparen"]=9395,
- ["xsuperior"]=739,
- ["y"]=121,
- ["yaadosquare"]=13134,
- ["yabengali"]=2479,
- ["yacute"]=253,
- ["yadeva"]=2351,
- ["yaekorean"]=12626,
- ["yagujarati"]=2735,
- ["yagurmukhi"]=2607,
- ["yahiragana"]=12420,
- ["yakatakana"]=12516,
- ["yakatakanahalfwidth"]=65428,
- ["yakorean"]=12625,
- ["yamakkanthai"]=3662,
- ["yasmallhiragana"]=12419,
- ["yasmallkatakana"]=12515,
- ["yasmallkatakanahalfwidth"]=65388,
- ["yatcyrillic"]=1123,
- ["ycircle"]=9448,
- ["ycircumflex"]=375,
- ["ydieresis"]=255,
- ["ydotaccent"]=7823,
- ["ydotbelow"]=7925,
- ["yeharabic"]=1610,
- ["yehbarreearabic"]=1746,
- ["yehbarreefinalarabic"]=64431,
- ["yehfinalarabic"]=65266,
- ["yehhamzaabovearabic"]=1574,
- ["yehhamzaabovefinalarabic"]=65162,
- ["yehhamzaaboveinitialarabic"]=65163,
- ["yehhamzaabovemedialarabic"]=65164,
- ["yehinitialarabic"]=65267,
- ["yehmedialarabic"]=65268,
- ["yehmeeminitialarabic"]=64733,
- ["yehmeemisolatedarabic"]=64600,
- ["yehnoonfinalarabic"]=64660,
- ["yehthreedotsbelowarabic"]=1745,
- ["yekorean"]=12630,
- ["yen"]=165,
- ["yenmonospace"]=65509,
- ["yeokorean"]=12629,
- ["yeorinhieuhkorean"]=12678,
- ["yerahbenyomohebrew"]=1450,
- ["yerahbenyomolefthebrew"]=1450,
- ["yericyrillic"]=1099,
- ["yerudieresiscyrillic"]=1273,
- ["yesieungkorean"]=12673,
- ["yesieungpansioskorean"]=12675,
- ["yesieungsioskorean"]=12674,
- ["yetivhebrew"]=1434,
- ["ygrave"]=7923,
- ["yhook"]=436,
- ["yhookabove"]=7927,
- ["yiarmenian"]=1397,
- ["yicyrillic"]=1111,
- ["yikorean"]=12642,
- ["yinyang"]=9775,
- ["yiwnarmenian"]=1410,
- ["ymonospace"]=65369,
- ["yod"]=1497,
- ["yoddagesh"]=64313,
- ["yoddageshhebrew"]=64313,
- ["yodhebrew"]=1497,
- ["yodyodhebrew"]=1522,
- ["yodyodpatahhebrew"]=64287,
- ["yohiragana"]=12424,
- ["yoikorean"]=12681,
- ["yokatakana"]=12520,
- ["yokatakanahalfwidth"]=65430,
- ["yokorean"]=12635,
- ["yosmallhiragana"]=12423,
- ["yosmallkatakana"]=12519,
- ["yosmallkatakanahalfwidth"]=65390,
- ["yotgreek"]=1011,
- ["yoyaekorean"]=12680,
- ["yoyakorean"]=12679,
- ["yoyakthai"]=3618,
- ["yoyingthai"]=3597,
- ["yparen"]=9396,
- ["ypogegrammeni"]=890,
- ["ypogegrammenigreekcmb"]=837,
- ["yr"]=422,
- ["yring"]=7833,
- ["ysuperior"]=696,
- ["ytilde"]=7929,
- ["yturned"]=654,
- ["yuhiragana"]=12422,
- ["yuikorean"]=12684,
- ["yukatakana"]=12518,
- ["yukatakanahalfwidth"]=65429,
- ["yukorean"]=12640,
- ["yusbigcyrillic"]=1131,
- ["yusbigiotifiedcyrillic"]=1133,
- ["yuslittlecyrillic"]=1127,
- ["yuslittleiotifiedcyrillic"]=1129,
- ["yusmallhiragana"]=12421,
- ["yusmallkatakana"]=12517,
- ["yusmallkatakanahalfwidth"]=65389,
- ["yuyekorean"]=12683,
- ["yuyeokorean"]=12682,
- ["yyabengali"]=2527,
- ["yyadeva"]=2399,
- ["z"]=122,
- ["zaarmenian"]=1382,
- ["zacute"]=378,
- ["zadeva"]=2395,
- ["zagurmukhi"]=2651,
- ["zaharabic"]=1592,
- ["zahfinalarabic"]=65222,
- ["zahinitialarabic"]=65223,
- ["zahiragana"]=12374,
- ["zahmedialarabic"]=65224,
- ["zainarabic"]=1586,
- ["zainfinalarabic"]=65200,
- ["zakatakana"]=12470,
- ["zaqefgadolhebrew"]=1429,
- ["zaqefqatanhebrew"]=1428,
- ["zarqahebrew"]=1432,
- ["zayin"]=1494,
- ["zayindagesh"]=64310,
- ["zayindageshhebrew"]=64310,
- ["zayinhebrew"]=1494,
- ["zbopomofo"]=12567,
- ["zcaron"]=382,
- ["zcircle"]=9449,
- ["zcircumflex"]=7825,
- ["zcurl"]=657,
- ["zdot"]=380,
- ["zdotaccent"]=380,
- ["zdotbelow"]=7827,
- ["zecyrillic"]=1079,
- ["zedescendercyrillic"]=1177,
- ["zedieresiscyrillic"]=1247,
- ["zehiragana"]=12380,
- ["zekatakana"]=12476,
- ["zero"]=48,
- ["zeroarabic"]=1632,
- ["zerobengali"]=2534,
- ["zerodeva"]=2406,
- ["zerogujarati"]=2790,
- ["zerogurmukhi"]=2662,
- ["zerohackarabic"]=1632,
- ["zeroinferior"]=8320,
- ["zeromonospace"]=65296,
- ["zeropersian"]=1776,
- ["zerosuperior"]=8304,
- ["zerothai"]=3664,
- ["zerowidthjoiner"]=65279,
- ["zerowidthnonjoiner"]=8204,
- ["zerowidthspace"]=8203,
- ["zeta"]=950,
- ["zhbopomofo"]=12563,
- ["zhearmenian"]=1386,
- ["zhebrevecyrillic"]=1218,
- ["zhecyrillic"]=1078,
- ["zhedescendercyrillic"]=1175,
- ["zhedieresiscyrillic"]=1245,
- ["zihiragana"]=12376,
- ["zikatakana"]=12472,
- ["zinorhebrew"]=1454,
- ["zlinebelow"]=7829,
- ["zmonospace"]=65370,
- ["zohiragana"]=12382,
- ["zokatakana"]=12478,
- ["zparen"]=9397,
- ["zretroflexhook"]=656,
- ["zstroke"]=438,
- ["zuhiragana"]=12378,
- ["zukatakana"]=12474,
-}
+if not modules then modules = { } end modules ['font-age'] = {
+ version = 1.001,
+ comment = "companion to luatex-fonts.lua",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "derived from http://www.adobe.com/devnet/opentype/archives/glyphlist.txt",
+ original = "Adobe Glyph List, version 2.0, September 20, 2002",
+ dataonly = true,
+}
+
+if context then
+ logs.report("fatal error","this module is not for context")
+ os.exit()
+end
+
+return { -- generated: inspect(fonts.encodings.agl.unicodes)
+ ["A"]=65,
+ ["AE"]=198,
+ ["AEacute"]=508,
+ ["AEmacron"]=482,
+ ["Aacute"]=193,
+ ["Abreve"]=258,
+ ["Abreveacute"]=7854,
+ ["Abrevecyrillic"]=1232,
+ ["Abrevedotbelow"]=7862,
+ ["Abrevegrave"]=7856,
+ ["Abrevehookabove"]=7858,
+ ["Abrevetilde"]=7860,
+ ["Acaron"]=461,
+ ["Acircle"]=9398,
+ ["Acircumflex"]=194,
+ ["Acircumflexacute"]=7844,
+ ["Acircumflexdotbelow"]=7852,
+ ["Acircumflexgrave"]=7846,
+ ["Acircumflexhookabove"]=7848,
+ ["Acircumflextilde"]=7850,
+ ["Acyrillic"]=1040,
+ ["Adblgrave"]=512,
+ ["Adieresis"]=196,
+ ["Adieresiscyrillic"]=1234,
+ ["Adieresismacron"]=478,
+ ["Adotbelow"]=7840,
+ ["Adotmacron"]=480,
+ ["Agrave"]=192,
+ ["Ahookabove"]=7842,
+ ["Aiecyrillic"]=1236,
+ ["Ainvertedbreve"]=514,
+ ["Alpha"]=913,
+ ["Alphatonos"]=902,
+ ["Amacron"]=256,
+ ["Amonospace"]=65313,
+ ["Aogonek"]=260,
+ ["Aring"]=197,
+ ["Aringacute"]=506,
+ ["Aringbelow"]=7680,
+ ["Atilde"]=195,
+ ["Aybarmenian"]=1329,
+ ["B"]=66,
+ ["Bcircle"]=9399,
+ ["Bdotaccent"]=7682,
+ ["Bdotbelow"]=7684,
+ ["Becyrillic"]=1041,
+ ["Benarmenian"]=1330,
+ ["Beta"]=914,
+ ["Bhook"]=385,
+ ["Blinebelow"]=7686,
+ ["Bmonospace"]=65314,
+ ["Btopbar"]=386,
+ ["C"]=67,
+ ["Caarmenian"]=1342,
+ ["Cacute"]=262,
+ ["Ccaron"]=268,
+ ["Ccedilla"]=199,
+ ["Ccedillaacute"]=7688,
+ ["Ccircle"]=9400,
+ ["Ccircumflex"]=264,
+ ["Cdot"]=266,
+ ["Cdotaccent"]=266,
+ ["Chaarmenian"]=1353,
+ ["Cheabkhasiancyrillic"]=1212,
+ ["Checyrillic"]=1063,
+ ["Chedescenderabkhasiancyrillic"]=1214,
+ ["Chedescendercyrillic"]=1206,
+ ["Chedieresiscyrillic"]=1268,
+ ["Cheharmenian"]=1347,
+ ["Chekhakassiancyrillic"]=1227,
+ ["Cheverticalstrokecyrillic"]=1208,
+ ["Chi"]=935,
+ ["Chook"]=391,
+ ["Cmonospace"]=65315,
+ ["Coarmenian"]=1361,
+ ["D"]=68,
+ ["DZ"]=497,
+ ["DZcaron"]=452,
+ ["Daarmenian"]=1332,
+ ["Dafrican"]=393,
+ ["Dcaron"]=270,
+ ["Dcedilla"]=7696,
+ ["Dcircle"]=9401,
+ ["Dcircumflexbelow"]=7698,
+ ["Dcroat"]=272,
+ ["Ddotaccent"]=7690,
+ ["Ddotbelow"]=7692,
+ ["Decyrillic"]=1044,
+ ["Deicoptic"]=1006,
+ ["Delta"]=8710,
+ ["Deltagreek"]=916,
+ ["Dhook"]=394,
+ ["Digammagreek"]=988,
+ ["Djecyrillic"]=1026,
+ ["Dlinebelow"]=7694,
+ ["Dmonospace"]=65316,
+ ["Dslash"]=272,
+ ["Dtopbar"]=395,
+ ["Dz"]=498,
+ ["Dzcaron"]=453,
+ ["Dzeabkhasiancyrillic"]=1248,
+ ["Dzecyrillic"]=1029,
+ ["Dzhecyrillic"]=1039,
+ ["E"]=69,
+ ["Eacute"]=201,
+ ["Ebreve"]=276,
+ ["Ecaron"]=282,
+ ["Ecedillabreve"]=7708,
+ ["Echarmenian"]=1333,
+ ["Ecircle"]=9402,
+ ["Ecircumflex"]=202,
+ ["Ecircumflexacute"]=7870,
+ ["Ecircumflexbelow"]=7704,
+ ["Ecircumflexdotbelow"]=7878,
+ ["Ecircumflexgrave"]=7872,
+ ["Ecircumflexhookabove"]=7874,
+ ["Ecircumflextilde"]=7876,
+ ["Ecyrillic"]=1028,
+ ["Edblgrave"]=516,
+ ["Edieresis"]=203,
+ ["Edot"]=278,
+ ["Edotaccent"]=278,
+ ["Edotbelow"]=7864,
+ ["Efcyrillic"]=1060,
+ ["Egrave"]=200,
+ ["Eharmenian"]=1335,
+ ["Ehookabove"]=7866,
+ ["Eightroman"]=8551,
+ ["Einvertedbreve"]=518,
+ ["Eiotifiedcyrillic"]=1124,
+ ["Elcyrillic"]=1051,
+ ["Elevenroman"]=8554,
+ ["Emacron"]=274,
+ ["Emacronacute"]=7702,
+ ["Emacrongrave"]=7700,
+ ["Emcyrillic"]=1052,
+ ["Emonospace"]=65317,
+ ["Encyrillic"]=1053,
+ ["Endescendercyrillic"]=1186,
+ ["Eng"]=330,
+ ["Enghecyrillic"]=1188,
+ ["Enhookcyrillic"]=1223,
+ ["Eogonek"]=280,
+ ["Eopen"]=400,
+ ["Epsilon"]=917,
+ ["Epsilontonos"]=904,
+ ["Ercyrillic"]=1056,
+ ["Ereversed"]=398,
+ ["Ereversedcyrillic"]=1069,
+ ["Escyrillic"]=1057,
+ ["Esdescendercyrillic"]=1194,
+ ["Esh"]=425,
+ ["Eta"]=919,
+ ["Etarmenian"]=1336,
+ ["Etatonos"]=905,
+ ["Eth"]=208,
+ ["Etilde"]=7868,
+ ["Etildebelow"]=7706,
+ ["Euro"]=8364,
+ ["Ezh"]=439,
+ ["Ezhcaron"]=494,
+ ["Ezhreversed"]=440,
+ ["F"]=70,
+ ["Fcircle"]=9403,
+ ["Fdotaccent"]=7710,
+ ["Feharmenian"]=1366,
+ ["Feicoptic"]=996,
+ ["Fhook"]=401,
+ ["Fitacyrillic"]=1138,
+ ["Fiveroman"]=8548,
+ ["Fmonospace"]=65318,
+ ["Fourroman"]=8547,
+ ["G"]=71,
+ ["GBsquare"]=13191,
+ ["Gacute"]=500,
+ ["Gamma"]=915,
+ ["Gammaafrican"]=404,
+ ["Gangiacoptic"]=1002,
+ ["Gbreve"]=286,
+ ["Gcaron"]=486,
+ ["Gcedilla"]=290,
+ ["Gcircle"]=9404,
+ ["Gcircumflex"]=284,
+ ["Gcommaaccent"]=290,
+ ["Gdot"]=288,
+ ["Gdotaccent"]=288,
+ ["Gecyrillic"]=1043,
+ ["Ghadarmenian"]=1346,
+ ["Ghemiddlehookcyrillic"]=1172,
+ ["Ghestrokecyrillic"]=1170,
+ ["Gheupturncyrillic"]=1168,
+ ["Ghook"]=403,
+ ["Gimarmenian"]=1331,
+ ["Gjecyrillic"]=1027,
+ ["Gmacron"]=7712,
+ ["Gmonospace"]=65319,
+ ["Gsmallhook"]=667,
+ ["Gstroke"]=484,
+ ["H"]=72,
+ ["H18533"]=9679,
+ ["H18543"]=9642,
+ ["H18551"]=9643,
+ ["H22073"]=9633,
+ ["HPsquare"]=13259,
+ ["Haabkhasiancyrillic"]=1192,
+ ["Hadescendercyrillic"]=1202,
+ ["Hardsigncyrillic"]=1066,
+ ["Hbar"]=294,
+ ["Hbrevebelow"]=7722,
+ ["Hcedilla"]=7720,
+ ["Hcircle"]=9405,
+ ["Hcircumflex"]=292,
+ ["Hdieresis"]=7718,
+ ["Hdotaccent"]=7714,
+ ["Hdotbelow"]=7716,
+ ["Hmonospace"]=65320,
+ ["Hoarmenian"]=1344,
+ ["Horicoptic"]=1000,
+ ["Hzsquare"]=13200,
+ ["I"]=73,
+ ["IAcyrillic"]=1071,
+ ["IJ"]=306,
+ ["IUcyrillic"]=1070,
+ ["Iacute"]=205,
+ ["Ibreve"]=300,
+ ["Icaron"]=463,
+ ["Icircle"]=9406,
+ ["Icircumflex"]=206,
+ ["Icyrillic"]=1030,
+ ["Idblgrave"]=520,
+ ["Idieresis"]=207,
+ ["Idieresisacute"]=7726,
+ ["Idieresiscyrillic"]=1252,
+ ["Idot"]=304,
+ ["Idotaccent"]=304,
+ ["Idotbelow"]=7882,
+ ["Iebrevecyrillic"]=1238,
+ ["Iecyrillic"]=1045,
+ ["Ifraktur"]=8465,
+ ["Igrave"]=204,
+ ["Ihookabove"]=7880,
+ ["Iicyrillic"]=1048,
+ ["Iinvertedbreve"]=522,
+ ["Iishortcyrillic"]=1049,
+ ["Imacron"]=298,
+ ["Imacroncyrillic"]=1250,
+ ["Imonospace"]=65321,
+ ["Iniarmenian"]=1339,
+ ["Iocyrillic"]=1025,
+ ["Iogonek"]=302,
+ ["Iota"]=921,
+ ["Iotaafrican"]=406,
+ ["Iotadieresis"]=938,
+ ["Iotatonos"]=906,
+ ["Istroke"]=407,
+ ["Itilde"]=296,
+ ["Itildebelow"]=7724,
+ ["Izhitsacyrillic"]=1140,
+ ["Izhitsadblgravecyrillic"]=1142,
+ ["J"]=74,
+ ["Jaarmenian"]=1345,
+ ["Jcircle"]=9407,
+ ["Jcircumflex"]=308,
+ ["Jecyrillic"]=1032,
+ ["Jheharmenian"]=1355,
+ ["Jmonospace"]=65322,
+ ["K"]=75,
+ ["KBsquare"]=13189,
+ ["KKsquare"]=13261,
+ ["Kabashkircyrillic"]=1184,
+ ["Kacute"]=7728,
+ ["Kacyrillic"]=1050,
+ ["Kadescendercyrillic"]=1178,
+ ["Kahookcyrillic"]=1219,
+ ["Kappa"]=922,
+ ["Kastrokecyrillic"]=1182,
+ ["Kaverticalstrokecyrillic"]=1180,
+ ["Kcaron"]=488,
+ ["Kcedilla"]=310,
+ ["Kcircle"]=9408,
+ ["Kcommaaccent"]=310,
+ ["Kdotbelow"]=7730,
+ ["Keharmenian"]=1364,
+ ["Kenarmenian"]=1343,
+ ["Khacyrillic"]=1061,
+ ["Kheicoptic"]=998,
+ ["Khook"]=408,
+ ["Kjecyrillic"]=1036,
+ ["Klinebelow"]=7732,
+ ["Kmonospace"]=65323,
+ ["Koppacyrillic"]=1152,
+ ["Koppagreek"]=990,
+ ["Ksicyrillic"]=1134,
+ ["L"]=76,
+ ["LJ"]=455,
+ ["Lacute"]=313,
+ ["Lambda"]=923,
+ ["Lcaron"]=317,
+ ["Lcedilla"]=315,
+ ["Lcircle"]=9409,
+ ["Lcircumflexbelow"]=7740,
+ ["Lcommaaccent"]=315,
+ ["Ldot"]=319,
+ ["Ldotaccent"]=319,
+ ["Ldotbelow"]=7734,
+ ["Ldotbelowmacron"]=7736,
+ ["Liwnarmenian"]=1340,
+ ["Lj"]=456,
+ ["Ljecyrillic"]=1033,
+ ["Llinebelow"]=7738,
+ ["Lmonospace"]=65324,
+ ["Lslash"]=321,
+ ["M"]=77,
+ ["MBsquare"]=13190,
+ ["Macute"]=7742,
+ ["Mcircle"]=9410,
+ ["Mdotaccent"]=7744,
+ ["Mdotbelow"]=7746,
+ ["Menarmenian"]=1348,
+ ["Mmonospace"]=65325,
+ ["Mturned"]=412,
+ ["Mu"]=924,
+ ["N"]=78,
+ ["NJ"]=458,
+ ["Nacute"]=323,
+ ["Ncaron"]=327,
+ ["Ncedilla"]=325,
+ ["Ncircle"]=9411,
+ ["Ncircumflexbelow"]=7754,
+ ["Ncommaaccent"]=325,
+ ["Ndotaccent"]=7748,
+ ["Ndotbelow"]=7750,
+ ["Nhookleft"]=413,
+ ["Nineroman"]=8552,
+ ["Nj"]=459,
+ ["Njecyrillic"]=1034,
+ ["Nlinebelow"]=7752,
+ ["Nmonospace"]=65326,
+ ["Nowarmenian"]=1350,
+ ["Ntilde"]=209,
+ ["Nu"]=925,
+ ["O"]=79,
+ ["OE"]=338,
+ ["Oacute"]=211,
+ ["Obarredcyrillic"]=1256,
+ ["Obarreddieresiscyrillic"]=1258,
+ ["Obreve"]=334,
+ ["Ocaron"]=465,
+ ["Ocenteredtilde"]=415,
+ ["Ocircle"]=9412,
+ ["Ocircumflex"]=212,
+ ["Ocircumflexacute"]=7888,
+ ["Ocircumflexdotbelow"]=7896,
+ ["Ocircumflexgrave"]=7890,
+ ["Ocircumflexhookabove"]=7892,
+ ["Ocircumflextilde"]=7894,
+ ["Ocyrillic"]=1054,
+ ["Odblacute"]=336,
+ ["Odblgrave"]=524,
+ ["Odieresis"]=214,
+ ["Odieresiscyrillic"]=1254,
+ ["Odotbelow"]=7884,
+ ["Ograve"]=210,
+ ["Oharmenian"]=1365,
+ ["Ohm"]=8486,
+ ["Ohookabove"]=7886,
+ ["Ohorn"]=416,
+ ["Ohornacute"]=7898,
+ ["Ohorndotbelow"]=7906,
+ ["Ohorngrave"]=7900,
+ ["Ohornhookabove"]=7902,
+ ["Ohorntilde"]=7904,
+ ["Ohungarumlaut"]=336,
+ ["Oi"]=418,
+ ["Oinvertedbreve"]=526,
+ ["Omacron"]=332,
+ ["Omacronacute"]=7762,
+ ["Omacrongrave"]=7760,
+ ["Omega"]=8486,
+ ["Omegacyrillic"]=1120,
+ ["Omegagreek"]=937,
+ ["Omegaroundcyrillic"]=1146,
+ ["Omegatitlocyrillic"]=1148,
+ ["Omegatonos"]=911,
+ ["Omicron"]=927,
+ ["Omicrontonos"]=908,
+ ["Omonospace"]=65327,
+ ["Oneroman"]=8544,
+ ["Oogonek"]=490,
+ ["Oogonekmacron"]=492,
+ ["Oopen"]=390,
+ ["Oslash"]=216,
+ ["Oslashacute"]=510,
+ ["Ostrokeacute"]=510,
+ ["Otcyrillic"]=1150,
+ ["Otilde"]=213,
+ ["Otildeacute"]=7756,
+ ["Otildedieresis"]=7758,
+ ["P"]=80,
+ ["Pacute"]=7764,
+ ["Pcircle"]=9413,
+ ["Pdotaccent"]=7766,
+ ["Pecyrillic"]=1055,
+ ["Peharmenian"]=1354,
+ ["Pemiddlehookcyrillic"]=1190,
+ ["Phi"]=934,
+ ["Phook"]=420,
+ ["Pi"]=928,
+ ["Piwrarmenian"]=1363,
+ ["Pmonospace"]=65328,
+ ["Psi"]=936,
+ ["Psicyrillic"]=1136,
+ ["Q"]=81,
+ ["Qcircle"]=9414,
+ ["Qmonospace"]=65329,
+ ["R"]=82,
+ ["Raarmenian"]=1356,
+ ["Racute"]=340,
+ ["Rcaron"]=344,
+ ["Rcedilla"]=342,
+ ["Rcircle"]=9415,
+ ["Rcommaaccent"]=342,
+ ["Rdblgrave"]=528,
+ ["Rdotaccent"]=7768,
+ ["Rdotbelow"]=7770,
+ ["Rdotbelowmacron"]=7772,
+ ["Reharmenian"]=1360,
+ ["Rfraktur"]=8476,
+ ["Rho"]=929,
+ ["Rinvertedbreve"]=530,
+ ["Rlinebelow"]=7774,
+ ["Rmonospace"]=65330,
+ ["Rsmallinverted"]=641,
+ ["Rsmallinvertedsuperior"]=694,
+ ["S"]=83,
+ ["SF010000"]=9484,
+ ["SF020000"]=9492,
+ ["SF030000"]=9488,
+ ["SF040000"]=9496,
+ ["SF050000"]=9532,
+ ["SF060000"]=9516,
+ ["SF070000"]=9524,
+ ["SF080000"]=9500,
+ ["SF090000"]=9508,
+ ["SF10000"]=9484,
+ ["SF100000"]=9472,
+ ["SF110000"]=9474,
+ ["SF190000"]=9569,
+ ["SF20000"]=9492,
+ ["SF200000"]=9570,
+ ["SF210000"]=9558,
+ ["SF220000"]=9557,
+ ["SF230000"]=9571,
+ ["SF240000"]=9553,
+ ["SF250000"]=9559,
+ ["SF260000"]=9565,
+ ["SF270000"]=9564,
+ ["SF280000"]=9563,
+ ["SF30000"]=9488,
+ ["SF360000"]=9566,
+ ["SF370000"]=9567,
+ ["SF380000"]=9562,
+ ["SF390000"]=9556,
+ ["SF40000"]=9496,
+ ["SF400000"]=9577,
+ ["SF410000"]=9574,
+ ["SF420000"]=9568,
+ ["SF430000"]=9552,
+ ["SF440000"]=9580,
+ ["SF450000"]=9575,
+ ["SF460000"]=9576,
+ ["SF470000"]=9572,
+ ["SF480000"]=9573,
+ ["SF490000"]=9561,
+ ["SF50000"]=9532,
+ ["SF500000"]=9560,
+ ["SF510000"]=9554,
+ ["SF520000"]=9555,
+ ["SF530000"]=9579,
+ ["SF540000"]=9578,
+ ["SF60000"]=9516,
+ ["SF70000"]=9524,
+ ["SF80000"]=9500,
+ ["SF90000"]=9508,
+ ["Sacute"]=346,
+ ["Sacutedotaccent"]=7780,
+ ["Sampigreek"]=992,
+ ["Scaron"]=352,
+ ["Scarondotaccent"]=7782,
+ ["Scedilla"]=350,
+ ["Schwa"]=399,
+ ["Schwacyrillic"]=1240,
+ ["Schwadieresiscyrillic"]=1242,
+ ["Scircle"]=9416,
+ ["Scircumflex"]=348,
+ ["Scommaaccent"]=536,
+ ["Sdotaccent"]=7776,
+ ["Sdotbelow"]=7778,
+ ["Sdotbelowdotaccent"]=7784,
+ ["Seharmenian"]=1357,
+ ["Sevenroman"]=8550,
+ ["Shaarmenian"]=1351,
+ ["Shacyrillic"]=1064,
+ ["Shchacyrillic"]=1065,
+ ["Sheicoptic"]=994,
+ ["Shhacyrillic"]=1210,
+ ["Shimacoptic"]=1004,
+ ["Sigma"]=931,
+ ["Sixroman"]=8549,
+ ["Smonospace"]=65331,
+ ["Softsigncyrillic"]=1068,
+ ["Stigmagreek"]=986,
+ ["T"]=84,
+ ["Tau"]=932,
+ ["Tbar"]=358,
+ ["Tcaron"]=356,
+ ["Tcedilla"]=354,
+ ["Tcircle"]=9417,
+ ["Tcircumflexbelow"]=7792,
+ ["Tcommaaccent"]=354,
+ ["Tdotaccent"]=7786,
+ ["Tdotbelow"]=7788,
+ ["Tecyrillic"]=1058,
+ ["Tedescendercyrillic"]=1196,
+ ["Tenroman"]=8553,
+ ["Tetsecyrillic"]=1204,
+ ["Theta"]=920,
+ ["Thook"]=428,
+ ["Thorn"]=222,
+ ["Threeroman"]=8546,
+ ["Tiwnarmenian"]=1359,
+ ["Tlinebelow"]=7790,
+ ["Tmonospace"]=65332,
+ ["Toarmenian"]=1337,
+ ["Tonefive"]=444,
+ ["Tonesix"]=388,
+ ["Tonetwo"]=423,
+ ["Tretroflexhook"]=430,
+ ["Tsecyrillic"]=1062,
+ ["Tshecyrillic"]=1035,
+ ["Twelveroman"]=8555,
+ ["Tworoman"]=8545,
+ ["U"]=85,
+ ["Uacute"]=218,
+ ["Ubreve"]=364,
+ ["Ucaron"]=467,
+ ["Ucircle"]=9418,
+ ["Ucircumflex"]=219,
+ ["Ucircumflexbelow"]=7798,
+ ["Ucyrillic"]=1059,
+ ["Udblacute"]=368,
+ ["Udblgrave"]=532,
+ ["Udieresis"]=220,
+ ["Udieresisacute"]=471,
+ ["Udieresisbelow"]=7794,
+ ["Udieresiscaron"]=473,
+ ["Udieresiscyrillic"]=1264,
+ ["Udieresisgrave"]=475,
+ ["Udieresismacron"]=469,
+ ["Udotbelow"]=7908,
+ ["Ugrave"]=217,
+ ["Uhookabove"]=7910,
+ ["Uhorn"]=431,
+ ["Uhornacute"]=7912,
+ ["Uhorndotbelow"]=7920,
+ ["Uhorngrave"]=7914,
+ ["Uhornhookabove"]=7916,
+ ["Uhorntilde"]=7918,
+ ["Uhungarumlaut"]=368,
+ ["Uhungarumlautcyrillic"]=1266,
+ ["Uinvertedbreve"]=534,
+ ["Ukcyrillic"]=1144,
+ ["Umacron"]=362,
+ ["Umacroncyrillic"]=1262,
+ ["Umacrondieresis"]=7802,
+ ["Umonospace"]=65333,
+ ["Uogonek"]=370,
+ ["Upsilon"]=933,
+ ["Upsilon1"]=978,
+ ["Upsilonacutehooksymbolgreek"]=979,
+ ["Upsilonafrican"]=433,
+ ["Upsilondieresis"]=939,
+ ["Upsilondieresishooksymbolgreek"]=980,
+ ["Upsilonhooksymbol"]=978,
+ ["Upsilontonos"]=910,
+ ["Uring"]=366,
+ ["Ushortcyrillic"]=1038,
+ ["Ustraightcyrillic"]=1198,
+ ["Ustraightstrokecyrillic"]=1200,
+ ["Utilde"]=360,
+ ["Utildeacute"]=7800,
+ ["Utildebelow"]=7796,
+ ["V"]=86,
+ ["Vcircle"]=9419,
+ ["Vdotbelow"]=7806,
+ ["Vecyrillic"]=1042,
+ ["Vewarmenian"]=1358,
+ ["Vhook"]=434,
+ ["Vmonospace"]=65334,
+ ["Voarmenian"]=1352,
+ ["Vtilde"]=7804,
+ ["W"]=87,
+ ["Wacute"]=7810,
+ ["Wcircle"]=9420,
+ ["Wcircumflex"]=372,
+ ["Wdieresis"]=7812,
+ ["Wdotaccent"]=7814,
+ ["Wdotbelow"]=7816,
+ ["Wgrave"]=7808,
+ ["Wmonospace"]=65335,
+ ["X"]=88,
+ ["Xcircle"]=9421,
+ ["Xdieresis"]=7820,
+ ["Xdotaccent"]=7818,
+ ["Xeharmenian"]=1341,
+ ["Xi"]=926,
+ ["Xmonospace"]=65336,
+ ["Y"]=89,
+ ["Yacute"]=221,
+ ["Yatcyrillic"]=1122,
+ ["Ycircle"]=9422,
+ ["Ycircumflex"]=374,
+ ["Ydieresis"]=376,
+ ["Ydotaccent"]=7822,
+ ["Ydotbelow"]=7924,
+ ["Yericyrillic"]=1067,
+ ["Yerudieresiscyrillic"]=1272,
+ ["Ygrave"]=7922,
+ ["Yhook"]=435,
+ ["Yhookabove"]=7926,
+ ["Yiarmenian"]=1349,
+ ["Yicyrillic"]=1031,
+ ["Yiwnarmenian"]=1362,
+ ["Ymonospace"]=65337,
+ ["Ytilde"]=7928,
+ ["Yusbigcyrillic"]=1130,
+ ["Yusbigiotifiedcyrillic"]=1132,
+ ["Yuslittlecyrillic"]=1126,
+ ["Yuslittleiotifiedcyrillic"]=1128,
+ ["Z"]=90,
+ ["Zaarmenian"]=1334,
+ ["Zacute"]=377,
+ ["Zcaron"]=381,
+ ["Zcircle"]=9423,
+ ["Zcircumflex"]=7824,
+ ["Zdot"]=379,
+ ["Zdotaccent"]=379,
+ ["Zdotbelow"]=7826,
+ ["Zecyrillic"]=1047,
+ ["Zedescendercyrillic"]=1176,
+ ["Zedieresiscyrillic"]=1246,
+ ["Zeta"]=918,
+ ["Zhearmenian"]=1338,
+ ["Zhebrevecyrillic"]=1217,
+ ["Zhecyrillic"]=1046,
+ ["Zhedescendercyrillic"]=1174,
+ ["Zhedieresiscyrillic"]=1244,
+ ["Zlinebelow"]=7828,
+ ["Zmonospace"]=65338,
+ ["Zstroke"]=437,
+ ["a"]=97,
+ ["aabengali"]=2438,
+ ["aacute"]=225,
+ ["aadeva"]=2310,
+ ["aagujarati"]=2694,
+ ["aagurmukhi"]=2566,
+ ["aamatragurmukhi"]=2622,
+ ["aarusquare"]=13059,
+ ["aavowelsignbengali"]=2494,
+ ["aavowelsigndeva"]=2366,
+ ["aavowelsigngujarati"]=2750,
+ ["abbreviationmarkarmenian"]=1375,
+ ["abbreviationsigndeva"]=2416,
+ ["abengali"]=2437,
+ ["abopomofo"]=12570,
+ ["abreve"]=259,
+ ["abreveacute"]=7855,
+ ["abrevecyrillic"]=1233,
+ ["abrevedotbelow"]=7863,
+ ["abrevegrave"]=7857,
+ ["abrevehookabove"]=7859,
+ ["abrevetilde"]=7861,
+ ["acaron"]=462,
+ ["acircle"]=9424,
+ ["acircumflex"]=226,
+ ["acircumflexacute"]=7845,
+ ["acircumflexdotbelow"]=7853,
+ ["acircumflexgrave"]=7847,
+ ["acircumflexhookabove"]=7849,
+ ["acircumflextilde"]=7851,
+ ["acute"]=180,
+ ["acutebelowcmb"]=791,
+ ["acutecmb"]=769,
+ ["acutecomb"]=769,
+ ["acutedeva"]=2388,
+ ["acutelowmod"]=719,
+ ["acutetonecmb"]=833,
+ ["acyrillic"]=1072,
+ ["adblgrave"]=513,
+ ["addakgurmukhi"]=2673,
+ ["adeva"]=2309,
+ ["adieresis"]=228,
+ ["adieresiscyrillic"]=1235,
+ ["adieresismacron"]=479,
+ ["adotbelow"]=7841,
+ ["adotmacron"]=481,
+ ["ae"]=230,
+ ["aeacute"]=509,
+ ["aekorean"]=12624,
+ ["aemacron"]=483,
+ ["afii00208"]=8213,
+ ["afii08941"]=8356,
+ ["afii10017"]=1040,
+ ["afii10018"]=1041,
+ ["afii10019"]=1042,
+ ["afii10020"]=1043,
+ ["afii10021"]=1044,
+ ["afii10022"]=1045,
+ ["afii10023"]=1025,
+ ["afii10024"]=1046,
+ ["afii10025"]=1047,
+ ["afii10026"]=1048,
+ ["afii10027"]=1049,
+ ["afii10028"]=1050,
+ ["afii10029"]=1051,
+ ["afii10030"]=1052,
+ ["afii10031"]=1053,
+ ["afii10032"]=1054,
+ ["afii10033"]=1055,
+ ["afii10034"]=1056,
+ ["afii10035"]=1057,
+ ["afii10036"]=1058,
+ ["afii10037"]=1059,
+ ["afii10038"]=1060,
+ ["afii10039"]=1061,
+ ["afii10040"]=1062,
+ ["afii10041"]=1063,
+ ["afii10042"]=1064,
+ ["afii10043"]=1065,
+ ["afii10044"]=1066,
+ ["afii10045"]=1067,
+ ["afii10046"]=1068,
+ ["afii10047"]=1069,
+ ["afii10048"]=1070,
+ ["afii10049"]=1071,
+ ["afii10050"]=1168,
+ ["afii10051"]=1026,
+ ["afii10052"]=1027,
+ ["afii10053"]=1028,
+ ["afii10054"]=1029,
+ ["afii10055"]=1030,
+ ["afii10056"]=1031,
+ ["afii10057"]=1032,
+ ["afii10058"]=1033,
+ ["afii10059"]=1034,
+ ["afii10060"]=1035,
+ ["afii10061"]=1036,
+ ["afii10062"]=1038,
+ ["afii10065"]=1072,
+ ["afii10066"]=1073,
+ ["afii10067"]=1074,
+ ["afii10068"]=1075,
+ ["afii10069"]=1076,
+ ["afii10070"]=1077,
+ ["afii10071"]=1105,
+ ["afii10072"]=1078,
+ ["afii10073"]=1079,
+ ["afii10074"]=1080,
+ ["afii10075"]=1081,
+ ["afii10076"]=1082,
+ ["afii10077"]=1083,
+ ["afii10078"]=1084,
+ ["afii10079"]=1085,
+ ["afii10080"]=1086,
+ ["afii10081"]=1087,
+ ["afii10082"]=1088,
+ ["afii10083"]=1089,
+ ["afii10084"]=1090,
+ ["afii10085"]=1091,
+ ["afii10086"]=1092,
+ ["afii10087"]=1093,
+ ["afii10088"]=1094,
+ ["afii10089"]=1095,
+ ["afii10090"]=1096,
+ ["afii10091"]=1097,
+ ["afii10092"]=1098,
+ ["afii10093"]=1099,
+ ["afii10094"]=1100,
+ ["afii10095"]=1101,
+ ["afii10096"]=1102,
+ ["afii10097"]=1103,
+ ["afii10098"]=1169,
+ ["afii10099"]=1106,
+ ["afii10100"]=1107,
+ ["afii10101"]=1108,
+ ["afii10102"]=1109,
+ ["afii10103"]=1110,
+ ["afii10104"]=1111,
+ ["afii10105"]=1112,
+ ["afii10106"]=1113,
+ ["afii10107"]=1114,
+ ["afii10108"]=1115,
+ ["afii10109"]=1116,
+ ["afii10110"]=1118,
+ ["afii10145"]=1039,
+ ["afii10146"]=1122,
+ ["afii10147"]=1138,
+ ["afii10148"]=1140,
+ ["afii10193"]=1119,
+ ["afii10194"]=1123,
+ ["afii10195"]=1139,
+ ["afii10196"]=1141,
+ ["afii10846"]=1241,
+ ["afii208"]=8213,
+ ["afii299"]=8206,
+ ["afii300"]=8207,
+ ["afii301"]=8205,
+ ["afii57381"]=1642,
+ ["afii57388"]=1548,
+ ["afii57392"]=1632,
+ ["afii57393"]=1633,
+ ["afii57394"]=1634,
+ ["afii57395"]=1635,
+ ["afii57396"]=1636,
+ ["afii57397"]=1637,
+ ["afii57398"]=1638,
+ ["afii57399"]=1639,
+ ["afii57400"]=1640,
+ ["afii57401"]=1641,
+ ["afii57403"]=1563,
+ ["afii57407"]=1567,
+ ["afii57409"]=1569,
+ ["afii57410"]=1570,
+ ["afii57411"]=1571,
+ ["afii57412"]=1572,
+ ["afii57413"]=1573,
+ ["afii57414"]=1574,
+ ["afii57415"]=1575,
+ ["afii57416"]=1576,
+ ["afii57417"]=1577,
+ ["afii57418"]=1578,
+ ["afii57419"]=1579,
+ ["afii57420"]=1580,
+ ["afii57421"]=1581,
+ ["afii57422"]=1582,
+ ["afii57423"]=1583,
+ ["afii57424"]=1584,
+ ["afii57425"]=1585,
+ ["afii57426"]=1586,
+ ["afii57427"]=1587,
+ ["afii57428"]=1588,
+ ["afii57429"]=1589,
+ ["afii57430"]=1590,
+ ["afii57431"]=1591,
+ ["afii57432"]=1592,
+ ["afii57433"]=1593,
+ ["afii57434"]=1594,
+ ["afii57440"]=1600,
+ ["afii57441"]=1601,
+ ["afii57442"]=1602,
+ ["afii57443"]=1603,
+ ["afii57444"]=1604,
+ ["afii57445"]=1605,
+ ["afii57446"]=1606,
+ ["afii57448"]=1608,
+ ["afii57449"]=1609,
+ ["afii57450"]=1610,
+ ["afii57451"]=1611,
+ ["afii57452"]=1612,
+ ["afii57453"]=1613,
+ ["afii57454"]=1614,
+ ["afii57455"]=1615,
+ ["afii57456"]=1616,
+ ["afii57457"]=1617,
+ ["afii57458"]=1618,
+ ["afii57470"]=1607,
+ ["afii57505"]=1700,
+ ["afii57506"]=1662,
+ ["afii57507"]=1670,
+ ["afii57508"]=1688,
+ ["afii57509"]=1711,
+ ["afii57511"]=1657,
+ ["afii57512"]=1672,
+ ["afii57513"]=1681,
+ ["afii57514"]=1722,
+ ["afii57519"]=1746,
+ ["afii57534"]=1749,
+ ["afii57636"]=8362,
+ ["afii57645"]=1470,
+ ["afii57658"]=1475,
+ ["afii57664"]=1488,
+ ["afii57665"]=1489,
+ ["afii57666"]=1490,
+ ["afii57667"]=1491,
+ ["afii57668"]=1492,
+ ["afii57669"]=1493,
+ ["afii57670"]=1494,
+ ["afii57671"]=1495,
+ ["afii57672"]=1496,
+ ["afii57673"]=1497,
+ ["afii57674"]=1498,
+ ["afii57675"]=1499,
+ ["afii57676"]=1500,
+ ["afii57677"]=1501,
+ ["afii57678"]=1502,
+ ["afii57679"]=1503,
+ ["afii57680"]=1504,
+ ["afii57681"]=1505,
+ ["afii57682"]=1506,
+ ["afii57683"]=1507,
+ ["afii57684"]=1508,
+ ["afii57685"]=1509,
+ ["afii57686"]=1510,
+ ["afii57687"]=1511,
+ ["afii57688"]=1512,
+ ["afii57689"]=1513,
+ ["afii57690"]=1514,
+ ["afii57694"]=64298,
+ ["afii57695"]=64299,
+ ["afii57700"]=64331,
+ ["afii57705"]=64287,
+ ["afii57716"]=1520,
+ ["afii57717"]=1521,
+ ["afii57718"]=1522,
+ ["afii57723"]=64309,
+ ["afii57793"]=1460,
+ ["afii57794"]=1461,
+ ["afii57795"]=1462,
+ ["afii57796"]=1467,
+ ["afii57797"]=1464,
+ ["afii57798"]=1463,
+ ["afii57799"]=1456,
+ ["afii57800"]=1458,
+ ["afii57801"]=1457,
+ ["afii57802"]=1459,
+ ["afii57803"]=1474,
+ ["afii57804"]=1473,
+ ["afii57806"]=1465,
+ ["afii57807"]=1468,
+ ["afii57839"]=1469,
+ ["afii57841"]=1471,
+ ["afii57842"]=1472,
+ ["afii57929"]=700,
+ ["afii61248"]=8453,
+ ["afii61289"]=8467,
+ ["afii61352"]=8470,
+ ["afii61573"]=8236,
+ ["afii61574"]=8237,
+ ["afii61575"]=8238,
+ ["afii61664"]=8204,
+ ["afii63167"]=1645,
+ ["afii64937"]=701,
+ ["agrave"]=224,
+ ["agujarati"]=2693,
+ ["agurmukhi"]=2565,
+ ["ahiragana"]=12354,
+ ["ahookabove"]=7843,
+ ["aibengali"]=2448,
+ ["aibopomofo"]=12574,
+ ["aideva"]=2320,
+ ["aiecyrillic"]=1237,
+ ["aigujarati"]=2704,
+ ["aigurmukhi"]=2576,
+ ["aimatragurmukhi"]=2632,
+ ["ainarabic"]=1593,
+ ["ainfinalarabic"]=65226,
+ ["aininitialarabic"]=65227,
+ ["ainmedialarabic"]=65228,
+ ["ainvertedbreve"]=515,
+ ["aivowelsignbengali"]=2504,
+ ["aivowelsigndeva"]=2376,
+ ["aivowelsigngujarati"]=2760,
+ ["akatakana"]=12450,
+ ["akatakanahalfwidth"]=65393,
+ ["akorean"]=12623,
+ ["alef"]=1488,
+ ["alefarabic"]=1575,
+ ["alefdageshhebrew"]=64304,
+ ["aleffinalarabic"]=65166,
+ ["alefhamzaabovearabic"]=1571,
+ ["alefhamzaabovefinalarabic"]=65156,
+ ["alefhamzabelowarabic"]=1573,
+ ["alefhamzabelowfinalarabic"]=65160,
+ ["alefhebrew"]=1488,
+ ["aleflamedhebrew"]=64335,
+ ["alefmaddaabovearabic"]=1570,
+ ["alefmaddaabovefinalarabic"]=65154,
+ ["alefmaksuraarabic"]=1609,
+ ["alefmaksurafinalarabic"]=65264,
+ ["alefmaksurainitialarabic"]=65267,
+ ["alefmaksuramedialarabic"]=65268,
+ ["alefpatahhebrew"]=64302,
+ ["alefqamatshebrew"]=64303,
+ ["aleph"]=8501,
+ ["allequal"]=8780,
+ ["alpha"]=945,
+ ["alphatonos"]=940,
+ ["amacron"]=257,
+ ["amonospace"]=65345,
+ ["ampersand"]=38,
+ ["ampersandmonospace"]=65286,
+ ["amsquare"]=13250,
+ ["anbopomofo"]=12578,
+ ["angbopomofo"]=12580,
+ ["angkhankhuthai"]=3674,
+ ["angle"]=8736,
+ ["anglebracketleft"]=12296,
+ ["anglebracketleftvertical"]=65087,
+ ["anglebracketright"]=12297,
+ ["anglebracketrightvertical"]=65088,
+ ["angleleft"]=9001,
+ ["angleright"]=9002,
+ ["angstrom"]=8491,
+ ["anoteleia"]=903,
+ ["anudattadeva"]=2386,
+ ["anusvarabengali"]=2434,
+ ["anusvaradeva"]=2306,
+ ["anusvaragujarati"]=2690,
+ ["aogonek"]=261,
+ ["apaatosquare"]=13056,
+ ["aparen"]=9372,
+ ["apostrophearmenian"]=1370,
+ ["apostrophemod"]=700,
+ ["apple"]=63743,
+ ["approaches"]=8784,
+ ["approxequal"]=8776,
+ ["approxequalorimage"]=8786,
+ ["approximatelyequal"]=8773,
+ ["araeaekorean"]=12686,
+ ["araeakorean"]=12685,
+ ["arc"]=8978,
+ ["arighthalfring"]=7834,
+ ["aring"]=229,
+ ["aringacute"]=507,
+ ["aringbelow"]=7681,
+ ["arrowboth"]=8596,
+ ["arrowdashdown"]=8675,
+ ["arrowdashleft"]=8672,
+ ["arrowdashright"]=8674,
+ ["arrowdashup"]=8673,
+ ["arrowdblboth"]=8660,
+ ["arrowdbldown"]=8659,
+ ["arrowdblleft"]=8656,
+ ["arrowdblright"]=8658,
+ ["arrowdblup"]=8657,
+ ["arrowdown"]=8595,
+ ["arrowdownleft"]=8601,
+ ["arrowdownright"]=8600,
+ ["arrowdownwhite"]=8681,
+ ["arrowheaddownmod"]=709,
+ ["arrowheadleftmod"]=706,
+ ["arrowheadrightmod"]=707,
+ ["arrowheadupmod"]=708,
+ ["arrowleft"]=8592,
+ ["arrowleftdbl"]=8656,
+ ["arrowleftdblstroke"]=8653,
+ ["arrowleftoverright"]=8646,
+ ["arrowleftwhite"]=8678,
+ ["arrowright"]=8594,
+ ["arrowrightdblstroke"]=8655,
+ ["arrowrightheavy"]=10142,
+ ["arrowrightoverleft"]=8644,
+ ["arrowrightwhite"]=8680,
+ ["arrowtableft"]=8676,
+ ["arrowtabright"]=8677,
+ ["arrowup"]=8593,
+ ["arrowupdn"]=8597,
+ ["arrowupdnbse"]=8616,
+ ["arrowupdownbase"]=8616,
+ ["arrowupleft"]=8598,
+ ["arrowupleftofdown"]=8645,
+ ["arrowupright"]=8599,
+ ["arrowupwhite"]=8679,
+ ["asciicircum"]=94,
+ ["asciicircummonospace"]=65342,
+ ["asciitilde"]=126,
+ ["asciitildemonospace"]=65374,
+ ["ascript"]=593,
+ ["ascriptturned"]=594,
+ ["asmallhiragana"]=12353,
+ ["asmallkatakana"]=12449,
+ ["asmallkatakanahalfwidth"]=65383,
+ ["asterisk"]=42,
+ ["asteriskaltonearabic"]=1645,
+ ["asteriskarabic"]=1645,
+ ["asteriskmath"]=8727,
+ ["asteriskmonospace"]=65290,
+ ["asterisksmall"]=65121,
+ ["asterism"]=8258,
+ ["asymptoticallyequal"]=8771,
+ ["at"]=64,
+ ["atilde"]=227,
+ ["atmonospace"]=65312,
+ ["atsmall"]=65131,
+ ["aturned"]=592,
+ ["aubengali"]=2452,
+ ["aubopomofo"]=12576,
+ ["audeva"]=2324,
+ ["augujarati"]=2708,
+ ["augurmukhi"]=2580,
+ ["aulengthmarkbengali"]=2519,
+ ["aumatragurmukhi"]=2636,
+ ["auvowelsignbengali"]=2508,
+ ["auvowelsigndeva"]=2380,
+ ["auvowelsigngujarati"]=2764,
+ ["avagrahadeva"]=2365,
+ ["aybarmenian"]=1377,
+ ["ayin"]=1506,
+ ["ayinaltonehebrew"]=64288,
+ ["ayinhebrew"]=1506,
+ ["b"]=98,
+ ["babengali"]=2476,
+ ["backslash"]=92,
+ ["backslashmonospace"]=65340,
+ ["badeva"]=2348,
+ ["bagujarati"]=2732,
+ ["bagurmukhi"]=2604,
+ ["bahiragana"]=12400,
+ ["bahtthai"]=3647,
+ ["bakatakana"]=12496,
+ ["bar"]=124,
+ ["barmonospace"]=65372,
+ ["bbopomofo"]=12549,
+ ["bcircle"]=9425,
+ ["bdotaccent"]=7683,
+ ["bdotbelow"]=7685,
+ ["beamedsixteenthnotes"]=9836,
+ ["because"]=8757,
+ ["becyrillic"]=1073,
+ ["beharabic"]=1576,
+ ["behfinalarabic"]=65168,
+ ["behinitialarabic"]=65169,
+ ["behiragana"]=12409,
+ ["behmedialarabic"]=65170,
+ ["behmeeminitialarabic"]=64671,
+ ["behmeemisolatedarabic"]=64520,
+ ["behnoonfinalarabic"]=64621,
+ ["bekatakana"]=12505,
+ ["benarmenian"]=1378,
+ ["bet"]=1489,
+ ["beta"]=946,
+ ["betasymbolgreek"]=976,
+ ["betdagesh"]=64305,
+ ["betdageshhebrew"]=64305,
+ ["bethebrew"]=1489,
+ ["betrafehebrew"]=64332,
+ ["bhabengali"]=2477,
+ ["bhadeva"]=2349,
+ ["bhagujarati"]=2733,
+ ["bhagurmukhi"]=2605,
+ ["bhook"]=595,
+ ["bihiragana"]=12403,
+ ["bikatakana"]=12499,
+ ["bilabialclick"]=664,
+ ["bindigurmukhi"]=2562,
+ ["birusquare"]=13105,
+ ["blackcircle"]=9679,
+ ["blackdiamond"]=9670,
+ ["blackdownpointingtriangle"]=9660,
+ ["blackleftpointingpointer"]=9668,
+ ["blackleftpointingtriangle"]=9664,
+ ["blacklenticularbracketleft"]=12304,
+ ["blacklenticularbracketleftvertical"]=65083,
+ ["blacklenticularbracketright"]=12305,
+ ["blacklenticularbracketrightvertical"]=65084,
+ ["blacklowerlefttriangle"]=9699,
+ ["blacklowerrighttriangle"]=9698,
+ ["blackrectangle"]=9644,
+ ["blackrightpointingpointer"]=9658,
+ ["blackrightpointingtriangle"]=9654,
+ ["blacksmallsquare"]=9642,
+ ["blacksmilingface"]=9787,
+ ["blacksquare"]=9632,
+ ["blackstar"]=9733,
+ ["blackupperlefttriangle"]=9700,
+ ["blackupperrighttriangle"]=9701,
+ ["blackuppointingsmalltriangle"]=9652,
+ ["blackuppointingtriangle"]=9650,
+ ["blank"]=9251,
+ ["blinebelow"]=7687,
+ ["block"]=9608,
+ ["bmonospace"]=65346,
+ ["bobaimaithai"]=3610,
+ ["bohiragana"]=12412,
+ ["bokatakana"]=12508,
+ ["bparen"]=9373,
+ ["bqsquare"]=13251,
+ ["braceleft"]=123,
+ ["braceleftmonospace"]=65371,
+ ["braceleftsmall"]=65115,
+ ["braceleftvertical"]=65079,
+ ["braceright"]=125,
+ ["bracerightmonospace"]=65373,
+ ["bracerightsmall"]=65116,
+ ["bracerightvertical"]=65080,
+ ["bracketleft"]=91,
+ ["bracketleftmonospace"]=65339,
+ ["bracketright"]=93,
+ ["bracketrightmonospace"]=65341,
+ ["breve"]=728,
+ ["brevebelowcmb"]=814,
+ ["brevecmb"]=774,
+ ["breveinvertedbelowcmb"]=815,
+ ["breveinvertedcmb"]=785,
+ ["breveinverteddoublecmb"]=865,
+ ["bridgebelowcmb"]=810,
+ ["bridgeinvertedbelowcmb"]=826,
+ ["brokenbar"]=166,
+ ["bstroke"]=384,
+ ["btopbar"]=387,
+ ["buhiragana"]=12406,
+ ["bukatakana"]=12502,
+ ["bullet"]=8226,
+ ["bulletinverse"]=9688,
+ ["bulletoperator"]=8729,
+ ["bullseye"]=9678,
+ ["c"]=99,
+ ["caarmenian"]=1390,
+ ["cabengali"]=2458,
+ ["cacute"]=263,
+ ["cadeva"]=2330,
+ ["cagujarati"]=2714,
+ ["cagurmukhi"]=2586,
+ ["calsquare"]=13192,
+ ["candrabindubengali"]=2433,
+ ["candrabinducmb"]=784,
+ ["candrabindudeva"]=2305,
+ ["candrabindugujarati"]=2689,
+ ["capslock"]=8682,
+ ["careof"]=8453,
+ ["caron"]=711,
+ ["caronbelowcmb"]=812,
+ ["caroncmb"]=780,
+ ["carriagereturn"]=8629,
+ ["cbopomofo"]=12568,
+ ["ccaron"]=269,
+ ["ccedilla"]=231,
+ ["ccedillaacute"]=7689,
+ ["ccircle"]=9426,
+ ["ccircumflex"]=265,
+ ["ccurl"]=597,
+ ["cdot"]=267,
+ ["cdotaccent"]=267,
+ ["cdsquare"]=13253,
+ ["cedilla"]=184,
+ ["cedillacmb"]=807,
+ ["cent"]=162,
+ ["centigrade"]=8451,
+ ["centmonospace"]=65504,
+ ["chaarmenian"]=1401,
+ ["chabengali"]=2459,
+ ["chadeva"]=2331,
+ ["chagujarati"]=2715,
+ ["chagurmukhi"]=2587,
+ ["chbopomofo"]=12564,
+ ["cheabkhasiancyrillic"]=1213,
+ ["checkmark"]=10003,
+ ["checyrillic"]=1095,
+ ["chedescenderabkhasiancyrillic"]=1215,
+ ["chedescendercyrillic"]=1207,
+ ["chedieresiscyrillic"]=1269,
+ ["cheharmenian"]=1395,
+ ["chekhakassiancyrillic"]=1228,
+ ["cheverticalstrokecyrillic"]=1209,
+ ["chi"]=967,
+ ["chieuchacirclekorean"]=12919,
+ ["chieuchaparenkorean"]=12823,
+ ["chieuchcirclekorean"]=12905,
+ ["chieuchkorean"]=12618,
+ ["chieuchparenkorean"]=12809,
+ ["chochangthai"]=3594,
+ ["chochanthai"]=3592,
+ ["chochingthai"]=3593,
+ ["chochoethai"]=3596,
+ ["chook"]=392,
+ ["cieucacirclekorean"]=12918,
+ ["cieucaparenkorean"]=12822,
+ ["cieuccirclekorean"]=12904,
+ ["cieuckorean"]=12616,
+ ["cieucparenkorean"]=12808,
+ ["cieucuparenkorean"]=12828,
+ ["circle"]=9675,
+ ["circlemultiply"]=8855,
+ ["circleot"]=8857,
+ ["circleplus"]=8853,
+ ["circlepostalmark"]=12342,
+ ["circlewithlefthalfblack"]=9680,
+ ["circlewithrighthalfblack"]=9681,
+ ["circumflex"]=710,
+ ["circumflexbelowcmb"]=813,
+ ["circumflexcmb"]=770,
+ ["clear"]=8999,
+ ["clickalveolar"]=450,
+ ["clickdental"]=448,
+ ["clicklateral"]=449,
+ ["clickretroflex"]=451,
+ ["club"]=9827,
+ ["clubsuitblack"]=9827,
+ ["clubsuitwhite"]=9831,
+ ["cmcubedsquare"]=13220,
+ ["cmonospace"]=65347,
+ ["cmsquaredsquare"]=13216,
+ ["coarmenian"]=1409,
+ ["colon"]=58,
+ ["colonmonetary"]=8353,
+ ["colonmonospace"]=65306,
+ ["colonsign"]=8353,
+ ["colonsmall"]=65109,
+ ["colontriangularhalfmod"]=721,
+ ["colontriangularmod"]=720,
+ ["comma"]=44,
+ ["commaabovecmb"]=787,
+ ["commaaboverightcmb"]=789,
+ ["commaarabic"]=1548,
+ ["commaarmenian"]=1373,
+ ["commamonospace"]=65292,
+ ["commareversedabovecmb"]=788,
+ ["commareversedmod"]=701,
+ ["commasmall"]=65104,
+ ["commaturnedabovecmb"]=786,
+ ["commaturnedmod"]=699,
+ ["compass"]=9788,
+ ["congruent"]=8773,
+ ["contourintegral"]=8750,
+ ["control"]=8963,
+ ["controlACK"]=6,
+ ["controlBEL"]=7,
+ ["controlBS"]=8,
+ ["controlCAN"]=24,
+ ["controlCR"]=13,
+ ["controlDC1"]=17,
+ ["controlDC2"]=18,
+ ["controlDC3"]=19,
+ ["controlDC4"]=20,
+ ["controlDEL"]=127,
+ ["controlDLE"]=16,
+ ["controlEM"]=25,
+ ["controlENQ"]=5,
+ ["controlEOT"]=4,
+ ["controlESC"]=27,
+ ["controlETB"]=23,
+ ["controlETX"]=3,
+ ["controlFF"]=12,
+ ["controlFS"]=28,
+ ["controlGS"]=29,
+ ["controlHT"]=9,
+ ["controlLF"]=10,
+ ["controlNAK"]=21,
+ ["controlRS"]=30,
+ ["controlSI"]=15,
+ ["controlSO"]=14,
+ ["controlSOT"]=2,
+ ["controlSTX"]=1,
+ ["controlSUB"]=26,
+ ["controlSYN"]=22,
+ ["controlUS"]=31,
+ ["controlVT"]=11,
+ ["copyright"]=169,
+ ["cornerbracketleft"]=12300,
+ ["cornerbracketlefthalfwidth"]=65378,
+ ["cornerbracketleftvertical"]=65089,
+ ["cornerbracketright"]=12301,
+ ["cornerbracketrighthalfwidth"]=65379,
+ ["cornerbracketrightvertical"]=65090,
+ ["corporationsquare"]=13183,
+ ["cosquare"]=13255,
+ ["coverkgsquare"]=13254,
+ ["cparen"]=9374,
+ ["cruzeiro"]=8354,
+ ["cstretched"]=663,
+ ["curlyand"]=8911,
+ ["curlyor"]=8910,
+ ["currency"]=164,
+ ["d"]=100,
+ ["daarmenian"]=1380,
+ ["dabengali"]=2470,
+ ["dadarabic"]=1590,
+ ["dadeva"]=2342,
+ ["dadfinalarabic"]=65214,
+ ["dadinitialarabic"]=65215,
+ ["dadmedialarabic"]=65216,
+ ["dagesh"]=1468,
+ ["dageshhebrew"]=1468,
+ ["dagger"]=8224,
+ ["daggerdbl"]=8225,
+ ["dagujarati"]=2726,
+ ["dagurmukhi"]=2598,
+ ["dahiragana"]=12384,
+ ["dakatakana"]=12480,
+ ["dalarabic"]=1583,
+ ["dalet"]=1491,
+ ["daletdagesh"]=64307,
+ ["daletdageshhebrew"]=64307,
+ ["dalethatafpatah"]=1491,
+ ["dalethatafpatahhebrew"]=1491,
+ ["dalethatafsegol"]=1491,
+ ["dalethatafsegolhebrew"]=1491,
+ ["dalethebrew"]=1491,
+ ["dalethiriq"]=1491,
+ ["dalethiriqhebrew"]=1491,
+ ["daletholam"]=1491,
+ ["daletholamhebrew"]=1491,
+ ["daletpatah"]=1491,
+ ["daletpatahhebrew"]=1491,
+ ["daletqamats"]=1491,
+ ["daletqamatshebrew"]=1491,
+ ["daletqubuts"]=1491,
+ ["daletqubutshebrew"]=1491,
+ ["daletsegol"]=1491,
+ ["daletsegolhebrew"]=1491,
+ ["daletsheva"]=1491,
+ ["daletshevahebrew"]=1491,
+ ["dalettsere"]=1491,
+ ["dalettserehebrew"]=1491,
+ ["dalfinalarabic"]=65194,
+ ["dammaarabic"]=1615,
+ ["dammalowarabic"]=1615,
+ ["dammatanaltonearabic"]=1612,
+ ["dammatanarabic"]=1612,
+ ["danda"]=2404,
+ ["dargahebrew"]=1447,
+ ["dargalefthebrew"]=1447,
+ ["dasiapneumatacyrilliccmb"]=1157,
+ ["dblanglebracketleft"]=12298,
+ ["dblanglebracketleftvertical"]=65085,
+ ["dblanglebracketright"]=12299,
+ ["dblanglebracketrightvertical"]=65086,
+ ["dblarchinvertedbelowcmb"]=811,
+ ["dblarrowleft"]=8660,
+ ["dblarrowright"]=8658,
+ ["dbldanda"]=2405,
+ ["dblgravecmb"]=783,
+ ["dblintegral"]=8748,
+ ["dbllowline"]=8215,
+ ["dbllowlinecmb"]=819,
+ ["dbloverlinecmb"]=831,
+ ["dblprimemod"]=698,
+ ["dblverticalbar"]=8214,
+ ["dblverticallineabovecmb"]=782,
+ ["dbopomofo"]=12553,
+ ["dbsquare"]=13256,
+ ["dcaron"]=271,
+ ["dcedilla"]=7697,
+ ["dcircle"]=9427,
+ ["dcircumflexbelow"]=7699,
+ ["dcroat"]=273,
+ ["ddabengali"]=2465,
+ ["ddadeva"]=2337,
+ ["ddagujarati"]=2721,
+ ["ddagurmukhi"]=2593,
+ ["ddalarabic"]=1672,
+ ["ddalfinalarabic"]=64393,
+ ["dddhadeva"]=2396,
+ ["ddhabengali"]=2466,
+ ["ddhadeva"]=2338,
+ ["ddhagujarati"]=2722,
+ ["ddhagurmukhi"]=2594,
+ ["ddotaccent"]=7691,
+ ["ddotbelow"]=7693,
+ ["decimalseparatorarabic"]=1643,
+ ["decimalseparatorpersian"]=1643,
+ ["decyrillic"]=1076,
+ ["degree"]=176,
+ ["dehihebrew"]=1453,
+ ["dehiragana"]=12391,
+ ["deicoptic"]=1007,
+ ["dekatakana"]=12487,
+ ["deleteleft"]=9003,
+ ["deleteright"]=8998,
+ ["delta"]=948,
+ ["deltaturned"]=397,
+ ["denominatorminusonenumeratorbengali"]=2552,
+ ["dezh"]=676,
+ ["dhabengali"]=2471,
+ ["dhadeva"]=2343,
+ ["dhagujarati"]=2727,
+ ["dhagurmukhi"]=2599,
+ ["dhook"]=599,
+ ["dialytikatonos"]=901,
+ ["dialytikatonoscmb"]=836,
+ ["diamond"]=9830,
+ ["diamondsuitwhite"]=9826,
+ ["dieresis"]=168,
+ ["dieresisbelowcmb"]=804,
+ ["dieresiscmb"]=776,
+ ["dieresistonos"]=901,
+ ["dihiragana"]=12386,
+ ["dikatakana"]=12482,
+ ["dittomark"]=12291,
+ ["divide"]=247,
+ ["divides"]=8739,
+ ["divisionslash"]=8725,
+ ["djecyrillic"]=1106,
+ ["dkshade"]=9619,
+ ["dlinebelow"]=7695,
+ ["dlsquare"]=13207,
+ ["dmacron"]=273,
+ ["dmonospace"]=65348,
+ ["dnblock"]=9604,
+ ["dochadathai"]=3598,
+ ["dodekthai"]=3604,
+ ["dohiragana"]=12393,
+ ["dokatakana"]=12489,
+ ["dollar"]=36,
+ ["dollarmonospace"]=65284,
+ ["dollarsmall"]=65129,
+ ["dong"]=8363,
+ ["dorusquare"]=13094,
+ ["dotaccent"]=729,
+ ["dotaccentcmb"]=775,
+ ["dotbelowcmb"]=803,
+ ["dotbelowcomb"]=803,
+ ["dotkatakana"]=12539,
+ ["dotlessi"]=305,
+ ["dotlessjstrokehook"]=644,
+ ["dotmath"]=8901,
+ ["dottedcircle"]=9676,
+ ["doubleyodpatah"]=64287,
+ ["doubleyodpatahhebrew"]=64287,
+ ["downtackbelowcmb"]=798,
+ ["downtackmod"]=725,
+ ["dparen"]=9375,
+ ["dtail"]=598,
+ ["dtopbar"]=396,
+ ["duhiragana"]=12389,
+ ["dukatakana"]=12485,
+ ["dz"]=499,
+ ["dzaltone"]=675,
+ ["dzcaron"]=454,
+ ["dzcurl"]=677,
+ ["dzeabkhasiancyrillic"]=1249,
+ ["dzecyrillic"]=1109,
+ ["dzhecyrillic"]=1119,
+ ["e"]=101,
+ ["eacute"]=233,
+ ["earth"]=9793,
+ ["ebengali"]=2447,
+ ["ebopomofo"]=12572,
+ ["ebreve"]=277,
+ ["ecandradeva"]=2317,
+ ["ecandragujarati"]=2701,
+ ["ecandravowelsigndeva"]=2373,
+ ["ecandravowelsigngujarati"]=2757,
+ ["ecaron"]=283,
+ ["ecedillabreve"]=7709,
+ ["echarmenian"]=1381,
+ ["echyiwnarmenian"]=1415,
+ ["ecircle"]=9428,
+ ["ecircumflex"]=234,
+ ["ecircumflexacute"]=7871,
+ ["ecircumflexbelow"]=7705,
+ ["ecircumflexdotbelow"]=7879,
+ ["ecircumflexgrave"]=7873,
+ ["ecircumflexhookabove"]=7875,
+ ["ecircumflextilde"]=7877,
+ ["ecyrillic"]=1108,
+ ["edblgrave"]=517,
+ ["edeva"]=2319,
+ ["edieresis"]=235,
+ ["edot"]=279,
+ ["edotaccent"]=279,
+ ["edotbelow"]=7865,
+ ["eegurmukhi"]=2575,
+ ["eematragurmukhi"]=2631,
+ ["efcyrillic"]=1092,
+ ["egrave"]=232,
+ ["egujarati"]=2703,
+ ["eharmenian"]=1383,
+ ["ehbopomofo"]=12573,
+ ["ehiragana"]=12360,
+ ["ehookabove"]=7867,
+ ["eibopomofo"]=12575,
+ ["eight"]=56,
+ ["eightarabic"]=1640,
+ ["eightbengali"]=2542,
+ ["eightcircle"]=9319,
+ ["eightcircleinversesansserif"]=10129,
+ ["eightdeva"]=2414,
+ ["eighteencircle"]=9329,
+ ["eighteenparen"]=9349,
+ ["eighteenperiod"]=9369,
+ ["eightgujarati"]=2798,
+ ["eightgurmukhi"]=2670,
+ ["eighthackarabic"]=1640,
+ ["eighthangzhou"]=12328,
+ ["eighthnotebeamed"]=9835,
+ ["eightideographicparen"]=12839,
+ ["eightinferior"]=8328,
+ ["eightmonospace"]=65304,
+ ["eightparen"]=9339,
+ ["eightperiod"]=9359,
+ ["eightpersian"]=1784,
+ ["eightroman"]=8567,
+ ["eightsuperior"]=8312,
+ ["eightthai"]=3672,
+ ["einvertedbreve"]=519,
+ ["eiotifiedcyrillic"]=1125,
+ ["ekatakana"]=12456,
+ ["ekatakanahalfwidth"]=65396,
+ ["ekonkargurmukhi"]=2676,
+ ["ekorean"]=12628,
+ ["elcyrillic"]=1083,
+ ["element"]=8712,
+ ["elevencircle"]=9322,
+ ["elevenparen"]=9342,
+ ["elevenperiod"]=9362,
+ ["elevenroman"]=8570,
+ ["ellipsis"]=8230,
+ ["ellipsisvertical"]=8942,
+ ["emacron"]=275,
+ ["emacronacute"]=7703,
+ ["emacrongrave"]=7701,
+ ["emcyrillic"]=1084,
+ ["emdash"]=8212,
+ ["emdashvertical"]=65073,
+ ["emonospace"]=65349,
+ ["emphasismarkarmenian"]=1371,
+ ["emptyset"]=8709,
+ ["enbopomofo"]=12579,
+ ["encyrillic"]=1085,
+ ["endash"]=8211,
+ ["endashvertical"]=65074,
+ ["endescendercyrillic"]=1187,
+ ["eng"]=331,
+ ["engbopomofo"]=12581,
+ ["enghecyrillic"]=1189,
+ ["enhookcyrillic"]=1224,
+ ["enspace"]=8194,
+ ["eogonek"]=281,
+ ["eokorean"]=12627,
+ ["eopen"]=603,
+ ["eopenclosed"]=666,
+ ["eopenreversed"]=604,
+ ["eopenreversedclosed"]=606,
+ ["eopenreversedhook"]=605,
+ ["eparen"]=9376,
+ ["epsilon"]=949,
+ ["epsilontonos"]=941,
+ ["equal"]=61,
+ ["equalmonospace"]=65309,
+ ["equalsmall"]=65126,
+ ["equalsuperior"]=8316,
+ ["equivalence"]=8801,
+ ["erbopomofo"]=12582,
+ ["ercyrillic"]=1088,
+ ["ereversed"]=600,
+ ["ereversedcyrillic"]=1101,
+ ["escyrillic"]=1089,
+ ["esdescendercyrillic"]=1195,
+ ["esh"]=643,
+ ["eshcurl"]=646,
+ ["eshortdeva"]=2318,
+ ["eshortvowelsigndeva"]=2374,
+ ["eshreversedloop"]=426,
+ ["eshsquatreversed"]=645,
+ ["esmallhiragana"]=12359,
+ ["esmallkatakana"]=12455,
+ ["esmallkatakanahalfwidth"]=65386,
+ ["estimated"]=8494,
+ ["eta"]=951,
+ ["etarmenian"]=1384,
+ ["etatonos"]=942,
+ ["eth"]=240,
+ ["etilde"]=7869,
+ ["etildebelow"]=7707,
+ ["etnahtafoukhhebrew"]=1425,
+ ["etnahtafoukhlefthebrew"]=1425,
+ ["etnahtahebrew"]=1425,
+ ["etnahtalefthebrew"]=1425,
+ ["eturned"]=477,
+ ["eukorean"]=12641,
+ ["euro"]=8364,
+ ["evowelsignbengali"]=2503,
+ ["evowelsigndeva"]=2375,
+ ["evowelsigngujarati"]=2759,
+ ["exclam"]=33,
+ ["exclamarmenian"]=1372,
+ ["exclamdbl"]=8252,
+ ["exclamdown"]=161,
+ ["exclammonospace"]=65281,
+ ["existential"]=8707,
+ ["ezh"]=658,
+ ["ezhcaron"]=495,
+ ["ezhcurl"]=659,
+ ["ezhreversed"]=441,
+ ["ezhtail"]=442,
+ ["f"]=102,
+ ["fadeva"]=2398,
+ ["fagurmukhi"]=2654,
+ ["fahrenheit"]=8457,
+ ["fathaarabic"]=1614,
+ ["fathalowarabic"]=1614,
+ ["fathatanarabic"]=1611,
+ ["fbopomofo"]=12552,
+ ["fcircle"]=9429,
+ ["fdotaccent"]=7711,
+ ["feharabic"]=1601,
+ ["feharmenian"]=1414,
+ ["fehfinalarabic"]=65234,
+ ["fehinitialarabic"]=65235,
+ ["fehmedialarabic"]=65236,
+ ["feicoptic"]=997,
+ ["female"]=9792,
+ ["ff"]=64256,
+ ["ffi"]=64259,
+ ["ffl"]=64260,
+ ["fi"]=64257,
+ ["fifteencircle"]=9326,
+ ["fifteenparen"]=9346,
+ ["fifteenperiod"]=9366,
+ ["figuredash"]=8210,
+ ["filledbox"]=9632,
+ ["filledrect"]=9644,
+ ["finalkaf"]=1498,
+ ["finalkafdagesh"]=64314,
+ ["finalkafdageshhebrew"]=64314,
+ ["finalkafhebrew"]=1498,
+ ["finalkafqamats"]=1498,
+ ["finalkafqamatshebrew"]=1498,
+ ["finalkafsheva"]=1498,
+ ["finalkafshevahebrew"]=1498,
+ ["finalmem"]=1501,
+ ["finalmemhebrew"]=1501,
+ ["finalnun"]=1503,
+ ["finalnunhebrew"]=1503,
+ ["finalpe"]=1507,
+ ["finalpehebrew"]=1507,
+ ["finaltsadi"]=1509,
+ ["finaltsadihebrew"]=1509,
+ ["firsttonechinese"]=713,
+ ["fisheye"]=9673,
+ ["fitacyrillic"]=1139,
+ ["five"]=53,
+ ["fivearabic"]=1637,
+ ["fivebengali"]=2539,
+ ["fivecircle"]=9316,
+ ["fivecircleinversesansserif"]=10126,
+ ["fivedeva"]=2411,
+ ["fiveeighths"]=8541,
+ ["fivegujarati"]=2795,
+ ["fivegurmukhi"]=2667,
+ ["fivehackarabic"]=1637,
+ ["fivehangzhou"]=12325,
+ ["fiveideographicparen"]=12836,
+ ["fiveinferior"]=8325,
+ ["fivemonospace"]=65301,
+ ["fiveparen"]=9336,
+ ["fiveperiod"]=9356,
+ ["fivepersian"]=1781,
+ ["fiveroman"]=8564,
+ ["fivesuperior"]=8309,
+ ["fivethai"]=3669,
+ ["fl"]=64258,
+ ["florin"]=402,
+ ["fmonospace"]=65350,
+ ["fmsquare"]=13209,
+ ["fofanthai"]=3615,
+ ["fofathai"]=3613,
+ ["fongmanthai"]=3663,
+ ["forall"]=8704,
+ ["four"]=52,
+ ["fourarabic"]=1636,
+ ["fourbengali"]=2538,
+ ["fourcircle"]=9315,
+ ["fourcircleinversesansserif"]=10125,
+ ["fourdeva"]=2410,
+ ["fourgujarati"]=2794,
+ ["fourgurmukhi"]=2666,
+ ["fourhackarabic"]=1636,
+ ["fourhangzhou"]=12324,
+ ["fourideographicparen"]=12835,
+ ["fourinferior"]=8324,
+ ["fourmonospace"]=65300,
+ ["fournumeratorbengali"]=2551,
+ ["fourparen"]=9335,
+ ["fourperiod"]=9355,
+ ["fourpersian"]=1780,
+ ["fourroman"]=8563,
+ ["foursuperior"]=8308,
+ ["fourteencircle"]=9325,
+ ["fourteenparen"]=9345,
+ ["fourteenperiod"]=9365,
+ ["fourthai"]=3668,
+ ["fourthtonechinese"]=715,
+ ["fparen"]=9377,
+ ["fraction"]=8260,
+ ["franc"]=8355,
+ ["g"]=103,
+ ["gabengali"]=2455,
+ ["gacute"]=501,
+ ["gadeva"]=2327,
+ ["gafarabic"]=1711,
+ ["gaffinalarabic"]=64403,
+ ["gafinitialarabic"]=64404,
+ ["gafmedialarabic"]=64405,
+ ["gagujarati"]=2711,
+ ["gagurmukhi"]=2583,
+ ["gahiragana"]=12364,
+ ["gakatakana"]=12460,
+ ["gamma"]=947,
+ ["gammalatinsmall"]=611,
+ ["gammasuperior"]=736,
+ ["gangiacoptic"]=1003,
+ ["gbopomofo"]=12557,
+ ["gbreve"]=287,
+ ["gcaron"]=487,
+ ["gcedilla"]=291,
+ ["gcircle"]=9430,
+ ["gcircumflex"]=285,
+ ["gcommaaccent"]=291,
+ ["gdot"]=289,
+ ["gdotaccent"]=289,
+ ["gecyrillic"]=1075,
+ ["gehiragana"]=12370,
+ ["gekatakana"]=12466,
+ ["geometricallyequal"]=8785,
+ ["gereshaccenthebrew"]=1436,
+ ["gereshhebrew"]=1523,
+ ["gereshmuqdamhebrew"]=1437,
+ ["germandbls"]=223,
+ ["gershayimaccenthebrew"]=1438,
+ ["gershayimhebrew"]=1524,
+ ["getamark"]=12307,
+ ["ghabengali"]=2456,
+ ["ghadarmenian"]=1394,
+ ["ghadeva"]=2328,
+ ["ghagujarati"]=2712,
+ ["ghagurmukhi"]=2584,
+ ["ghainarabic"]=1594,
+ ["ghainfinalarabic"]=65230,
+ ["ghaininitialarabic"]=65231,
+ ["ghainmedialarabic"]=65232,
+ ["ghemiddlehookcyrillic"]=1173,
+ ["ghestrokecyrillic"]=1171,
+ ["gheupturncyrillic"]=1169,
+ ["ghhadeva"]=2394,
+ ["ghhagurmukhi"]=2650,
+ ["ghook"]=608,
+ ["ghzsquare"]=13203,
+ ["gihiragana"]=12366,
+ ["gikatakana"]=12462,
+ ["gimarmenian"]=1379,
+ ["gimel"]=1490,
+ ["gimeldagesh"]=64306,
+ ["gimeldageshhebrew"]=64306,
+ ["gimelhebrew"]=1490,
+ ["gjecyrillic"]=1107,
+ ["glottalinvertedstroke"]=446,
+ ["glottalstop"]=660,
+ ["glottalstopinverted"]=662,
+ ["glottalstopmod"]=704,
+ ["glottalstopreversed"]=661,
+ ["glottalstopreversedmod"]=705,
+ ["glottalstopreversedsuperior"]=740,
+ ["glottalstopstroke"]=673,
+ ["glottalstopstrokereversed"]=674,
+ ["gmacron"]=7713,
+ ["gmonospace"]=65351,
+ ["gohiragana"]=12372,
+ ["gokatakana"]=12468,
+ ["gparen"]=9378,
+ ["gpasquare"]=13228,
+ ["gradient"]=8711,
+ ["grave"]=96,
+ ["gravebelowcmb"]=790,
+ ["gravecmb"]=768,
+ ["gravecomb"]=768,
+ ["gravedeva"]=2387,
+ ["gravelowmod"]=718,
+ ["gravemonospace"]=65344,
+ ["gravetonecmb"]=832,
+ ["greater"]=62,
+ ["greaterequal"]=8805,
+ ["greaterequalorless"]=8923,
+ ["greatermonospace"]=65310,
+ ["greaterorequivalent"]=8819,
+ ["greaterorless"]=8823,
+ ["greateroverequal"]=8807,
+ ["greatersmall"]=65125,
+ ["gscript"]=609,
+ ["gstroke"]=485,
+ ["guhiragana"]=12368,
+ ["guillemotleft"]=171,
+ ["guillemotright"]=187,
+ ["guilsinglleft"]=8249,
+ ["guilsinglright"]=8250,
+ ["gukatakana"]=12464,
+ ["guramusquare"]=13080,
+ ["gysquare"]=13257,
+ ["h"]=104,
+ ["haabkhasiancyrillic"]=1193,
+ ["haaltonearabic"]=1729,
+ ["habengali"]=2489,
+ ["hadescendercyrillic"]=1203,
+ ["hadeva"]=2361,
+ ["hagujarati"]=2745,
+ ["hagurmukhi"]=2617,
+ ["haharabic"]=1581,
+ ["hahfinalarabic"]=65186,
+ ["hahinitialarabic"]=65187,
+ ["hahiragana"]=12399,
+ ["hahmedialarabic"]=65188,
+ ["haitusquare"]=13098,
+ ["hakatakana"]=12495,
+ ["hakatakanahalfwidth"]=65418,
+ ["halantgurmukhi"]=2637,
+ ["hamzaarabic"]=1569,
+ ["hamzadammaarabic"]=1569,
+ ["hamzadammatanarabic"]=1569,
+ ["hamzafathaarabic"]=1569,
+ ["hamzafathatanarabic"]=1569,
+ ["hamzalowarabic"]=1569,
+ ["hamzalowkasraarabic"]=1569,
+ ["hamzalowkasratanarabic"]=1569,
+ ["hamzasukunarabic"]=1569,
+ ["hangulfiller"]=12644,
+ ["hardsigncyrillic"]=1098,
+ ["harpoonleftbarbup"]=8636,
+ ["harpoonrightbarbup"]=8640,
+ ["hasquare"]=13258,
+ ["hatafpatah"]=1458,
+ ["hatafpatah16"]=1458,
+ ["hatafpatah23"]=1458,
+ ["hatafpatah2f"]=1458,
+ ["hatafpatahhebrew"]=1458,
+ ["hatafpatahnarrowhebrew"]=1458,
+ ["hatafpatahquarterhebrew"]=1458,
+ ["hatafpatahwidehebrew"]=1458,
+ ["hatafqamats"]=1459,
+ ["hatafqamats1b"]=1459,
+ ["hatafqamats28"]=1459,
+ ["hatafqamats34"]=1459,
+ ["hatafqamatshebrew"]=1459,
+ ["hatafqamatsnarrowhebrew"]=1459,
+ ["hatafqamatsquarterhebrew"]=1459,
+ ["hatafqamatswidehebrew"]=1459,
+ ["hatafsegol"]=1457,
+ ["hatafsegol17"]=1457,
+ ["hatafsegol24"]=1457,
+ ["hatafsegol30"]=1457,
+ ["hatafsegolhebrew"]=1457,
+ ["hatafsegolnarrowhebrew"]=1457,
+ ["hatafsegolquarterhebrew"]=1457,
+ ["hatafsegolwidehebrew"]=1457,
+ ["hbar"]=295,
+ ["hbopomofo"]=12559,
+ ["hbrevebelow"]=7723,
+ ["hcedilla"]=7721,
+ ["hcircle"]=9431,
+ ["hcircumflex"]=293,
+ ["hdieresis"]=7719,
+ ["hdotaccent"]=7715,
+ ["hdotbelow"]=7717,
+ ["he"]=1492,
+ ["heart"]=9829,
+ ["heartsuitblack"]=9829,
+ ["heartsuitwhite"]=9825,
+ ["hedagesh"]=64308,
+ ["hedageshhebrew"]=64308,
+ ["hehaltonearabic"]=1729,
+ ["heharabic"]=1607,
+ ["hehebrew"]=1492,
+ ["hehfinalaltonearabic"]=64423,
+ ["hehfinalalttwoarabic"]=65258,
+ ["hehfinalarabic"]=65258,
+ ["hehhamzaabovefinalarabic"]=64421,
+ ["hehhamzaaboveisolatedarabic"]=64420,
+ ["hehinitialaltonearabic"]=64424,
+ ["hehinitialarabic"]=65259,
+ ["hehiragana"]=12408,
+ ["hehmedialaltonearabic"]=64425,
+ ["hehmedialarabic"]=65260,
+ ["heiseierasquare"]=13179,
+ ["hekatakana"]=12504,
+ ["hekatakanahalfwidth"]=65421,
+ ["hekutaarusquare"]=13110,
+ ["henghook"]=615,
+ ["herutusquare"]=13113,
+ ["het"]=1495,
+ ["hethebrew"]=1495,
+ ["hhook"]=614,
+ ["hhooksuperior"]=689,
+ ["hieuhacirclekorean"]=12923,
+ ["hieuhaparenkorean"]=12827,
+ ["hieuhcirclekorean"]=12909,
+ ["hieuhkorean"]=12622,
+ ["hieuhparenkorean"]=12813,
+ ["hihiragana"]=12402,
+ ["hikatakana"]=12498,
+ ["hikatakanahalfwidth"]=65419,
+ ["hiriq"]=1460,
+ ["hiriq14"]=1460,
+ ["hiriq21"]=1460,
+ ["hiriq2d"]=1460,
+ ["hiriqhebrew"]=1460,
+ ["hiriqnarrowhebrew"]=1460,
+ ["hiriqquarterhebrew"]=1460,
+ ["hiriqwidehebrew"]=1460,
+ ["hlinebelow"]=7830,
+ ["hmonospace"]=65352,
+ ["hoarmenian"]=1392,
+ ["hohipthai"]=3627,
+ ["hohiragana"]=12411,
+ ["hokatakana"]=12507,
+ ["hokatakanahalfwidth"]=65422,
+ ["holam"]=1465,
+ ["holam19"]=1465,
+ ["holam26"]=1465,
+ ["holam32"]=1465,
+ ["holamhebrew"]=1465,
+ ["holamnarrowhebrew"]=1465,
+ ["holamquarterhebrew"]=1465,
+ ["holamwidehebrew"]=1465,
+ ["honokhukthai"]=3630,
+ ["hookabovecomb"]=777,
+ ["hookcmb"]=777,
+ ["hookpalatalizedbelowcmb"]=801,
+ ["hookretroflexbelowcmb"]=802,
+ ["hoonsquare"]=13122,
+ ["horicoptic"]=1001,
+ ["horizontalbar"]=8213,
+ ["horncmb"]=795,
+ ["hotsprings"]=9832,
+ ["house"]=8962,
+ ["hparen"]=9379,
+ ["hsuperior"]=688,
+ ["hturned"]=613,
+ ["huhiragana"]=12405,
+ ["huiitosquare"]=13107,
+ ["hukatakana"]=12501,
+ ["hukatakanahalfwidth"]=65420,
+ ["hungarumlaut"]=733,
+ ["hungarumlautcmb"]=779,
+ ["hv"]=405,
+ ["hyphen"]=45,
+ ["hyphenmonospace"]=65293,
+ ["hyphensmall"]=65123,
+ ["hyphentwo"]=8208,
+ ["i"]=105,
+ ["iacute"]=237,
+ ["iacyrillic"]=1103,
+ ["ibengali"]=2439,
+ ["ibopomofo"]=12583,
+ ["ibreve"]=301,
+ ["icaron"]=464,
+ ["icircle"]=9432,
+ ["icircumflex"]=238,
+ ["icyrillic"]=1110,
+ ["idblgrave"]=521,
+ ["ideographearthcircle"]=12943,
+ ["ideographfirecircle"]=12939,
+ ["ideographicallianceparen"]=12863,
+ ["ideographiccallparen"]=12858,
+ ["ideographiccentrecircle"]=12965,
+ ["ideographicclose"]=12294,
+ ["ideographiccomma"]=12289,
+ ["ideographiccommaleft"]=65380,
+ ["ideographiccongratulationparen"]=12855,
+ ["ideographiccorrectcircle"]=12963,
+ ["ideographicearthparen"]=12847,
+ ["ideographicenterpriseparen"]=12861,
+ ["ideographicexcellentcircle"]=12957,
+ ["ideographicfestivalparen"]=12864,
+ ["ideographicfinancialcircle"]=12950,
+ ["ideographicfinancialparen"]=12854,
+ ["ideographicfireparen"]=12843,
+ ["ideographichaveparen"]=12850,
+ ["ideographichighcircle"]=12964,
+ ["ideographiciterationmark"]=12293,
+ ["ideographiclaborcircle"]=12952,
+ ["ideographiclaborparen"]=12856,
+ ["ideographicleftcircle"]=12967,
+ ["ideographiclowcircle"]=12966,
+ ["ideographicmedicinecircle"]=12969,
+ ["ideographicmetalparen"]=12846,
+ ["ideographicmoonparen"]=12842,
+ ["ideographicnameparen"]=12852,
+ ["ideographicperiod"]=12290,
+ ["ideographicprintcircle"]=12958,
+ ["ideographicreachparen"]=12867,
+ ["ideographicrepresentparen"]=12857,
+ ["ideographicresourceparen"]=12862,
+ ["ideographicrightcircle"]=12968,
+ ["ideographicsecretcircle"]=12953,
+ ["ideographicselfparen"]=12866,
+ ["ideographicsocietyparen"]=12851,
+ ["ideographicspace"]=12288,
+ ["ideographicspecialparen"]=12853,
+ ["ideographicstockparen"]=12849,
+ ["ideographicstudyparen"]=12859,
+ ["ideographicsunparen"]=12848,
+ ["ideographicsuperviseparen"]=12860,
+ ["ideographicwaterparen"]=12844,
+ ["ideographicwoodparen"]=12845,
+ ["ideographiczero"]=12295,
+ ["ideographmetalcircle"]=12942,
+ ["ideographmooncircle"]=12938,
+ ["ideographnamecircle"]=12948,
+ ["ideographsuncircle"]=12944,
+ ["ideographwatercircle"]=12940,
+ ["ideographwoodcircle"]=12941,
+ ["ideva"]=2311,
+ ["idieresis"]=239,
+ ["idieresisacute"]=7727,
+ ["idieresiscyrillic"]=1253,
+ ["idotbelow"]=7883,
+ ["iebrevecyrillic"]=1239,
+ ["iecyrillic"]=1077,
+ ["ieungacirclekorean"]=12917,
+ ["ieungaparenkorean"]=12821,
+ ["ieungcirclekorean"]=12903,
+ ["ieungkorean"]=12615,
+ ["ieungparenkorean"]=12807,
+ ["igrave"]=236,
+ ["igujarati"]=2695,
+ ["igurmukhi"]=2567,
+ ["ihiragana"]=12356,
+ ["ihookabove"]=7881,
+ ["iibengali"]=2440,
+ ["iicyrillic"]=1080,
+ ["iideva"]=2312,
+ ["iigujarati"]=2696,
+ ["iigurmukhi"]=2568,
+ ["iimatragurmukhi"]=2624,
+ ["iinvertedbreve"]=523,
+ ["iishortcyrillic"]=1081,
+ ["iivowelsignbengali"]=2496,
+ ["iivowelsigndeva"]=2368,
+ ["iivowelsigngujarati"]=2752,
+ ["ij"]=307,
+ ["ikatakana"]=12452,
+ ["ikatakanahalfwidth"]=65394,
+ ["ikorean"]=12643,
+ ["ilde"]=732,
+ ["iluyhebrew"]=1452,
+ ["imacron"]=299,
+ ["imacroncyrillic"]=1251,
+ ["imageorapproximatelyequal"]=8787,
+ ["imatragurmukhi"]=2623,
+ ["imonospace"]=65353,
+ ["increment"]=8710,
+ ["infinity"]=8734,
+ ["iniarmenian"]=1387,
+ ["integral"]=8747,
+ ["integralbottom"]=8993,
+ ["integralbt"]=8993,
+ ["integraltop"]=8992,
+ ["integraltp"]=8992,
+ ["intersection"]=8745,
+ ["intisquare"]=13061,
+ ["invbullet"]=9688,
+ ["invcircle"]=9689,
+ ["invsmileface"]=9787,
+ ["iocyrillic"]=1105,
+ ["iogonek"]=303,
+ ["iota"]=953,
+ ["iotadieresis"]=970,
+ ["iotadieresistonos"]=912,
+ ["iotalatin"]=617,
+ ["iotatonos"]=943,
+ ["iparen"]=9380,
+ ["irigurmukhi"]=2674,
+ ["ismallhiragana"]=12355,
+ ["ismallkatakana"]=12451,
+ ["ismallkatakanahalfwidth"]=65384,
+ ["issharbengali"]=2554,
+ ["istroke"]=616,
+ ["iterationhiragana"]=12445,
+ ["iterationkatakana"]=12541,
+ ["itilde"]=297,
+ ["itildebelow"]=7725,
+ ["iubopomofo"]=12585,
+ ["iucyrillic"]=1102,
+ ["ivowelsignbengali"]=2495,
+ ["ivowelsigndeva"]=2367,
+ ["ivowelsigngujarati"]=2751,
+ ["izhitsacyrillic"]=1141,
+ ["izhitsadblgravecyrillic"]=1143,
+ ["j"]=106,
+ ["jaarmenian"]=1393,
+ ["jabengali"]=2460,
+ ["jadeva"]=2332,
+ ["jagujarati"]=2716,
+ ["jagurmukhi"]=2588,
+ ["jbopomofo"]=12560,
+ ["jcaron"]=496,
+ ["jcircle"]=9433,
+ ["jcircumflex"]=309,
+ ["jcrossedtail"]=669,
+ ["jdotlessstroke"]=607,
+ ["jecyrillic"]=1112,
+ ["jeemarabic"]=1580,
+ ["jeemfinalarabic"]=65182,
+ ["jeeminitialarabic"]=65183,
+ ["jeemmedialarabic"]=65184,
+ ["jeharabic"]=1688,
+ ["jehfinalarabic"]=64395,
+ ["jhabengali"]=2461,
+ ["jhadeva"]=2333,
+ ["jhagujarati"]=2717,
+ ["jhagurmukhi"]=2589,
+ ["jheharmenian"]=1403,
+ ["jis"]=12292,
+ ["jmonospace"]=65354,
+ ["jparen"]=9381,
+ ["jsuperior"]=690,
+ ["k"]=107,
+ ["kabashkircyrillic"]=1185,
+ ["kabengali"]=2453,
+ ["kacute"]=7729,
+ ["kacyrillic"]=1082,
+ ["kadescendercyrillic"]=1179,
+ ["kadeva"]=2325,
+ ["kaf"]=1499,
+ ["kafarabic"]=1603,
+ ["kafdagesh"]=64315,
+ ["kafdageshhebrew"]=64315,
+ ["kaffinalarabic"]=65242,
+ ["kafhebrew"]=1499,
+ ["kafinitialarabic"]=65243,
+ ["kafmedialarabic"]=65244,
+ ["kafrafehebrew"]=64333,
+ ["kagujarati"]=2709,
+ ["kagurmukhi"]=2581,
+ ["kahiragana"]=12363,
+ ["kahookcyrillic"]=1220,
+ ["kakatakana"]=12459,
+ ["kakatakanahalfwidth"]=65398,
+ ["kappa"]=954,
+ ["kappasymbolgreek"]=1008,
+ ["kapyeounmieumkorean"]=12657,
+ ["kapyeounphieuphkorean"]=12676,
+ ["kapyeounpieupkorean"]=12664,
+ ["kapyeounssangpieupkorean"]=12665,
+ ["karoriisquare"]=13069,
+ ["kashidaautoarabic"]=1600,
+ ["kashidaautonosidebearingarabic"]=1600,
+ ["kasmallkatakana"]=12533,
+ ["kasquare"]=13188,
+ ["kasraarabic"]=1616,
+ ["kasratanarabic"]=1613,
+ ["kastrokecyrillic"]=1183,
+ ["katahiraprolongmarkhalfwidth"]=65392,
+ ["kaverticalstrokecyrillic"]=1181,
+ ["kbopomofo"]=12558,
+ ["kcalsquare"]=13193,
+ ["kcaron"]=489,
+ ["kcedilla"]=311,
+ ["kcircle"]=9434,
+ ["kcommaaccent"]=311,
+ ["kdotbelow"]=7731,
+ ["keharmenian"]=1412,
+ ["kehiragana"]=12369,
+ ["kekatakana"]=12465,
+ ["kekatakanahalfwidth"]=65401,
+ ["kenarmenian"]=1391,
+ ["kesmallkatakana"]=12534,
+ ["kgreenlandic"]=312,
+ ["khabengali"]=2454,
+ ["khacyrillic"]=1093,
+ ["khadeva"]=2326,
+ ["khagujarati"]=2710,
+ ["khagurmukhi"]=2582,
+ ["khaharabic"]=1582,
+ ["khahfinalarabic"]=65190,
+ ["khahinitialarabic"]=65191,
+ ["khahmedialarabic"]=65192,
+ ["kheicoptic"]=999,
+ ["khhadeva"]=2393,
+ ["khhagurmukhi"]=2649,
+ ["khieukhacirclekorean"]=12920,
+ ["khieukhaparenkorean"]=12824,
+ ["khieukhcirclekorean"]=12906,
+ ["khieukhkorean"]=12619,
+ ["khieukhparenkorean"]=12810,
+ ["khokhaithai"]=3586,
+ ["khokhonthai"]=3589,
+ ["khokhuatthai"]=3587,
+ ["khokhwaithai"]=3588,
+ ["khomutthai"]=3675,
+ ["khook"]=409,
+ ["khorakhangthai"]=3590,
+ ["khzsquare"]=13201,
+ ["kihiragana"]=12365,
+ ["kikatakana"]=12461,
+ ["kikatakanahalfwidth"]=65399,
+ ["kiroguramusquare"]=13077,
+ ["kiromeetorusquare"]=13078,
+ ["kirosquare"]=13076,
+ ["kiyeokacirclekorean"]=12910,
+ ["kiyeokaparenkorean"]=12814,
+ ["kiyeokcirclekorean"]=12896,
+ ["kiyeokkorean"]=12593,
+ ["kiyeokparenkorean"]=12800,
+ ["kiyeoksioskorean"]=12595,
+ ["kjecyrillic"]=1116,
+ ["klinebelow"]=7733,
+ ["klsquare"]=13208,
+ ["kmcubedsquare"]=13222,
+ ["kmonospace"]=65355,
+ ["kmsquaredsquare"]=13218,
+ ["kohiragana"]=12371,
+ ["kohmsquare"]=13248,
+ ["kokaithai"]=3585,
+ ["kokatakana"]=12467,
+ ["kokatakanahalfwidth"]=65402,
+ ["kooposquare"]=13086,
+ ["koppacyrillic"]=1153,
+ ["koreanstandardsymbol"]=12927,
+ ["koroniscmb"]=835,
+ ["kparen"]=9382,
+ ["kpasquare"]=13226,
+ ["ksicyrillic"]=1135,
+ ["ktsquare"]=13263,
+ ["kturned"]=670,
+ ["kuhiragana"]=12367,
+ ["kukatakana"]=12463,
+ ["kukatakanahalfwidth"]=65400,
+ ["kvsquare"]=13240,
+ ["kwsquare"]=13246,
+ ["l"]=108,
+ ["labengali"]=2482,
+ ["lacute"]=314,
+ ["ladeva"]=2354,
+ ["lagujarati"]=2738,
+ ["lagurmukhi"]=2610,
+ ["lakkhangyaothai"]=3653,
+ ["lamaleffinalarabic"]=65276,
+ ["lamalefhamzaabovefinalarabic"]=65272,
+ ["lamalefhamzaaboveisolatedarabic"]=65271,
+ ["lamalefhamzabelowfinalarabic"]=65274,
+ ["lamalefhamzabelowisolatedarabic"]=65273,
+ ["lamalefisolatedarabic"]=65275,
+ ["lamalefmaddaabovefinalarabic"]=65270,
+ ["lamalefmaddaaboveisolatedarabic"]=65269,
+ ["lamarabic"]=1604,
+ ["lambda"]=955,
+ ["lambdastroke"]=411,
+ ["lamed"]=1500,
+ ["lameddagesh"]=64316,
+ ["lameddageshhebrew"]=64316,
+ ["lamedhebrew"]=1500,
+ ["lamedholam"]=1500,
+ ["lamedholamdagesh"]=1500,
+ ["lamedholamdageshhebrew"]=1500,
+ ["lamedholamhebrew"]=1500,
+ ["lamfinalarabic"]=65246,
+ ["lamhahinitialarabic"]=64714,
+ ["laminitialarabic"]=65247,
+ ["lamjeeminitialarabic"]=64713,
+ ["lamkhahinitialarabic"]=64715,
+ ["lamlamhehisolatedarabic"]=65010,
+ ["lammedialarabic"]=65248,
+ ["lammeemhahinitialarabic"]=64904,
+ ["lammeeminitialarabic"]=64716,
+ ["lammeemjeeminitialarabic"]=65247,
+ ["lammeemkhahinitialarabic"]=65247,
+ ["largecircle"]=9711,
+ ["lbar"]=410,
+ ["lbelt"]=620,
+ ["lbopomofo"]=12556,
+ ["lcaron"]=318,
+ ["lcedilla"]=316,
+ ["lcircle"]=9435,
+ ["lcircumflexbelow"]=7741,
+ ["lcommaaccent"]=316,
+ ["ldot"]=320,
+ ["ldotaccent"]=320,
+ ["ldotbelow"]=7735,
+ ["ldotbelowmacron"]=7737,
+ ["leftangleabovecmb"]=794,
+ ["lefttackbelowcmb"]=792,
+ ["less"]=60,
+ ["lessequal"]=8804,
+ ["lessequalorgreater"]=8922,
+ ["lessmonospace"]=65308,
+ ["lessorequivalent"]=8818,
+ ["lessorgreater"]=8822,
+ ["lessoverequal"]=8806,
+ ["lesssmall"]=65124,
+ ["lezh"]=622,
+ ["lfblock"]=9612,
+ ["lhookretroflex"]=621,
+ ["lira"]=8356,
+ ["liwnarmenian"]=1388,
+ ["lj"]=457,
+ ["ljecyrillic"]=1113,
+ ["lladeva"]=2355,
+ ["llagujarati"]=2739,
+ ["llinebelow"]=7739,
+ ["llladeva"]=2356,
+ ["llvocalicbengali"]=2529,
+ ["llvocalicdeva"]=2401,
+ ["llvocalicvowelsignbengali"]=2531,
+ ["llvocalicvowelsigndeva"]=2403,
+ ["lmiddletilde"]=619,
+ ["lmonospace"]=65356,
+ ["lmsquare"]=13264,
+ ["lochulathai"]=3628,
+ ["logicaland"]=8743,
+ ["logicalnot"]=172,
+ ["logicalnotreversed"]=8976,
+ ["logicalor"]=8744,
+ ["lolingthai"]=3621,
+ ["longs"]=383,
+ ["lowlinecenterline"]=65102,
+ ["lowlinecmb"]=818,
+ ["lowlinedashed"]=65101,
+ ["lozenge"]=9674,
+ ["lparen"]=9383,
+ ["lslash"]=322,
+ ["lsquare"]=8467,
+ ["ltshade"]=9617,
+ ["luthai"]=3622,
+ ["lvocalicbengali"]=2444,
+ ["lvocalicdeva"]=2316,
+ ["lvocalicvowelsignbengali"]=2530,
+ ["lvocalicvowelsigndeva"]=2402,
+ ["lxsquare"]=13267,
+ ["m"]=109,
+ ["mabengali"]=2478,
+ ["macron"]=175,
+ ["macronbelowcmb"]=817,
+ ["macroncmb"]=772,
+ ["macronlowmod"]=717,
+ ["macronmonospace"]=65507,
+ ["macute"]=7743,
+ ["madeva"]=2350,
+ ["magujarati"]=2734,
+ ["magurmukhi"]=2606,
+ ["mahapakhhebrew"]=1444,
+ ["mahapakhlefthebrew"]=1444,
+ ["mahiragana"]=12414,
+ ["maichattawathai"]=3659,
+ ["maiekthai"]=3656,
+ ["maihanakatthai"]=3633,
+ ["maitaikhuthai"]=3655,
+ ["maithothai"]=3657,
+ ["maitrithai"]=3658,
+ ["maiyamokthai"]=3654,
+ ["makatakana"]=12510,
+ ["makatakanahalfwidth"]=65423,
+ ["male"]=9794,
+ ["mansyonsquare"]=13127,
+ ["maqafhebrew"]=1470,
+ ["mars"]=9794,
+ ["masoracirclehebrew"]=1455,
+ ["masquare"]=13187,
+ ["mbopomofo"]=12551,
+ ["mbsquare"]=13268,
+ ["mcircle"]=9436,
+ ["mcubedsquare"]=13221,
+ ["mdotaccent"]=7745,
+ ["mdotbelow"]=7747,
+ ["meemarabic"]=1605,
+ ["meemfinalarabic"]=65250,
+ ["meeminitialarabic"]=65251,
+ ["meemmedialarabic"]=65252,
+ ["meemmeeminitialarabic"]=64721,
+ ["meemmeemisolatedarabic"]=64584,
+ ["meetorusquare"]=13133,
+ ["mehiragana"]=12417,
+ ["meizierasquare"]=13182,
+ ["mekatakana"]=12513,
+ ["mekatakanahalfwidth"]=65426,
+ ["mem"]=1502,
+ ["memdagesh"]=64318,
+ ["memdageshhebrew"]=64318,
+ ["memhebrew"]=1502,
+ ["menarmenian"]=1396,
+ ["merkhahebrew"]=1445,
+ ["merkhakefulahebrew"]=1446,
+ ["merkhakefulalefthebrew"]=1446,
+ ["merkhalefthebrew"]=1445,
+ ["mhook"]=625,
+ ["mhzsquare"]=13202,
+ ["middledotkatakanahalfwidth"]=65381,
+ ["middot"]=183,
+ ["mieumacirclekorean"]=12914,
+ ["mieumaparenkorean"]=12818,
+ ["mieumcirclekorean"]=12900,
+ ["mieumkorean"]=12609,
+ ["mieumpansioskorean"]=12656,
+ ["mieumparenkorean"]=12804,
+ ["mieumpieupkorean"]=12654,
+ ["mieumsioskorean"]=12655,
+ ["mihiragana"]=12415,
+ ["mikatakana"]=12511,
+ ["mikatakanahalfwidth"]=65424,
+ ["minus"]=8722,
+ ["minusbelowcmb"]=800,
+ ["minuscircle"]=8854,
+ ["minusmod"]=727,
+ ["minusplus"]=8723,
+ ["minute"]=8242,
+ ["miribaarusquare"]=13130,
+ ["mirisquare"]=13129,
+ ["mlonglegturned"]=624,
+ ["mlsquare"]=13206,
+ ["mmcubedsquare"]=13219,
+ ["mmonospace"]=65357,
+ ["mmsquaredsquare"]=13215,
+ ["mohiragana"]=12418,
+ ["mohmsquare"]=13249,
+ ["mokatakana"]=12514,
+ ["mokatakanahalfwidth"]=65427,
+ ["molsquare"]=13270,
+ ["momathai"]=3617,
+ ["moverssquare"]=13223,
+ ["moverssquaredsquare"]=13224,
+ ["mparen"]=9384,
+ ["mpasquare"]=13227,
+ ["mssquare"]=13235,
+ ["mturned"]=623,
+ ["mu"]=181,
+ ["mu1"]=181,
+ ["muasquare"]=13186,
+ ["muchgreater"]=8811,
+ ["muchless"]=8810,
+ ["mufsquare"]=13196,
+ ["mugreek"]=956,
+ ["mugsquare"]=13197,
+ ["muhiragana"]=12416,
+ ["mukatakana"]=12512,
+ ["mukatakanahalfwidth"]=65425,
+ ["mulsquare"]=13205,
+ ["multiply"]=215,
+ ["mumsquare"]=13211,
+ ["munahhebrew"]=1443,
+ ["munahlefthebrew"]=1443,
+ ["musicalnote"]=9834,
+ ["musicalnotedbl"]=9835,
+ ["musicflatsign"]=9837,
+ ["musicsharpsign"]=9839,
+ ["mussquare"]=13234,
+ ["muvsquare"]=13238,
+ ["muwsquare"]=13244,
+ ["mvmegasquare"]=13241,
+ ["mvsquare"]=13239,
+ ["mwmegasquare"]=13247,
+ ["mwsquare"]=13245,
+ ["n"]=110,
+ ["nabengali"]=2472,
+ ["nabla"]=8711,
+ ["nacute"]=324,
+ ["nadeva"]=2344,
+ ["nagujarati"]=2728,
+ ["nagurmukhi"]=2600,
+ ["nahiragana"]=12394,
+ ["nakatakana"]=12490,
+ ["nakatakanahalfwidth"]=65413,
+ ["napostrophe"]=329,
+ ["nasquare"]=13185,
+ ["nbopomofo"]=12555,
+ ["nbspace"]=160,
+ ["ncaron"]=328,
+ ["ncedilla"]=326,
+ ["ncircle"]=9437,
+ ["ncircumflexbelow"]=7755,
+ ["ncommaaccent"]=326,
+ ["ndotaccent"]=7749,
+ ["ndotbelow"]=7751,
+ ["nehiragana"]=12397,
+ ["nekatakana"]=12493,
+ ["nekatakanahalfwidth"]=65416,
+ ["newsheqelsign"]=8362,
+ ["nfsquare"]=13195,
+ ["ngabengali"]=2457,
+ ["ngadeva"]=2329,
+ ["ngagujarati"]=2713,
+ ["ngagurmukhi"]=2585,
+ ["ngonguthai"]=3591,
+ ["nhiragana"]=12435,
+ ["nhookleft"]=626,
+ ["nhookretroflex"]=627,
+ ["nieunacirclekorean"]=12911,
+ ["nieunaparenkorean"]=12815,
+ ["nieuncieuckorean"]=12597,
+ ["nieuncirclekorean"]=12897,
+ ["nieunhieuhkorean"]=12598,
+ ["nieunkorean"]=12596,
+ ["nieunpansioskorean"]=12648,
+ ["nieunparenkorean"]=12801,
+ ["nieunsioskorean"]=12647,
+ ["nieuntikeutkorean"]=12646,
+ ["nihiragana"]=12395,
+ ["nikatakana"]=12491,
+ ["nikatakanahalfwidth"]=65414,
+ ["nikhahitthai"]=3661,
+ ["nine"]=57,
+ ["ninearabic"]=1641,
+ ["ninebengali"]=2543,
+ ["ninecircle"]=9320,
+ ["ninecircleinversesansserif"]=10130,
+ ["ninedeva"]=2415,
+ ["ninegujarati"]=2799,
+ ["ninegurmukhi"]=2671,
+ ["ninehackarabic"]=1641,
+ ["ninehangzhou"]=12329,
+ ["nineideographicparen"]=12840,
+ ["nineinferior"]=8329,
+ ["ninemonospace"]=65305,
+ ["nineparen"]=9340,
+ ["nineperiod"]=9360,
+ ["ninepersian"]=1785,
+ ["nineroman"]=8568,
+ ["ninesuperior"]=8313,
+ ["nineteencircle"]=9330,
+ ["nineteenparen"]=9350,
+ ["nineteenperiod"]=9370,
+ ["ninethai"]=3673,
+ ["nj"]=460,
+ ["njecyrillic"]=1114,
+ ["nkatakana"]=12531,
+ ["nkatakanahalfwidth"]=65437,
+ ["nlegrightlong"]=414,
+ ["nlinebelow"]=7753,
+ ["nmonospace"]=65358,
+ ["nmsquare"]=13210,
+ ["nnabengali"]=2467,
+ ["nnadeva"]=2339,
+ ["nnagujarati"]=2723,
+ ["nnagurmukhi"]=2595,
+ ["nnnadeva"]=2345,
+ ["nohiragana"]=12398,
+ ["nokatakana"]=12494,
+ ["nokatakanahalfwidth"]=65417,
+ ["nonbreakingspace"]=160,
+ ["nonenthai"]=3603,
+ ["nonuthai"]=3609,
+ ["noonarabic"]=1606,
+ ["noonfinalarabic"]=65254,
+ ["noonghunnaarabic"]=1722,
+ ["noonghunnafinalarabic"]=64415,
+ ["noonhehinitialarabic"]=65255,
+ ["nooninitialarabic"]=65255,
+ ["noonjeeminitialarabic"]=64722,
+ ["noonjeemisolatedarabic"]=64587,
+ ["noonmedialarabic"]=65256,
+ ["noonmeeminitialarabic"]=64725,
+ ["noonmeemisolatedarabic"]=64590,
+ ["noonnoonfinalarabic"]=64653,
+ ["notcontains"]=8716,
+ ["notelement"]=8713,
+ ["notelementof"]=8713,
+ ["notequal"]=8800,
+ ["notgreater"]=8815,
+ ["notgreaternorequal"]=8817,
+ ["notgreaternorless"]=8825,
+ ["notidentical"]=8802,
+ ["notless"]=8814,
+ ["notlessnorequal"]=8816,
+ ["notparallel"]=8742,
+ ["notprecedes"]=8832,
+ ["notsubset"]=8836,
+ ["notsucceeds"]=8833,
+ ["notsuperset"]=8837,
+ ["nowarmenian"]=1398,
+ ["nparen"]=9385,
+ ["nssquare"]=13233,
+ ["nsuperior"]=8319,
+ ["ntilde"]=241,
+ ["nu"]=957,
+ ["nuhiragana"]=12396,
+ ["nukatakana"]=12492,
+ ["nukatakanahalfwidth"]=65415,
+ ["nuktabengali"]=2492,
+ ["nuktadeva"]=2364,
+ ["nuktagujarati"]=2748,
+ ["nuktagurmukhi"]=2620,
+ ["numbersign"]=35,
+ ["numbersignmonospace"]=65283,
+ ["numbersignsmall"]=65119,
+ ["numeralsigngreek"]=884,
+ ["numeralsignlowergreek"]=885,
+ ["numero"]=8470,
+ ["nun"]=1504,
+ ["nundagesh"]=64320,
+ ["nundageshhebrew"]=64320,
+ ["nunhebrew"]=1504,
+ ["nvsquare"]=13237,
+ ["nwsquare"]=13243,
+ ["nyabengali"]=2462,
+ ["nyadeva"]=2334,
+ ["nyagujarati"]=2718,
+ ["nyagurmukhi"]=2590,
+ ["o"]=111,
+ ["oacute"]=243,
+ ["oangthai"]=3629,
+ ["obarred"]=629,
+ ["obarredcyrillic"]=1257,
+ ["obarreddieresiscyrillic"]=1259,
+ ["obengali"]=2451,
+ ["obopomofo"]=12571,
+ ["obreve"]=335,
+ ["ocandradeva"]=2321,
+ ["ocandragujarati"]=2705,
+ ["ocandravowelsigndeva"]=2377,
+ ["ocandravowelsigngujarati"]=2761,
+ ["ocaron"]=466,
+ ["ocircle"]=9438,
+ ["ocircumflex"]=244,
+ ["ocircumflexacute"]=7889,
+ ["ocircumflexdotbelow"]=7897,
+ ["ocircumflexgrave"]=7891,
+ ["ocircumflexhookabove"]=7893,
+ ["ocircumflextilde"]=7895,
+ ["ocyrillic"]=1086,
+ ["odblacute"]=337,
+ ["odblgrave"]=525,
+ ["odeva"]=2323,
+ ["odieresis"]=246,
+ ["odieresiscyrillic"]=1255,
+ ["odotbelow"]=7885,
+ ["oe"]=339,
+ ["oekorean"]=12634,
+ ["ogonek"]=731,
+ ["ogonekcmb"]=808,
+ ["ograve"]=242,
+ ["ogujarati"]=2707,
+ ["oharmenian"]=1413,
+ ["ohiragana"]=12362,
+ ["ohookabove"]=7887,
+ ["ohorn"]=417,
+ ["ohornacute"]=7899,
+ ["ohorndotbelow"]=7907,
+ ["ohorngrave"]=7901,
+ ["ohornhookabove"]=7903,
+ ["ohorntilde"]=7905,
+ ["ohungarumlaut"]=337,
+ ["oi"]=419,
+ ["oinvertedbreve"]=527,
+ ["okatakana"]=12458,
+ ["okatakanahalfwidth"]=65397,
+ ["okorean"]=12631,
+ ["olehebrew"]=1451,
+ ["omacron"]=333,
+ ["omacronacute"]=7763,
+ ["omacrongrave"]=7761,
+ ["omdeva"]=2384,
+ ["omega"]=969,
+ ["omega1"]=982,
+ ["omegacyrillic"]=1121,
+ ["omegalatinclosed"]=631,
+ ["omegaroundcyrillic"]=1147,
+ ["omegatitlocyrillic"]=1149,
+ ["omegatonos"]=974,
+ ["omgujarati"]=2768,
+ ["omicron"]=959,
+ ["omicrontonos"]=972,
+ ["omonospace"]=65359,
+ ["one"]=49,
+ ["onearabic"]=1633,
+ ["onebengali"]=2535,
+ ["onecircle"]=9312,
+ ["onecircleinversesansserif"]=10122,
+ ["onedeva"]=2407,
+ ["onedotenleader"]=8228,
+ ["oneeighth"]=8539,
+ ["onegujarati"]=2791,
+ ["onegurmukhi"]=2663,
+ ["onehackarabic"]=1633,
+ ["onehalf"]=189,
+ ["onehangzhou"]=12321,
+ ["oneideographicparen"]=12832,
+ ["oneinferior"]=8321,
+ ["onemonospace"]=65297,
+ ["onenumeratorbengali"]=2548,
+ ["oneparen"]=9332,
+ ["oneperiod"]=9352,
+ ["onepersian"]=1777,
+ ["onequarter"]=188,
+ ["oneroman"]=8560,
+ ["onesuperior"]=185,
+ ["onethai"]=3665,
+ ["onethird"]=8531,
+ ["oogonek"]=491,
+ ["oogonekmacron"]=493,
+ ["oogurmukhi"]=2579,
+ ["oomatragurmukhi"]=2635,
+ ["oopen"]=596,
+ ["oparen"]=9386,
+ ["openbullet"]=9702,
+ ["option"]=8997,
+ ["ordfeminine"]=170,
+ ["ordmasculine"]=186,
+ ["orthogonal"]=8735,
+ ["oshortdeva"]=2322,
+ ["oshortvowelsigndeva"]=2378,
+ ["oslash"]=248,
+ ["oslashacute"]=511,
+ ["osmallhiragana"]=12361,
+ ["osmallkatakana"]=12457,
+ ["osmallkatakanahalfwidth"]=65387,
+ ["ostrokeacute"]=511,
+ ["otcyrillic"]=1151,
+ ["otilde"]=245,
+ ["otildeacute"]=7757,
+ ["otildedieresis"]=7759,
+ ["oubopomofo"]=12577,
+ ["overline"]=8254,
+ ["overlinecenterline"]=65098,
+ ["overlinecmb"]=773,
+ ["overlinedashed"]=65097,
+ ["overlinedblwavy"]=65100,
+ ["overlinewavy"]=65099,
+ ["overscore"]=175,
+ ["ovowelsignbengali"]=2507,
+ ["ovowelsigndeva"]=2379,
+ ["ovowelsigngujarati"]=2763,
+ ["p"]=112,
+ ["paampssquare"]=13184,
+ ["paasentosquare"]=13099,
+ ["pabengali"]=2474,
+ ["pacute"]=7765,
+ ["padeva"]=2346,
+ ["pagedown"]=8671,
+ ["pageup"]=8670,
+ ["pagujarati"]=2730,
+ ["pagurmukhi"]=2602,
+ ["pahiragana"]=12401,
+ ["paiyannoithai"]=3631,
+ ["pakatakana"]=12497,
+ ["palatalizationcyrilliccmb"]=1156,
+ ["palochkacyrillic"]=1216,
+ ["pansioskorean"]=12671,
+ ["paragraph"]=182,
+ ["parallel"]=8741,
+ ["parenleft"]=40,
+ ["parenleftaltonearabic"]=64830,
+ ["parenleftinferior"]=8333,
+ ["parenleftmonospace"]=65288,
+ ["parenleftsmall"]=65113,
+ ["parenleftsuperior"]=8317,
+ ["parenleftvertical"]=65077,
+ ["parenright"]=41,
+ ["parenrightaltonearabic"]=64831,
+ ["parenrightinferior"]=8334,
+ ["parenrightmonospace"]=65289,
+ ["parenrightsmall"]=65114,
+ ["parenrightsuperior"]=8318,
+ ["parenrightvertical"]=65078,
+ ["partialdiff"]=8706,
+ ["paseqhebrew"]=1472,
+ ["pashtahebrew"]=1433,
+ ["pasquare"]=13225,
+ ["patah"]=1463,
+ ["patah11"]=1463,
+ ["patah1d"]=1463,
+ ["patah2a"]=1463,
+ ["patahhebrew"]=1463,
+ ["patahnarrowhebrew"]=1463,
+ ["patahquarterhebrew"]=1463,
+ ["patahwidehebrew"]=1463,
+ ["pazerhebrew"]=1441,
+ ["pbopomofo"]=12550,
+ ["pcircle"]=9439,
+ ["pdotaccent"]=7767,
+ ["pe"]=1508,
+ ["pecyrillic"]=1087,
+ ["pedagesh"]=64324,
+ ["pedageshhebrew"]=64324,
+ ["peezisquare"]=13115,
+ ["pefinaldageshhebrew"]=64323,
+ ["peharabic"]=1662,
+ ["peharmenian"]=1402,
+ ["pehebrew"]=1508,
+ ["pehfinalarabic"]=64343,
+ ["pehinitialarabic"]=64344,
+ ["pehiragana"]=12410,
+ ["pehmedialarabic"]=64345,
+ ["pekatakana"]=12506,
+ ["pemiddlehookcyrillic"]=1191,
+ ["perafehebrew"]=64334,
+ ["percent"]=37,
+ ["percentarabic"]=1642,
+ ["percentmonospace"]=65285,
+ ["percentsmall"]=65130,
+ ["period"]=46,
+ ["periodarmenian"]=1417,
+ ["periodcentered"]=183,
+ ["periodhalfwidth"]=65377,
+ ["periodmonospace"]=65294,
+ ["periodsmall"]=65106,
+ ["perispomenigreekcmb"]=834,
+ ["perpendicular"]=8869,
+ ["perthousand"]=8240,
+ ["peseta"]=8359,
+ ["pfsquare"]=13194,
+ ["phabengali"]=2475,
+ ["phadeva"]=2347,
+ ["phagujarati"]=2731,
+ ["phagurmukhi"]=2603,
+ ["phi"]=966,
+ ["phi1"]=981,
+ ["phieuphacirclekorean"]=12922,
+ ["phieuphaparenkorean"]=12826,
+ ["phieuphcirclekorean"]=12908,
+ ["phieuphkorean"]=12621,
+ ["phieuphparenkorean"]=12812,
+ ["philatin"]=632,
+ ["phinthuthai"]=3642,
+ ["phisymbolgreek"]=981,
+ ["phook"]=421,
+ ["phophanthai"]=3614,
+ ["phophungthai"]=3612,
+ ["phosamphaothai"]=3616,
+ ["pi"]=960,
+ ["pieupacirclekorean"]=12915,
+ ["pieupaparenkorean"]=12819,
+ ["pieupcieuckorean"]=12662,
+ ["pieupcirclekorean"]=12901,
+ ["pieupkiyeokkorean"]=12658,
+ ["pieupkorean"]=12610,
+ ["pieupparenkorean"]=12805,
+ ["pieupsioskiyeokkorean"]=12660,
+ ["pieupsioskorean"]=12612,
+ ["pieupsiostikeutkorean"]=12661,
+ ["pieupthieuthkorean"]=12663,
+ ["pieuptikeutkorean"]=12659,
+ ["pihiragana"]=12404,
+ ["pikatakana"]=12500,
+ ["pisymbolgreek"]=982,
+ ["piwrarmenian"]=1411,
+ ["plus"]=43,
+ ["plusbelowcmb"]=799,
+ ["pluscircle"]=8853,
+ ["plusminus"]=177,
+ ["plusmod"]=726,
+ ["plusmonospace"]=65291,
+ ["plussmall"]=65122,
+ ["plussuperior"]=8314,
+ ["pmonospace"]=65360,
+ ["pmsquare"]=13272,
+ ["pohiragana"]=12413,
+ ["pointingindexdownwhite"]=9759,
+ ["pointingindexleftwhite"]=9756,
+ ["pointingindexrightwhite"]=9758,
+ ["pointingindexupwhite"]=9757,
+ ["pokatakana"]=12509,
+ ["poplathai"]=3611,
+ ["postalmark"]=12306,
+ ["postalmarkface"]=12320,
+ ["pparen"]=9387,
+ ["precedes"]=8826,
+ ["prescription"]=8478,
+ ["primemod"]=697,
+ ["primereversed"]=8245,
+ ["product"]=8719,
+ ["projective"]=8965,
+ ["prolongedkana"]=12540,
+ ["propellor"]=8984,
+ ["propersubset"]=8834,
+ ["propersuperset"]=8835,
+ ["proportion"]=8759,
+ ["proportional"]=8733,
+ ["psi"]=968,
+ ["psicyrillic"]=1137,
+ ["psilipneumatacyrilliccmb"]=1158,
+ ["pssquare"]=13232,
+ ["puhiragana"]=12407,
+ ["pukatakana"]=12503,
+ ["pvsquare"]=13236,
+ ["pwsquare"]=13242,
+ ["q"]=113,
+ ["qadeva"]=2392,
+ ["qadmahebrew"]=1448,
+ ["qafarabic"]=1602,
+ ["qaffinalarabic"]=65238,
+ ["qafinitialarabic"]=65239,
+ ["qafmedialarabic"]=65240,
+ ["qamats"]=1464,
+ ["qamats10"]=1464,
+ ["qamats1a"]=1464,
+ ["qamats1c"]=1464,
+ ["qamats27"]=1464,
+ ["qamats29"]=1464,
+ ["qamats33"]=1464,
+ ["qamatsde"]=1464,
+ ["qamatshebrew"]=1464,
+ ["qamatsnarrowhebrew"]=1464,
+ ["qamatsqatanhebrew"]=1464,
+ ["qamatsqatannarrowhebrew"]=1464,
+ ["qamatsqatanquarterhebrew"]=1464,
+ ["qamatsqatanwidehebrew"]=1464,
+ ["qamatsquarterhebrew"]=1464,
+ ["qamatswidehebrew"]=1464,
+ ["qarneyparahebrew"]=1439,
+ ["qbopomofo"]=12561,
+ ["qcircle"]=9440,
+ ["qhook"]=672,
+ ["qmonospace"]=65361,
+ ["qof"]=1511,
+ ["qofdagesh"]=64327,
+ ["qofdageshhebrew"]=64327,
+ ["qofhatafpatah"]=1511,
+ ["qofhatafpatahhebrew"]=1511,
+ ["qofhatafsegol"]=1511,
+ ["qofhatafsegolhebrew"]=1511,
+ ["qofhebrew"]=1511,
+ ["qofhiriq"]=1511,
+ ["qofhiriqhebrew"]=1511,
+ ["qofholam"]=1511,
+ ["qofholamhebrew"]=1511,
+ ["qofpatah"]=1511,
+ ["qofpatahhebrew"]=1511,
+ ["qofqamats"]=1511,
+ ["qofqamatshebrew"]=1511,
+ ["qofqubuts"]=1511,
+ ["qofqubutshebrew"]=1511,
+ ["qofsegol"]=1511,
+ ["qofsegolhebrew"]=1511,
+ ["qofsheva"]=1511,
+ ["qofshevahebrew"]=1511,
+ ["qoftsere"]=1511,
+ ["qoftserehebrew"]=1511,
+ ["qparen"]=9388,
+ ["quarternote"]=9833,
+ ["qubuts"]=1467,
+ ["qubuts18"]=1467,
+ ["qubuts25"]=1467,
+ ["qubuts31"]=1467,
+ ["qubutshebrew"]=1467,
+ ["qubutsnarrowhebrew"]=1467,
+ ["qubutsquarterhebrew"]=1467,
+ ["qubutswidehebrew"]=1467,
+ ["question"]=63,
+ ["questionarabic"]=1567,
+ ["questionarmenian"]=1374,
+ ["questiondown"]=191,
+ ["questiongreek"]=894,
+ ["questionmonospace"]=65311,
+ ["quotedbl"]=34,
+ ["quotedblbase"]=8222,
+ ["quotedblleft"]=8220,
+ ["quotedblmonospace"]=65282,
+ ["quotedblprime"]=12318,
+ ["quotedblprimereversed"]=12317,
+ ["quotedblright"]=8221,
+ ["quoteleft"]=8216,
+ ["quoteleftreversed"]=8219,
+ ["quotereversed"]=8219,
+ ["quoteright"]=8217,
+ ["quoterightn"]=329,
+ ["quotesinglbase"]=8218,
+ ["quotesingle"]=39,
+ ["quotesinglemonospace"]=65287,
+ ["r"]=114,
+ ["raarmenian"]=1404,
+ ["rabengali"]=2480,
+ ["racute"]=341,
+ ["radeva"]=2352,
+ ["radical"]=8730,
+ ["radoverssquare"]=13230,
+ ["radoverssquaredsquare"]=13231,
+ ["radsquare"]=13229,
+ ["rafe"]=1471,
+ ["rafehebrew"]=1471,
+ ["ragujarati"]=2736,
+ ["ragurmukhi"]=2608,
+ ["rahiragana"]=12425,
+ ["rakatakana"]=12521,
+ ["rakatakanahalfwidth"]=65431,
+ ["ralowerdiagonalbengali"]=2545,
+ ["ramiddlediagonalbengali"]=2544,
+ ["ramshorn"]=612,
+ ["ratio"]=8758,
+ ["rbopomofo"]=12566,
+ ["rcaron"]=345,
+ ["rcedilla"]=343,
+ ["rcircle"]=9441,
+ ["rcommaaccent"]=343,
+ ["rdblgrave"]=529,
+ ["rdotaccent"]=7769,
+ ["rdotbelow"]=7771,
+ ["rdotbelowmacron"]=7773,
+ ["referencemark"]=8251,
+ ["reflexsubset"]=8838,
+ ["reflexsuperset"]=8839,
+ ["registered"]=174,
+ ["reharabic"]=1585,
+ ["reharmenian"]=1408,
+ ["rehfinalarabic"]=65198,
+ ["rehiragana"]=12428,
+ ["rehyehaleflamarabic"]=1585,
+ ["rekatakana"]=12524,
+ ["rekatakanahalfwidth"]=65434,
+ ["resh"]=1512,
+ ["reshdageshhebrew"]=64328,
+ ["reshhatafpatah"]=1512,
+ ["reshhatafpatahhebrew"]=1512,
+ ["reshhatafsegol"]=1512,
+ ["reshhatafsegolhebrew"]=1512,
+ ["reshhebrew"]=1512,
+ ["reshhiriq"]=1512,
+ ["reshhiriqhebrew"]=1512,
+ ["reshholam"]=1512,
+ ["reshholamhebrew"]=1512,
+ ["reshpatah"]=1512,
+ ["reshpatahhebrew"]=1512,
+ ["reshqamats"]=1512,
+ ["reshqamatshebrew"]=1512,
+ ["reshqubuts"]=1512,
+ ["reshqubutshebrew"]=1512,
+ ["reshsegol"]=1512,
+ ["reshsegolhebrew"]=1512,
+ ["reshsheva"]=1512,
+ ["reshshevahebrew"]=1512,
+ ["reshtsere"]=1512,
+ ["reshtserehebrew"]=1512,
+ ["reversedtilde"]=8765,
+ ["reviahebrew"]=1431,
+ ["reviamugrashhebrew"]=1431,
+ ["revlogicalnot"]=8976,
+ ["rfishhook"]=638,
+ ["rfishhookreversed"]=639,
+ ["rhabengali"]=2525,
+ ["rhadeva"]=2397,
+ ["rho"]=961,
+ ["rhook"]=637,
+ ["rhookturned"]=635,
+ ["rhookturnedsuperior"]=693,
+ ["rhosymbolgreek"]=1009,
+ ["rhotichookmod"]=734,
+ ["rieulacirclekorean"]=12913,
+ ["rieulaparenkorean"]=12817,
+ ["rieulcirclekorean"]=12899,
+ ["rieulhieuhkorean"]=12608,
+ ["rieulkiyeokkorean"]=12602,
+ ["rieulkiyeoksioskorean"]=12649,
+ ["rieulkorean"]=12601,
+ ["rieulmieumkorean"]=12603,
+ ["rieulpansioskorean"]=12652,
+ ["rieulparenkorean"]=12803,
+ ["rieulphieuphkorean"]=12607,
+ ["rieulpieupkorean"]=12604,
+ ["rieulpieupsioskorean"]=12651,
+ ["rieulsioskorean"]=12605,
+ ["rieulthieuthkorean"]=12606,
+ ["rieultikeutkorean"]=12650,
+ ["rieulyeorinhieuhkorean"]=12653,
+ ["rightangle"]=8735,
+ ["righttackbelowcmb"]=793,
+ ["righttriangle"]=8895,
+ ["rihiragana"]=12426,
+ ["rikatakana"]=12522,
+ ["rikatakanahalfwidth"]=65432,
+ ["ring"]=730,
+ ["ringbelowcmb"]=805,
+ ["ringcmb"]=778,
+ ["ringhalfleft"]=703,
+ ["ringhalfleftarmenian"]=1369,
+ ["ringhalfleftbelowcmb"]=796,
+ ["ringhalfleftcentered"]=723,
+ ["ringhalfright"]=702,
+ ["ringhalfrightbelowcmb"]=825,
+ ["ringhalfrightcentered"]=722,
+ ["rinvertedbreve"]=531,
+ ["rittorusquare"]=13137,
+ ["rlinebelow"]=7775,
+ ["rlongleg"]=636,
+ ["rlonglegturned"]=634,
+ ["rmonospace"]=65362,
+ ["rohiragana"]=12429,
+ ["rokatakana"]=12525,
+ ["rokatakanahalfwidth"]=65435,
+ ["roruathai"]=3619,
+ ["rparen"]=9389,
+ ["rrabengali"]=2524,
+ ["rradeva"]=2353,
+ ["rragurmukhi"]=2652,
+ ["rreharabic"]=1681,
+ ["rrehfinalarabic"]=64397,
+ ["rrvocalicbengali"]=2528,
+ ["rrvocalicdeva"]=2400,
+ ["rrvocalicgujarati"]=2784,
+ ["rrvocalicvowelsignbengali"]=2500,
+ ["rrvocalicvowelsigndeva"]=2372,
+ ["rrvocalicvowelsigngujarati"]=2756,
+ ["rtblock"]=9616,
+ ["rturned"]=633,
+ ["rturnedsuperior"]=692,
+ ["ruhiragana"]=12427,
+ ["rukatakana"]=12523,
+ ["rukatakanahalfwidth"]=65433,
+ ["rupeemarkbengali"]=2546,
+ ["rupeesignbengali"]=2547,
+ ["ruthai"]=3620,
+ ["rvocalicbengali"]=2443,
+ ["rvocalicdeva"]=2315,
+ ["rvocalicgujarati"]=2699,
+ ["rvocalicvowelsignbengali"]=2499,
+ ["rvocalicvowelsigndeva"]=2371,
+ ["rvocalicvowelsigngujarati"]=2755,
+ ["s"]=115,
+ ["sabengali"]=2488,
+ ["sacute"]=347,
+ ["sacutedotaccent"]=7781,
+ ["sadarabic"]=1589,
+ ["sadeva"]=2360,
+ ["sadfinalarabic"]=65210,
+ ["sadinitialarabic"]=65211,
+ ["sadmedialarabic"]=65212,
+ ["sagujarati"]=2744,
+ ["sagurmukhi"]=2616,
+ ["sahiragana"]=12373,
+ ["sakatakana"]=12469,
+ ["sakatakanahalfwidth"]=65403,
+ ["sallallahoualayhewasallamarabic"]=65018,
+ ["samekh"]=1505,
+ ["samekhdagesh"]=64321,
+ ["samekhdageshhebrew"]=64321,
+ ["samekhhebrew"]=1505,
+ ["saraaathai"]=3634,
+ ["saraaethai"]=3649,
+ ["saraaimaimalaithai"]=3652,
+ ["saraaimaimuanthai"]=3651,
+ ["saraamthai"]=3635,
+ ["saraathai"]=3632,
+ ["saraethai"]=3648,
+ ["saraiithai"]=3637,
+ ["saraithai"]=3636,
+ ["saraothai"]=3650,
+ ["saraueethai"]=3639,
+ ["sarauethai"]=3638,
+ ["sarauthai"]=3640,
+ ["sarauuthai"]=3641,
+ ["sbopomofo"]=12569,
+ ["scaron"]=353,
+ ["scarondotaccent"]=7783,
+ ["scedilla"]=351,
+ ["schwa"]=601,
+ ["schwacyrillic"]=1241,
+ ["schwadieresiscyrillic"]=1243,
+ ["schwahook"]=602,
+ ["scircle"]=9442,
+ ["scircumflex"]=349,
+ ["scommaaccent"]=537,
+ ["sdotaccent"]=7777,
+ ["sdotbelow"]=7779,
+ ["sdotbelowdotaccent"]=7785,
+ ["seagullbelowcmb"]=828,
+ ["second"]=8243,
+ ["secondtonechinese"]=714,
+ ["section"]=167,
+ ["seenarabic"]=1587,
+ ["seenfinalarabic"]=65202,
+ ["seeninitialarabic"]=65203,
+ ["seenmedialarabic"]=65204,
+ ["segol"]=1462,
+ ["segol13"]=1462,
+ ["segol1f"]=1462,
+ ["segol2c"]=1462,
+ ["segolhebrew"]=1462,
+ ["segolnarrowhebrew"]=1462,
+ ["segolquarterhebrew"]=1462,
+ ["segoltahebrew"]=1426,
+ ["segolwidehebrew"]=1462,
+ ["seharmenian"]=1405,
+ ["sehiragana"]=12379,
+ ["sekatakana"]=12475,
+ ["sekatakanahalfwidth"]=65406,
+ ["semicolon"]=59,
+ ["semicolonarabic"]=1563,
+ ["semicolonmonospace"]=65307,
+ ["semicolonsmall"]=65108,
+ ["semivoicedmarkkana"]=12444,
+ ["semivoicedmarkkanahalfwidth"]=65439,
+ ["sentisquare"]=13090,
+ ["sentosquare"]=13091,
+ ["seven"]=55,
+ ["sevenarabic"]=1639,
+ ["sevenbengali"]=2541,
+ ["sevencircle"]=9318,
+ ["sevencircleinversesansserif"]=10128,
+ ["sevendeva"]=2413,
+ ["seveneighths"]=8542,
+ ["sevengujarati"]=2797,
+ ["sevengurmukhi"]=2669,
+ ["sevenhackarabic"]=1639,
+ ["sevenhangzhou"]=12327,
+ ["sevenideographicparen"]=12838,
+ ["seveninferior"]=8327,
+ ["sevenmonospace"]=65303,
+ ["sevenparen"]=9338,
+ ["sevenperiod"]=9358,
+ ["sevenpersian"]=1783,
+ ["sevenroman"]=8566,
+ ["sevensuperior"]=8311,
+ ["seventeencircle"]=9328,
+ ["seventeenparen"]=9348,
+ ["seventeenperiod"]=9368,
+ ["seventhai"]=3671,
+ ["sfthyphen"]=173,
+ ["shaarmenian"]=1399,
+ ["shabengali"]=2486,
+ ["shacyrillic"]=1096,
+ ["shaddaarabic"]=1617,
+ ["shaddadammaarabic"]=64609,
+ ["shaddadammatanarabic"]=64606,
+ ["shaddafathaarabic"]=64608,
+ ["shaddafathatanarabic"]=1617,
+ ["shaddakasraarabic"]=64610,
+ ["shaddakasratanarabic"]=64607,
+ ["shade"]=9618,
+ ["shadedark"]=9619,
+ ["shadelight"]=9617,
+ ["shademedium"]=9618,
+ ["shadeva"]=2358,
+ ["shagujarati"]=2742,
+ ["shagurmukhi"]=2614,
+ ["shalshelethebrew"]=1427,
+ ["shbopomofo"]=12565,
+ ["shchacyrillic"]=1097,
+ ["sheenarabic"]=1588,
+ ["sheenfinalarabic"]=65206,
+ ["sheeninitialarabic"]=65207,
+ ["sheenmedialarabic"]=65208,
+ ["sheicoptic"]=995,
+ ["sheqel"]=8362,
+ ["sheqelhebrew"]=8362,
+ ["sheva"]=1456,
+ ["sheva115"]=1456,
+ ["sheva15"]=1456,
+ ["sheva22"]=1456,
+ ["sheva2e"]=1456,
+ ["shevahebrew"]=1456,
+ ["shevanarrowhebrew"]=1456,
+ ["shevaquarterhebrew"]=1456,
+ ["shevawidehebrew"]=1456,
+ ["shhacyrillic"]=1211,
+ ["shimacoptic"]=1005,
+ ["shin"]=1513,
+ ["shindagesh"]=64329,
+ ["shindageshhebrew"]=64329,
+ ["shindageshshindot"]=64300,
+ ["shindageshshindothebrew"]=64300,
+ ["shindageshsindot"]=64301,
+ ["shindageshsindothebrew"]=64301,
+ ["shindothebrew"]=1473,
+ ["shinhebrew"]=1513,
+ ["shinshindot"]=64298,
+ ["shinshindothebrew"]=64298,
+ ["shinsindot"]=64299,
+ ["shinsindothebrew"]=64299,
+ ["shook"]=642,
+ ["sigma"]=963,
+ ["sigma1"]=962,
+ ["sigmafinal"]=962,
+ ["sigmalunatesymbolgreek"]=1010,
+ ["sihiragana"]=12375,
+ ["sikatakana"]=12471,
+ ["sikatakanahalfwidth"]=65404,
+ ["siluqhebrew"]=1469,
+ ["siluqlefthebrew"]=1469,
+ ["similar"]=8764,
+ ["sindothebrew"]=1474,
+ ["siosacirclekorean"]=12916,
+ ["siosaparenkorean"]=12820,
+ ["sioscieuckorean"]=12670,
+ ["sioscirclekorean"]=12902,
+ ["sioskiyeokkorean"]=12666,
+ ["sioskorean"]=12613,
+ ["siosnieunkorean"]=12667,
+ ["siosparenkorean"]=12806,
+ ["siospieupkorean"]=12669,
+ ["siostikeutkorean"]=12668,
+ ["six"]=54,
+ ["sixarabic"]=1638,
+ ["sixbengali"]=2540,
+ ["sixcircle"]=9317,
+ ["sixcircleinversesansserif"]=10127,
+ ["sixdeva"]=2412,
+ ["sixgujarati"]=2796,
+ ["sixgurmukhi"]=2668,
+ ["sixhackarabic"]=1638,
+ ["sixhangzhou"]=12326,
+ ["sixideographicparen"]=12837,
+ ["sixinferior"]=8326,
+ ["sixmonospace"]=65302,
+ ["sixparen"]=9337,
+ ["sixperiod"]=9357,
+ ["sixpersian"]=1782,
+ ["sixroman"]=8565,
+ ["sixsuperior"]=8310,
+ ["sixteencircle"]=9327,
+ ["sixteencurrencydenominatorbengali"]=2553,
+ ["sixteenparen"]=9347,
+ ["sixteenperiod"]=9367,
+ ["sixthai"]=3670,
+ ["slash"]=47,
+ ["slashmonospace"]=65295,
+ ["slong"]=383,
+ ["slongdotaccent"]=7835,
+ ["smileface"]=9786,
+ ["smonospace"]=65363,
+ ["sofpasuqhebrew"]=1475,
+ ["softhyphen"]=173,
+ ["softsigncyrillic"]=1100,
+ ["sohiragana"]=12381,
+ ["sokatakana"]=12477,
+ ["sokatakanahalfwidth"]=65407,
+ ["soliduslongoverlaycmb"]=824,
+ ["solidusshortoverlaycmb"]=823,
+ ["sorusithai"]=3625,
+ ["sosalathai"]=3624,
+ ["sosothai"]=3595,
+ ["sosuathai"]=3626,
+ ["space"]=32,
+ ["spacehackarabic"]=32,
+ ["spade"]=9824,
+ ["spadesuitblack"]=9824,
+ ["spadesuitwhite"]=9828,
+ ["sparen"]=9390,
+ ["squarebelowcmb"]=827,
+ ["squarecc"]=13252,
+ ["squarecm"]=13213,
+ ["squarediagonalcrosshatchfill"]=9641,
+ ["squarehorizontalfill"]=9636,
+ ["squarekg"]=13199,
+ ["squarekm"]=13214,
+ ["squarekmcapital"]=13262,
+ ["squareln"]=13265,
+ ["squarelog"]=13266,
+ ["squaremg"]=13198,
+ ["squaremil"]=13269,
+ ["squaremm"]=13212,
+ ["squaremsquared"]=13217,
+ ["squareorthogonalcrosshatchfill"]=9638,
+ ["squareupperlefttolowerrightfill"]=9639,
+ ["squareupperrighttolowerleftfill"]=9640,
+ ["squareverticalfill"]=9637,
+ ["squarewhitewithsmallblack"]=9635,
+ ["srsquare"]=13275,
+ ["ssabengali"]=2487,
+ ["ssadeva"]=2359,
+ ["ssagujarati"]=2743,
+ ["ssangcieuckorean"]=12617,
+ ["ssanghieuhkorean"]=12677,
+ ["ssangieungkorean"]=12672,
+ ["ssangkiyeokkorean"]=12594,
+ ["ssangnieunkorean"]=12645,
+ ["ssangpieupkorean"]=12611,
+ ["ssangsioskorean"]=12614,
+ ["ssangtikeutkorean"]=12600,
+ ["sterling"]=163,
+ ["sterlingmonospace"]=65505,
+ ["strokelongoverlaycmb"]=822,
+ ["strokeshortoverlaycmb"]=821,
+ ["subset"]=8834,
+ ["subsetnotequal"]=8842,
+ ["subsetorequal"]=8838,
+ ["succeeds"]=8827,
+ ["suchthat"]=8715,
+ ["suhiragana"]=12377,
+ ["sukatakana"]=12473,
+ ["sukatakanahalfwidth"]=65405,
+ ["sukunarabic"]=1618,
+ ["summation"]=8721,
+ ["sun"]=9788,
+ ["superset"]=8835,
+ ["supersetnotequal"]=8843,
+ ["supersetorequal"]=8839,
+ ["svsquare"]=13276,
+ ["syouwaerasquare"]=13180,
+ ["t"]=116,
+ ["tabengali"]=2468,
+ ["tackdown"]=8868,
+ ["tackleft"]=8867,
+ ["tadeva"]=2340,
+ ["tagujarati"]=2724,
+ ["tagurmukhi"]=2596,
+ ["taharabic"]=1591,
+ ["tahfinalarabic"]=65218,
+ ["tahinitialarabic"]=65219,
+ ["tahiragana"]=12383,
+ ["tahmedialarabic"]=65220,
+ ["taisyouerasquare"]=13181,
+ ["takatakana"]=12479,
+ ["takatakanahalfwidth"]=65408,
+ ["tatweelarabic"]=1600,
+ ["tau"]=964,
+ ["tav"]=1514,
+ ["tavdages"]=64330,
+ ["tavdagesh"]=64330,
+ ["tavdageshhebrew"]=64330,
+ ["tavhebrew"]=1514,
+ ["tbar"]=359,
+ ["tbopomofo"]=12554,
+ ["tcaron"]=357,
+ ["tccurl"]=680,
+ ["tcedilla"]=355,
+ ["tcheharabic"]=1670,
+ ["tchehfinalarabic"]=64379,
+ ["tchehinitialarabic"]=64380,
+ ["tchehmedialarabic"]=64381,
+ ["tchehmeeminitialarabic"]=64380,
+ ["tcircle"]=9443,
+ ["tcircumflexbelow"]=7793,
+ ["tcommaaccent"]=355,
+ ["tdieresis"]=7831,
+ ["tdotaccent"]=7787,
+ ["tdotbelow"]=7789,
+ ["tecyrillic"]=1090,
+ ["tedescendercyrillic"]=1197,
+ ["teharabic"]=1578,
+ ["tehfinalarabic"]=65174,
+ ["tehhahinitialarabic"]=64674,
+ ["tehhahisolatedarabic"]=64524,
+ ["tehinitialarabic"]=65175,
+ ["tehiragana"]=12390,
+ ["tehjeeminitialarabic"]=64673,
+ ["tehjeemisolatedarabic"]=64523,
+ ["tehmarbutaarabic"]=1577,
+ ["tehmarbutafinalarabic"]=65172,
+ ["tehmedialarabic"]=65176,
+ ["tehmeeminitialarabic"]=64676,
+ ["tehmeemisolatedarabic"]=64526,
+ ["tehnoonfinalarabic"]=64627,
+ ["tekatakana"]=12486,
+ ["tekatakanahalfwidth"]=65411,
+ ["telephone"]=8481,
+ ["telephoneblack"]=9742,
+ ["telishagedolahebrew"]=1440,
+ ["telishaqetanahebrew"]=1449,
+ ["tencircle"]=9321,
+ ["tenideographicparen"]=12841,
+ ["tenparen"]=9341,
+ ["tenperiod"]=9361,
+ ["tenroman"]=8569,
+ ["tesh"]=679,
+ ["tet"]=1496,
+ ["tetdagesh"]=64312,
+ ["tetdageshhebrew"]=64312,
+ ["tethebrew"]=1496,
+ ["tetsecyrillic"]=1205,
+ ["tevirhebrew"]=1435,
+ ["tevirlefthebrew"]=1435,
+ ["thabengali"]=2469,
+ ["thadeva"]=2341,
+ ["thagujarati"]=2725,
+ ["thagurmukhi"]=2597,
+ ["thalarabic"]=1584,
+ ["thalfinalarabic"]=65196,
+ ["thanthakhatthai"]=3660,
+ ["theharabic"]=1579,
+ ["thehfinalarabic"]=65178,
+ ["thehinitialarabic"]=65179,
+ ["thehmedialarabic"]=65180,
+ ["thereexists"]=8707,
+ ["therefore"]=8756,
+ ["theta"]=952,
+ ["theta1"]=977,
+ ["thetasymbolgreek"]=977,
+ ["thieuthacirclekorean"]=12921,
+ ["thieuthaparenkorean"]=12825,
+ ["thieuthcirclekorean"]=12907,
+ ["thieuthkorean"]=12620,
+ ["thieuthparenkorean"]=12811,
+ ["thirteencircle"]=9324,
+ ["thirteenparen"]=9344,
+ ["thirteenperiod"]=9364,
+ ["thonangmonthothai"]=3601,
+ ["thook"]=429,
+ ["thophuthaothai"]=3602,
+ ["thorn"]=254,
+ ["thothahanthai"]=3607,
+ ["thothanthai"]=3600,
+ ["thothongthai"]=3608,
+ ["thothungthai"]=3606,
+ ["thousandcyrillic"]=1154,
+ ["thousandsseparatorarabic"]=1644,
+ ["thousandsseparatorpersian"]=1644,
+ ["three"]=51,
+ ["threearabic"]=1635,
+ ["threebengali"]=2537,
+ ["threecircle"]=9314,
+ ["threecircleinversesansserif"]=10124,
+ ["threedeva"]=2409,
+ ["threeeighths"]=8540,
+ ["threegujarati"]=2793,
+ ["threegurmukhi"]=2665,
+ ["threehackarabic"]=1635,
+ ["threehangzhou"]=12323,
+ ["threeideographicparen"]=12834,
+ ["threeinferior"]=8323,
+ ["threemonospace"]=65299,
+ ["threenumeratorbengali"]=2550,
+ ["threeparen"]=9334,
+ ["threeperiod"]=9354,
+ ["threepersian"]=1779,
+ ["threequarters"]=190,
+ ["threeroman"]=8562,
+ ["threesuperior"]=179,
+ ["threethai"]=3667,
+ ["thzsquare"]=13204,
+ ["tihiragana"]=12385,
+ ["tikatakana"]=12481,
+ ["tikatakanahalfwidth"]=65409,
+ ["tikeutacirclekorean"]=12912,
+ ["tikeutaparenkorean"]=12816,
+ ["tikeutcirclekorean"]=12898,
+ ["tikeutkorean"]=12599,
+ ["tikeutparenkorean"]=12802,
+ ["tilde"]=732,
+ ["tildebelowcmb"]=816,
+ ["tildecmb"]=771,
+ ["tildecomb"]=771,
+ ["tildedoublecmb"]=864,
+ ["tildeoperator"]=8764,
+ ["tildeoverlaycmb"]=820,
+ ["tildeverticalcmb"]=830,
+ ["timescircle"]=8855,
+ ["tipehahebrew"]=1430,
+ ["tipehalefthebrew"]=1430,
+ ["tippigurmukhi"]=2672,
+ ["titlocyrilliccmb"]=1155,
+ ["tiwnarmenian"]=1407,
+ ["tlinebelow"]=7791,
+ ["tmonospace"]=65364,
+ ["toarmenian"]=1385,
+ ["tohiragana"]=12392,
+ ["tokatakana"]=12488,
+ ["tokatakanahalfwidth"]=65412,
+ ["tonebarextrahighmod"]=741,
+ ["tonebarextralowmod"]=745,
+ ["tonebarhighmod"]=742,
+ ["tonebarlowmod"]=744,
+ ["tonebarmidmod"]=743,
+ ["tonefive"]=445,
+ ["tonesix"]=389,
+ ["tonetwo"]=424,
+ ["tonos"]=900,
+ ["tonsquare"]=13095,
+ ["topatakthai"]=3599,
+ ["tortoiseshellbracketleft"]=12308,
+ ["tortoiseshellbracketleftsmall"]=65117,
+ ["tortoiseshellbracketleftvertical"]=65081,
+ ["tortoiseshellbracketright"]=12309,
+ ["tortoiseshellbracketrightsmall"]=65118,
+ ["tortoiseshellbracketrightvertical"]=65082,
+ ["totaothai"]=3605,
+ ["tpalatalhook"]=427,
+ ["tparen"]=9391,
+ ["trademark"]=8482,
+ ["tretroflexhook"]=648,
+ ["triagdn"]=9660,
+ ["triaglf"]=9668,
+ ["triagrt"]=9658,
+ ["triagup"]=9650,
+ ["ts"]=678,
+ ["tsadi"]=1510,
+ ["tsadidagesh"]=64326,
+ ["tsadidageshhebrew"]=64326,
+ ["tsadihebrew"]=1510,
+ ["tsecyrillic"]=1094,
+ ["tsere"]=1461,
+ ["tsere12"]=1461,
+ ["tsere1e"]=1461,
+ ["tsere2b"]=1461,
+ ["tserehebrew"]=1461,
+ ["tserenarrowhebrew"]=1461,
+ ["tserequarterhebrew"]=1461,
+ ["tserewidehebrew"]=1461,
+ ["tshecyrillic"]=1115,
+ ["ttabengali"]=2463,
+ ["ttadeva"]=2335,
+ ["ttagujarati"]=2719,
+ ["ttagurmukhi"]=2591,
+ ["tteharabic"]=1657,
+ ["ttehfinalarabic"]=64359,
+ ["ttehinitialarabic"]=64360,
+ ["ttehmedialarabic"]=64361,
+ ["tthabengali"]=2464,
+ ["tthadeva"]=2336,
+ ["tthagujarati"]=2720,
+ ["tthagurmukhi"]=2592,
+ ["tturned"]=647,
+ ["tuhiragana"]=12388,
+ ["tukatakana"]=12484,
+ ["tukatakanahalfwidth"]=65410,
+ ["tusmallhiragana"]=12387,
+ ["tusmallkatakana"]=12483,
+ ["tusmallkatakanahalfwidth"]=65391,
+ ["twelvecircle"]=9323,
+ ["twelveparen"]=9343,
+ ["twelveperiod"]=9363,
+ ["twelveroman"]=8571,
+ ["twentycircle"]=9331,
+ ["twentyparen"]=9351,
+ ["twentyperiod"]=9371,
+ ["two"]=50,
+ ["twoarabic"]=1634,
+ ["twobengali"]=2536,
+ ["twocircle"]=9313,
+ ["twocircleinversesansserif"]=10123,
+ ["twodeva"]=2408,
+ ["twodotenleader"]=8229,
+ ["twodotleader"]=8229,
+ ["twodotleadervertical"]=65072,
+ ["twogujarati"]=2792,
+ ["twogurmukhi"]=2664,
+ ["twohackarabic"]=1634,
+ ["twohangzhou"]=12322,
+ ["twoideographicparen"]=12833,
+ ["twoinferior"]=8322,
+ ["twomonospace"]=65298,
+ ["twonumeratorbengali"]=2549,
+ ["twoparen"]=9333,
+ ["twoperiod"]=9353,
+ ["twopersian"]=1778,
+ ["tworoman"]=8561,
+ ["twostroke"]=443,
+ ["twosuperior"]=178,
+ ["twothai"]=3666,
+ ["twothirds"]=8532,
+ ["u"]=117,
+ ["uacute"]=250,
+ ["ubar"]=649,
+ ["ubengali"]=2441,
+ ["ubopomofo"]=12584,
+ ["ubreve"]=365,
+ ["ucaron"]=468,
+ ["ucircle"]=9444,
+ ["ucircumflex"]=251,
+ ["ucircumflexbelow"]=7799,
+ ["ucyrillic"]=1091,
+ ["udattadeva"]=2385,
+ ["udblacute"]=369,
+ ["udblgrave"]=533,
+ ["udeva"]=2313,
+ ["udieresis"]=252,
+ ["udieresisacute"]=472,
+ ["udieresisbelow"]=7795,
+ ["udieresiscaron"]=474,
+ ["udieresiscyrillic"]=1265,
+ ["udieresisgrave"]=476,
+ ["udieresismacron"]=470,
+ ["udotbelow"]=7909,
+ ["ugrave"]=249,
+ ["ugujarati"]=2697,
+ ["ugurmukhi"]=2569,
+ ["uhiragana"]=12358,
+ ["uhookabove"]=7911,
+ ["uhorn"]=432,
+ ["uhornacute"]=7913,
+ ["uhorndotbelow"]=7921,
+ ["uhorngrave"]=7915,
+ ["uhornhookabove"]=7917,
+ ["uhorntilde"]=7919,
+ ["uhungarumlaut"]=369,
+ ["uhungarumlautcyrillic"]=1267,
+ ["uinvertedbreve"]=535,
+ ["ukatakana"]=12454,
+ ["ukatakanahalfwidth"]=65395,
+ ["ukcyrillic"]=1145,
+ ["ukorean"]=12636,
+ ["umacron"]=363,
+ ["umacroncyrillic"]=1263,
+ ["umacrondieresis"]=7803,
+ ["umatragurmukhi"]=2625,
+ ["umonospace"]=65365,
+ ["underscore"]=95,
+ ["underscoredbl"]=8215,
+ ["underscoremonospace"]=65343,
+ ["underscorevertical"]=65075,
+ ["underscorewavy"]=65103,
+ ["union"]=8746,
+ ["universal"]=8704,
+ ["uogonek"]=371,
+ ["uparen"]=9392,
+ ["upblock"]=9600,
+ ["upperdothebrew"]=1476,
+ ["upsilon"]=965,
+ ["upsilondieresis"]=971,
+ ["upsilondieresistonos"]=944,
+ ["upsilonlatin"]=650,
+ ["upsilontonos"]=973,
+ ["uptackbelowcmb"]=797,
+ ["uptackmod"]=724,
+ ["uragurmukhi"]=2675,
+ ["uring"]=367,
+ ["ushortcyrillic"]=1118,
+ ["usmallhiragana"]=12357,
+ ["usmallkatakana"]=12453,
+ ["usmallkatakanahalfwidth"]=65385,
+ ["ustraightcyrillic"]=1199,
+ ["ustraightstrokecyrillic"]=1201,
+ ["utilde"]=361,
+ ["utildeacute"]=7801,
+ ["utildebelow"]=7797,
+ ["uubengali"]=2442,
+ ["uudeva"]=2314,
+ ["uugujarati"]=2698,
+ ["uugurmukhi"]=2570,
+ ["uumatragurmukhi"]=2626,
+ ["uuvowelsignbengali"]=2498,
+ ["uuvowelsigndeva"]=2370,
+ ["uuvowelsigngujarati"]=2754,
+ ["uvowelsignbengali"]=2497,
+ ["uvowelsigndeva"]=2369,
+ ["uvowelsigngujarati"]=2753,
+ ["v"]=118,
+ ["vadeva"]=2357,
+ ["vagujarati"]=2741,
+ ["vagurmukhi"]=2613,
+ ["vakatakana"]=12535,
+ ["vav"]=1493,
+ ["vavdagesh"]=64309,
+ ["vavdagesh65"]=64309,
+ ["vavdageshhebrew"]=64309,
+ ["vavhebrew"]=1493,
+ ["vavholam"]=64331,
+ ["vavholamhebrew"]=64331,
+ ["vavvavhebrew"]=1520,
+ ["vavyodhebrew"]=1521,
+ ["vcircle"]=9445,
+ ["vdotbelow"]=7807,
+ ["vecyrillic"]=1074,
+ ["veharabic"]=1700,
+ ["vehfinalarabic"]=64363,
+ ["vehinitialarabic"]=64364,
+ ["vehmedialarabic"]=64365,
+ ["vekatakana"]=12537,
+ ["venus"]=9792,
+ ["verticalbar"]=124,
+ ["verticallineabovecmb"]=781,
+ ["verticallinebelowcmb"]=809,
+ ["verticallinelowmod"]=716,
+ ["verticallinemod"]=712,
+ ["vewarmenian"]=1406,
+ ["vhook"]=651,
+ ["vikatakana"]=12536,
+ ["viramabengali"]=2509,
+ ["viramadeva"]=2381,
+ ["viramagujarati"]=2765,
+ ["visargabengali"]=2435,
+ ["visargadeva"]=2307,
+ ["visargagujarati"]=2691,
+ ["vmonospace"]=65366,
+ ["voarmenian"]=1400,
+ ["voicediterationhiragana"]=12446,
+ ["voicediterationkatakana"]=12542,
+ ["voicedmarkkana"]=12443,
+ ["voicedmarkkanahalfwidth"]=65438,
+ ["vokatakana"]=12538,
+ ["vparen"]=9393,
+ ["vtilde"]=7805,
+ ["vturned"]=652,
+ ["vuhiragana"]=12436,
+ ["vukatakana"]=12532,
+ ["w"]=119,
+ ["wacute"]=7811,
+ ["waekorean"]=12633,
+ ["wahiragana"]=12431,
+ ["wakatakana"]=12527,
+ ["wakatakanahalfwidth"]=65436,
+ ["wakorean"]=12632,
+ ["wasmallhiragana"]=12430,
+ ["wasmallkatakana"]=12526,
+ ["wattosquare"]=13143,
+ ["wavedash"]=12316,
+ ["wavyunderscorevertical"]=65076,
+ ["wawarabic"]=1608,
+ ["wawfinalarabic"]=65262,
+ ["wawhamzaabovearabic"]=1572,
+ ["wawhamzaabovefinalarabic"]=65158,
+ ["wbsquare"]=13277,
+ ["wcircle"]=9446,
+ ["wcircumflex"]=373,
+ ["wdieresis"]=7813,
+ ["wdotaccent"]=7815,
+ ["wdotbelow"]=7817,
+ ["wehiragana"]=12433,
+ ["weierstrass"]=8472,
+ ["wekatakana"]=12529,
+ ["wekorean"]=12638,
+ ["weokorean"]=12637,
+ ["wgrave"]=7809,
+ ["whitebullet"]=9702,
+ ["whitecircle"]=9675,
+ ["whitecircleinverse"]=9689,
+ ["whitecornerbracketleft"]=12302,
+ ["whitecornerbracketleftvertical"]=65091,
+ ["whitecornerbracketright"]=12303,
+ ["whitecornerbracketrightvertical"]=65092,
+ ["whitediamond"]=9671,
+ ["whitediamondcontainingblacksmalldiamond"]=9672,
+ ["whitedownpointingsmalltriangle"]=9663,
+ ["whitedownpointingtriangle"]=9661,
+ ["whiteleftpointingsmalltriangle"]=9667,
+ ["whiteleftpointingtriangle"]=9665,
+ ["whitelenticularbracketleft"]=12310,
+ ["whitelenticularbracketright"]=12311,
+ ["whiterightpointingsmalltriangle"]=9657,
+ ["whiterightpointingtriangle"]=9655,
+ ["whitesmallsquare"]=9643,
+ ["whitesmilingface"]=9786,
+ ["whitesquare"]=9633,
+ ["whitestar"]=9734,
+ ["whitetelephone"]=9743,
+ ["whitetortoiseshellbracketleft"]=12312,
+ ["whitetortoiseshellbracketright"]=12313,
+ ["whiteuppointingsmalltriangle"]=9653,
+ ["whiteuppointingtriangle"]=9651,
+ ["wihiragana"]=12432,
+ ["wikatakana"]=12528,
+ ["wikorean"]=12639,
+ ["wmonospace"]=65367,
+ ["wohiragana"]=12434,
+ ["wokatakana"]=12530,
+ ["wokatakanahalfwidth"]=65382,
+ ["won"]=8361,
+ ["wonmonospace"]=65510,
+ ["wowaenthai"]=3623,
+ ["wparen"]=9394,
+ ["wring"]=7832,
+ ["wsuperior"]=695,
+ ["wturned"]=653,
+ ["wynn"]=447,
+ ["x"]=120,
+ ["xabovecmb"]=829,
+ ["xbopomofo"]=12562,
+ ["xcircle"]=9447,
+ ["xdieresis"]=7821,
+ ["xdotaccent"]=7819,
+ ["xeharmenian"]=1389,
+ ["xi"]=958,
+ ["xmonospace"]=65368,
+ ["xparen"]=9395,
+ ["xsuperior"]=739,
+ ["y"]=121,
+ ["yaadosquare"]=13134,
+ ["yabengali"]=2479,
+ ["yacute"]=253,
+ ["yadeva"]=2351,
+ ["yaekorean"]=12626,
+ ["yagujarati"]=2735,
+ ["yagurmukhi"]=2607,
+ ["yahiragana"]=12420,
+ ["yakatakana"]=12516,
+ ["yakatakanahalfwidth"]=65428,
+ ["yakorean"]=12625,
+ ["yamakkanthai"]=3662,
+ ["yasmallhiragana"]=12419,
+ ["yasmallkatakana"]=12515,
+ ["yasmallkatakanahalfwidth"]=65388,
+ ["yatcyrillic"]=1123,
+ ["ycircle"]=9448,
+ ["ycircumflex"]=375,
+ ["ydieresis"]=255,
+ ["ydotaccent"]=7823,
+ ["ydotbelow"]=7925,
+ ["yeharabic"]=1610,
+ ["yehbarreearabic"]=1746,
+ ["yehbarreefinalarabic"]=64431,
+ ["yehfinalarabic"]=65266,
+ ["yehhamzaabovearabic"]=1574,
+ ["yehhamzaabovefinalarabic"]=65162,
+ ["yehhamzaaboveinitialarabic"]=65163,
+ ["yehhamzaabovemedialarabic"]=65164,
+ ["yehinitialarabic"]=65267,
+ ["yehmedialarabic"]=65268,
+ ["yehmeeminitialarabic"]=64733,
+ ["yehmeemisolatedarabic"]=64600,
+ ["yehnoonfinalarabic"]=64660,
+ ["yehthreedotsbelowarabic"]=1745,
+ ["yekorean"]=12630,
+ ["yen"]=165,
+ ["yenmonospace"]=65509,
+ ["yeokorean"]=12629,
+ ["yeorinhieuhkorean"]=12678,
+ ["yerahbenyomohebrew"]=1450,
+ ["yerahbenyomolefthebrew"]=1450,
+ ["yericyrillic"]=1099,
+ ["yerudieresiscyrillic"]=1273,
+ ["yesieungkorean"]=12673,
+ ["yesieungpansioskorean"]=12675,
+ ["yesieungsioskorean"]=12674,
+ ["yetivhebrew"]=1434,
+ ["ygrave"]=7923,
+ ["yhook"]=436,
+ ["yhookabove"]=7927,
+ ["yiarmenian"]=1397,
+ ["yicyrillic"]=1111,
+ ["yikorean"]=12642,
+ ["yinyang"]=9775,
+ ["yiwnarmenian"]=1410,
+ ["ymonospace"]=65369,
+ ["yod"]=1497,
+ ["yoddagesh"]=64313,
+ ["yoddageshhebrew"]=64313,
+ ["yodhebrew"]=1497,
+ ["yodyodhebrew"]=1522,
+ ["yodyodpatahhebrew"]=64287,
+ ["yohiragana"]=12424,
+ ["yoikorean"]=12681,
+ ["yokatakana"]=12520,
+ ["yokatakanahalfwidth"]=65430,
+ ["yokorean"]=12635,
+ ["yosmallhiragana"]=12423,
+ ["yosmallkatakana"]=12519,
+ ["yosmallkatakanahalfwidth"]=65390,
+ ["yotgreek"]=1011,
+ ["yoyaekorean"]=12680,
+ ["yoyakorean"]=12679,
+ ["yoyakthai"]=3618,
+ ["yoyingthai"]=3597,
+ ["yparen"]=9396,
+ ["ypogegrammeni"]=890,
+ ["ypogegrammenigreekcmb"]=837,
+ ["yr"]=422,
+ ["yring"]=7833,
+ ["ysuperior"]=696,
+ ["ytilde"]=7929,
+ ["yturned"]=654,
+ ["yuhiragana"]=12422,
+ ["yuikorean"]=12684,
+ ["yukatakana"]=12518,
+ ["yukatakanahalfwidth"]=65429,
+ ["yukorean"]=12640,
+ ["yusbigcyrillic"]=1131,
+ ["yusbigiotifiedcyrillic"]=1133,
+ ["yuslittlecyrillic"]=1127,
+ ["yuslittleiotifiedcyrillic"]=1129,
+ ["yusmallhiragana"]=12421,
+ ["yusmallkatakana"]=12517,
+ ["yusmallkatakanahalfwidth"]=65389,
+ ["yuyekorean"]=12683,
+ ["yuyeokorean"]=12682,
+ ["yyabengali"]=2527,
+ ["yyadeva"]=2399,
+ ["z"]=122,
+ ["zaarmenian"]=1382,
+ ["zacute"]=378,
+ ["zadeva"]=2395,
+ ["zagurmukhi"]=2651,
+ ["zaharabic"]=1592,
+ ["zahfinalarabic"]=65222,
+ ["zahinitialarabic"]=65223,
+ ["zahiragana"]=12374,
+ ["zahmedialarabic"]=65224,
+ ["zainarabic"]=1586,
+ ["zainfinalarabic"]=65200,
+ ["zakatakana"]=12470,
+ ["zaqefgadolhebrew"]=1429,
+ ["zaqefqatanhebrew"]=1428,
+ ["zarqahebrew"]=1432,
+ ["zayin"]=1494,
+ ["zayindagesh"]=64310,
+ ["zayindageshhebrew"]=64310,
+ ["zayinhebrew"]=1494,
+ ["zbopomofo"]=12567,
+ ["zcaron"]=382,
+ ["zcircle"]=9449,
+ ["zcircumflex"]=7825,
+ ["zcurl"]=657,
+ ["zdot"]=380,
+ ["zdotaccent"]=380,
+ ["zdotbelow"]=7827,
+ ["zecyrillic"]=1079,
+ ["zedescendercyrillic"]=1177,
+ ["zedieresiscyrillic"]=1247,
+ ["zehiragana"]=12380,
+ ["zekatakana"]=12476,
+ ["zero"]=48,
+ ["zeroarabic"]=1632,
+ ["zerobengali"]=2534,
+ ["zerodeva"]=2406,
+ ["zerogujarati"]=2790,
+ ["zerogurmukhi"]=2662,
+ ["zerohackarabic"]=1632,
+ ["zeroinferior"]=8320,
+ ["zeromonospace"]=65296,
+ ["zeropersian"]=1776,
+ ["zerosuperior"]=8304,
+ ["zerothai"]=3664,
+ ["zerowidthjoiner"]=65279,
+ ["zerowidthnonjoiner"]=8204,
+ ["zerowidthspace"]=8203,
+ ["zeta"]=950,
+ ["zhbopomofo"]=12563,
+ ["zhearmenian"]=1386,
+ ["zhebrevecyrillic"]=1218,
+ ["zhecyrillic"]=1078,
+ ["zhedescendercyrillic"]=1175,
+ ["zhedieresiscyrillic"]=1245,
+ ["zihiragana"]=12376,
+ ["zikatakana"]=12472,
+ ["zinorhebrew"]=1454,
+ ["zlinebelow"]=7829,
+ ["zmonospace"]=65370,
+ ["zohiragana"]=12382,
+ ["zokatakana"]=12478,
+ ["zparen"]=9397,
+ ["zretroflexhook"]=656,
+ ["zstroke"]=438,
+ ["zuhiragana"]=12378,
+ ["zukatakana"]=12474,
+}
diff --git a/tex/context/base/font-agl.lua b/tex/context/base/font-agl.lua
index 19121c358..5ee34b028 100644
--- a/tex/context/base/font-agl.lua
+++ b/tex/context/base/font-agl.lua
@@ -1,667 +1,667 @@
-if not modules then modules = { } end modules ['font-agl'] = {
- version = 1.001,
- comment = "companion to font-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "derived from http://www.adobe.com/devnet/opentype/archives/glyphlist.txt",
- original = "Adobe Glyph List, version 2.0, September 20, 2002",
-}
-
-local allocate = utilities.storage.allocate
-
-local names = allocate {
- -- filled from char-def.lua
-}
-local unicodes = allocate {
- -- filled from char-def.lua
-}
-
-local synonyms = {
- Acyrillic = 0x0410,
- Becyrillic = 0x0411,
- Cdot = 0x010A,
- Checyrillic = 0x0427,
- Decyrillic = 0x0414,
- Djecyrillic = 0x0402,
- Dzecyrillic = 0x0405,
- Dzhecyrillic = 0x040F,
- Ecyrillic = 0x0404,
- Edot = 0x0116,
- Efcyrillic = 0x0424,
- Elcyrillic = 0x041B,
- Emcyrillic = 0x041C,
- Encyrillic = 0x041D,
- Ercyrillic = 0x0420,
- Ereversedcyrillic = 0x042D,
- Escyrillic = 0x0421,
- Fitacyrillic = 0x0472,
- Gcedilla = 0x0122,
- Gdot = 0x0120,
- Gecyrillic = 0x0413,
- Gheupturncyrillic = 0x0490,
- Gjecyrillic = 0x0403,
- Hardsigncyrillic = 0x042A,
- IAcyrillic = 0x042F,
- IUcyrillic = 0x042E,
- Icyrillic = 0x0406,
- Idot = 0x0130,
- Iecyrillic = 0x0415,
- Iicyrillic = 0x0418,
- Iishortcyrillic = 0x0419,
- Iocyrillic = 0x0401,
- Izhitsacyrillic = 0x0474,
- Jecyrillic = 0x0408,
- Kacyrillic = 0x041A,
- Kcedilla = 0x0136,
- Khacyrillic = 0x0425,
- Kjecyrillic = 0x040C,
- Lcedilla = 0x013B,
- Ljecyrillic = 0x0409,
- Ncedilla = 0x0145,
- Njecyrillic = 0x040A,
- Ocyrillic = 0x041E,
- Odblacute = 0x0150,
- Ohm = 0x2126,
- Pecyrillic = 0x041F,
- Rcedilla = 0x0156,
- Shacyrillic = 0x0428,
- Shchacyrillic = 0x0429,
- Softsigncyrillic = 0x042C,
- Tcedilla = 0x0162,
- Tecyrillic = 0x0422,
- Tsecyrillic = 0x0426,
- Tshecyrillic = 0x040B,
- Ucyrillic = 0x0423,
- Udblacute = 0x0170,
- Ushortcyrillic = 0x040E,
- Vecyrillic = 0x0412,
- Yatcyrillic = 0x0462,
- Yericyrillic = 0x042B,
- Yicyrillic = 0x0407,
- Zdot = 0x017B,
- Zecyrillic = 0x0417,
- Zhecyrillic = 0x0416,
- acutecmb = 0x0301,
- acyrillic = 0x0430,
- afii00208 = 0x2015,
- afii08941 = 0x20A4,
- afii57694 = 0xFB2A,
- afii57695 = 0xFB2B,
- afii57700 = 0xFB4B,
- afii57705 = 0xFB1F,
- afii57723 = 0xFB35,
- alef = 0x05D0,
- alefmaksurainitialarabic = 0xFEF3,
- alefmaksuramedialarabic = 0xFEF4,
- approximatelyequal = 0x2245,
- asteriskaltonearabic = 0x066D,
- ayin = 0x05E2,
- bet = 0x05D1,
- betdagesh = 0xFB31,
- blackdownpointingtriangle = 0x25BC,
- blackleftpointingpointer = 0x25C4,
- blackrectangle = 0x25AC,
- blackrightpointingpointer = 0x25BA,
- blacksmilingface = 0x263B,
- blacksquare = 0x25A0,
- blackuppointingtriangle = 0x25B2,
- bulletinverse = 0x25D8,
- cdot = 0x010B,
- compass = 0x263C,
- dagesh = 0x05BC,
- dalet = 0x05D3,
- daletdagesh = 0xFB33,
- dalethatafpatah = 0x05D3,
- dalethatafpatahhebrew = 0x05D3,
- dalethatafsegol = 0x05D3,
- dalethatafsegolhebrew = 0x05D3,
- dalethebrew = 0x05D3,
- dalethiriq = 0x05D3,
- dalethiriqhebrew = 0x05D3,
- daletholam = 0x05D3,
- daletholamhebrew = 0x05D3,
- daletpatah = 0x05D3,
- daletpatahhebrew = 0x05D3,
- daletqamats = 0x05D3,
- daletqamatshebrew = 0x05D3,
- daletqubuts = 0x05D3,
- daletqubutshebrew = 0x05D3,
- daletsegol = 0x05D3,
- daletsegolhebrew = 0x05D3,
- daletsheva = 0x05D3,
- daletshevahebrew = 0x05D3,
- dalettsere = 0x05D3,
- dammaarabic = 0x064F,
- dammatanaltonearabic = 0x064C,
- dargahebrew = 0x05A7,
- dbllowline = 0x2017,
- decimalseparatorarabic = 0x066B,
- dialytikatonos = 0x0385,
- dotbelowcmb = 0x0323,
- doubleyodpatah = 0xFB1F,
- doubleyodpatahhebrew = 0xFB1F,
- edot = 0x0117,
- eightarabic = 0x0668,
- eighthnotebeamed = 0x266B,
- etnahtafoukhhebrew = 0x0591,
- etnahtafoukhlefthebrew = 0x0591,
- etnahtahebrew = 0x0591,
- fathaarabic = 0x064E,
- finalkaf = 0x05DA,
- finalkafdagesh = 0xFB3A,
- finalkafhebrew = 0x05DA,
- finalkafqamats = 0x05DA,
- finalkafqamatshebrew = 0x05DA,
- finalkafsheva = 0x05DA,
- finalmem = 0x05DD,
- finalnun = 0x05DF,
- finalpe = 0x05E3,
- finaltsadi = 0x05E5,
- fivearabic = 0x0665,
- forall = 0x2200,
- fourarabic = 0x0664,
- gcedilla = 0x0123,
- gdot = 0x0121,
- gimel = 0x05D2,
- gimeldagesh = 0xFB32,
- gravecmb = 0x0300,
- haaltonearabic = 0x06C1,
- hamzaarabic = 0x0621,
- hamzadammaarabic = 0x0621,
- hamzadammatanarabic = 0x0621,
- hamzafathaarabic = 0x0621,
- hamzafathatanarabic = 0x0621,
- hamzalowarabic = 0x0621,
- hamzalowkasraarabic = 0x0621,
- hamzalowkasratanarabic = 0x0621,
- hatafpatah = 0x05B2,
- hatafpatah16 = 0x05B2,
- hatafpatah23 = 0x05B2,
- hatafpatah2f = 0x05B2,
- hatafpatahhebrew = 0x05B2,
- hatafpatahnarrowhebrew = 0x05B2,
- hatafpatahquarterhebrew = 0x05B2,
- hatafqamats = 0x05B3,
- hatafqamats1b = 0x05B3,
- hatafqamats28 = 0x05B3,
- hatafqamats34 = 0x05B3,
- hatafqamatshebrew = 0x05B3,
- hatafqamatsnarrowhebrew = 0x05B3,
- hatafqamatsquarterhebrew = 0x05B3,
- hatafsegol = 0x05B1,
- hatafsegol17 = 0x05B1,
- hatafsegol24 = 0x05B1,
- hatafsegol30 = 0x05B1,
- hatafsegolhebrew = 0x05B1,
- hatafsegolnarrowhebrew = 0x05B1,
- hatafsegolquarterhebrew = 0x05B1,
- he = 0x05D4,
- hedagesh = 0xFB34,
- hehfinalalttwoarabic = 0xFEEA,
- het = 0x05D7,
- hiriq = 0x05B4,
- hiriq14 = 0x05B4,
- hiriq21 = 0x05B4,
- hiriq2d = 0x05B4,
- hiriqhebrew = 0x05B4,
- hiriqnarrowhebrew = 0x05B4,
- hiriqquarterhebrew = 0x05B4,
- holam = 0x05B9,
- holam19 = 0x05B9,
- holam26 = 0x05B9,
- holam32 = 0x05B9,
- holamhebrew = 0x05B9,
- holamnarrowhebrew = 0x05B9,
- holamquarterhebrew = 0x05B9,
- ilde = 0x02DC,
- integralbottom = 0x2321,
- integraltop = 0x2320,
- kaf = 0x05DB,
- kafdagesh = 0xFB3B,
- kashidaautoarabic = 0x0640,
- kashidaautonosidebearingarabic = 0x0640,
- kcedilla = 0x0137,
- lamed = 0x05DC,
- lameddagesh = 0xFB3C,
- lamedhebrew = 0x05DC,
- lamedholam = 0x05DC,
- lamedholamdagesh = 0x05DC,
- lamedholamdageshhebrew = 0x05DC,
- laminitialarabic = 0xFEDF,
- lammeemjeeminitialarabic = 0xFEDF,
- lcedilla = 0x013C,
- logicalnotreversed = 0x2310,
- mahapakhhebrew = 0x05A4,
- mem = 0x05DE,
- memdagesh = 0xFB3E,
- merkhahebrew = 0x05A5,
- merkhakefulahebrew = 0x05A6,
- middot = 0x00B7,
- munahhebrew = 0x05A3,
- nbspace = 0x00A0,
- ncedilla = 0x0146,
- newsheqelsign = 0x20AA,
- ninearabic = 0x0669,
- noonhehinitialarabic = 0xFEE7,
- nun = 0x05E0,
- nundagesh = 0xFB40,
- odblacute = 0x0151,
- onearabic = 0x0661,
- overscore = 0x00AF,
- patah = 0x05B7,
- patah11 = 0x05B7,
- patah1d = 0x05B7,
- patah2a = 0x05B7,
- patahhebrew = 0x05B7,
- patahnarrowhebrew = 0x05B7,
- patahquarterhebrew = 0x05B7,
- pe = 0x05E4,
- pedagesh = 0xFB44,
- qamats = 0x05B8,
- qamats10 = 0x05B8,
- qamats1a = 0x05B8,
- qamats1c = 0x05B8,
- qamats27 = 0x05B8,
- qamats29 = 0x05B8,
- qamats33 = 0x05B8,
- qamatsde = 0x05B8,
- qamatshebrew = 0x05B8,
- qamatsnarrowhebrew = 0x05B8,
- qamatsqatanhebrew = 0x05B8,
- qamatsqatannarrowhebrew = 0x05B8,
- qamatsqatanquarterhebrew = 0x05B8,
- qamatsqatanwidehebrew = 0x05B8,
- qamatsquarterhebrew = 0x05B8,
- qof = 0x05E7,
- qofdagesh = 0xFB47,
- qofhatafpatah = 0x05E7,
- qofhatafpatahhebrew = 0x05E7,
- qofhatafsegol = 0x05E7,
- qofhatafsegolhebrew = 0x05E7,
- qofhebrew = 0x05E7,
- qofhiriq = 0x05E7,
- qofhiriqhebrew = 0x05E7,
- qofholam = 0x05E7,
- qofholamhebrew = 0x05E7,
- qofpatah = 0x05E7,
- qofpatahhebrew = 0x05E7,
- qofqamats = 0x05E7,
- qofqamatshebrew = 0x05E7,
- qofqubuts = 0x05E7,
- qofqubutshebrew = 0x05E7,
- qofsegol = 0x05E7,
- qofsegolhebrew = 0x05E7,
- qofsheva = 0x05E7,
- qofshevahebrew = 0x05E7,
- qoftsere = 0x05E7,
- qubuts = 0x05BB,
- qubuts18 = 0x05BB,
- qubuts25 = 0x05BB,
- qubuts31 = 0x05BB,
- qubutshebrew = 0x05BB,
- qubutsnarrowhebrew = 0x05BB,
- qubutsquarterhebrew = 0x05BB,
- quoteleftreversed = 0x201B,
- rafe = 0x05BF,
- rcedilla = 0x0157,
- reharabic = 0x0631,
- resh = 0x05E8,
- reshhatafpatah = 0x05E8,
- reshhatafpatahhebrew = 0x05E8,
- reshhatafsegol = 0x05E8,
- reshhatafsegolhebrew = 0x05E8,
- reshhebrew = 0x05E8,
- reshhiriq = 0x05E8,
- reshhiriqhebrew = 0x05E8,
- reshholam = 0x05E8,
- reshholamhebrew = 0x05E8,
- reshpatah = 0x05E8,
- reshpatahhebrew = 0x05E8,
- reshqamats = 0x05E8,
- reshqamatshebrew = 0x05E8,
- reshqubuts = 0x05E8,
- reshqubutshebrew = 0x05E8,
- reshsegol = 0x05E8,
- reshsegolhebrew = 0x05E8,
- reshsheva = 0x05E8,
- reshshevahebrew = 0x05E8,
- reshtsere = 0x05E8,
- reviahebrew = 0x0597,
- samekh = 0x05E1,
- samekhdagesh = 0xFB41,
- segol = 0x05B6,
- segol13 = 0x05B6,
- segol1f = 0x05B6,
- segol2c = 0x05B6,
- segolhebrew = 0x05B6,
- segolnarrowhebrew = 0x05B6,
- segolquarterhebrew = 0x05B6,
- sevenarabic = 0x0667,
- sfthyphen = 0x00AD,
- shaddaarabic = 0x0651,
- sheqel = 0x20AA,
- sheva = 0x05B0,
- sheva115 = 0x05B0,
- sheva15 = 0x05B0,
- sheva22 = 0x05B0,
- sheva2e = 0x05B0,
- shevahebrew = 0x05B0,
- shevanarrowhebrew = 0x05B0,
- shevaquarterhebrew = 0x05B0,
- shin = 0x05E9,
- shindagesh = 0xFB49,
- shindageshshindot = 0xFB2C,
- shindageshsindot = 0xFB2D,
- shinshindot = 0xFB2A,
- shinsindot = 0xFB2B,
- siluqhebrew = 0x05BD,
- sixarabic = 0x0666,
- tav = 0x05EA,
- tavdages = 0xFB4A,
- tavdagesh = 0xFB4A,
- tcedilla = 0x0163,
- tchehinitialarabic = 0xFB7C,
- tet = 0x05D8,
- tetdagesh = 0xFB38,
- tevirhebrew = 0x059B,
- thousandsseparatorarabic = 0x066C,
- threearabic = 0x0663,
- tildecmb = 0x0303,
- tipehahebrew = 0x0596,
- tsadi = 0x05E6,
- tsadidagesh = 0xFB46,
- tsere = 0x05B5,
- tsere12 = 0x05B5,
- tsere1e = 0x05B5,
- tsere2b = 0x05B5,
- tserehebrew = 0x05B5,
- tserenarrowhebrew = 0x05B5,
- tserequarterhebrew = 0x05B5,
- twoarabic = 0x0662,
- udblacute = 0x0171,
- vav = 0x05D5,
- vavdagesh = 0xFB35,
- vavdagesh65 = 0xFB35,
- vavholam = 0xFB4B,
- yerahbenyomohebrew = 0x05AA,
- yod = 0x05D9,
- yoddagesh = 0xFB39,
- zayin = 0x05D6,
- zayindagesh = 0xFB36,
- zdot = 0x017C,
- zeroarabic = 0x0660,
-}
-
-local extras = allocate { -- private extensions
- Dcroat = 0x0110,
- Delta = 0x2206,
- Euro = 0x20AC,
- H18533 = 0x25CF,
- H18543 = 0x25AA,
- H18551 = 0x25AB,
- H22073 = 0x25A1,
- Ldot = 0x013F,
- Oslashacute = 0x01FE,
- SF10000 = 0x250C,
- SF20000 = 0x2514,
- SF30000 = 0x2510,
- SF40000 = 0x2518,
- SF50000 = 0x253C,
- SF60000 = 0x252C,
- SF70000 = 0x2534,
- SF80000 = 0x251C,
- SF90000 = 0x2524,
- Upsilon1 = 0x03D2,
- afii10066 = 0x0431,
- afii10067 = 0x0432,
- afii10068 = 0x0433,
- afii10069 = 0x0434,
- afii10070 = 0x0435,
- afii10071 = 0x0451,
- afii10072 = 0x0436,
- afii10073 = 0x0437,
- afii10074 = 0x0438,
- afii10075 = 0x0439,
- afii10076 = 0x043A,
- afii10077 = 0x043B,
- afii10078 = 0x043C,
- afii10079 = 0x043D,
- afii10080 = 0x043E,
- afii10081 = 0x043F,
- afii10082 = 0x0440,
- afii10083 = 0x0441,
- afii10084 = 0x0442,
- afii10085 = 0x0443,
- afii10086 = 0x0444,
- afii10087 = 0x0445,
- afii10088 = 0x0446,
- afii10089 = 0x0447,
- afii10090 = 0x0448,
- afii10091 = 0x0449,
- afii10092 = 0x044A,
- afii10093 = 0x044B,
- afii10094 = 0x044C,
- afii10095 = 0x044D,
- afii10096 = 0x044E,
- afii10097 = 0x044F,
- afii10098 = 0x0491,
- afii10099 = 0x0452,
- afii10100 = 0x0453,
- afii10101 = 0x0454,
- afii10102 = 0x0455,
- afii10103 = 0x0456,
- afii10104 = 0x0457,
- afii10105 = 0x0458,
- afii10106 = 0x0459,
- afii10107 = 0x045A,
- afii10108 = 0x045B,
- afii10109 = 0x045C,
- afii10110 = 0x045E,
- afii10193 = 0x045F,
- afii10194 = 0x0463,
- afii10195 = 0x0473,
- afii10196 = 0x0475,
- afii10846 = 0x04D9,
- afii208 = 0x2015,
- afii57381 = 0x066A,
- afii57388 = 0x060C,
- afii57392 = 0x0660,
- afii57393 = 0x0661,
- afii57394 = 0x0662,
- afii57395 = 0x0663,
- afii57396 = 0x0664,
- afii57397 = 0x0665,
- afii57398 = 0x0666,
- afii57399 = 0x0667,
- afii57400 = 0x0668,
- afii57401 = 0x0669,
- afii57403 = 0x061B,
- afii57407 = 0x061F,
- afii57409 = 0x0621,
- afii57410 = 0x0622,
- afii57411 = 0x0623,
- afii57412 = 0x0624,
- afii57413 = 0x0625,
- afii57414 = 0x0626,
- afii57415 = 0x0627,
- afii57416 = 0x0628,
- afii57417 = 0x0629,
- afii57418 = 0x062A,
- afii57419 = 0x062B,
- afii57420 = 0x062C,
- afii57421 = 0x062D,
- afii57422 = 0x062E,
- afii57423 = 0x062F,
- afii57424 = 0x0630,
- afii57425 = 0x0631,
- afii57426 = 0x0632,
- afii57427 = 0x0633,
- afii57428 = 0x0634,
- afii57429 = 0x0635,
- afii57430 = 0x0636,
- afii57431 = 0x0637,
- afii57432 = 0x0638,
- afii57433 = 0x0639,
- afii57434 = 0x063A,
- afii57440 = 0x0640,
- afii57441 = 0x0641,
- afii57442 = 0x0642,
- afii57443 = 0x0643,
- afii57444 = 0x0644,
- afii57445 = 0x0645,
- afii57446 = 0x0646,
- afii57448 = 0x0648,
- afii57449 = 0x0649,
- afii57450 = 0x064A,
- afii57451 = 0x064B,
- afii57452 = 0x064C,
- afii57453 = 0x064D,
- afii57454 = 0x064E,
- afii57455 = 0x064F,
- afii57456 = 0x0650,
- afii57457 = 0x0651,
- afii57458 = 0x0652,
- afii57470 = 0x0647,
- afii57505 = 0x06A4,
- afii57506 = 0x067E,
- afii57507 = 0x0686,
- afii57508 = 0x0698,
- afii57509 = 0x06AF,
- afii57511 = 0x0679,
- afii57512 = 0x0688,
- afii57513 = 0x0691,
- afii57514 = 0x06BA,
- afii57519 = 0x06D2,
- afii57636 = 0x20AA,
- afii57645 = 0x05BE,
- afii57658 = 0x05C3,
- afii57664 = 0x05D0,
- afii57665 = 0x05D1,
- afii57666 = 0x05D2,
- afii57667 = 0x05D3,
- afii57668 = 0x05D4,
- afii57669 = 0x05D5,
- afii57670 = 0x05D6,
- afii57671 = 0x05D7,
- afii57672 = 0x05D8,
- afii57673 = 0x05D9,
- afii57674 = 0x05DA,
- afii57675 = 0x05DB,
- afii57676 = 0x05DC,
- afii57677 = 0x05DD,
- afii57678 = 0x05DE,
- afii57679 = 0x05DF,
- afii57680 = 0x05E0,
- afii57681 = 0x05E1,
- afii57682 = 0x05E2,
- afii57683 = 0x05E3,
- afii57684 = 0x05E4,
- afii57685 = 0x05E5,
- afii57686 = 0x05E6,
- afii57687 = 0x05E7,
- afii57688 = 0x05E8,
- afii57689 = 0x05E9,
- afii57690 = 0x05EA,
- afii57716 = 0x05F0,
- afii57717 = 0x05F1,
- afii57718 = 0x05F2,
- afii57793 = 0x05B4,
- afii57794 = 0x05B5,
- afii57795 = 0x05B6,
- afii57796 = 0x05BB,
- afii57797 = 0x05B8,
- afii57798 = 0x05B7,
- afii57799 = 0x05B0,
- afii57800 = 0x05B2,
- afii57801 = 0x05B1,
- afii57802 = 0x05B3,
- afii57803 = 0x05C2,
- afii57804 = 0x05C1,
- afii57806 = 0x05B9,
- afii57807 = 0x05BC,
- afii57839 = 0x05BD,
- afii57841 = 0x05BF,
- afii57842 = 0x05C0,
- afii57929 = 0x02BC,
- afii61248 = 0x2105,
- afii61289 = 0x2113,
- afii61352 = 0x2116,
- afii61664 = 0x200C,
- afii63167 = 0x066D,
- afii64937 = 0x02BD,
- arrowdblboth = 0x21D4,
- arrowdblleft = 0x21D0,
- arrowdblright = 0x21D2,
- arrowupdnbse = 0x21A8,
- bar = 0x007C,
- circle = 0x25CB,
- circlemultiply = 0x2297,
- circleplus = 0x2295,
- club = 0x2663,
- colonmonetary = 0x20A1,
- dcroat = 0x0111,
- dkshade = 0x2593,
- existential = 0x2203,
- female = 0x2640,
- gradient = 0x2207,
- heart = 0x2665,
- hookabovecomb = 0x0309,
- invcircle = 0x25D9,
- ldot = 0x0140,
- longs = 0x017F,
- ltshade = 0x2591,
- male = 0x2642,
- mu = 0x00B5,
- napostrophe = 0x0149,
- notelement = 0x2209,
- omega1 = 0x03D6,
- openbullet = 0x25E6,
- orthogonal = 0x221F,
- oslashacute = 0x01FF,
- phi1 = 0x03D5,
- propersubset = 0x2282,
- propersuperset = 0x2283,
- reflexsubset = 0x2286,
- reflexsuperset = 0x2287,
- shade = 0x2592,
- sigma1 = 0x03C2,
- similar = 0x223C,
- smileface = 0x263A,
- spacehackarabic = 0x0020,
- spade = 0x2660,
- theta1 = 0x03D1,
- twodotenleader = 0x2025,
-}
-
-for u, c in next, characters.data do
- local a = c.adobename
- if a then
- unicodes[a] = u
- names [u] = a
- end
-end
-
-for a, u in next, extras do
- unicodes[a] = u
- if not names[u] then
- names[u] = a
- end
-end
-
-for s, u in next, synonyms do
- unicodes[s] = u
- if not names[u] then
- names[u] = s
- end
-end
-
--- We load this table only when needed. We could use a loading mechanism
--- return the table but there are no more vectors like this so why bother.
-
-fonts.encodings = fonts.encodings or { }
-
-fonts.encodings.agl = {
- names = names, -- unicode -> name
- unicodes = unicodes, -- name -> unicode
- synonyms = synonyms, -- merged into the other two
- extras = extras, -- merged into the other two
-}
+if not modules then modules = { } end modules ['font-agl'] = {
+ version = 1.001,
+ comment = "companion to font-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "derived from http://www.adobe.com/devnet/opentype/archives/glyphlist.txt",
+ original = "Adobe Glyph List, version 2.0, September 20, 2002",
+}
+
+local allocate = utilities.storage.allocate
+
+local names = allocate {
+ -- filled from char-def.lua
+}
+local unicodes = allocate {
+ -- filled from char-def.lua
+}
+
+local synonyms = {
+ Acyrillic = 0x0410,
+ Becyrillic = 0x0411,
+ Cdot = 0x010A,
+ Checyrillic = 0x0427,
+ Decyrillic = 0x0414,
+ Djecyrillic = 0x0402,
+ Dzecyrillic = 0x0405,
+ Dzhecyrillic = 0x040F,
+ Ecyrillic = 0x0404,
+ Edot = 0x0116,
+ Efcyrillic = 0x0424,
+ Elcyrillic = 0x041B,
+ Emcyrillic = 0x041C,
+ Encyrillic = 0x041D,
+ Ercyrillic = 0x0420,
+ Ereversedcyrillic = 0x042D,
+ Escyrillic = 0x0421,
+ Fitacyrillic = 0x0472,
+ Gcedilla = 0x0122,
+ Gdot = 0x0120,
+ Gecyrillic = 0x0413,
+ Gheupturncyrillic = 0x0490,
+ Gjecyrillic = 0x0403,
+ Hardsigncyrillic = 0x042A,
+ IAcyrillic = 0x042F,
+ IUcyrillic = 0x042E,
+ Icyrillic = 0x0406,
+ Idot = 0x0130,
+ Iecyrillic = 0x0415,
+ Iicyrillic = 0x0418,
+ Iishortcyrillic = 0x0419,
+ Iocyrillic = 0x0401,
+ Izhitsacyrillic = 0x0474,
+ Jecyrillic = 0x0408,
+ Kacyrillic = 0x041A,
+ Kcedilla = 0x0136,
+ Khacyrillic = 0x0425,
+ Kjecyrillic = 0x040C,
+ Lcedilla = 0x013B,
+ Ljecyrillic = 0x0409,
+ Ncedilla = 0x0145,
+ Njecyrillic = 0x040A,
+ Ocyrillic = 0x041E,
+ Odblacute = 0x0150,
+ Ohm = 0x2126,
+ Pecyrillic = 0x041F,
+ Rcedilla = 0x0156,
+ Shacyrillic = 0x0428,
+ Shchacyrillic = 0x0429,
+ Softsigncyrillic = 0x042C,
+ Tcedilla = 0x0162,
+ Tecyrillic = 0x0422,
+ Tsecyrillic = 0x0426,
+ Tshecyrillic = 0x040B,
+ Ucyrillic = 0x0423,
+ Udblacute = 0x0170,
+ Ushortcyrillic = 0x040E,
+ Vecyrillic = 0x0412,
+ Yatcyrillic = 0x0462,
+ Yericyrillic = 0x042B,
+ Yicyrillic = 0x0407,
+ Zdot = 0x017B,
+ Zecyrillic = 0x0417,
+ Zhecyrillic = 0x0416,
+ acutecmb = 0x0301,
+ acyrillic = 0x0430,
+ afii00208 = 0x2015,
+ afii08941 = 0x20A4,
+ afii57694 = 0xFB2A,
+ afii57695 = 0xFB2B,
+ afii57700 = 0xFB4B,
+ afii57705 = 0xFB1F,
+ afii57723 = 0xFB35,
+ alef = 0x05D0,
+ alefmaksurainitialarabic = 0xFEF3,
+ alefmaksuramedialarabic = 0xFEF4,
+ approximatelyequal = 0x2245,
+ asteriskaltonearabic = 0x066D,
+ ayin = 0x05E2,
+ bet = 0x05D1,
+ betdagesh = 0xFB31,
+ blackdownpointingtriangle = 0x25BC,
+ blackleftpointingpointer = 0x25C4,
+ blackrectangle = 0x25AC,
+ blackrightpointingpointer = 0x25BA,
+ blacksmilingface = 0x263B,
+ blacksquare = 0x25A0,
+ blackuppointingtriangle = 0x25B2,
+ bulletinverse = 0x25D8,
+ cdot = 0x010B,
+ compass = 0x263C,
+ dagesh = 0x05BC,
+ dalet = 0x05D3,
+ daletdagesh = 0xFB33,
+ dalethatafpatah = 0x05D3,
+ dalethatafpatahhebrew = 0x05D3,
+ dalethatafsegol = 0x05D3,
+ dalethatafsegolhebrew = 0x05D3,
+ dalethebrew = 0x05D3,
+ dalethiriq = 0x05D3,
+ dalethiriqhebrew = 0x05D3,
+ daletholam = 0x05D3,
+ daletholamhebrew = 0x05D3,
+ daletpatah = 0x05D3,
+ daletpatahhebrew = 0x05D3,
+ daletqamats = 0x05D3,
+ daletqamatshebrew = 0x05D3,
+ daletqubuts = 0x05D3,
+ daletqubutshebrew = 0x05D3,
+ daletsegol = 0x05D3,
+ daletsegolhebrew = 0x05D3,
+ daletsheva = 0x05D3,
+ daletshevahebrew = 0x05D3,
+ dalettsere = 0x05D3,
+ dammaarabic = 0x064F,
+ dammatanaltonearabic = 0x064C,
+ dargahebrew = 0x05A7,
+ dbllowline = 0x2017,
+ decimalseparatorarabic = 0x066B,
+ dialytikatonos = 0x0385,
+ dotbelowcmb = 0x0323,
+ doubleyodpatah = 0xFB1F,
+ doubleyodpatahhebrew = 0xFB1F,
+ edot = 0x0117,
+ eightarabic = 0x0668,
+ eighthnotebeamed = 0x266B,
+ etnahtafoukhhebrew = 0x0591,
+ etnahtafoukhlefthebrew = 0x0591,
+ etnahtahebrew = 0x0591,
+ fathaarabic = 0x064E,
+ finalkaf = 0x05DA,
+ finalkafdagesh = 0xFB3A,
+ finalkafhebrew = 0x05DA,
+ finalkafqamats = 0x05DA,
+ finalkafqamatshebrew = 0x05DA,
+ finalkafsheva = 0x05DA,
+ finalmem = 0x05DD,
+ finalnun = 0x05DF,
+ finalpe = 0x05E3,
+ finaltsadi = 0x05E5,
+ fivearabic = 0x0665,
+ forall = 0x2200,
+ fourarabic = 0x0664,
+ gcedilla = 0x0123,
+ gdot = 0x0121,
+ gimel = 0x05D2,
+ gimeldagesh = 0xFB32,
+ gravecmb = 0x0300,
+ haaltonearabic = 0x06C1,
+ hamzaarabic = 0x0621,
+ hamzadammaarabic = 0x0621,
+ hamzadammatanarabic = 0x0621,
+ hamzafathaarabic = 0x0621,
+ hamzafathatanarabic = 0x0621,
+ hamzalowarabic = 0x0621,
+ hamzalowkasraarabic = 0x0621,
+ hamzalowkasratanarabic = 0x0621,
+ hatafpatah = 0x05B2,
+ hatafpatah16 = 0x05B2,
+ hatafpatah23 = 0x05B2,
+ hatafpatah2f = 0x05B2,
+ hatafpatahhebrew = 0x05B2,
+ hatafpatahnarrowhebrew = 0x05B2,
+ hatafpatahquarterhebrew = 0x05B2,
+ hatafqamats = 0x05B3,
+ hatafqamats1b = 0x05B3,
+ hatafqamats28 = 0x05B3,
+ hatafqamats34 = 0x05B3,
+ hatafqamatshebrew = 0x05B3,
+ hatafqamatsnarrowhebrew = 0x05B3,
+ hatafqamatsquarterhebrew = 0x05B3,
+ hatafsegol = 0x05B1,
+ hatafsegol17 = 0x05B1,
+ hatafsegol24 = 0x05B1,
+ hatafsegol30 = 0x05B1,
+ hatafsegolhebrew = 0x05B1,
+ hatafsegolnarrowhebrew = 0x05B1,
+ hatafsegolquarterhebrew = 0x05B1,
+ he = 0x05D4,
+ hedagesh = 0xFB34,
+ hehfinalalttwoarabic = 0xFEEA,
+ het = 0x05D7,
+ hiriq = 0x05B4,
+ hiriq14 = 0x05B4,
+ hiriq21 = 0x05B4,
+ hiriq2d = 0x05B4,
+ hiriqhebrew = 0x05B4,
+ hiriqnarrowhebrew = 0x05B4,
+ hiriqquarterhebrew = 0x05B4,
+ holam = 0x05B9,
+ holam19 = 0x05B9,
+ holam26 = 0x05B9,
+ holam32 = 0x05B9,
+ holamhebrew = 0x05B9,
+ holamnarrowhebrew = 0x05B9,
+ holamquarterhebrew = 0x05B9,
+ ilde = 0x02DC,
+ integralbottom = 0x2321,
+ integraltop = 0x2320,
+ kaf = 0x05DB,
+ kafdagesh = 0xFB3B,
+ kashidaautoarabic = 0x0640,
+ kashidaautonosidebearingarabic = 0x0640,
+ kcedilla = 0x0137,
+ lamed = 0x05DC,
+ lameddagesh = 0xFB3C,
+ lamedhebrew = 0x05DC,
+ lamedholam = 0x05DC,
+ lamedholamdagesh = 0x05DC,
+ lamedholamdageshhebrew = 0x05DC,
+ laminitialarabic = 0xFEDF,
+ lammeemjeeminitialarabic = 0xFEDF,
+ lcedilla = 0x013C,
+ logicalnotreversed = 0x2310,
+ mahapakhhebrew = 0x05A4,
+ mem = 0x05DE,
+ memdagesh = 0xFB3E,
+ merkhahebrew = 0x05A5,
+ merkhakefulahebrew = 0x05A6,
+ middot = 0x00B7,
+ munahhebrew = 0x05A3,
+ nbspace = 0x00A0,
+ ncedilla = 0x0146,
+ newsheqelsign = 0x20AA,
+ ninearabic = 0x0669,
+ noonhehinitialarabic = 0xFEE7,
+ nun = 0x05E0,
+ nundagesh = 0xFB40,
+ odblacute = 0x0151,
+ onearabic = 0x0661,
+ overscore = 0x00AF,
+ patah = 0x05B7,
+ patah11 = 0x05B7,
+ patah1d = 0x05B7,
+ patah2a = 0x05B7,
+ patahhebrew = 0x05B7,
+ patahnarrowhebrew = 0x05B7,
+ patahquarterhebrew = 0x05B7,
+ pe = 0x05E4,
+ pedagesh = 0xFB44,
+ qamats = 0x05B8,
+ qamats10 = 0x05B8,
+ qamats1a = 0x05B8,
+ qamats1c = 0x05B8,
+ qamats27 = 0x05B8,
+ qamats29 = 0x05B8,
+ qamats33 = 0x05B8,
+ qamatsde = 0x05B8,
+ qamatshebrew = 0x05B8,
+ qamatsnarrowhebrew = 0x05B8,
+ qamatsqatanhebrew = 0x05B8,
+ qamatsqatannarrowhebrew = 0x05B8,
+ qamatsqatanquarterhebrew = 0x05B8,
+ qamatsqatanwidehebrew = 0x05B8,
+ qamatsquarterhebrew = 0x05B8,
+ qof = 0x05E7,
+ qofdagesh = 0xFB47,
+ qofhatafpatah = 0x05E7,
+ qofhatafpatahhebrew = 0x05E7,
+ qofhatafsegol = 0x05E7,
+ qofhatafsegolhebrew = 0x05E7,
+ qofhebrew = 0x05E7,
+ qofhiriq = 0x05E7,
+ qofhiriqhebrew = 0x05E7,
+ qofholam = 0x05E7,
+ qofholamhebrew = 0x05E7,
+ qofpatah = 0x05E7,
+ qofpatahhebrew = 0x05E7,
+ qofqamats = 0x05E7,
+ qofqamatshebrew = 0x05E7,
+ qofqubuts = 0x05E7,
+ qofqubutshebrew = 0x05E7,
+ qofsegol = 0x05E7,
+ qofsegolhebrew = 0x05E7,
+ qofsheva = 0x05E7,
+ qofshevahebrew = 0x05E7,
+ qoftsere = 0x05E7,
+ qubuts = 0x05BB,
+ qubuts18 = 0x05BB,
+ qubuts25 = 0x05BB,
+ qubuts31 = 0x05BB,
+ qubutshebrew = 0x05BB,
+ qubutsnarrowhebrew = 0x05BB,
+ qubutsquarterhebrew = 0x05BB,
+ quoteleftreversed = 0x201B,
+ rafe = 0x05BF,
+ rcedilla = 0x0157,
+ reharabic = 0x0631,
+ resh = 0x05E8,
+ reshhatafpatah = 0x05E8,
+ reshhatafpatahhebrew = 0x05E8,
+ reshhatafsegol = 0x05E8,
+ reshhatafsegolhebrew = 0x05E8,
+ reshhebrew = 0x05E8,
+ reshhiriq = 0x05E8,
+ reshhiriqhebrew = 0x05E8,
+ reshholam = 0x05E8,
+ reshholamhebrew = 0x05E8,
+ reshpatah = 0x05E8,
+ reshpatahhebrew = 0x05E8,
+ reshqamats = 0x05E8,
+ reshqamatshebrew = 0x05E8,
+ reshqubuts = 0x05E8,
+ reshqubutshebrew = 0x05E8,
+ reshsegol = 0x05E8,
+ reshsegolhebrew = 0x05E8,
+ reshsheva = 0x05E8,
+ reshshevahebrew = 0x05E8,
+ reshtsere = 0x05E8,
+ reviahebrew = 0x0597,
+ samekh = 0x05E1,
+ samekhdagesh = 0xFB41,
+ segol = 0x05B6,
+ segol13 = 0x05B6,
+ segol1f = 0x05B6,
+ segol2c = 0x05B6,
+ segolhebrew = 0x05B6,
+ segolnarrowhebrew = 0x05B6,
+ segolquarterhebrew = 0x05B6,
+ sevenarabic = 0x0667,
+ sfthyphen = 0x00AD,
+ shaddaarabic = 0x0651,
+ sheqel = 0x20AA,
+ sheva = 0x05B0,
+ sheva115 = 0x05B0,
+ sheva15 = 0x05B0,
+ sheva22 = 0x05B0,
+ sheva2e = 0x05B0,
+ shevahebrew = 0x05B0,
+ shevanarrowhebrew = 0x05B0,
+ shevaquarterhebrew = 0x05B0,
+ shin = 0x05E9,
+ shindagesh = 0xFB49,
+ shindageshshindot = 0xFB2C,
+ shindageshsindot = 0xFB2D,
+ shinshindot = 0xFB2A,
+ shinsindot = 0xFB2B,
+ siluqhebrew = 0x05BD,
+ sixarabic = 0x0666,
+ tav = 0x05EA,
+ tavdages = 0xFB4A,
+ tavdagesh = 0xFB4A,
+ tcedilla = 0x0163,
+ tchehinitialarabic = 0xFB7C,
+ tet = 0x05D8,
+ tetdagesh = 0xFB38,
+ tevirhebrew = 0x059B,
+ thousandsseparatorarabic = 0x066C,
+ threearabic = 0x0663,
+ tildecmb = 0x0303,
+ tipehahebrew = 0x0596,
+ tsadi = 0x05E6,
+ tsadidagesh = 0xFB46,
+ tsere = 0x05B5,
+ tsere12 = 0x05B5,
+ tsere1e = 0x05B5,
+ tsere2b = 0x05B5,
+ tserehebrew = 0x05B5,
+ tserenarrowhebrew = 0x05B5,
+ tserequarterhebrew = 0x05B5,
+ twoarabic = 0x0662,
+ udblacute = 0x0171,
+ vav = 0x05D5,
+ vavdagesh = 0xFB35,
+ vavdagesh65 = 0xFB35,
+ vavholam = 0xFB4B,
+ yerahbenyomohebrew = 0x05AA,
+ yod = 0x05D9,
+ yoddagesh = 0xFB39,
+ zayin = 0x05D6,
+ zayindagesh = 0xFB36,
+ zdot = 0x017C,
+ zeroarabic = 0x0660,
+}
+
+local extras = allocate { -- private extensions
+ Dcroat = 0x0110,
+ Delta = 0x2206,
+ Euro = 0x20AC,
+ H18533 = 0x25CF,
+ H18543 = 0x25AA,
+ H18551 = 0x25AB,
+ H22073 = 0x25A1,
+ Ldot = 0x013F,
+ Oslashacute = 0x01FE,
+ SF10000 = 0x250C,
+ SF20000 = 0x2514,
+ SF30000 = 0x2510,
+ SF40000 = 0x2518,
+ SF50000 = 0x253C,
+ SF60000 = 0x252C,
+ SF70000 = 0x2534,
+ SF80000 = 0x251C,
+ SF90000 = 0x2524,
+ Upsilon1 = 0x03D2,
+ afii10066 = 0x0431,
+ afii10067 = 0x0432,
+ afii10068 = 0x0433,
+ afii10069 = 0x0434,
+ afii10070 = 0x0435,
+ afii10071 = 0x0451,
+ afii10072 = 0x0436,
+ afii10073 = 0x0437,
+ afii10074 = 0x0438,
+ afii10075 = 0x0439,
+ afii10076 = 0x043A,
+ afii10077 = 0x043B,
+ afii10078 = 0x043C,
+ afii10079 = 0x043D,
+ afii10080 = 0x043E,
+ afii10081 = 0x043F,
+ afii10082 = 0x0440,
+ afii10083 = 0x0441,
+ afii10084 = 0x0442,
+ afii10085 = 0x0443,
+ afii10086 = 0x0444,
+ afii10087 = 0x0445,
+ afii10088 = 0x0446,
+ afii10089 = 0x0447,
+ afii10090 = 0x0448,
+ afii10091 = 0x0449,
+ afii10092 = 0x044A,
+ afii10093 = 0x044B,
+ afii10094 = 0x044C,
+ afii10095 = 0x044D,
+ afii10096 = 0x044E,
+ afii10097 = 0x044F,
+ afii10098 = 0x0491,
+ afii10099 = 0x0452,
+ afii10100 = 0x0453,
+ afii10101 = 0x0454,
+ afii10102 = 0x0455,
+ afii10103 = 0x0456,
+ afii10104 = 0x0457,
+ afii10105 = 0x0458,
+ afii10106 = 0x0459,
+ afii10107 = 0x045A,
+ afii10108 = 0x045B,
+ afii10109 = 0x045C,
+ afii10110 = 0x045E,
+ afii10193 = 0x045F,
+ afii10194 = 0x0463,
+ afii10195 = 0x0473,
+ afii10196 = 0x0475,
+ afii10846 = 0x04D9,
+ afii208 = 0x2015,
+ afii57381 = 0x066A,
+ afii57388 = 0x060C,
+ afii57392 = 0x0660,
+ afii57393 = 0x0661,
+ afii57394 = 0x0662,
+ afii57395 = 0x0663,
+ afii57396 = 0x0664,
+ afii57397 = 0x0665,
+ afii57398 = 0x0666,
+ afii57399 = 0x0667,
+ afii57400 = 0x0668,
+ afii57401 = 0x0669,
+ afii57403 = 0x061B,
+ afii57407 = 0x061F,
+ afii57409 = 0x0621,
+ afii57410 = 0x0622,
+ afii57411 = 0x0623,
+ afii57412 = 0x0624,
+ afii57413 = 0x0625,
+ afii57414 = 0x0626,
+ afii57415 = 0x0627,
+ afii57416 = 0x0628,
+ afii57417 = 0x0629,
+ afii57418 = 0x062A,
+ afii57419 = 0x062B,
+ afii57420 = 0x062C,
+ afii57421 = 0x062D,
+ afii57422 = 0x062E,
+ afii57423 = 0x062F,
+ afii57424 = 0x0630,
+ afii57425 = 0x0631,
+ afii57426 = 0x0632,
+ afii57427 = 0x0633,
+ afii57428 = 0x0634,
+ afii57429 = 0x0635,
+ afii57430 = 0x0636,
+ afii57431 = 0x0637,
+ afii57432 = 0x0638,
+ afii57433 = 0x0639,
+ afii57434 = 0x063A,
+ afii57440 = 0x0640,
+ afii57441 = 0x0641,
+ afii57442 = 0x0642,
+ afii57443 = 0x0643,
+ afii57444 = 0x0644,
+ afii57445 = 0x0645,
+ afii57446 = 0x0646,
+ afii57448 = 0x0648,
+ afii57449 = 0x0649,
+ afii57450 = 0x064A,
+ afii57451 = 0x064B,
+ afii57452 = 0x064C,
+ afii57453 = 0x064D,
+ afii57454 = 0x064E,
+ afii57455 = 0x064F,
+ afii57456 = 0x0650,
+ afii57457 = 0x0651,
+ afii57458 = 0x0652,
+ afii57470 = 0x0647,
+ afii57505 = 0x06A4,
+ afii57506 = 0x067E,
+ afii57507 = 0x0686,
+ afii57508 = 0x0698,
+ afii57509 = 0x06AF,
+ afii57511 = 0x0679,
+ afii57512 = 0x0688,
+ afii57513 = 0x0691,
+ afii57514 = 0x06BA,
+ afii57519 = 0x06D2,
+ afii57636 = 0x20AA,
+ afii57645 = 0x05BE,
+ afii57658 = 0x05C3,
+ afii57664 = 0x05D0,
+ afii57665 = 0x05D1,
+ afii57666 = 0x05D2,
+ afii57667 = 0x05D3,
+ afii57668 = 0x05D4,
+ afii57669 = 0x05D5,
+ afii57670 = 0x05D6,
+ afii57671 = 0x05D7,
+ afii57672 = 0x05D8,
+ afii57673 = 0x05D9,
+ afii57674 = 0x05DA,
+ afii57675 = 0x05DB,
+ afii57676 = 0x05DC,
+ afii57677 = 0x05DD,
+ afii57678 = 0x05DE,
+ afii57679 = 0x05DF,
+ afii57680 = 0x05E0,
+ afii57681 = 0x05E1,
+ afii57682 = 0x05E2,
+ afii57683 = 0x05E3,
+ afii57684 = 0x05E4,
+ afii57685 = 0x05E5,
+ afii57686 = 0x05E6,
+ afii57687 = 0x05E7,
+ afii57688 = 0x05E8,
+ afii57689 = 0x05E9,
+ afii57690 = 0x05EA,
+ afii57716 = 0x05F0,
+ afii57717 = 0x05F1,
+ afii57718 = 0x05F2,
+ afii57793 = 0x05B4,
+ afii57794 = 0x05B5,
+ afii57795 = 0x05B6,
+ afii57796 = 0x05BB,
+ afii57797 = 0x05B8,
+ afii57798 = 0x05B7,
+ afii57799 = 0x05B0,
+ afii57800 = 0x05B2,
+ afii57801 = 0x05B1,
+ afii57802 = 0x05B3,
+ afii57803 = 0x05C2,
+ afii57804 = 0x05C1,
+ afii57806 = 0x05B9,
+ afii57807 = 0x05BC,
+ afii57839 = 0x05BD,
+ afii57841 = 0x05BF,
+ afii57842 = 0x05C0,
+ afii57929 = 0x02BC,
+ afii61248 = 0x2105,
+ afii61289 = 0x2113,
+ afii61352 = 0x2116,
+ afii61664 = 0x200C,
+ afii63167 = 0x066D,
+ afii64937 = 0x02BD,
+ arrowdblboth = 0x21D4,
+ arrowdblleft = 0x21D0,
+ arrowdblright = 0x21D2,
+ arrowupdnbse = 0x21A8,
+ bar = 0x007C,
+ circle = 0x25CB,
+ circlemultiply = 0x2297,
+ circleplus = 0x2295,
+ club = 0x2663,
+ colonmonetary = 0x20A1,
+ dcroat = 0x0111,
+ dkshade = 0x2593,
+ existential = 0x2203,
+ female = 0x2640,
+ gradient = 0x2207,
+ heart = 0x2665,
+ hookabovecomb = 0x0309,
+ invcircle = 0x25D9,
+ ldot = 0x0140,
+ longs = 0x017F,
+ ltshade = 0x2591,
+ male = 0x2642,
+ mu = 0x00B5,
+ napostrophe = 0x0149,
+ notelement = 0x2209,
+ omega1 = 0x03D6,
+ openbullet = 0x25E6,
+ orthogonal = 0x221F,
+ oslashacute = 0x01FF,
+ phi1 = 0x03D5,
+ propersubset = 0x2282,
+ propersuperset = 0x2283,
+ reflexsubset = 0x2286,
+ reflexsuperset = 0x2287,
+ shade = 0x2592,
+ sigma1 = 0x03C2,
+ similar = 0x223C,
+ smileface = 0x263A,
+ spacehackarabic = 0x0020,
+ spade = 0x2660,
+ theta1 = 0x03D1,
+ twodotenleader = 0x2025,
+}
+
+for u, c in next, characters.data do
+ local a = c.adobename
+ if a then
+ unicodes[a] = u
+ names [u] = a
+ end
+end
+
+for a, u in next, extras do
+ unicodes[a] = u
+ if not names[u] then
+ names[u] = a
+ end
+end
+
+for s, u in next, synonyms do
+ unicodes[s] = u
+ if not names[u] then
+ names[u] = s
+ end
+end
+
+-- We load this table only when needed. We could use a loading mechanism
+-- return the table but there are no more vectors like this so why bother.
+
+fonts.encodings = fonts.encodings or { }
+
+fonts.encodings.agl = {
+ names = names, -- unicode -> name
+ unicodes = unicodes, -- name -> unicode
+ synonyms = synonyms, -- merged into the other two
+ extras = extras, -- merged into the other two
+}
diff --git a/tex/context/base/font-aux.lua b/tex/context/base/font-aux.lua
index e50b69881..2a605d224 100644
--- a/tex/context/base/font-aux.lua
+++ b/tex/context/base/font-aux.lua
@@ -1,165 +1,165 @@
-if not modules then modules = { } end modules ['font-aux'] = {
- 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, type = tonumber, type
------ wrap, yield = coroutine.wrap, coroutine.yield
-
-local fonts, font = fonts, font
-
-local iterators = { }
-fonts.iterators = iterators
-
-local currentfont = font.current
-local identifiers = fonts.hashes.identifiers
-local sortedkeys = table.sortedkeys
-
--- for unicode, character in fonts.iterators.characters () do print(unicode) end
--- for unicode, description in fonts.iterators.descriptions() do print(unicode) end
--- for index, glyph in fonts.iterators.glyphs () do print(index ) end
-
-local function dummy() end
-
-local function checkeddata(data) -- beware, nullfont is the fallback in identifiers
- local t = type(data)
- if t == "table" then
- return data
- elseif t ~= "number" then
- data = currentfont()
- end
- return identifiers[data] -- has nullfont as fallback
-end
-
-local function getindices(data)
- data = checkeddata(data)
- local indices = { }
- local characters = data.characters
- if characters then
- for unicode, character in next, characters do
- indices[character.index or unicode] = unicode
- end
- end
- return indices
-end
-
--- function iterators.characters(data)
--- data = checkeddata(data)
--- local characters = data.characters
--- if characters then
--- local collected = sortedkeys(characters)
--- return wrap(function()
--- for c=1,#collected do
--- local cc = collected[c]
--- local dc = characters[cc]
--- if dc then
--- yield(cc,dc)
--- end
--- end
--- end)
--- else
--- return wrap(function() end)
--- end
--- end
-
--- function iterators.descriptions(data)
--- data = checkeddata(data)
--- local characters = data.characters
--- local descriptions = data.descriptions
--- if characters and descriptions then
--- local collected = sortedkeys(characters)
--- return wrap(function()
--- for c=1,#collected do
--- local cc = collected[c]
--- local dc = descriptions[cc]
--- if dc then
--- yield(cc,dc)
--- end
--- end
--- end)
--- else
--- return wrap(function() end)
--- end
--- end
-
--- function iterators.glyphs(data)
--- data = checkeddata(data)
--- local descriptions = data.descriptions
--- if descriptions then
--- local indices = getindices(data)
--- local collected = sortedkeys(indices)
--- return wrap(function()
--- for c=1,#collected do
--- local cc = collected[c]
--- local dc = descriptions[indices[cc]]
--- if dc then
--- yield(cc,dc)
--- end
--- end
--- end)
--- else
--- return wrap(function() end)
--- end
--- end
-
-function iterators.characters(data)
- data = checkeddata(data)
- local characters = data.characters
- if characters then
- local collected = sortedkeys(characters)
- local n, i = #collected, 0
- return function()
- i = i + 1
- if i <= n then
- local cc = collected[i]
- local dc = characters[cc]
- return cc, dc or { }
- end
- end
- else
- return dummy
- end
-end
-
-function iterators.descriptions(data)
- data = checkeddata(data)
- local characters = data.characters
- local descriptions = data.descriptions
- if characters and descriptions then
- local collected = sortedkeys(characters)
- local n, i = #collected, 0
- return function()
- i = i + 1
- if i <= n then
- local cc = collected[i]
- local dc = descriptions[cc]
- return cc, dc or { }
- end
- end
- else
- return dummy
- end
-end
-
-function iterators.glyphs(data)
- data = checkeddata(data)
- local descriptions = data.descriptions
- if descriptions then
- local indices = getindices(data)
- local collected = sortedkeys(indices)
- local n, i = #collected, 0
- return function()
- i = i + 1
- if i <= n then
- local cc = collected[i]
- local dc = descriptions[indices[cc]]
- return cc, dc or { }
- end
- end
- else
- return dummy
- end
-end
+if not modules then modules = { } end modules ['font-aux'] = {
+ 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, type = tonumber, type
+----- wrap, yield = coroutine.wrap, coroutine.yield
+
+local fonts, font = fonts, font
+
+local iterators = { }
+fonts.iterators = iterators
+
+local currentfont = font.current
+local identifiers = fonts.hashes.identifiers
+local sortedkeys = table.sortedkeys
+
+-- for unicode, character in fonts.iterators.characters () do print(unicode) end
+-- for unicode, description in fonts.iterators.descriptions() do print(unicode) end
+-- for index, glyph in fonts.iterators.glyphs () do print(index ) end
+
+local function dummy() end
+
+local function checkeddata(data) -- beware, nullfont is the fallback in identifiers
+ local t = type(data)
+ if t == "table" then
+ return data
+ elseif t ~= "number" then
+ data = currentfont()
+ end
+ return identifiers[data] -- has nullfont as fallback
+end
+
+local function getindices(data)
+ data = checkeddata(data)
+ local indices = { }
+ local characters = data.characters
+ if characters then
+ for unicode, character in next, characters do
+ indices[character.index or unicode] = unicode
+ end
+ end
+ return indices
+end
+
+-- function iterators.characters(data)
+-- data = checkeddata(data)
+-- local characters = data.characters
+-- if characters then
+-- local collected = sortedkeys(characters)
+-- return wrap(function()
+-- for c=1,#collected do
+-- local cc = collected[c]
+-- local dc = characters[cc]
+-- if dc then
+-- yield(cc,dc)
+-- end
+-- end
+-- end)
+-- else
+-- return wrap(function() end)
+-- end
+-- end
+
+-- function iterators.descriptions(data)
+-- data = checkeddata(data)
+-- local characters = data.characters
+-- local descriptions = data.descriptions
+-- if characters and descriptions then
+-- local collected = sortedkeys(characters)
+-- return wrap(function()
+-- for c=1,#collected do
+-- local cc = collected[c]
+-- local dc = descriptions[cc]
+-- if dc then
+-- yield(cc,dc)
+-- end
+-- end
+-- end)
+-- else
+-- return wrap(function() end)
+-- end
+-- end
+
+-- function iterators.glyphs(data)
+-- data = checkeddata(data)
+-- local descriptions = data.descriptions
+-- if descriptions then
+-- local indices = getindices(data)
+-- local collected = sortedkeys(indices)
+-- return wrap(function()
+-- for c=1,#collected do
+-- local cc = collected[c]
+-- local dc = descriptions[indices[cc]]
+-- if dc then
+-- yield(cc,dc)
+-- end
+-- end
+-- end)
+-- else
+-- return wrap(function() end)
+-- end
+-- end
+
+function iterators.characters(data)
+ data = checkeddata(data)
+ local characters = data.characters
+ if characters then
+ local collected = sortedkeys(characters)
+ local n, i = #collected, 0
+ return function()
+ i = i + 1
+ if i <= n then
+ local cc = collected[i]
+ local dc = characters[cc]
+ return cc, dc or { }
+ end
+ end
+ else
+ return dummy
+ end
+end
+
+function iterators.descriptions(data)
+ data = checkeddata(data)
+ local characters = data.characters
+ local descriptions = data.descriptions
+ if characters and descriptions then
+ local collected = sortedkeys(characters)
+ local n, i = #collected, 0
+ return function()
+ i = i + 1
+ if i <= n then
+ local cc = collected[i]
+ local dc = descriptions[cc]
+ return cc, dc or { }
+ end
+ end
+ else
+ return dummy
+ end
+end
+
+function iterators.glyphs(data)
+ data = checkeddata(data)
+ local descriptions = data.descriptions
+ if descriptions then
+ local indices = getindices(data)
+ local collected = sortedkeys(indices)
+ local n, i = #collected, 0
+ return function()
+ i = i + 1
+ if i <= n then
+ local cc = collected[i]
+ local dc = descriptions[indices[cc]]
+ return cc, dc or { }
+ end
+ end
+ else
+ return dummy
+ end
+end
diff --git a/tex/context/base/font-chk.lua b/tex/context/base/font-chk.lua
index 9e420744a..1b89366fd 100644
--- a/tex/context/base/font-chk.lua
+++ b/tex/context/base/font-chk.lua
@@ -1,359 +1,359 @@
-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"
-}
-
--- possible optimization: delayed initialization of vectors
--- move to the nodes namespace
-
-local format = string.format
-local bpfactor = number.dimenfactors.bp
-
-local report_fonts = logs.reporter("fonts","checking")
-
-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 addprivate = fonts.helpers.addprivate
-local hasprivate = fonts.helpers.hasprivate
-local getprivatenode = fonts.helpers.getprivatenode
-
-local otffeatures = fonts.constructors.newfeatures("otf")
-local registerotffeature = otffeatures.register
-
-local is_character = characters.is_character
-local chardata = characters.data
-
-local tasks = nodes.tasks
-local enableaction = tasks.enableaction
-local disableaction = tasks.disableaction
-
-local glyph_code = nodes.nodecodes.glyph
-local traverse_id = node.traverse_id
-local remove_node = nodes.remove
-local insert_node_after = node.insert_after
-
--- maybe in fonts namespace
--- deletion can be option
-
-local action = false
-
--- to tfmdata.properties ?
-
-local function onetimemessage(font,char,message) -- char == false returns table
- local tfmdata = fontdata[font]
- local shared = tfmdata.shared
- 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 table.sortedkeys(category)
- elseif not category[char] then
- report_fonts("char %U in font %a with id %a: %s",char,tfmdata.properties.fullname,font,message)
- category[char] = true
- end
-end
-
-fonts.loggers.onetimemessage = onetimemessage
-
-local mapping = { -- this is just an experiment to illustrate some principles elsewhere
- lu = "placeholder uppercase red",
- ll = "placeholder lowercase red",
- lt = "placeholder uppercase red",
- lm = "placeholder lowercase red",
- lo = "placeholder lowercase red",
- mn = "placeholder mark green",
- mc = "placeholder mark green",
- me = "placeholder mark green",
- nd = "placeholder lowercase blue",
- nl = "placeholder lowercase blue",
- no = "placeholder lowercase blue",
- pc = "placeholder punctuation cyan",
- pd = "placeholder punctuation cyan",
- ps = "placeholder punctuation cyan",
- pe = "placeholder punctuation cyan",
- pi = "placeholder punctuation cyan",
- pf = "placeholder punctuation cyan",
- po = "placeholder punctuation cyan",
- sm = "placeholder lowercase magenta",
- sc = "placeholder lowercase yellow",
- sk = "placeholder lowercase yellow",
- so = "placeholder lowercase yellow",
-}
-
-table.setmetatableindex(mapping,function(t,k) v = "placeholder unknown gray" t[k] = v return v end)
-
-local fakes = {
- {
- name = "lowercase",
- code = ".025 -.175 m .425 -.175 l .425 .525 l .025 .525 l .025 -.175 l .025 0 l .425 0 l .025 -.175 m h S",
- width = .45,
- height = .55,
- depth = .20,
- },
- {
- name = "uppercase",
- code = ".025 -.225 m .625 -.225 l .625 .675 l .025 .675 l .025 -.225 l .025 0 l .625 0 l .025 -.225 m h S",
- width = .65,
- height = .70,
- depth = .25,
- },
- {
- name = "mark",
- code = ".025 .475 m .125 .475 l .125 .675 l .025 .675 l .025 .475 l h B",
- width = .15,
- height = .70,
- depth = -.50,
- },
- {
- name = "punctuation",
- code = ".025 -.175 m .125 -.175 l .125 .525 l .025 .525 l .025 -.175 l h B",
- width = .15,
- height = .55,
- depth = .20,
- },
- {
- name = "unknown",
- code = ".025 0 m .425 0 l .425 .175 l .025 .175 l .025 0 l h B",
- width = .45,
- height = .20,
- depth = 0,
- },
-}
-
-local variants = {
- { tag = "gray", r = .6, g = .6, b = .6 },
- { tag = "red", r = .6, g = 0, b = 0 },
- { tag = "green", r = 0, g = .6, b = 0 },
- { tag = "blue", r = 0, g = 0, b = .6 },
- { tag = "cyan", r = 0, g = .6, b = .6 },
- { tag = "magenta", r = .6, g = 0, b = .6 },
- { tag = "yellow", r = .6, g = .6, b = 0 },
-}
-
-local package = "q %0.6f 0 0 %0.6f 0 0 cm %s %s %s rg %s %s %s RG 10 M 1 j 1 J 0.05 w %s Q"
-
-local cache = { } -- saves some tables but not that impressive
-
-local function addmissingsymbols(tfmdata) -- we can have an alternative with rules
- local characters = tfmdata.characters
- local size = tfmdata.parameters.size
- local privates = tfmdata.properties.privates
- local scale = size * bpfactor
- for i=1,#variants do
- local v = variants[i]
- local tag, r, g, b = v.tag, v.r, v.g, v.b
- for i =1, #fakes do
- local fake = fakes[i]
- local name = fake.name
- local privatename = format("placeholder %s %s",name,tag)
- if not hasprivate(tfmdata,privatename) then
- local hash = format("%s_%s_%s_%s_%s_%s",name,tag,r,g,b,size)
- local char = cache[hash]
- if not char then
- char = {
- width = size*fake.width,
- height = size*fake.height,
- depth = size*fake.depth,
- -- bah .. low level pdf ... should be a rule or plugged in
- commands = { { "special", "pdf: " .. format(package,scale,scale,r,g,b,r,g,b,fake.code) } }
- }
- cache[hash] = char
- end
- addprivate(tfmdata, privatename, char)
- end
- end
- end
-end
-
-registerotffeature {
- name = "missing",
- description = "missing symbols",
- manipulators = {
- base = addmissingsymbols,
- node = addmissingsymbols,
- }
-}
-
-fonts.loggers.add_placeholders = function(id) addmissingsymbols(fontdata[id or true]) end
-fonts.loggers.category_to_placeholder = mapping
-
-function commands.getplaceholderchar(name)
- local id = font.current()
- addmissingsymbols(fontdata[id])
- context(fonts.helpers.getprivatenode(fontdata[id],name))
-end
-
-function checkers.missing(head)
- local lastfont, characters, found = nil, nil, nil
- for n in traverse_id(glyph_code,head) do -- faster than while loop so we delay removal
- local font = n.font
- local char = n.char
- if font ~= lastfont then
- characters = fontcharacters[font]
- end
- if not characters[char] and is_character[chardata[char].category] then
- if action == "remove" then
- onetimemessage(font,char,"missing (will be deleted)")
- elseif action == "replace" then
- onetimemessage(font,char,"missing (will be flagged)")
- else
- onetimemessage(font,char,"missing")
- end
- if not found then
- found = { n }
- else
- found[#found+1] = n
- end
- end
- end
- if not found then
- -- all well
- elseif action == "remove" then
- for i=1,#found do
- head = remove_node(head,found[i],true)
- end
- elseif action == "replace" then
- for i=1,#found do
- local n = found[i]
- local font = n.font
- local char = n.char
- local tfmdata = fontdata[font]
- local properties = tfmdata.properties
- local privates = properties.privates
- local category = chardata[char].category
- local fakechar = mapping[category]
- local p = privates and privates[fakechar]
- if not p then
- addmissingsymbols(tfmdata)
- p = properties.privates[fakechar]
- end
- if properties.lateprivates then -- .frozen
- -- bad, we don't have them at the tex end
- local fake = getprivatenode(tfmdata,fakechar)
- insert_node_after(head,n,fake)
- head = remove_node(head,n,true)
- else
- -- good, we have \definefontfeature[default][default][missing=yes]
- n.char = p
- end
- end
- else
- -- maye write a report to the log
- end
- return head, false
-end
-
-local relevant = { "missing (will be deleted)", "missing (will be flagged)", "missing" }
-
-function checkers.getmissing(id)
- if id then
- local list = checkers.getmissing(font.current())
- if list then
- local _, list = next(checkers.getmissing(font.current()))
- return list
- else
- return { }
- end
- else
- local t = { }
- for id, d in next, fontdata do
- local shared = d.shared
- local messages = shared.messages
- if messages then
- local tf = t[d.properties.filename] or { }
- for i=1,#relevant do
- local tm = messages[relevant[i]]
- if tm then
- tf = table.merged(tf,tm)
- end
- end
- if next(tf) then
- t[d.properties.filename] = tf
- end
- end
- end
- for k, v in next, t do
- t[k] = table.sortedkeys(v)
- end
- return t
- end
-end
-
-local tracked = false
-
-trackers.register("fonts.missing", function(v)
- if v then
- enableaction("processors","fonts.checkers.missing")
- tracked = true
- else
- disableaction("processors","fonts.checkers.missing")
- end
- if v == "replace" then
- otffeatures.defaults.missing = true
- end
- action = v
-end)
-
-function commands.checkcharactersinfont()
- enableaction("processors","fonts.checkers.missing")
- tracked = true
-end
-
-function commands.removemissingcharacters()
- enableaction("processors","fonts.checkers.missing")
- action = "remove"
- tracked = true
-end
-
-function commands.replacemissingcharacters()
- enableaction("processors","fonts.checkers.missing")
- action = "replace"
- otffeatures.defaults.missing = true
- tracked = true
-end
-
-local report_characters = logs.reporter("fonts","characters")
-local report_character = logs.reporter("missing")
-
-local logsnewline = logs.newline
-local logspushtarget = logs.pushtarget
-local logspoptarget = logs.poptarget
-
-luatex.registerstopactions(function()
- if tracked then
- local collected = checkers.getmissing()
- if next(collected) then
- logspushtarget("logfile")
- for filename, list in table.sortedhash(collected) do
- logsnewline()
- report_characters("start missing characters: %s",filename)
- logsnewline()
- for i=1,#list do
- local u = list[i]
- report_character("%U %c %s",u,u,chardata[u].description)
- end
- logsnewline()
- report_characters("stop missing characters")
- logsnewline()
- end
- logspoptarget()
- end
- end
-end)
+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"
+}
+
+-- possible optimization: delayed initialization of vectors
+-- move to the nodes namespace
+
+local format = string.format
+local bpfactor = number.dimenfactors.bp
+
+local report_fonts = logs.reporter("fonts","checking")
+
+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 addprivate = fonts.helpers.addprivate
+local hasprivate = fonts.helpers.hasprivate
+local getprivatenode = fonts.helpers.getprivatenode
+
+local otffeatures = fonts.constructors.newfeatures("otf")
+local registerotffeature = otffeatures.register
+
+local is_character = characters.is_character
+local chardata = characters.data
+
+local tasks = nodes.tasks
+local enableaction = tasks.enableaction
+local disableaction = tasks.disableaction
+
+local glyph_code = nodes.nodecodes.glyph
+local traverse_id = node.traverse_id
+local remove_node = nodes.remove
+local insert_node_after = node.insert_after
+
+-- maybe in fonts namespace
+-- deletion can be option
+
+local action = false
+
+-- to tfmdata.properties ?
+
+local function onetimemessage(font,char,message) -- char == false returns table
+ local tfmdata = fontdata[font]
+ local shared = tfmdata.shared
+ 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 table.sortedkeys(category)
+ elseif not category[char] then
+ report_fonts("char %U in font %a with id %a: %s",char,tfmdata.properties.fullname,font,message)
+ category[char] = true
+ end
+end
+
+fonts.loggers.onetimemessage = onetimemessage
+
+local mapping = { -- this is just an experiment to illustrate some principles elsewhere
+ lu = "placeholder uppercase red",
+ ll = "placeholder lowercase red",
+ lt = "placeholder uppercase red",
+ lm = "placeholder lowercase red",
+ lo = "placeholder lowercase red",
+ mn = "placeholder mark green",
+ mc = "placeholder mark green",
+ me = "placeholder mark green",
+ nd = "placeholder lowercase blue",
+ nl = "placeholder lowercase blue",
+ no = "placeholder lowercase blue",
+ pc = "placeholder punctuation cyan",
+ pd = "placeholder punctuation cyan",
+ ps = "placeholder punctuation cyan",
+ pe = "placeholder punctuation cyan",
+ pi = "placeholder punctuation cyan",
+ pf = "placeholder punctuation cyan",
+ po = "placeholder punctuation cyan",
+ sm = "placeholder lowercase magenta",
+ sc = "placeholder lowercase yellow",
+ sk = "placeholder lowercase yellow",
+ so = "placeholder lowercase yellow",
+}
+
+table.setmetatableindex(mapping,function(t,k) v = "placeholder unknown gray" t[k] = v return v end)
+
+local fakes = {
+ {
+ name = "lowercase",
+ code = ".025 -.175 m .425 -.175 l .425 .525 l .025 .525 l .025 -.175 l .025 0 l .425 0 l .025 -.175 m h S",
+ width = .45,
+ height = .55,
+ depth = .20,
+ },
+ {
+ name = "uppercase",
+ code = ".025 -.225 m .625 -.225 l .625 .675 l .025 .675 l .025 -.225 l .025 0 l .625 0 l .025 -.225 m h S",
+ width = .65,
+ height = .70,
+ depth = .25,
+ },
+ {
+ name = "mark",
+ code = ".025 .475 m .125 .475 l .125 .675 l .025 .675 l .025 .475 l h B",
+ width = .15,
+ height = .70,
+ depth = -.50,
+ },
+ {
+ name = "punctuation",
+ code = ".025 -.175 m .125 -.175 l .125 .525 l .025 .525 l .025 -.175 l h B",
+ width = .15,
+ height = .55,
+ depth = .20,
+ },
+ {
+ name = "unknown",
+ code = ".025 0 m .425 0 l .425 .175 l .025 .175 l .025 0 l h B",
+ width = .45,
+ height = .20,
+ depth = 0,
+ },
+}
+
+local variants = {
+ { tag = "gray", r = .6, g = .6, b = .6 },
+ { tag = "red", r = .6, g = 0, b = 0 },
+ { tag = "green", r = 0, g = .6, b = 0 },
+ { tag = "blue", r = 0, g = 0, b = .6 },
+ { tag = "cyan", r = 0, g = .6, b = .6 },
+ { tag = "magenta", r = .6, g = 0, b = .6 },
+ { tag = "yellow", r = .6, g = .6, b = 0 },
+}
+
+local package = "q %0.6f 0 0 %0.6f 0 0 cm %s %s %s rg %s %s %s RG 10 M 1 j 1 J 0.05 w %s Q"
+
+local cache = { } -- saves some tables but not that impressive
+
+local function addmissingsymbols(tfmdata) -- we can have an alternative with rules
+ local characters = tfmdata.characters
+ local size = tfmdata.parameters.size
+ local privates = tfmdata.properties.privates
+ local scale = size * bpfactor
+ for i=1,#variants do
+ local v = variants[i]
+ local tag, r, g, b = v.tag, v.r, v.g, v.b
+ for i =1, #fakes do
+ local fake = fakes[i]
+ local name = fake.name
+ local privatename = format("placeholder %s %s",name,tag)
+ if not hasprivate(tfmdata,privatename) then
+ local hash = format("%s_%s_%s_%s_%s_%s",name,tag,r,g,b,size)
+ local char = cache[hash]
+ if not char then
+ char = {
+ width = size*fake.width,
+ height = size*fake.height,
+ depth = size*fake.depth,
+ -- bah .. low level pdf ... should be a rule or plugged in
+ commands = { { "special", "pdf: " .. format(package,scale,scale,r,g,b,r,g,b,fake.code) } }
+ }
+ cache[hash] = char
+ end
+ addprivate(tfmdata, privatename, char)
+ end
+ end
+ end
+end
+
+registerotffeature {
+ name = "missing",
+ description = "missing symbols",
+ manipulators = {
+ base = addmissingsymbols,
+ node = addmissingsymbols,
+ }
+}
+
+fonts.loggers.add_placeholders = function(id) addmissingsymbols(fontdata[id or true]) end
+fonts.loggers.category_to_placeholder = mapping
+
+function commands.getplaceholderchar(name)
+ local id = font.current()
+ addmissingsymbols(fontdata[id])
+ context(fonts.helpers.getprivatenode(fontdata[id],name))
+end
+
+function checkers.missing(head)
+ local lastfont, characters, found = nil, nil, nil
+ for n in traverse_id(glyph_code,head) do -- faster than while loop so we delay removal
+ local font = n.font
+ local char = n.char
+ if font ~= lastfont then
+ characters = fontcharacters[font]
+ end
+ if not characters[char] and is_character[chardata[char].category] then
+ if action == "remove" then
+ onetimemessage(font,char,"missing (will be deleted)")
+ elseif action == "replace" then
+ onetimemessage(font,char,"missing (will be flagged)")
+ else
+ onetimemessage(font,char,"missing")
+ end
+ if not found then
+ found = { n }
+ else
+ found[#found+1] = n
+ end
+ end
+ end
+ if not found then
+ -- all well
+ elseif action == "remove" then
+ for i=1,#found do
+ head = remove_node(head,found[i],true)
+ end
+ elseif action == "replace" then
+ for i=1,#found do
+ local n = found[i]
+ local font = n.font
+ local char = n.char
+ local tfmdata = fontdata[font]
+ local properties = tfmdata.properties
+ local privates = properties.privates
+ local category = chardata[char].category
+ local fakechar = mapping[category]
+ local p = privates and privates[fakechar]
+ if not p then
+ addmissingsymbols(tfmdata)
+ p = properties.privates[fakechar]
+ end
+ if properties.lateprivates then -- .frozen
+ -- bad, we don't have them at the tex end
+ local fake = getprivatenode(tfmdata,fakechar)
+ insert_node_after(head,n,fake)
+ head = remove_node(head,n,true)
+ else
+ -- good, we have \definefontfeature[default][default][missing=yes]
+ n.char = p
+ end
+ end
+ else
+ -- maye write a report to the log
+ end
+ return head, false
+end
+
+local relevant = { "missing (will be deleted)", "missing (will be flagged)", "missing" }
+
+function checkers.getmissing(id)
+ if id then
+ local list = checkers.getmissing(font.current())
+ if list then
+ local _, list = next(checkers.getmissing(font.current()))
+ return list
+ else
+ return { }
+ end
+ else
+ local t = { }
+ for id, d in next, fontdata do
+ local shared = d.shared
+ local messages = shared.messages
+ if messages then
+ local tf = t[d.properties.filename] or { }
+ for i=1,#relevant do
+ local tm = messages[relevant[i]]
+ if tm then
+ tf = table.merged(tf,tm)
+ end
+ end
+ if next(tf) then
+ t[d.properties.filename] = tf
+ end
+ end
+ end
+ for k, v in next, t do
+ t[k] = table.sortedkeys(v)
+ end
+ return t
+ end
+end
+
+local tracked = false
+
+trackers.register("fonts.missing", function(v)
+ if v then
+ enableaction("processors","fonts.checkers.missing")
+ tracked = true
+ else
+ disableaction("processors","fonts.checkers.missing")
+ end
+ if v == "replace" then
+ otffeatures.defaults.missing = true
+ end
+ action = v
+end)
+
+function commands.checkcharactersinfont()
+ enableaction("processors","fonts.checkers.missing")
+ tracked = true
+end
+
+function commands.removemissingcharacters()
+ enableaction("processors","fonts.checkers.missing")
+ action = "remove"
+ tracked = true
+end
+
+function commands.replacemissingcharacters()
+ enableaction("processors","fonts.checkers.missing")
+ action = "replace"
+ otffeatures.defaults.missing = true
+ tracked = true
+end
+
+local report_characters = logs.reporter("fonts","characters")
+local report_character = logs.reporter("missing")
+
+local logsnewline = logs.newline
+local logspushtarget = logs.pushtarget
+local logspoptarget = logs.poptarget
+
+luatex.registerstopactions(function()
+ if tracked then
+ local collected = checkers.getmissing()
+ if next(collected) then
+ logspushtarget("logfile")
+ for filename, list in table.sortedhash(collected) do
+ logsnewline()
+ report_characters("start missing characters: %s",filename)
+ logsnewline()
+ for i=1,#list do
+ local u = list[i]
+ report_character("%U %c %s",u,u,chardata[u].description)
+ end
+ logsnewline()
+ report_characters("stop missing characters")
+ logsnewline()
+ end
+ logspoptarget()
+ end
+ end
+end)
diff --git a/tex/context/base/font-cid.lua b/tex/context/base/font-cid.lua
index b588493b7..e4b565313 100644
--- a/tex/context/base/font-cid.lua
+++ b/tex/context/base/font-cid.lua
@@ -1,164 +1,164 @@
-if not modules then modules = { } end modules ['font-cid'] = {
- version = 1.001,
- comment = "companion to font-otf.lua (cidmaps)",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local format, match, lower = string.format, string.match, string.lower
-local tonumber = tonumber
-local P, S, R, C, V, lpegmatch = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.match
-
-local fonts, logs, trackers = fonts, logs, trackers
-
-local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end)
-
-local report_otf = logs.reporter("fonts","otf loading")
-
-local cid = { }
-fonts.cid = cid
-
-local cidmap = { }
-local cidmax = 10
-
--- original string parser: 0.109, lpeg parser: 0.036 seconds for Adobe-CNS1-4.cidmap
---
--- 18964 18964 (leader)
--- 0 /.notdef
--- 1..95 0020
--- 99 3000
-
-local number = C(R("09","af","AF")^1)
-local space = S(" \n\r\t")
-local spaces = space^0
-local period = P(".")
-local periods = period * period
-local name = P("/") * C((1-space)^1)
-
-local unicodes, names = { }, { } -- we could use Carg now
-
-local function do_one(a,b)
- unicodes[tonumber(a)] = tonumber(b,16)
-end
-
-local function do_range(a,b,c)
- c = tonumber(c,16)
- for i=tonumber(a),tonumber(b) do
- unicodes[i] = c
- c = c + 1
- end
-end
-
-local function do_name(a,b)
- names[tonumber(a)] = b
-end
-
-local grammar = P { "start",
- start = number * spaces * number * V("series"),
- series = (spaces * (V("one") + V("range") + V("named")))^1,
- one = (number * spaces * number) / do_one,
- range = (number * periods * number * spaces * number) / do_range,
- named = (number * spaces * name) / do_name
-}
-
-local function loadcidfile(filename)
- local data = io.loaddata(filename)
- if data then
- unicodes, names = { }, { }
- lpegmatch(grammar,data)
- local supplement, registry, ordering = match(filename,"^(.-)%-(.-)%-()%.(.-)$")
- return {
- supplement = supplement,
- registry = registry,
- ordering = ordering,
- filename = filename,
- unicodes = unicodes,
- names = names
- }
- end
-end
-
-cid.loadfile = loadcidfile -- we use the frozen variant
-local template = "%s-%s-%s.cidmap"
-
-local function locate(registry,ordering,supplement)
- local filename = format(template,registry,ordering,supplement)
- local hashname = lower(filename)
- local found = cidmap[hashname]
- if not found then
- if trace_loading then
- report_otf("checking cidmap, registry %a, ordering %a, supplement %a, filename %a",registry,ordering,supplement,filename)
- end
- local fullname = resolvers.findfile(filename,'cid') or ""
- if fullname ~= "" then
- found = loadcidfile(fullname)
- if found then
- if trace_loading then
- report_otf("using cidmap file %a",filename)
- end
- cidmap[hashname] = found
- found.usedname = file.basename(filename)
- end
- end
- end
- return found
-end
-
--- cf Arthur R. we can safely scan upwards since cids are downward compatible
-
-function cid.getmap(specification)
- if not specification then
- report_otf("invalid cidinfo specification, table expected")
- return
- end
- local registry = specification.registry
- local ordering = specification.ordering
- local supplement = specification.supplement
- -- check for already loaded file
- local filename = format(registry,ordering,supplement)
- local found = cidmap[lower(filename)]
- if found then
- return found
- end
- if trace_loading then
- report_otf("cidmap needed, registry %a, ordering %a, supplement %a",registry,ordering,supplement)
- end
- found = locate(registry,ordering,supplement)
- if not found then
- local supnum = tonumber(supplement)
- local cidnum = nil
- -- next highest (alternatively we could start high)
- if supnum < cidmax then
- for s=supnum+1,cidmax do
- local c = locate(registry,ordering,s)
- if c then
- found, cidnum = c, s
- break
- end
- end
- end
- -- next lowest (least worse fit)
- if not found and supnum > 0 then
- for s=supnum-1,0,-1 do
- local c = locate(registry,ordering,s)
- if c then
- found, cidnum = c, s
- break
- end
- end
- end
- -- prevent further lookups -- somewhat tricky
- registry = lower(registry)
- ordering = lower(ordering)
- if found and cidnum > 0 then
- for s=0,cidnum-1 do
- local filename = format(template,registry,ordering,s)
- if not cidmap[filename] then
- cidmap[filename] = found
- end
- end
- end
- end
- return found
-end
+if not modules then modules = { } end modules ['font-cid'] = {
+ version = 1.001,
+ comment = "companion to font-otf.lua (cidmaps)",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local format, match, lower = string.format, string.match, string.lower
+local tonumber = tonumber
+local P, S, R, C, V, lpegmatch = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.match
+
+local fonts, logs, trackers = fonts, logs, trackers
+
+local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end)
+
+local report_otf = logs.reporter("fonts","otf loading")
+
+local cid = { }
+fonts.cid = cid
+
+local cidmap = { }
+local cidmax = 10
+
+-- original string parser: 0.109, lpeg parser: 0.036 seconds for Adobe-CNS1-4.cidmap
+--
+-- 18964 18964 (leader)
+-- 0 /.notdef
+-- 1..95 0020
+-- 99 3000
+
+local number = C(R("09","af","AF")^1)
+local space = S(" \n\r\t")
+local spaces = space^0
+local period = P(".")
+local periods = period * period
+local name = P("/") * C((1-space)^1)
+
+local unicodes, names = { }, { } -- we could use Carg now
+
+local function do_one(a,b)
+ unicodes[tonumber(a)] = tonumber(b,16)
+end
+
+local function do_range(a,b,c)
+ c = tonumber(c,16)
+ for i=tonumber(a),tonumber(b) do
+ unicodes[i] = c
+ c = c + 1
+ end
+end
+
+local function do_name(a,b)
+ names[tonumber(a)] = b
+end
+
+local grammar = P { "start",
+ start = number * spaces * number * V("series"),
+ series = (spaces * (V("one") + V("range") + V("named")))^1,
+ one = (number * spaces * number) / do_one,
+ range = (number * periods * number * spaces * number) / do_range,
+ named = (number * spaces * name) / do_name
+}
+
+local function loadcidfile(filename)
+ local data = io.loaddata(filename)
+ if data then
+ unicodes, names = { }, { }
+ lpegmatch(grammar,data)
+ local supplement, registry, ordering = match(filename,"^(.-)%-(.-)%-()%.(.-)$")
+ return {
+ supplement = supplement,
+ registry = registry,
+ ordering = ordering,
+ filename = filename,
+ unicodes = unicodes,
+ names = names
+ }
+ end
+end
+
+cid.loadfile = loadcidfile -- we use the frozen variant
+local template = "%s-%s-%s.cidmap"
+
+local function locate(registry,ordering,supplement)
+ local filename = format(template,registry,ordering,supplement)
+ local hashname = lower(filename)
+ local found = cidmap[hashname]
+ if not found then
+ if trace_loading then
+ report_otf("checking cidmap, registry %a, ordering %a, supplement %a, filename %a",registry,ordering,supplement,filename)
+ end
+ local fullname = resolvers.findfile(filename,'cid') or ""
+ if fullname ~= "" then
+ found = loadcidfile(fullname)
+ if found then
+ if trace_loading then
+ report_otf("using cidmap file %a",filename)
+ end
+ cidmap[hashname] = found
+ found.usedname = file.basename(filename)
+ end
+ end
+ end
+ return found
+end
+
+-- cf Arthur R. we can safely scan upwards since cids are downward compatible
+
+function cid.getmap(specification)
+ if not specification then
+ report_otf("invalid cidinfo specification, table expected")
+ return
+ end
+ local registry = specification.registry
+ local ordering = specification.ordering
+ local supplement = specification.supplement
+ -- check for already loaded file
+ local filename = format(registry,ordering,supplement)
+ local found = cidmap[lower(filename)]
+ if found then
+ return found
+ end
+ if trace_loading then
+ report_otf("cidmap needed, registry %a, ordering %a, supplement %a",registry,ordering,supplement)
+ end
+ found = locate(registry,ordering,supplement)
+ if not found then
+ local supnum = tonumber(supplement)
+ local cidnum = nil
+ -- next highest (alternatively we could start high)
+ if supnum < cidmax then
+ for s=supnum+1,cidmax do
+ local c = locate(registry,ordering,s)
+ if c then
+ found, cidnum = c, s
+ break
+ end
+ end
+ end
+ -- next lowest (least worse fit)
+ if not found and supnum > 0 then
+ for s=supnum-1,0,-1 do
+ local c = locate(registry,ordering,s)
+ if c then
+ found, cidnum = c, s
+ break
+ end
+ end
+ end
+ -- prevent further lookups -- somewhat tricky
+ registry = lower(registry)
+ ordering = lower(ordering)
+ if found and cidnum > 0 then
+ for s=0,cidnum-1 do
+ local filename = format(template,registry,ordering,s)
+ if not cidmap[filename] then
+ cidmap[filename] = found
+ end
+ end
+ end
+ end
+ return found
+end
diff --git a/tex/context/base/font-col.lua b/tex/context/base/font-col.lua
index b8b221fc4..20c99c9b4 100644
--- a/tex/context/base/font-col.lua
+++ b/tex/context/base/font-col.lua
@@ -1,238 +1,238 @@
-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
-
-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, toboolean = type, next, toboolean
-local gmatch = string.gmatch
-local fastcopy = table.fastcopy
------ P, Cc, lpegmatch = lpeg.P, lpeg.Cc, lpeg.match
-
-local traverse_id = node.traverse_id
-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 collections = fonts.collections or { }
-fonts.collections = collections
-
-local definitions = collections.definitions or { }
-collections.definitions = definitions
-
-local vectors = collections.vectors or { }
-collections.vectors = vectors
-
-local fontdata = fonts.hashes.identifiers
-local glyph_code = nodes.nodecodes.glyph
-local currentfont = font.current
-
-local fontpatternhassize = fonts.helpers.fontpatternhassize
-
-local list = { }
-local current = 0
-local enabled = false
-
--- maybe also a copy
-
-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
- for s in gmatch(ranges,"[^, ]+") do
- local start, stop, description = characters.getrange(s)
- 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
- details.font, details.start, details.stop = font, start, stop
- d[#d+1] = fastcopy(details)
- 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
-
-function collections.clonevector(name)
- statistics.starttiming(fonts)
- local d = definitions[name]
- local t = { }
- if trace_collecting then
- report_fonts("processing collection %a",name)
- end
- for i=1,#d do
- local f = d[i]
- local id = list[i]
- local start, stop = f.start, f.stop
- if trace_collecting then
- report_fonts("remapping font %a to %a for range %U - %U",current,id,start,stop)
- end
- local check = toboolean(f.check or "false",true)
- local force = toboolean(f.force or "true",true)
- local remap = f.remap or nil
- -- check: when true, only set when present in font
- -- force: when false, then not set when already set
- local oldchars = fontdata[current].characters
- local newchars = fontdata[id].characters
- if check then
- for i=start,stop do
- if newchars[i] and (force or (not t[i] and not oldchars[i])) then
- if remap then
- t[i] = { id, remap[i] }
- else
- t[i] = id
- end
- end
- end
- else
- for i=start,stop do
- if force or (not t[i] and not oldchars[i]) then
- if remap then
- t[i] = { id, remap[i] }
- else
- t[i] = id
- end
- end
- end
- end
- end
- vectors[current] = t
- if trace_collecting then
- report_fonts("activating collection %a for font %a",name,current)
- end
- if not enabled then
- nodes.tasks.enableaction("processors","fonts.collections.process")
- enabled = true
- end
- statistics.stoptiming(fonts)
-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)
- current = currentfont()
- if vectors[current] then
- return
- end
- local d = definitions[name]
- if d then
- if trace_collecting then
- local filename = file.basename(fontdata[current].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 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()
- elseif trace_collecting then
- local filename = file.basename(fontdata[current].properties.filename or "?")
- report_fonts("error while applying collection %a to %a, file %a",name,current,filename)
- end
-end
-
-function collections.report(message)
- if trace_collecting then
- report_fonts("tex: %s",message)
- end
-end
-
-function collections.process(head) -- this way we keep feature processing
- local done = false
- for n in traverse_id(glyph_code,head) do
- local v = vectors[n.font]
- if v then
- local id = v[n.char]
- if id then
- if type(id) == "table" then
- local newid, newchar = id[1], id[2]
- if trace_collecting then
- report_fonts("remapping character %a in font %a to character %a in font %a",n.char,n.font,newchar,newid)
- end
- n.font, n.char = newid, newchar
- else
- if trace_collecting then
- report_fonts("remapping font %a to %a for character %a",n.font,id,n.char)
- end
- n.font = id
- end
- end
- end
- end
- return head, done
-end
-
--- interface
-
-commands.fontcollectiondefine = collections.define
-commands.fontcollectionreset = collections.reset
-commands.fontcollectionprepare = collections.prepare
-commands.fontcollectionreport = collections.report
-commands.fontcollectionregister = collections.registermain
-commands.fontcollectionclone = collections.clonevector
+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
+
+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, toboolean = type, next, toboolean
+local gmatch = string.gmatch
+local fastcopy = table.fastcopy
+----- P, Cc, lpegmatch = lpeg.P, lpeg.Cc, lpeg.match
+
+local traverse_id = node.traverse_id
+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 collections = fonts.collections or { }
+fonts.collections = collections
+
+local definitions = collections.definitions or { }
+collections.definitions = definitions
+
+local vectors = collections.vectors or { }
+collections.vectors = vectors
+
+local fontdata = fonts.hashes.identifiers
+local glyph_code = nodes.nodecodes.glyph
+local currentfont = font.current
+
+local fontpatternhassize = fonts.helpers.fontpatternhassize
+
+local list = { }
+local current = 0
+local enabled = false
+
+-- maybe also a copy
+
+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
+ for s in gmatch(ranges,"[^, ]+") do
+ local start, stop, description = characters.getrange(s)
+ 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
+ details.font, details.start, details.stop = font, start, stop
+ d[#d+1] = fastcopy(details)
+ 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
+
+function collections.clonevector(name)
+ statistics.starttiming(fonts)
+ local d = definitions[name]
+ local t = { }
+ if trace_collecting then
+ report_fonts("processing collection %a",name)
+ end
+ for i=1,#d do
+ local f = d[i]
+ local id = list[i]
+ local start, stop = f.start, f.stop
+ if trace_collecting then
+ report_fonts("remapping font %a to %a for range %U - %U",current,id,start,stop)
+ end
+ local check = toboolean(f.check or "false",true)
+ local force = toboolean(f.force or "true",true)
+ local remap = f.remap or nil
+ -- check: when true, only set when present in font
+ -- force: when false, then not set when already set
+ local oldchars = fontdata[current].characters
+ local newchars = fontdata[id].characters
+ if check then
+ for i=start,stop do
+ if newchars[i] and (force or (not t[i] and not oldchars[i])) then
+ if remap then
+ t[i] = { id, remap[i] }
+ else
+ t[i] = id
+ end
+ end
+ end
+ else
+ for i=start,stop do
+ if force or (not t[i] and not oldchars[i]) then
+ if remap then
+ t[i] = { id, remap[i] }
+ else
+ t[i] = id
+ end
+ end
+ end
+ end
+ end
+ vectors[current] = t
+ if trace_collecting then
+ report_fonts("activating collection %a for font %a",name,current)
+ end
+ if not enabled then
+ nodes.tasks.enableaction("processors","fonts.collections.process")
+ enabled = true
+ end
+ statistics.stoptiming(fonts)
+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)
+ current = currentfont()
+ if vectors[current] then
+ return
+ end
+ local d = definitions[name]
+ if d then
+ if trace_collecting then
+ local filename = file.basename(fontdata[current].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 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()
+ elseif trace_collecting then
+ local filename = file.basename(fontdata[current].properties.filename or "?")
+ report_fonts("error while applying collection %a to %a, file %a",name,current,filename)
+ end
+end
+
+function collections.report(message)
+ if trace_collecting then
+ report_fonts("tex: %s",message)
+ end
+end
+
+function collections.process(head) -- this way we keep feature processing
+ local done = false
+ for n in traverse_id(glyph_code,head) do
+ local v = vectors[n.font]
+ if v then
+ local id = v[n.char]
+ if id then
+ if type(id) == "table" then
+ local newid, newchar = id[1], id[2]
+ if trace_collecting then
+ report_fonts("remapping character %a in font %a to character %a in font %a",n.char,n.font,newchar,newid)
+ end
+ n.font, n.char = newid, newchar
+ else
+ if trace_collecting then
+ report_fonts("remapping font %a to %a for character %a",n.font,id,n.char)
+ end
+ n.font = id
+ end
+ end
+ end
+ end
+ return head, done
+end
+
+-- interface
+
+commands.fontcollectiondefine = collections.define
+commands.fontcollectionreset = collections.reset
+commands.fontcollectionprepare = collections.prepare
+commands.fontcollectionreport = collections.report
+commands.fontcollectionregister = collections.registermain
+commands.fontcollectionclone = collections.clonevector
diff --git a/tex/context/base/font-con.lua b/tex/context/base/font-con.lua
index e441ebefe..790d4877a 100644
--- a/tex/context/base/font-con.lua
+++ b/tex/context/base/font-con.lua
@@ -1,1330 +1,1330 @@
-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, rawget = next, tostring, rawget
-local format, match, lower, gsub = string.format, string.match, string.lower, string.gsub
-local utfbyte = utf.byte
-local sort, insert, concat, sortedkeys, serialize, fastcopy = table.sort, table.insert, table.concat, table.sortedkeys, table.serialize, table.fastcopy
-local derivetable = table.derive
-
-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.dontembed = allocate()
-constructors.autocleanup = true
-constructors.namemode = "fullpath" -- will be a function
-
-constructors.version = 1.01
-constructors.cache = containers.define("fonts", "constructors", constructors.version, false)
-
-constructors.privateoffset = 0xF0000 -- 0x10FFFF
-
--- Some experimental helpers (handy for tracing):
---
--- todo: extra:
---
--- extra_space => space.extra
--- space => space.width
--- space_stretch => space.stretch
--- space_shrink => space.shrink
-
--- We do keep the x-height, extra_space, space_shrink and space_stretch
--- around as these are low level official names.
-
-constructors.keys = {
- properties = {
- encodingbytes = "number",
- embedding = "number",
- cidinfo = {
- },
- format = "string",
- fontname = "string",
- fullname = "string",
- filename = "filename",
- psname = "string",
- name = "string",
- virtualized = "boolean",
- hasitalics = "boolean",
- autoitalicamount = "basepoints",
- nostackmath = "boolean",
- noglyphnames = "boolean",
- mode = "string",
- hasmath = "boolean",
- mathitalics = "boolean",
- textitalics = "boolean",
- finalized = "boolean",
- },
- parameters = {
- mathsize = "number",
- scriptpercentage = "float",
- scriptscriptpercentage = "float",
- units = "cardinal",
- designsize = "scaledpoints",
- expansion = {
- stretch = "integerscale", -- might become float
- shrink = "integerscale", -- might become float
- step = "integerscale", -- might become float
- auto = "boolean",
- },
- protrusion = {
- auto = "boolean",
- },
- slantfactor = "float",
- extendfactor = "float",
- factor = "float",
- hfactor = "float",
- vfactor = "float",
- size = "scaledpoints",
- units = "scaledpoints",
- scaledpoints = "scaledpoints",
- slantperpoint = "scaledpoints",
- spacing = {
- width = "scaledpoints",
- stretch = "scaledpoints",
- shrink = "scaledpoints",
- extra = "scaledpoints",
- },
- xheight = "scaledpoints",
- quad = "scaledpoints",
- ascender = "scaledpoints",
- descender = "scaledpoints",
- synonyms = {
- space = "spacing.width",
- spacestretch = "spacing.stretch",
- spaceshrink = "spacing.shrink",
- extraspace = "spacing.extra",
- x_height = "xheight",
- space_stretch = "spacing.stretch",
- space_shrink = "spacing.shrink",
- extra_space = "spacing.extra",
- em = "quad",
- ex = "xheight",
- slant = "slantperpoint",
- },
- },
- description = {
- width = "basepoints",
- height = "basepoints",
- depth = "basepoints",
- boundingbox = { },
- },
- character = {
- width = "scaledpoints",
- height = "scaledpoints",
- depth = "scaledpoints",
- italic = "scaledpoints",
- },
-}
-
--- This might become an interface:
-
-local designsizes = allocate()
-constructors.designsizes = designsizes
-local loadedfonts = allocate()
-constructors.loadedfonts = loadedfonts
-
---[[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
- if designsize then
- local factor = constructors.factor
- 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
-
---[[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]]--
-
--- 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
-
-function constructors.cleanuptable(tfmdata)
- if constructors.autocleanup and tfmdata.properties.virtualized then
- for k, v in next, tfmdata.characters do
- if v.commands then v.commands = nil end
- -- if v.kerns then v.kerns = nil end
- end
- end
-end
-
--- 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)
- local parameters = tfmdata.parameters
- if scaledpoints < 0 then
- scaledpoints = (- scaledpoints/1000) * (tfmdata.designsize or parameters.designsize) -- already in sp
- end
- return scaledpoints, scaledpoints / (parameters.units or 1000) -- delta
-end
-
-local unscaled = {
- ScriptPercentScaleDown = true,
- ScriptScriptPercentScaleDown = true,
- RadicalDegreeBottomRaisePercent = true
-}
-
-function constructors.assignmathparameters(target,original) -- simple variant, not used in context
- -- when a tfm file is loaded, it has already been scaled
- -- and it never enters the scaled so this is otf only and
- -- even then we do some extra in the context math plugins
- local mathparameters = original.mathparameters
- if mathparameters and next(mathparameters) then
- local targetparameters = target.parameters
- local targetproperties = target.properties
- local targetmathparameters = { }
- local factor = targetproperties.math_is_scaled and 1 or targetparameters.factor
- for name, value in next, mathparameters do
- if unscaled[name] then
- targetmathparameters[name] = value
- else
- targetmathparameters[name] = value * factor
- end
- end
- if not targetmathparameters.FractionDelimiterSize then
- targetmathparameters.FractionDelimiterSize = 1.01 * targetparameters.size
- end
- if not mathparameters.FractionDelimiterDisplayStyleSize then
- targetmathparameters.FractionDelimiterDisplayStyleSize = 2.40 * targetparameters.size
- end
- target.mathparameters = targetmathparameters
- end
-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.enhanceparameters(parameters)
- local xheight = parameters.x_height
- local quad = parameters.quad
- local space = parameters.space
- local stretch = parameters.space_stretch
- local shrink = parameters.space_shrink
- local extra = parameters.extra_space
- local slant = parameters.slant
- parameters.xheight = xheight
- parameters.spacestretch = stretch
- parameters.spaceshrink = shrink
- parameters.extraspace = extra
- parameters.em = quad
- parameters.ex = xheight
- parameters.slantperpoint = slant
- parameters.spacing = {
- width = space,
- stretch = stretch,
- shrink = shrink,
- extra = extra,
- }
-end
-
-function constructors.scale(tfmdata,specification)
- local target = { } -- the new table
- --
- if tonumber(specification) then
- specification = { size = specification }
- end
- --
- 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
- 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
- end
- targetparameters.mathsize = mathsize -- context specific
- targetparameters.textsize = textsize -- context specific
- targetparameters.forcedsize = forcedsize -- context specific
- targetparameters.extrafactor = extrafactor -- context specific
- --
- local tounicode = resources.tounicode
- local defaultwidth = resources.defaultwidth or 0
- local defaultheight = resources.defaultheight or 0
- local defaultdepth = resources.defaultdepth or 0
- local units = parameters.units or 1000
- --
- if target.fonts then
- target.fonts = fastcopy(target.fonts) -- maybe we virtualize more afterwards
- end
- --
- -- 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
- --
- local askedscaledpoints = scaledpoints
- local scaledpoints, delta = constructors.calculatescale(tfmdata,scaledpoints) -- no shortcut, dan be redefined
- --
- local hdelta = delta
- local vdelta = delta
- --
- target.designsize = parameters.designsize -- not really needed so it muight become obsolete
- target.units_per_em = units -- just a trigger for the backend (does luatex use this? if not it will go)
- --
- local direction = properties.direction or tfmdata.direction or 0 -- pointless, as we don't use omf fonts at all
- target.direction = direction
- properties.direction = direction
- --
- target.size = scaledpoints
- --
- target.encodingbytes = properties.encodingbytes or 1
- target.embedding = properties.embedding or "subset"
- target.tounicode = 1
- target.cidinfo = properties.cidinfo
- target.format = properties.format
- --
- local fontname = properties.fontname or tfmdata.fontname -- for the moment we fall back on
- local fullname = properties.fullname or tfmdata.fullname -- names in the tfmdata although
- local filename = properties.filename or tfmdata.filename -- that is not the right place to
- local psname = properties.psname or tfmdata.psname -- pass them
- local name = properties.name or tfmdata.name
- --
- if not psname or psname == "" then
- -- name used in pdf file as well as for selecting subfont in ttc/dfont
- psname = fontname or (fullname and fonts.names.cleanname(fullname))
- end
- target.fontname = fontname
- target.fullname = fullname
- target.filename = filename
- target.psname = psname
- target.name = name
- --
- -- inspect(properties)
- --
- 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
- target.auto_expand = expansion.auto
- end
- -- protrusion
- local protrusion = parameters.protrusion
- if protrusion then
- target.auto_protrude = protrusion.auto
- end
- -- widening
- local extendfactor = parameters.extendfactor or 0
- if extendfactor ~= 0 and extendfactor ~= 1 then
- hdelta = hdelta * extendfactor
- target.extend = extendfactor * 1000 -- extent ?
- else
- target.extend = 1000 -- extent ?
- end
- -- slanting
- local slantfactor = parameters.slantfactor or 0
- if slantfactor ~= 0 then
- target.slant = slantfactor * 1000
- else
- target.slant = 0
- end
- --
- targetparameters.factor = delta
- targetparameters.hfactor = hdelta
- targetparameters.vfactor = vdelta
- targetparameters.size = scaledpoints
- targetparameters.units = units
- targetparameters.scaledpoints = askedscaledpoints
- --
- local isvirtual = properties.virtualized or tfmdata.type == "virtual"
- local hasquality = target.auto_expand or target.auto_protrude
- local hasitalics = properties.hasitalics
- local autoitalicamount = properties.autoitalicamount
- local stackmath = not properties.nostackmath
- local nonames = properties.noglyphnames
- local nodemode = properties.mode == "node"
- --
- if changed and not next(changed) then
- changed = false
- end
- --
- target.type = isvirtual and "virtual" or "real"
- --
- target.postprocessors = tfmdata.postprocessors
- --
- local targetslant = (parameters.slant or parameters[1] or 0)
- 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 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
- --
- local protrusionfactor = (targetquad ~= 0 and 1000/targetquad) or 0
- 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
- else
- properties.hasmath = false
- target.nomath = true
- target.mathparameters = nil -- nop
- end
- --
- local italickey = "italic"
- local useitalics = true -- something context
- --
- -- some context specific trickery (this will move to a plugin)
- --
- if hasmath then
- -- the latest luatex can deal with it itself so we now disable this
- -- mechanism here
- --
- -- if properties.mathitalics then
- -- italickey = "italic_correction"
- -- if trace_defining then
- -- report_defining("math italics disabled for font %a, fullname %a, filename %a",name,fullname,filename)
- -- end
- -- end
- autoitalicamount = false -- new
- elseif properties.textitalics then
- italickey = "italic_correction"
- useitalics = false
- if properties.delaytextitalics then
- autoitalicamount = false
- end
- end
- --
- -- end of context specific trickery
- --
- if trace_defining then
- report_defining("defining tfm, name %a, fullname %a, filename %a, hscale %a, vscale %a, math %a, italics %a",
- name,fullname,filename,hdelta,vdelta,
- hasmath and "enabled" or "disabled",useitalics 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, touni
- if changed then
- -- basemode hack (we try to catch missing tounicodes, e.g. needed for ssty in math cambria)
- local c = changed[unicode]
- if c then
- description = descriptions[c] or descriptions[unicode] or character
- character = characters[c] or character
- index = description.index or c
- if tounicode then
- touni = tounicode[index] -- nb: index!
- if not touni then -- goodie
- local d = descriptions[unicode] or characters[unicode]
- local i = d.index or unicode
- touni = tounicode[i] -- nb: index!
- end
- end
- else
- description = descriptions[unicode] or character
- index = description.index or unicode
- if tounicode then
- touni = tounicode[index] -- nb: index!
- end
- end
- else
- description = descriptions[unicode] or character
- index = description.index or unicode
- if tounicode then
- touni = tounicode[index] -- nb: index!
- end
- end
- local width = description.width
- local height = description.height
- local depth = description.depth
- 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 nonames then
- chr = {
- index = index,
- height = height,
- depth = depth,
- width = width,
- }
- else
- chr = {
- name = description.name,
- index = index,
- height = height,
- depth = depth,
- width = width,
- }
- end
- else
- -- this saves a little bit of memory time and memory, esp for big cjk fonts
- if nonames then
- chr = {
- index = index,
- height = height,
- width = width,
- }
- else
- chr = {
- name = description.name,
- index = index,
- height = height,
- width = width,
- }
- end
- end
- if touni then
- chr.tounicode = touni
- 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 = protrusionfactor*width*vl
- end
- local vr = character.right_protruding
- if vr then
- chr.right_protruding = protrusionfactor*width*vr
- end
- end
- --
- if autoitalicamount then
- local vi = description.italic
- if not vi then
- local vi = description.boundingbox[3] - description.width + autoitalicamount
- if vi > 0 then -- < 0 indicates no overshoot or a very small auto italic
- chr[italickey] = vi*hdelta
- end
- elseif vi ~= 0 then
- chr[italickey] = vi*hdelta
- end
- elseif hasitalics then
- local vi = description.italic
- if vi and vi ~= 0 then
- chr[italickey] = vi*hdelta
- end
- end
- -- to be tested
- 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]
- t[i] = {
- ["start"] = (vvi["start"] or 0)*vdelta,
- ["end"] = (vvi["end"] or 0)*vdelta,
- ["advance"] = (vvi["advance"] or 0)*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]
- t[i] = {
- ["start"] = (hvi["start"] or 0)*hdelta,
- ["end"] = (hvi["end"] or 0)*hdelta,
- ["advance"] = (hvi["advance"] or 0)*hdelta,
- ["extender"] = hvi["extender"],
- ["glyph"] = hvi["glyph"],
- }
- end
- chr.horiz_variants = t
- end
- end
- end
- local va = character.top_accent
- if va then
- chr.top_accent = vdelta*va
- end
- if stackmath then
- local mk = character.mathkerns -- not in math ?
- if mk then
- local kerns = { }
- local v = mk.top_right if v then local k = { } for i=1,#v do local vi = v[i]
- k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern }
- end kerns.top_right = k end
- local v = mk.top_left if v then local k = { } for i=1,#v do local vi = v[i]
- k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern }
- end kerns.top_left = k end
- local v = mk.bottom_left if v then local k = { } for i=1,#v do local vi = v[i]
- k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern }
- end kerns.bottom_left = k end
- local v = mk.bottom_right if v then local k = { } for i=1,#v do local vi = v[i]
- k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern }
- end kerns.bottom_right = k end
- chr.mathkern = kerns -- singular -> should be patched in luatex !
- end
- end
- end
- if not nodemode 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
- 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
- if isvirtual then
- local vc = character.commands
- if vc then
- -- we assume non scaled commands here
- -- tricky .. we need to scale pseudo math glyphs too
- -- which is why we deal with rules too
- local ok = false
- for i=1,#vc do
- local key = vc[i][1]
- if key == "right" or key == "down" then
- ok = true
- break
- end
- end
- if ok then
- local tt = { }
- for i=1,#vc do
- local ivc = vc[i]
- local key = ivc[1]
- if key == "right" then
- tt[i] = { key, ivc[2]*hdelta }
- elseif key == "down" then
- tt[i] = { key, ivc[2]*vdelta }
- elseif key == "rule" then
- tt[i] = { key, ivc[2]*vdelta, ivc[3]*hdelta }
- else -- not comment
- tt[i] = ivc -- shared since in cache and untouched
- end
- end
- chr.commands = tt
- else
- chr.commands = vc
- end
- chr.index = nil
- end
- end
- targetcharacters[unicode] = chr
- end
- --
- constructors.aftercopyingcharacters(target,tfmdata)
- --
- 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 = { } -- context specific
- 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,
- auto = tfmdata.auto_expand or false,
- }
- end
- --
- if not parameters.protrusion then
- parameters.protrusion = {
- auto = auto_protrude
- }
- end
- --
- if not parameters.size then
- parameters.size = tfmdata.size
- end
- --
- if not parameters.extendfactor then
- parameters.extendfactor = tfmdata.extend or 0
- end
- --
- if not parameters.slantfactor then
- parameters.slantfactor = tfmdata.slant or 0
- end
- --
- if not parameters.designsize then
- parameters.designsize = tfmdata.designsize or 655360
- end
- --
- if not parameters.units then
- parameters.units = tfmdata.units_per_em 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
- --
- if not properties.virtualized then
- properties.virtualized = tfmdata.type == "virtual"
- end
- --
- if not tfmdata.properties then
- tfmdata.properties = {
- fontname = tfmdata.fontname,
- filename = tfmdata.filename,
- fullname = tfmdata.fullname,
- name = tfmdata.name,
- psname = tfmdata.psname,
- --
- encodingbytes = tfmdata.encodingbytes or 1,
- embedding = tfmdata.embedding or "subset",
- tounicode = tfmdata.tounicode or 1,
- cidinfo = tfmdata.cidinfo or nil,
- format = tfmdata.format or "type1",
- direction = tfmdata.direction or 0,
- }
- end
- 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.encodingbytes = nil
- tfmdata.embedding = nil
- tfmdata.tounicode = nil
- tfmdata.cidinfo = nil
- tfmdata.format = nil
- tfmdata.direction = nil
- tfmdata.type = nil
- tfmdata.nomath = nil
- tfmdata.designsize = nil
- --
- tfmdata.size = nil
- tfmdata.stretch = nil
- tfmdata.shrink = nil
- tfmdata.step = nil
- tfmdata.auto_expand = nil
- tfmdata.auto_protrude = nil
- tfmdata.extend = nil
- tfmdata.slant = nil
- tfmdata.units_per_em = 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, tn = { }, 0
- for category, list in next, features do
- if next(list) then
- local hasher = hashmethods[category]
- if hasher then
- local hash = hasher(list)
- if hash then
- tn = tn + 1
- t[tn] = category .. ":" .. hash
- end
- end
- end
- end
- if tn > 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
- s[n] = k
- end
- end
- if n > 0 then
- sort(s)
- for i=1,n do
- local k = s[i]
- s[i] = k .. '=' .. tostring(list[k])
- end
- return concat(s,"+")
- end
-end
-
---[[ldx--
-<p>In principle we can share tfm tables when we are in node 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)
- local hash, size, fallbacks = specification.hash, specification.size, specification.fallbacks
- if force or not hash then
- hash = constructors.hashfeatures(specification)
- specification.hash = hash
- end
- if size < 1000 and designsizes[hash] then
- size = math.round(constructors.scaled(size,designsizes[hash]))
- specification.size = size
- end
- -- local mathsize = specification.mathsize or 0
- -- if mathsize > 0 then
- -- local textsize = specification.textsize
- -- if fallbacks then
- -- return hash .. ' @ ' .. tostring(size) .. ' [ ' .. tostring(mathsize) .. ' : ' .. tostring(textsize) .. ' ] @ ' .. fallbacks
- -- else
- -- return hash .. ' @ ' .. tostring(size) .. ' [ ' .. tostring(mathsize) .. ' : ' .. tostring(textsize) .. ' ]'
- -- end
- -- else
- if fallbacks then
- return hash .. ' @ ' .. tostring(size) .. ' @ ' .. fallbacks
- else
- return hash .. ' @ ' .. tostring(size)
- end
- -- end
-end
-
-function constructors.setname(tfmdata,specification) -- todo: get specification from tfmdata
- if constructors.namemode == "specification" then
- -- not to be used in context !
- local specname = specification.specification
- if specname then
- tfmdata.properties.name = specname
- if trace_defining then
- report_otf("overloaded fontname %a",specname)
- end
- end
- end
-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)
-
-local locations = { }
-
-local function setindeed(mode,target,group,name,action,position)
- 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 node = source.node
- local base = source.base
- local position = source.position
- if node then
- setindeed("node",target,group,name,node,position)
- end
- if base then
- setindeed("base",target,group,name,base,position)
- end
-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
-
-function constructors.newhandler(what) -- could be a metatable newindex
- local handler = handlers[what]
- if not handler then
- handler = { }
- handlers[what] = handler
- end
- return handler
-end
-
-function constructors.newfeatures(what) -- could be a metatable newindex
- 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 = { } },
- processors = { base = { }, node = { } },
- manipulators = { base = { }, node = { } },
- }
- features.register = function(specification) return register(features,specification) end
- handler.features = features -- will also become hidden
- end
- return features
-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 whatinitializers = whatfeatures.initializers
- 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, 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 processors = whatprocessors[properties.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,tfmdata.properties.fullname)
- end
- end
- return processes
-end
-
--- after scaling
-
-function constructors.applymanipulators(what,tfmdata,features,trace,report)
- if features and next(features) then
- local properties = tfmdata.properties
- local whathandler = handlers[what]
- local whatfeatures = whathandler.features
- local whatmanipulators = whatfeatures.manipulators
- local manipulators = whatmanipulators[properties.mode]
- if manipulators then
- for i=1,#manipulators do
- local step = manipulators[i]
- local feature = step.name
- local value = features[feature]
- if value then
- local action = step.action
- if trace then
- report("applying feature manipulator %a for mode %a for font %a",feature,mode,tfmdata.properties.fullname)
- end
- if action then
- action(tfmdata,feature,value)
- end
- end
- end
- end
- end
-end
+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, rawget = next, tostring, rawget
+local format, match, lower, gsub = string.format, string.match, string.lower, string.gsub
+local utfbyte = utf.byte
+local sort, insert, concat, sortedkeys, serialize, fastcopy = table.sort, table.insert, table.concat, table.sortedkeys, table.serialize, table.fastcopy
+local derivetable = table.derive
+
+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.dontembed = allocate()
+constructors.autocleanup = true
+constructors.namemode = "fullpath" -- will be a function
+
+constructors.version = 1.01
+constructors.cache = containers.define("fonts", "constructors", constructors.version, false)
+
+constructors.privateoffset = 0xF0000 -- 0x10FFFF
+
+-- Some experimental helpers (handy for tracing):
+--
+-- todo: extra:
+--
+-- extra_space => space.extra
+-- space => space.width
+-- space_stretch => space.stretch
+-- space_shrink => space.shrink
+
+-- We do keep the x-height, extra_space, space_shrink and space_stretch
+-- around as these are low level official names.
+
+constructors.keys = {
+ properties = {
+ encodingbytes = "number",
+ embedding = "number",
+ cidinfo = {
+ },
+ format = "string",
+ fontname = "string",
+ fullname = "string",
+ filename = "filename",
+ psname = "string",
+ name = "string",
+ virtualized = "boolean",
+ hasitalics = "boolean",
+ autoitalicamount = "basepoints",
+ nostackmath = "boolean",
+ noglyphnames = "boolean",
+ mode = "string",
+ hasmath = "boolean",
+ mathitalics = "boolean",
+ textitalics = "boolean",
+ finalized = "boolean",
+ },
+ parameters = {
+ mathsize = "number",
+ scriptpercentage = "float",
+ scriptscriptpercentage = "float",
+ units = "cardinal",
+ designsize = "scaledpoints",
+ expansion = {
+ stretch = "integerscale", -- might become float
+ shrink = "integerscale", -- might become float
+ step = "integerscale", -- might become float
+ auto = "boolean",
+ },
+ protrusion = {
+ auto = "boolean",
+ },
+ slantfactor = "float",
+ extendfactor = "float",
+ factor = "float",
+ hfactor = "float",
+ vfactor = "float",
+ size = "scaledpoints",
+ units = "scaledpoints",
+ scaledpoints = "scaledpoints",
+ slantperpoint = "scaledpoints",
+ spacing = {
+ width = "scaledpoints",
+ stretch = "scaledpoints",
+ shrink = "scaledpoints",
+ extra = "scaledpoints",
+ },
+ xheight = "scaledpoints",
+ quad = "scaledpoints",
+ ascender = "scaledpoints",
+ descender = "scaledpoints",
+ synonyms = {
+ space = "spacing.width",
+ spacestretch = "spacing.stretch",
+ spaceshrink = "spacing.shrink",
+ extraspace = "spacing.extra",
+ x_height = "xheight",
+ space_stretch = "spacing.stretch",
+ space_shrink = "spacing.shrink",
+ extra_space = "spacing.extra",
+ em = "quad",
+ ex = "xheight",
+ slant = "slantperpoint",
+ },
+ },
+ description = {
+ width = "basepoints",
+ height = "basepoints",
+ depth = "basepoints",
+ boundingbox = { },
+ },
+ character = {
+ width = "scaledpoints",
+ height = "scaledpoints",
+ depth = "scaledpoints",
+ italic = "scaledpoints",
+ },
+}
+
+-- This might become an interface:
+
+local designsizes = allocate()
+constructors.designsizes = designsizes
+local loadedfonts = allocate()
+constructors.loadedfonts = loadedfonts
+
+--[[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
+ if designsize then
+ local factor = constructors.factor
+ 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
+
+--[[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]]--
+
+-- 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
+
+function constructors.cleanuptable(tfmdata)
+ if constructors.autocleanup and tfmdata.properties.virtualized then
+ for k, v in next, tfmdata.characters do
+ if v.commands then v.commands = nil end
+ -- if v.kerns then v.kerns = nil end
+ end
+ end
+end
+
+-- 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)
+ local parameters = tfmdata.parameters
+ if scaledpoints < 0 then
+ scaledpoints = (- scaledpoints/1000) * (tfmdata.designsize or parameters.designsize) -- already in sp
+ end
+ return scaledpoints, scaledpoints / (parameters.units or 1000) -- delta
+end
+
+local unscaled = {
+ ScriptPercentScaleDown = true,
+ ScriptScriptPercentScaleDown = true,
+ RadicalDegreeBottomRaisePercent = true
+}
+
+function constructors.assignmathparameters(target,original) -- simple variant, not used in context
+ -- when a tfm file is loaded, it has already been scaled
+ -- and it never enters the scaled so this is otf only and
+ -- even then we do some extra in the context math plugins
+ local mathparameters = original.mathparameters
+ if mathparameters and next(mathparameters) then
+ local targetparameters = target.parameters
+ local targetproperties = target.properties
+ local targetmathparameters = { }
+ local factor = targetproperties.math_is_scaled and 1 or targetparameters.factor
+ for name, value in next, mathparameters do
+ if unscaled[name] then
+ targetmathparameters[name] = value
+ else
+ targetmathparameters[name] = value * factor
+ end
+ end
+ if not targetmathparameters.FractionDelimiterSize then
+ targetmathparameters.FractionDelimiterSize = 1.01 * targetparameters.size
+ end
+ if not mathparameters.FractionDelimiterDisplayStyleSize then
+ targetmathparameters.FractionDelimiterDisplayStyleSize = 2.40 * targetparameters.size
+ end
+ target.mathparameters = targetmathparameters
+ end
+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.enhanceparameters(parameters)
+ local xheight = parameters.x_height
+ local quad = parameters.quad
+ local space = parameters.space
+ local stretch = parameters.space_stretch
+ local shrink = parameters.space_shrink
+ local extra = parameters.extra_space
+ local slant = parameters.slant
+ parameters.xheight = xheight
+ parameters.spacestretch = stretch
+ parameters.spaceshrink = shrink
+ parameters.extraspace = extra
+ parameters.em = quad
+ parameters.ex = xheight
+ parameters.slantperpoint = slant
+ parameters.spacing = {
+ width = space,
+ stretch = stretch,
+ shrink = shrink,
+ extra = extra,
+ }
+end
+
+function constructors.scale(tfmdata,specification)
+ local target = { } -- the new table
+ --
+ if tonumber(specification) then
+ specification = { size = specification }
+ end
+ --
+ 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
+ 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
+ end
+ targetparameters.mathsize = mathsize -- context specific
+ targetparameters.textsize = textsize -- context specific
+ targetparameters.forcedsize = forcedsize -- context specific
+ targetparameters.extrafactor = extrafactor -- context specific
+ --
+ local tounicode = resources.tounicode
+ local defaultwidth = resources.defaultwidth or 0
+ local defaultheight = resources.defaultheight or 0
+ local defaultdepth = resources.defaultdepth or 0
+ local units = parameters.units or 1000
+ --
+ if target.fonts then
+ target.fonts = fastcopy(target.fonts) -- maybe we virtualize more afterwards
+ end
+ --
+ -- 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
+ --
+ local askedscaledpoints = scaledpoints
+ local scaledpoints, delta = constructors.calculatescale(tfmdata,scaledpoints) -- no shortcut, dan be redefined
+ --
+ local hdelta = delta
+ local vdelta = delta
+ --
+ target.designsize = parameters.designsize -- not really needed so it muight become obsolete
+ target.units_per_em = units -- just a trigger for the backend (does luatex use this? if not it will go)
+ --
+ local direction = properties.direction or tfmdata.direction or 0 -- pointless, as we don't use omf fonts at all
+ target.direction = direction
+ properties.direction = direction
+ --
+ target.size = scaledpoints
+ --
+ target.encodingbytes = properties.encodingbytes or 1
+ target.embedding = properties.embedding or "subset"
+ target.tounicode = 1
+ target.cidinfo = properties.cidinfo
+ target.format = properties.format
+ --
+ local fontname = properties.fontname or tfmdata.fontname -- for the moment we fall back on
+ local fullname = properties.fullname or tfmdata.fullname -- names in the tfmdata although
+ local filename = properties.filename or tfmdata.filename -- that is not the right place to
+ local psname = properties.psname or tfmdata.psname -- pass them
+ local name = properties.name or tfmdata.name
+ --
+ if not psname or psname == "" then
+ -- name used in pdf file as well as for selecting subfont in ttc/dfont
+ psname = fontname or (fullname and fonts.names.cleanname(fullname))
+ end
+ target.fontname = fontname
+ target.fullname = fullname
+ target.filename = filename
+ target.psname = psname
+ target.name = name
+ --
+ -- inspect(properties)
+ --
+ 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
+ target.auto_expand = expansion.auto
+ end
+ -- protrusion
+ local protrusion = parameters.protrusion
+ if protrusion then
+ target.auto_protrude = protrusion.auto
+ end
+ -- widening
+ local extendfactor = parameters.extendfactor or 0
+ if extendfactor ~= 0 and extendfactor ~= 1 then
+ hdelta = hdelta * extendfactor
+ target.extend = extendfactor * 1000 -- extent ?
+ else
+ target.extend = 1000 -- extent ?
+ end
+ -- slanting
+ local slantfactor = parameters.slantfactor or 0
+ if slantfactor ~= 0 then
+ target.slant = slantfactor * 1000
+ else
+ target.slant = 0
+ end
+ --
+ targetparameters.factor = delta
+ targetparameters.hfactor = hdelta
+ targetparameters.vfactor = vdelta
+ targetparameters.size = scaledpoints
+ targetparameters.units = units
+ targetparameters.scaledpoints = askedscaledpoints
+ --
+ local isvirtual = properties.virtualized or tfmdata.type == "virtual"
+ local hasquality = target.auto_expand or target.auto_protrude
+ local hasitalics = properties.hasitalics
+ local autoitalicamount = properties.autoitalicamount
+ local stackmath = not properties.nostackmath
+ local nonames = properties.noglyphnames
+ local nodemode = properties.mode == "node"
+ --
+ if changed and not next(changed) then
+ changed = false
+ end
+ --
+ target.type = isvirtual and "virtual" or "real"
+ --
+ target.postprocessors = tfmdata.postprocessors
+ --
+ local targetslant = (parameters.slant or parameters[1] or 0)
+ 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 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
+ --
+ local protrusionfactor = (targetquad ~= 0 and 1000/targetquad) or 0
+ 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
+ else
+ properties.hasmath = false
+ target.nomath = true
+ target.mathparameters = nil -- nop
+ end
+ --
+ local italickey = "italic"
+ local useitalics = true -- something context
+ --
+ -- some context specific trickery (this will move to a plugin)
+ --
+ if hasmath then
+ -- the latest luatex can deal with it itself so we now disable this
+ -- mechanism here
+ --
+ -- if properties.mathitalics then
+ -- italickey = "italic_correction"
+ -- if trace_defining then
+ -- report_defining("math italics disabled for font %a, fullname %a, filename %a",name,fullname,filename)
+ -- end
+ -- end
+ autoitalicamount = false -- new
+ elseif properties.textitalics then
+ italickey = "italic_correction"
+ useitalics = false
+ if properties.delaytextitalics then
+ autoitalicamount = false
+ end
+ end
+ --
+ -- end of context specific trickery
+ --
+ if trace_defining then
+ report_defining("defining tfm, name %a, fullname %a, filename %a, hscale %a, vscale %a, math %a, italics %a",
+ name,fullname,filename,hdelta,vdelta,
+ hasmath and "enabled" or "disabled",useitalics 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, touni
+ if changed then
+ -- basemode hack (we try to catch missing tounicodes, e.g. needed for ssty in math cambria)
+ local c = changed[unicode]
+ if c then
+ description = descriptions[c] or descriptions[unicode] or character
+ character = characters[c] or character
+ index = description.index or c
+ if tounicode then
+ touni = tounicode[index] -- nb: index!
+ if not touni then -- goodie
+ local d = descriptions[unicode] or characters[unicode]
+ local i = d.index or unicode
+ touni = tounicode[i] -- nb: index!
+ end
+ end
+ else
+ description = descriptions[unicode] or character
+ index = description.index or unicode
+ if tounicode then
+ touni = tounicode[index] -- nb: index!
+ end
+ end
+ else
+ description = descriptions[unicode] or character
+ index = description.index or unicode
+ if tounicode then
+ touni = tounicode[index] -- nb: index!
+ end
+ end
+ local width = description.width
+ local height = description.height
+ local depth = description.depth
+ 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 nonames then
+ chr = {
+ index = index,
+ height = height,
+ depth = depth,
+ width = width,
+ }
+ else
+ chr = {
+ name = description.name,
+ index = index,
+ height = height,
+ depth = depth,
+ width = width,
+ }
+ end
+ else
+ -- this saves a little bit of memory time and memory, esp for big cjk fonts
+ if nonames then
+ chr = {
+ index = index,
+ height = height,
+ width = width,
+ }
+ else
+ chr = {
+ name = description.name,
+ index = index,
+ height = height,
+ width = width,
+ }
+ end
+ end
+ if touni then
+ chr.tounicode = touni
+ 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 = protrusionfactor*width*vl
+ end
+ local vr = character.right_protruding
+ if vr then
+ chr.right_protruding = protrusionfactor*width*vr
+ end
+ end
+ --
+ if autoitalicamount then
+ local vi = description.italic
+ if not vi then
+ local vi = description.boundingbox[3] - description.width + autoitalicamount
+ if vi > 0 then -- < 0 indicates no overshoot or a very small auto italic
+ chr[italickey] = vi*hdelta
+ end
+ elseif vi ~= 0 then
+ chr[italickey] = vi*hdelta
+ end
+ elseif hasitalics then
+ local vi = description.italic
+ if vi and vi ~= 0 then
+ chr[italickey] = vi*hdelta
+ end
+ end
+ -- to be tested
+ 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]
+ t[i] = {
+ ["start"] = (vvi["start"] or 0)*vdelta,
+ ["end"] = (vvi["end"] or 0)*vdelta,
+ ["advance"] = (vvi["advance"] or 0)*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]
+ t[i] = {
+ ["start"] = (hvi["start"] or 0)*hdelta,
+ ["end"] = (hvi["end"] or 0)*hdelta,
+ ["advance"] = (hvi["advance"] or 0)*hdelta,
+ ["extender"] = hvi["extender"],
+ ["glyph"] = hvi["glyph"],
+ }
+ end
+ chr.horiz_variants = t
+ end
+ end
+ end
+ local va = character.top_accent
+ if va then
+ chr.top_accent = vdelta*va
+ end
+ if stackmath then
+ local mk = character.mathkerns -- not in math ?
+ if mk then
+ local kerns = { }
+ local v = mk.top_right if v then local k = { } for i=1,#v do local vi = v[i]
+ k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern }
+ end kerns.top_right = k end
+ local v = mk.top_left if v then local k = { } for i=1,#v do local vi = v[i]
+ k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern }
+ end kerns.top_left = k end
+ local v = mk.bottom_left if v then local k = { } for i=1,#v do local vi = v[i]
+ k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern }
+ end kerns.bottom_left = k end
+ local v = mk.bottom_right if v then local k = { } for i=1,#v do local vi = v[i]
+ k[i] = { height = vdelta*vi.height, kern = vdelta*vi.kern }
+ end kerns.bottom_right = k end
+ chr.mathkern = kerns -- singular -> should be patched in luatex !
+ end
+ end
+ end
+ if not nodemode 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
+ 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
+ if isvirtual then
+ local vc = character.commands
+ if vc then
+ -- we assume non scaled commands here
+ -- tricky .. we need to scale pseudo math glyphs too
+ -- which is why we deal with rules too
+ local ok = false
+ for i=1,#vc do
+ local key = vc[i][1]
+ if key == "right" or key == "down" then
+ ok = true
+ break
+ end
+ end
+ if ok then
+ local tt = { }
+ for i=1,#vc do
+ local ivc = vc[i]
+ local key = ivc[1]
+ if key == "right" then
+ tt[i] = { key, ivc[2]*hdelta }
+ elseif key == "down" then
+ tt[i] = { key, ivc[2]*vdelta }
+ elseif key == "rule" then
+ tt[i] = { key, ivc[2]*vdelta, ivc[3]*hdelta }
+ else -- not comment
+ tt[i] = ivc -- shared since in cache and untouched
+ end
+ end
+ chr.commands = tt
+ else
+ chr.commands = vc
+ end
+ chr.index = nil
+ end
+ end
+ targetcharacters[unicode] = chr
+ end
+ --
+ constructors.aftercopyingcharacters(target,tfmdata)
+ --
+ 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 = { } -- context specific
+ 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,
+ auto = tfmdata.auto_expand or false,
+ }
+ end
+ --
+ if not parameters.protrusion then
+ parameters.protrusion = {
+ auto = auto_protrude
+ }
+ end
+ --
+ if not parameters.size then
+ parameters.size = tfmdata.size
+ end
+ --
+ if not parameters.extendfactor then
+ parameters.extendfactor = tfmdata.extend or 0
+ end
+ --
+ if not parameters.slantfactor then
+ parameters.slantfactor = tfmdata.slant or 0
+ end
+ --
+ if not parameters.designsize then
+ parameters.designsize = tfmdata.designsize or 655360
+ end
+ --
+ if not parameters.units then
+ parameters.units = tfmdata.units_per_em 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
+ --
+ if not properties.virtualized then
+ properties.virtualized = tfmdata.type == "virtual"
+ end
+ --
+ if not tfmdata.properties then
+ tfmdata.properties = {
+ fontname = tfmdata.fontname,
+ filename = tfmdata.filename,
+ fullname = tfmdata.fullname,
+ name = tfmdata.name,
+ psname = tfmdata.psname,
+ --
+ encodingbytes = tfmdata.encodingbytes or 1,
+ embedding = tfmdata.embedding or "subset",
+ tounicode = tfmdata.tounicode or 1,
+ cidinfo = tfmdata.cidinfo or nil,
+ format = tfmdata.format or "type1",
+ direction = tfmdata.direction or 0,
+ }
+ end
+ 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.encodingbytes = nil
+ tfmdata.embedding = nil
+ tfmdata.tounicode = nil
+ tfmdata.cidinfo = nil
+ tfmdata.format = nil
+ tfmdata.direction = nil
+ tfmdata.type = nil
+ tfmdata.nomath = nil
+ tfmdata.designsize = nil
+ --
+ tfmdata.size = nil
+ tfmdata.stretch = nil
+ tfmdata.shrink = nil
+ tfmdata.step = nil
+ tfmdata.auto_expand = nil
+ tfmdata.auto_protrude = nil
+ tfmdata.extend = nil
+ tfmdata.slant = nil
+ tfmdata.units_per_em = 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, tn = { }, 0
+ for category, list in next, features do
+ if next(list) then
+ local hasher = hashmethods[category]
+ if hasher then
+ local hash = hasher(list)
+ if hash then
+ tn = tn + 1
+ t[tn] = category .. ":" .. hash
+ end
+ end
+ end
+ end
+ if tn > 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
+ s[n] = k
+ end
+ end
+ if n > 0 then
+ sort(s)
+ for i=1,n do
+ local k = s[i]
+ s[i] = k .. '=' .. tostring(list[k])
+ end
+ return concat(s,"+")
+ end
+end
+
+--[[ldx--
+<p>In principle we can share tfm tables when we are in node 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)
+ local hash, size, fallbacks = specification.hash, specification.size, specification.fallbacks
+ if force or not hash then
+ hash = constructors.hashfeatures(specification)
+ specification.hash = hash
+ end
+ if size < 1000 and designsizes[hash] then
+ size = math.round(constructors.scaled(size,designsizes[hash]))
+ specification.size = size
+ end
+ -- local mathsize = specification.mathsize or 0
+ -- if mathsize > 0 then
+ -- local textsize = specification.textsize
+ -- if fallbacks then
+ -- return hash .. ' @ ' .. tostring(size) .. ' [ ' .. tostring(mathsize) .. ' : ' .. tostring(textsize) .. ' ] @ ' .. fallbacks
+ -- else
+ -- return hash .. ' @ ' .. tostring(size) .. ' [ ' .. tostring(mathsize) .. ' : ' .. tostring(textsize) .. ' ]'
+ -- end
+ -- else
+ if fallbacks then
+ return hash .. ' @ ' .. tostring(size) .. ' @ ' .. fallbacks
+ else
+ return hash .. ' @ ' .. tostring(size)
+ end
+ -- end
+end
+
+function constructors.setname(tfmdata,specification) -- todo: get specification from tfmdata
+ if constructors.namemode == "specification" then
+ -- not to be used in context !
+ local specname = specification.specification
+ if specname then
+ tfmdata.properties.name = specname
+ if trace_defining then
+ report_otf("overloaded fontname %a",specname)
+ end
+ end
+ end
+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)
+
+local locations = { }
+
+local function setindeed(mode,target,group,name,action,position)
+ 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 node = source.node
+ local base = source.base
+ local position = source.position
+ if node then
+ setindeed("node",target,group,name,node,position)
+ end
+ if base then
+ setindeed("base",target,group,name,base,position)
+ end
+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
+
+function constructors.newhandler(what) -- could be a metatable newindex
+ local handler = handlers[what]
+ if not handler then
+ handler = { }
+ handlers[what] = handler
+ end
+ return handler
+end
+
+function constructors.newfeatures(what) -- could be a metatable newindex
+ 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 = { } },
+ processors = { base = { }, node = { } },
+ manipulators = { base = { }, node = { } },
+ }
+ features.register = function(specification) return register(features,specification) end
+ handler.features = features -- will also become hidden
+ end
+ return features
+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 whatinitializers = whatfeatures.initializers
+ 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, 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 processors = whatprocessors[properties.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,tfmdata.properties.fullname)
+ end
+ end
+ return processes
+end
+
+-- after scaling
+
+function constructors.applymanipulators(what,tfmdata,features,trace,report)
+ if features and next(features) then
+ local properties = tfmdata.properties
+ local whathandler = handlers[what]
+ local whatfeatures = whathandler.features
+ local whatmanipulators = whatfeatures.manipulators
+ local manipulators = whatmanipulators[properties.mode]
+ if manipulators then
+ for i=1,#manipulators do
+ local step = manipulators[i]
+ local feature = step.name
+ local value = features[feature]
+ if value then
+ local action = step.action
+ if trace then
+ report("applying feature manipulator %a for mode %a for font %a",feature,mode,tfmdata.properties.fullname)
+ end
+ if action then
+ action(tfmdata,feature,value)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/tex/context/base/font-ctx.lua b/tex/context/base/font-ctx.lua
index 965542f0a..2583c6520 100644
--- a/tex/context/base/font-ctx.lua
+++ b/tex/context/base/font-ctx.lua
@@ -1,1819 +1,1819 @@
-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.
-
-local context, commands = context, commands
-
-local texcount, texsetcount = tex.count, tex.setcount
-local format, gmatch, match, find, lower, gsub, byte = string.format, string.gmatch, string.match, string.find, string.lower, string.gsub, string.byte
-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 settings_to_hash, hash_to_string = utilities.parsers.settings_to_hash, utilities.parsers.hash_to_string
-local formatcolumns = utilities.formatters.formatcolumns
-local mergehashes = utilities.parsers.mergehashes
-local formatters = string.formatters
-
-local tostring, next, type, rawget, tonumber = tostring, next, type, rawget, tonumber
-local utfchar, utfbyte = utf.char, utf.byte
-local round = math.round
-
-local P, S, C, Cc, Cf, Cg, Ct, lpegmatch = lpeg.P, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Ct, 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 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 fonts = fonts
-local handlers = fonts.handlers
-local otf = handlers.otf -- 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 texattribute = tex.attribute
-local texdimen = tex.dimen
-
-local fontdata = hashes.identifiers
-local characters = hashes.chardata
-local descriptions = hashes.descriptions
-local properties = hashes.properties
-local resources = hashes.resources
-local csnames = hashes.csnames
-local marks = hashes.markdata
-local lastmathids = hashes.lastmathids
-
-local designsizefilename = fontgoodies.designsizes.filename
-
-local otffeatures = otf.features
-local otftables = otf.tables
-
-local registerotffeature = otffeatures.register
-local baseprocessors = otffeatures.processors.base
-local baseinitializers = otffeatures.initializers.base
-
-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)
-
-if environment.initex then
- setmetatableindex(setups,function(t,k)
- return type(k) == "number" and rawget(t,numbers[k]) or nil
- end)
-else
- setmetatableindex(setups,function(t,k)
- local v = type(k) == "number" and rawget(t,numbers[k])
- if v then
- t[k] = v
- return v
- end
- end)
-end
-
--- this will move elsewhere ...
-
-utilities.strings.formatters.add(formatters,"font:name", [["'"..file.basename(%s.properties.name).."'"]])
-utilities.strings.formatters.add(formatters,"font:features",[["'"..table.sequenced(%s," ",true).."'"]])
-
--- ... like font-sfm or so
-
-constructors.resolvevirtualtoo = true -- context specific (due to resolver)
-
-local limited = false
-
-directives.register("system.inputmode", function(v)
- if not limited then
- local i_limiter = io.i_limiter(v)
- if i_limiter then
- fontloader.open = i_limiter.protect(fontloader.open)
- fontloader.info = i_limiter.protect(fontloader.info)
- limited = true
- end
- end
-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
- --
- constructors.enhanceparameters(parameters) -- official copies for us
- --
- definers.resetnullfont = function() end
-end
-
-commands.resetnullfont = definers.resetnullfont
-
--- 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 = {
- gpos_mark2mark = true,
- gpos_mark2base = true,
- gpos_mark2ligature = true,
-}
-
-otftables.scripts.auto = "automatic fallback to latn when no dflt present"
-
--- setmetatableindex(otffeatures.descriptions,otftables.features)
-
-local privatefeatures = {
- tlig = true,
- trep = true,
- anum = true,
-}
-
-local function checkedscript(tfmdata,resources,features)
- local latn = false
- local script = false
- for g, list in next, resources.features do
- for f, scripts in next, list do
- if privatefeatures[f] then
- -- skip
- elseif scripts.dflt then
- script = "dflt"
- break
- elseif scripts.latn then
- latn = true
- 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
-
-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
- features.mode = "base" -- new, or is this wrong?
- return "base"
-end
-
-definers.checkedscript = checkedscript
-definers.checkedmode = checkedmode
-
-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 {
- -- we only set the checker and leave other settings of the mode
- -- feature as they are
- name = "mode",
- modechecker = modechecker,
-}
-
--- -- default = true anyway
---
--- local normalinitializer = constructors.getfeatureaction("otf","initializers","node","analyze")
---
--- local function analyzeinitializer(tfmdata,value,features) -- attr
--- if value == "auto" and features then
--- value = features.init or features.medi or features.fina or features.isol or false
--- end
--- return normalinitializer(tfmdata,value,features)
--- end
---
--- registerotffeature {
--- name = "analyze",
--- initializers = {
--- node = analyzeinitializer,
--- },
--- }
-
-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
-
---[[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
- local tfmdata = loadfont(specification,size,id)
- -- constructors.checkvirtualid(tfmdata,id)
- return tfmdata
- 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
-
-local function presetcontext(name,parent,features) -- will go to con and shared
- if features == "" and find(parent,"=") then
- features = parent
- parent = ""
- end
- if not features or features == "" then
- features = { }
- elseif type(features) == "string" then
- features = normalize_features(settings_to_hash(features))
- 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
- 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
- -- needed for dynamic features
- -- maybe number should always be renewed as we can redefine features
- 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
-
-local function contextnumber(name) -- will be replaced
- local t = setups[name]
- if not t then
- return 0
- elseif t.auto then
- local lng = tonumber(tex.language)
- local tag = name .. ":" .. lng
- local s = setups[tag]
- if s then
- return s.number or 0
- else
- local script, language = languages.association(lng)
- if t.script ~= script or t.language ~= language then
- local s = fastcopy(t)
- local n = #numbers + 1
- setups[tag] = s
- numbers[n] = tag
- s.number = n
- s.script = script
- s.language = language
- return n
- else
- setups[tag] = t
- return t.number or 0
- end
- end
- else
- return t.number or 0
- end
-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, 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 -- contextnumber(mergedname)
- 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
- 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
- else -- =
- for k, v in next, extra do
- mergedfeatures[k] = v
- 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, 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 -- contextnumber(mergedname)
- 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
- mergedfeatures.number = number
- numbers[number] = mergedname
- merged[number] = how == "=" and 1 or 2 -- 1=replace, 2=combine
- setups[mergedname] = mergedfeatures
- return number -- contextnumber(mergedname)
- else
- return 0
- end
-end
-
-specifiers.presetcontext = presetcontext
-specifiers.contextnumber = contextnumber
-specifiers.mergecontext = mergecontext
-specifiers.registercontext = registercontext
-specifiers.definecontext = definecontext
-
--- we extend the hasher:
-
-constructors.hashmethods.virtual = function(list)
- local s = { }
- local n = 0
- for k, v in next, list do
- n = n + 1
- s[n] = k -- no checking on k
- end
- if n > 0 then
- sort(s)
- for i=1,n do
- local k = s[i]
- s[i] = k .. '=' .. tostring(list[k])
- end
- 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 = texattribute[0]
--- local hash = zero .. "+" .. name .. "*" .. what
--- local done = withcache[hash]
--- if not done then
--- done = mergecontext(zero,name,what)
--- withcache[hash] = done
--- end
--- texattribute[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
--- texattribute[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 splitter = lpeg.splitat("=")
---
--- local function splitcontext(features)
--- local setup = setups[features]
--- if setup then
--- return setup
--- elseif find(features,",") 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,"=") 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)
-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 separator = S(";,")
-local equal = P("=")
-local spaces = space^0
-local sometext = C((1-equal-space-separator)^1)
-local truevalue = P("+") * spaces * sometext * Cc(true) -- "yes"
-local falsevalue = P("-") * spaces * sometext * Cc(false) -- "no"
-local keyvalue = sometext * spaces * equal * spaces * sometext
-local somevalue = sometext * spaces * Cc(true) -- "yes"
-local pattern = Cf(Ct("") * (space + separator + Cg(keyvalue + falsevalue + truevalue + 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 space = P(" ")
-local spaces = space^0
-local leftparent = (P"(")
-local rightparent = (P")")
-local value = C((leftparent * (1-rightparent)^0 * rightparent + (1-space))^1)
-local dimension = C((space/"" + P(1))^1)
-local rest = C(P(1)^0)
-local scale_none = Cc(0)
-local scale_at = P("at") * Cc(1) * spaces * dimension -- value
-local scale_sa = P("sa") * Cc(2) * spaces * dimension -- value
-local scale_mo = P("mo") * Cc(3) * spaces * dimension -- value
-local scale_scaled = P("scaled") * Cc(4) * spaces * dimension -- value
-
-local sizepattern = spaces * (scale_at + scale_sa + scale_mo + scale_scaled + scale_none)
-local splitpattern = spaces * value * spaces * rest
-
-function helpers.splitfontpattern(str)
- local name, size = lpegmatch(splitpattern,str)
- local kind, size = lpegmatch(sizepattern,size)
- return name, kind, size
-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 setdefaultfontname = context.fntsetdefname
-local setsomefontname = context.fntsetsomename
-local setemptyfontsize = context.fntsetnopsize
-local setsomefontsize = context.fntsetsomesize
-local letvaluerelax = context.letvaluerelax
-
-function commands.definefont_one(str)
- statistics.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)
- setdefaultfontname()
- elseif name == "unknown" then
- setdefaultfontname()
- else
- setsomefontname(name)
- end
- -- we can also use a count for the size
- if size and size ~= "" then
- local mode, size = lpegmatch(sizepattern,size)
- if size and mode then
- texcount.scaledfontmode = mode
- setsomefontsize(size)
- else
- texcount.scaledfontmode = 0
- setemptyfontsize()
- end
- elseif true then
- -- so we don't need to check in tex
- texcount.scaledfontmode = 2
- setemptyfontsize()
- else
- texcount.scaledfontmode = 0
- setemptyfontsize()
- end
- specification = definers.makespecification(str,lookup,name,sub,method,detail,size)
- if trace_defining then
- report_defining("stop stage one")
- end
-end
-
-local n = 0
-
--- we can also move rscale to here (more consistent)
--- the argument list will become a table
-
-local function nice_cs(cs)
- return (gsub(cs,".->", ""))
-end
-
-function commands.definefont_two(global,cs,str,size,inheritancemode,classfeatures,fontfeatures,classfallbacks,fontfallbacks,
- mathsize,textsize,relativeid,classgoodies,goodies,classdesignsize,fontdesignsize)
- if trace_defining then
- report_defining("start stage two: %s (size %s)",str,size)
- 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
- -- 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
- 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 fontfeatures and fontfeatures ~= "" then
- specification.method = "*"
- specification.detail = fontfeatures
- elseif classfeatures and classfeatures ~= "" then
- specification.method = "*"
- specification.detail = classfeatures
- end
- if fontfallbacks and fontfallbacks ~= "" then
- specification.fallbacks = fontfallbacks
- elseif classfallbacks and classfallbacks ~= "" then
- specification.fallbacks = classfallbacks
- end
- elseif inheritancemode == 4 then
- -- classfirst
- if classfeatures and classfeatures ~= "" then
- specification.method = "*"
- specification.detail = classfeatures
- elseif fontfeatures and fontfeatures ~= "" then
- specification.method = "*"
- specification.detail = fontfeatures
- 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
- if not tfmdata then
- report_defining("unable to define %a as %a",name,nice_cs(cs))
- lastfontid = -1
- letvaluerelax(cs) -- otherwise the current definition takes the previous one
- elseif type(tfmdata) == "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
- tex.definefont(global,cs,tfmdata)
- -- resolved (when designsize is used):
- setsomefontsize((fontdata[tfmdata].parameters.size or 0) .. "sp")
- lastfontid = tfmdata
- else
- -- setting the extra characters will move elsewhere
- local characters = tfmdata.characters
- local parameters = tfmdata.parameters
- -- 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 id = font.define(tfmdata)
- csnames[id] = specification.cs
- tfmdata.properties.id = id
- definers.register(tfmdata,id) -- to be sure, normally already done
- tex.definefont(global,cs,id)
- constructors.cleanuptable(tfmdata)
- constructors.finalize(tfmdata)
- if trace_defining then
- report_defining("defining %a, id %a, target %a, features %a / %a, fallbacks %a / %a",
- name,id,nice_cs(cs),classfeatures,fontfeatures,classfallbacks,fontfallbacks)
- end
- -- resolved (when designsize is used):
- setsomefontsize((tfmdata.parameters.size or 655360) .. "sp")
- lastfontid = id
- end
- if trace_defining then
- report_defining("memory usage after: %s",statistics.memused())
- report_defining("stop stage two")
- end
- --
- texsetcount("global","lastfontid",lastfontid)
- if not mathsize then
- -- forget about it
- elseif mathsize == 0 then
- lastmathids[1] = lastfontid
- else
- lastmathids[mathsize] = lastfontid
- end
- --
- statistics.stoptiming(fonts)
-end
-
-function definers.define(specification)
- --
- local name = specification.name
- if not name or name == "" then
- return -1
- else
- statistics.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 = tex.sp(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
- tex.definefont(specification.global,cs,tfmdata)
- csnames[tfmdata] = cs
- end
- return tfmdata, fontdata[tfmdata]
- else
- local id = font.define(tfmdata)
- tfmdata.properties.id = id
- definers.register(tfmdata,id)
- if cs then
- tex.definefont(specification.global,cs,id)
- csnames[id] = cs
- end
- constructors.cleanuptable(tfmdata)
- constructors.finalize(tfmdata)
- return id, tfmdata
- end
- statistics.stoptiming(fonts)
- end
-end
-
--- 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 = specification.size and number.todimen(specification.size) or texdimen.bodyfontsize
- local number = tonumber(specification.number)
- local id = nil
- 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 = definers.define {
- name = name,
- size = size,
- cs = cs,
- }
- end
- if not id then
- id = currentfont()
- end
- return id, csnames[id]
-end
-
-local enable_auto_r_scale = false
-
-experiments.register("fonts.autorscale", function(v)
- enable_auto_r_scale = v
-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.
-
-local calculatescale = constructors.calculatescale
-
-function constructors.calculatescale(tfmdata,scaledpoints,relativeid)
- local scaledpoints, delta = calculatescale(tfmdata,scaledpoints)
- -- if enable_auto_r_scale and relativeid then -- for the moment this is rather context specific
- -- local relativedata = fontdata[relativeid]
- -- local rfmdata = relativedata and relativedata.unscaled and relativedata.unscaled
- -- local id_x_height = rfmdata and rfmdata.parameters and rfmdata.parameters.x_height
- -- local tf_x_height = tfmdata and tfmdata.parameters and tfmdata.parameters.x_height
- -- if id_x_height and tf_x_height then
- -- local rscale = id_x_height/tf_x_height
- -- delta = rscale * delta
- -- scaledpoints = rscale * scaledpoints
- -- end
- -- end
- return scaledpoints, delta
-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 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
- specification.hash = lower(specification.name .. ' @ ' .. hashfeatures(specification))
- if specification.sub and specification.sub ~= "" then
- specification.hash = specification.sub .. ' @ ' .. specification.hash
- end
- return specification
-end
-
-
--- soon to be obsolete:
-
-local mappings = fonts.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
- pdf.mapfile(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
- pdf.mapline(how)
- loaded[how] = true
- end
-end
-
-function mappings.reset()
- pdf.mapfile("")
-end
-
-mappings.reset() -- resets the default file
-
--- we need an 'do after the banner hook'
-
--- => commands
-
-local function nametoslot(name)
- local t = type(name)
- if t == "string" then
- return resources[true].unicodes[name]
- elseif t == "number" then
- return n
- end
-end
-
-helpers.nametoslot = nametoslot
-
--- 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] = {
- format("%03i",id or 0),
- format("%09i",parameters.size or 0),
- properties.type or "real",
- properties.format or "unknown",
- properties.name or "",
- properties.psname or "",
- properties.fullname or "",
- }
- report_status("%s: % t",properties.name,sortedkeys(data))
- end
- formatcolumns(t," ")
- report_status()
- report_status("defined fonts:")
- report_status()
- for k=1,tn do
- report_status(t[k])
- end
- end
-end
-
-luatex.registerstopactions(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," ")
- report_status()
- report_status("defined featuresets:")
- report_status()
- for k=1,n do
- report_status(t[k])
- end
- end
-end
-
-luatex.registerstopactions(loggers.reportusedfeatures)
-
-statistics.register("fonts load time", function()
- return statistics.elapsedseconds(fonts)
-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",
-}
-
-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"
- context.startfontclass { name }
- context.definefontsynonym( { format("%s", Shape) }, { format("spec:%s-%s-regular-%s", fontname, normalweight, normalwidth) } )
- context.definefontsynonym( { format("%sBold", Shape) }, { format("spec:%s-%s-regular-%s", fontname, boldweight, boldwidth ) } )
- context.definefontsynonym( { format("%sBoldItalic", Shape) }, { format("spec:%s-%s-italic-%s", fontname, boldweight, boldwidth ) } )
- context.definefontsynonym( { format("%sItalic", Shape) }, { format("spec:%s-%s-italic-%s", fontname, normalweight, normalwidth) } )
- context.stopfontclass()
- local settings = sequenced({ features= t.features },",")
- context.dofastdefinetypeface(name, shortcut, shape, size, settings)
-end
-
-function fonts.current() -- todo: also handle name
- return fontdata[currentfont()] or fontdata[0]
-end
-
-function fonts.currentid()
- return currentfont() or 0
-end
-
--- interfaces
-
-function commands.fontchar(n)
- n = nametoslot(n)
- if n then
- context.char(n)
- end
-end
-
-function commands.doifelsecurrentfonthasfeature(name) -- can be made faster with a supportedfeatures hash
- local f = fontdata[currentfont()]
- f = f and f.shared
- f = f and f.rawdata
- f = f and f.resources
- f = f and f.features
- commands.doifelse(f and (f.gpos[name] or f.gsub[name]))
-end
-
-local p, f = 1, formatters["%0.1fpt"] -- normally this value is changed only once
-
-local stripper = lpeg.patterns.stripzeros
-
-function commands.nbfs(amount,precision)
- if precision ~= p then
- p = precision
- f = formatters["%0." .. p .. "fpt"]
- end
- context(lpegmatch(stripper,f(amount/65536)))
-end
-
-function commands.featureattribute(tag)
- context(contextnumber(tag))
-end
-
-function commands.setfontfeature(tag)
- texattribute[0] = contextnumber(tag)
-end
-
-function commands.resetfontfeature()
- texattribute[0] = 0
-end
-
--- function commands.addfs(tag) withset(tag, 1) end
--- function commands.subfs(tag) withset(tag,-1) end
--- function commands.addff(tag) withfnt(tag, 2) end -- on top of font features
--- function commands.subff(tag) withfnt(tag,-2) end -- on top of font features
-
-function commands.cleanfontname (name) context(names.cleanname(name)) end
-
-function commands.fontlookupinitialize (name) names.lookup(name) end
-function commands.fontlookupnoffound () context(names.noflookups()) end
-function commands.fontlookupgetkeyofindex(key,index) context(names.getlookupkey(key,index)) end
-function commands.fontlookupgetkey (key) context(names.getlookupkey(key)) end
-
--- 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
-
--- 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,tfmdata) -- could be a method of a font instance
- if unit == "ex" then
- return (tfmdata and tfmdata.parameters.x_height) or 655360
- elseif unit == "em" then
- return (tfmdata and tfmdata.parameters.em_width) or 655360
- 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.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 quads = hashes.quads
-local xheights = hashes.xheights
-
-setmetatableindex(number.dimenfactors, function(t,k)
- if k == "ex" then
- return xheigths[currentfont()]
- elseif k == "em" then
- return quads[currentfont()]
- elseif k == "%" then
- return dimen.hsize/100
- else
- -- error("wrong dimension: " .. (s or "?")) -- better a message
- return false
- end
-end)
-
---[[ldx--
-<p>Before a font is passed to <l n='tex'/> we scale it. Here we also need
-to scale virtual characters.</p>
---ldx]]--
-
-function constructors.checkvirtualids(tfmdata)
- -- begin of experiment: we can use { "slot", 0, number } in virtual fonts
- local fonts = tfmdata.fonts
- local selfid = font.nextid()
- if fonts and #fonts > 0 then
- for i=1,#fonts do
- if fonts[i][2] == 0 then
- fonts[i][2] = selfid
- end
- end
- else
- -- tfmdata.fonts = { "id", selfid } -- conflicts with other next id's (vf math), too late anyway
- end
- -- end of experiment
-end
-
--- function constructors.getvirtualid(tfmdata)
--- -- since we don't know the id yet, we use 0 as signal
--- local tf = tfmdata.fonts
--- if not tf then
--- local properties = tfmdata.properties
--- if properties then
--- properties.virtualized = true
--- else
--- tfmdata.properties = { virtualized = true }
--- end
--- tf = { }
--- tfmdata.fonts = tf
--- end
--- local ntf = #tf + 1
--- tf[ntf] = { id = 0 }
--- return ntf
--- end
---
--- function constructors.checkvirtualid(tfmdata, id) -- will go
--- local properties = tfmdata.properties
--- if tfmdata and tfmdata.type == "virtual" or (properties and properties.virtualized) then
--- local vfonts = tfmdata.fonts
--- if not vffonts or #vfonts == 0 then
--- if properties then
--- properties.virtualized = false
--- end
--- tfmdata.fonts = nil
--- else
--- for f=1,#vfonts do
--- local fnt = vfonts[f]
--- if fnt.id and fnt.id == 0 then
--- fnt.id = id
--- end
--- end
--- end
--- end
--- end
-
-function commands.setfontofid(id)
- context.getvalue(csnames[id])
-end
-
--- more interfacing:
-
-commands.definefontfeature = presetcontext
-
-local cache = { }
-
-local hows = {
- ["+"] = "add",
- ["-"] = "subtract",
- ["="] = "replace",
-}
-
-function commands.feature(how,parent,name,font)
- if not how then
- if trace_features and texattribute[0] ~= 0 then
- report_cummulative("font %!font:name!, reset",fontdata[font or true])
- end
- texattribute[0] = 0
- elseif how == true 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
- texattribute[0] = 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
- texattribute[0] = done
- end
-end
-
-function commands.featurelist(...)
- context(fonts.specifiers.contexttostring(...))
-end
-
-function commands.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
-
--- a fontkern plug:
-
-local copy_node = node.copy
-local kern = nodes.pool.register(nodes.pool.kern())
-
-node.set_attribute(kern,attributes.private('fontkern'),1) -- we can have several, attributes are shared
-
-nodes.injections.installnewkern(function(k)
- local c = copy_node(kern)
- c.kern = k
- return c
-end)
-
-directives.register("nodes.injections.fontkern", function(v) kern.subtype = v and 0 or 1 end)
-
--- here
-
-local trace_analyzing = false trackers.register("otf.analyzing", function(v) trace_analyzing = v end)
-
-local otffeatures = fonts.constructors.newfeatures("otf")
-local registerotffeature = otffeatures.register
-
-local analyzers = fonts.analyzers
-local methods = analyzers.methods
-
-local unsetvalue = attributes.unsetvalue
-
-local traverse_by_id = node.traverse_id
-
-local a_color = attributes.private('color')
-local a_colormodel = attributes.private('colormodel')
-local a_state = attributes.private('state')
-local m_color = attributes.list[a_color] or { }
-
-local glyph_code = nodes.nodecodes.glyph
-
-local states = analyzers.states
-
-local names = {
- [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",
-}
-
-local function markstates(head)
- if head then
- local model = head[a_colormodel] or 1
- for glyph in traverse_by_id(glyph_code,head) do
- local a = glyph[a_state]
- if a then
- local name = names[a]
- if name then
- local color = m_color[name]
- if color then
- glyph[a_colormodel] = model
- 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 in traverse_by_id(glyph_code,head) do
- if not font or n.font == font then
- n[a_color] = unsetvalue
- end
- end
- return head, true
-end
+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.
+
+local context, commands = context, commands
+
+local texcount, texsetcount = tex.count, tex.setcount
+local format, gmatch, match, find, lower, gsub, byte = string.format, string.gmatch, string.match, string.find, string.lower, string.gsub, string.byte
+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 settings_to_hash, hash_to_string = utilities.parsers.settings_to_hash, utilities.parsers.hash_to_string
+local formatcolumns = utilities.formatters.formatcolumns
+local mergehashes = utilities.parsers.mergehashes
+local formatters = string.formatters
+
+local tostring, next, type, rawget, tonumber = tostring, next, type, rawget, tonumber
+local utfchar, utfbyte = utf.char, utf.byte
+local round = math.round
+
+local P, S, C, Cc, Cf, Cg, Ct, lpegmatch = lpeg.P, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cf, lpeg.Cg, lpeg.Ct, 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 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 fonts = fonts
+local handlers = fonts.handlers
+local otf = handlers.otf -- 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 texattribute = tex.attribute
+local texdimen = tex.dimen
+
+local fontdata = hashes.identifiers
+local characters = hashes.chardata
+local descriptions = hashes.descriptions
+local properties = hashes.properties
+local resources = hashes.resources
+local csnames = hashes.csnames
+local marks = hashes.markdata
+local lastmathids = hashes.lastmathids
+
+local designsizefilename = fontgoodies.designsizes.filename
+
+local otffeatures = otf.features
+local otftables = otf.tables
+
+local registerotffeature = otffeatures.register
+local baseprocessors = otffeatures.processors.base
+local baseinitializers = otffeatures.initializers.base
+
+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)
+
+if environment.initex then
+ setmetatableindex(setups,function(t,k)
+ return type(k) == "number" and rawget(t,numbers[k]) or nil
+ end)
+else
+ setmetatableindex(setups,function(t,k)
+ local v = type(k) == "number" and rawget(t,numbers[k])
+ if v then
+ t[k] = v
+ return v
+ end
+ end)
+end
+
+-- this will move elsewhere ...
+
+utilities.strings.formatters.add(formatters,"font:name", [["'"..file.basename(%s.properties.name).."'"]])
+utilities.strings.formatters.add(formatters,"font:features",[["'"..table.sequenced(%s," ",true).."'"]])
+
+-- ... like font-sfm or so
+
+constructors.resolvevirtualtoo = true -- context specific (due to resolver)
+
+local limited = false
+
+directives.register("system.inputmode", function(v)
+ if not limited then
+ local i_limiter = io.i_limiter(v)
+ if i_limiter then
+ fontloader.open = i_limiter.protect(fontloader.open)
+ fontloader.info = i_limiter.protect(fontloader.info)
+ limited = true
+ end
+ end
+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
+ --
+ constructors.enhanceparameters(parameters) -- official copies for us
+ --
+ definers.resetnullfont = function() end
+end
+
+commands.resetnullfont = definers.resetnullfont
+
+-- 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 = {
+ gpos_mark2mark = true,
+ gpos_mark2base = true,
+ gpos_mark2ligature = true,
+}
+
+otftables.scripts.auto = "automatic fallback to latn when no dflt present"
+
+-- setmetatableindex(otffeatures.descriptions,otftables.features)
+
+local privatefeatures = {
+ tlig = true,
+ trep = true,
+ anum = true,
+}
+
+local function checkedscript(tfmdata,resources,features)
+ local latn = false
+ local script = false
+ for g, list in next, resources.features do
+ for f, scripts in next, list do
+ if privatefeatures[f] then
+ -- skip
+ elseif scripts.dflt then
+ script = "dflt"
+ break
+ elseif scripts.latn then
+ latn = true
+ 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
+
+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
+ features.mode = "base" -- new, or is this wrong?
+ return "base"
+end
+
+definers.checkedscript = checkedscript
+definers.checkedmode = checkedmode
+
+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 {
+ -- we only set the checker and leave other settings of the mode
+ -- feature as they are
+ name = "mode",
+ modechecker = modechecker,
+}
+
+-- -- default = true anyway
+--
+-- local normalinitializer = constructors.getfeatureaction("otf","initializers","node","analyze")
+--
+-- local function analyzeinitializer(tfmdata,value,features) -- attr
+-- if value == "auto" and features then
+-- value = features.init or features.medi or features.fina or features.isol or false
+-- end
+-- return normalinitializer(tfmdata,value,features)
+-- end
+--
+-- registerotffeature {
+-- name = "analyze",
+-- initializers = {
+-- node = analyzeinitializer,
+-- },
+-- }
+
+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
+
+--[[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
+ local tfmdata = loadfont(specification,size,id)
+ -- constructors.checkvirtualid(tfmdata,id)
+ return tfmdata
+ 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
+
+local function presetcontext(name,parent,features) -- will go to con and shared
+ if features == "" and find(parent,"=") then
+ features = parent
+ parent = ""
+ end
+ if not features or features == "" then
+ features = { }
+ elseif type(features) == "string" then
+ features = normalize_features(settings_to_hash(features))
+ 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
+ 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
+ -- needed for dynamic features
+ -- maybe number should always be renewed as we can redefine features
+ 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
+
+local function contextnumber(name) -- will be replaced
+ local t = setups[name]
+ if not t then
+ return 0
+ elseif t.auto then
+ local lng = tonumber(tex.language)
+ local tag = name .. ":" .. lng
+ local s = setups[tag]
+ if s then
+ return s.number or 0
+ else
+ local script, language = languages.association(lng)
+ if t.script ~= script or t.language ~= language then
+ local s = fastcopy(t)
+ local n = #numbers + 1
+ setups[tag] = s
+ numbers[n] = tag
+ s.number = n
+ s.script = script
+ s.language = language
+ return n
+ else
+ setups[tag] = t
+ return t.number or 0
+ end
+ end
+ else
+ return t.number or 0
+ end
+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, 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 -- contextnumber(mergedname)
+ 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
+ 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
+ else -- =
+ for k, v in next, extra do
+ mergedfeatures[k] = v
+ 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, 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 -- contextnumber(mergedname)
+ 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
+ mergedfeatures.number = number
+ numbers[number] = mergedname
+ merged[number] = how == "=" and 1 or 2 -- 1=replace, 2=combine
+ setups[mergedname] = mergedfeatures
+ return number -- contextnumber(mergedname)
+ else
+ return 0
+ end
+end
+
+specifiers.presetcontext = presetcontext
+specifiers.contextnumber = contextnumber
+specifiers.mergecontext = mergecontext
+specifiers.registercontext = registercontext
+specifiers.definecontext = definecontext
+
+-- we extend the hasher:
+
+constructors.hashmethods.virtual = function(list)
+ local s = { }
+ local n = 0
+ for k, v in next, list do
+ n = n + 1
+ s[n] = k -- no checking on k
+ end
+ if n > 0 then
+ sort(s)
+ for i=1,n do
+ local k = s[i]
+ s[i] = k .. '=' .. tostring(list[k])
+ end
+ 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 = texattribute[0]
+-- local hash = zero .. "+" .. name .. "*" .. what
+-- local done = withcache[hash]
+-- if not done then
+-- done = mergecontext(zero,name,what)
+-- withcache[hash] = done
+-- end
+-- texattribute[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
+-- texattribute[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 splitter = lpeg.splitat("=")
+--
+-- local function splitcontext(features)
+-- local setup = setups[features]
+-- if setup then
+-- return setup
+-- elseif find(features,",") 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,"=") 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)
+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 separator = S(";,")
+local equal = P("=")
+local spaces = space^0
+local sometext = C((1-equal-space-separator)^1)
+local truevalue = P("+") * spaces * sometext * Cc(true) -- "yes"
+local falsevalue = P("-") * spaces * sometext * Cc(false) -- "no"
+local keyvalue = sometext * spaces * equal * spaces * sometext
+local somevalue = sometext * spaces * Cc(true) -- "yes"
+local pattern = Cf(Ct("") * (space + separator + Cg(keyvalue + falsevalue + truevalue + 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 space = P(" ")
+local spaces = space^0
+local leftparent = (P"(")
+local rightparent = (P")")
+local value = C((leftparent * (1-rightparent)^0 * rightparent + (1-space))^1)
+local dimension = C((space/"" + P(1))^1)
+local rest = C(P(1)^0)
+local scale_none = Cc(0)
+local scale_at = P("at") * Cc(1) * spaces * dimension -- value
+local scale_sa = P("sa") * Cc(2) * spaces * dimension -- value
+local scale_mo = P("mo") * Cc(3) * spaces * dimension -- value
+local scale_scaled = P("scaled") * Cc(4) * spaces * dimension -- value
+
+local sizepattern = spaces * (scale_at + scale_sa + scale_mo + scale_scaled + scale_none)
+local splitpattern = spaces * value * spaces * rest
+
+function helpers.splitfontpattern(str)
+ local name, size = lpegmatch(splitpattern,str)
+ local kind, size = lpegmatch(sizepattern,size)
+ return name, kind, size
+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 setdefaultfontname = context.fntsetdefname
+local setsomefontname = context.fntsetsomename
+local setemptyfontsize = context.fntsetnopsize
+local setsomefontsize = context.fntsetsomesize
+local letvaluerelax = context.letvaluerelax
+
+function commands.definefont_one(str)
+ statistics.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)
+ setdefaultfontname()
+ elseif name == "unknown" then
+ setdefaultfontname()
+ else
+ setsomefontname(name)
+ end
+ -- we can also use a count for the size
+ if size and size ~= "" then
+ local mode, size = lpegmatch(sizepattern,size)
+ if size and mode then
+ texcount.scaledfontmode = mode
+ setsomefontsize(size)
+ else
+ texcount.scaledfontmode = 0
+ setemptyfontsize()
+ end
+ elseif true then
+ -- so we don't need to check in tex
+ texcount.scaledfontmode = 2
+ setemptyfontsize()
+ else
+ texcount.scaledfontmode = 0
+ setemptyfontsize()
+ end
+ specification = definers.makespecification(str,lookup,name,sub,method,detail,size)
+ if trace_defining then
+ report_defining("stop stage one")
+ end
+end
+
+local n = 0
+
+-- we can also move rscale to here (more consistent)
+-- the argument list will become a table
+
+local function nice_cs(cs)
+ return (gsub(cs,".->", ""))
+end
+
+function commands.definefont_two(global,cs,str,size,inheritancemode,classfeatures,fontfeatures,classfallbacks,fontfallbacks,
+ mathsize,textsize,relativeid,classgoodies,goodies,classdesignsize,fontdesignsize)
+ if trace_defining then
+ report_defining("start stage two: %s (size %s)",str,size)
+ 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
+ -- 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
+ 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 fontfeatures and fontfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = fontfeatures
+ elseif classfeatures and classfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = classfeatures
+ end
+ if fontfallbacks and fontfallbacks ~= "" then
+ specification.fallbacks = fontfallbacks
+ elseif classfallbacks and classfallbacks ~= "" then
+ specification.fallbacks = classfallbacks
+ end
+ elseif inheritancemode == 4 then
+ -- classfirst
+ if classfeatures and classfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = classfeatures
+ elseif fontfeatures and fontfeatures ~= "" then
+ specification.method = "*"
+ specification.detail = fontfeatures
+ 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
+ if not tfmdata then
+ report_defining("unable to define %a as %a",name,nice_cs(cs))
+ lastfontid = -1
+ letvaluerelax(cs) -- otherwise the current definition takes the previous one
+ elseif type(tfmdata) == "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
+ tex.definefont(global,cs,tfmdata)
+ -- resolved (when designsize is used):
+ setsomefontsize((fontdata[tfmdata].parameters.size or 0) .. "sp")
+ lastfontid = tfmdata
+ else
+ -- setting the extra characters will move elsewhere
+ local characters = tfmdata.characters
+ local parameters = tfmdata.parameters
+ -- 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 id = font.define(tfmdata)
+ csnames[id] = specification.cs
+ tfmdata.properties.id = id
+ definers.register(tfmdata,id) -- to be sure, normally already done
+ tex.definefont(global,cs,id)
+ constructors.cleanuptable(tfmdata)
+ constructors.finalize(tfmdata)
+ if trace_defining then
+ report_defining("defining %a, id %a, target %a, features %a / %a, fallbacks %a / %a",
+ name,id,nice_cs(cs),classfeatures,fontfeatures,classfallbacks,fontfallbacks)
+ end
+ -- resolved (when designsize is used):
+ setsomefontsize((tfmdata.parameters.size or 655360) .. "sp")
+ lastfontid = id
+ end
+ if trace_defining then
+ report_defining("memory usage after: %s",statistics.memused())
+ report_defining("stop stage two")
+ end
+ --
+ texsetcount("global","lastfontid",lastfontid)
+ if not mathsize then
+ -- forget about it
+ elseif mathsize == 0 then
+ lastmathids[1] = lastfontid
+ else
+ lastmathids[mathsize] = lastfontid
+ end
+ --
+ statistics.stoptiming(fonts)
+end
+
+function definers.define(specification)
+ --
+ local name = specification.name
+ if not name or name == "" then
+ return -1
+ else
+ statistics.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 = tex.sp(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
+ tex.definefont(specification.global,cs,tfmdata)
+ csnames[tfmdata] = cs
+ end
+ return tfmdata, fontdata[tfmdata]
+ else
+ local id = font.define(tfmdata)
+ tfmdata.properties.id = id
+ definers.register(tfmdata,id)
+ if cs then
+ tex.definefont(specification.global,cs,id)
+ csnames[id] = cs
+ end
+ constructors.cleanuptable(tfmdata)
+ constructors.finalize(tfmdata)
+ return id, tfmdata
+ end
+ statistics.stoptiming(fonts)
+ end
+end
+
+-- 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 = specification.size and number.todimen(specification.size) or texdimen.bodyfontsize
+ local number = tonumber(specification.number)
+ local id = nil
+ 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 = definers.define {
+ name = name,
+ size = size,
+ cs = cs,
+ }
+ end
+ if not id then
+ id = currentfont()
+ end
+ return id, csnames[id]
+end
+
+local enable_auto_r_scale = false
+
+experiments.register("fonts.autorscale", function(v)
+ enable_auto_r_scale = v
+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.
+
+local calculatescale = constructors.calculatescale
+
+function constructors.calculatescale(tfmdata,scaledpoints,relativeid)
+ local scaledpoints, delta = calculatescale(tfmdata,scaledpoints)
+ -- if enable_auto_r_scale and relativeid then -- for the moment this is rather context specific
+ -- local relativedata = fontdata[relativeid]
+ -- local rfmdata = relativedata and relativedata.unscaled and relativedata.unscaled
+ -- local id_x_height = rfmdata and rfmdata.parameters and rfmdata.parameters.x_height
+ -- local tf_x_height = tfmdata and tfmdata.parameters and tfmdata.parameters.x_height
+ -- if id_x_height and tf_x_height then
+ -- local rscale = id_x_height/tf_x_height
+ -- delta = rscale * delta
+ -- scaledpoints = rscale * scaledpoints
+ -- end
+ -- end
+ return scaledpoints, delta
+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 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
+ specification.hash = lower(specification.name .. ' @ ' .. hashfeatures(specification))
+ if specification.sub and specification.sub ~= "" then
+ specification.hash = specification.sub .. ' @ ' .. specification.hash
+ end
+ return specification
+end
+
+
+-- soon to be obsolete:
+
+local mappings = fonts.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
+ pdf.mapfile(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
+ pdf.mapline(how)
+ loaded[how] = true
+ end
+end
+
+function mappings.reset()
+ pdf.mapfile("")
+end
+
+mappings.reset() -- resets the default file
+
+-- we need an 'do after the banner hook'
+
+-- => commands
+
+local function nametoslot(name)
+ local t = type(name)
+ if t == "string" then
+ return resources[true].unicodes[name]
+ elseif t == "number" then
+ return n
+ end
+end
+
+helpers.nametoslot = nametoslot
+
+-- 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] = {
+ format("%03i",id or 0),
+ format("%09i",parameters.size or 0),
+ properties.type or "real",
+ properties.format or "unknown",
+ properties.name or "",
+ properties.psname or "",
+ properties.fullname or "",
+ }
+ report_status("%s: % t",properties.name,sortedkeys(data))
+ end
+ formatcolumns(t," ")
+ report_status()
+ report_status("defined fonts:")
+ report_status()
+ for k=1,tn do
+ report_status(t[k])
+ end
+ end
+end
+
+luatex.registerstopactions(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," ")
+ report_status()
+ report_status("defined featuresets:")
+ report_status()
+ for k=1,n do
+ report_status(t[k])
+ end
+ end
+end
+
+luatex.registerstopactions(loggers.reportusedfeatures)
+
+statistics.register("fonts load time", function()
+ return statistics.elapsedseconds(fonts)
+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",
+}
+
+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"
+ context.startfontclass { name }
+ context.definefontsynonym( { format("%s", Shape) }, { format("spec:%s-%s-regular-%s", fontname, normalweight, normalwidth) } )
+ context.definefontsynonym( { format("%sBold", Shape) }, { format("spec:%s-%s-regular-%s", fontname, boldweight, boldwidth ) } )
+ context.definefontsynonym( { format("%sBoldItalic", Shape) }, { format("spec:%s-%s-italic-%s", fontname, boldweight, boldwidth ) } )
+ context.definefontsynonym( { format("%sItalic", Shape) }, { format("spec:%s-%s-italic-%s", fontname, normalweight, normalwidth) } )
+ context.stopfontclass()
+ local settings = sequenced({ features= t.features },",")
+ context.dofastdefinetypeface(name, shortcut, shape, size, settings)
+end
+
+function fonts.current() -- todo: also handle name
+ return fontdata[currentfont()] or fontdata[0]
+end
+
+function fonts.currentid()
+ return currentfont() or 0
+end
+
+-- interfaces
+
+function commands.fontchar(n)
+ n = nametoslot(n)
+ if n then
+ context.char(n)
+ end
+end
+
+function commands.doifelsecurrentfonthasfeature(name) -- can be made faster with a supportedfeatures hash
+ local f = fontdata[currentfont()]
+ f = f and f.shared
+ f = f and f.rawdata
+ f = f and f.resources
+ f = f and f.features
+ commands.doifelse(f and (f.gpos[name] or f.gsub[name]))
+end
+
+local p, f = 1, formatters["%0.1fpt"] -- normally this value is changed only once
+
+local stripper = lpeg.patterns.stripzeros
+
+function commands.nbfs(amount,precision)
+ if precision ~= p then
+ p = precision
+ f = formatters["%0." .. p .. "fpt"]
+ end
+ context(lpegmatch(stripper,f(amount/65536)))
+end
+
+function commands.featureattribute(tag)
+ context(contextnumber(tag))
+end
+
+function commands.setfontfeature(tag)
+ texattribute[0] = contextnumber(tag)
+end
+
+function commands.resetfontfeature()
+ texattribute[0] = 0
+end
+
+-- function commands.addfs(tag) withset(tag, 1) end
+-- function commands.subfs(tag) withset(tag,-1) end
+-- function commands.addff(tag) withfnt(tag, 2) end -- on top of font features
+-- function commands.subff(tag) withfnt(tag,-2) end -- on top of font features
+
+function commands.cleanfontname (name) context(names.cleanname(name)) end
+
+function commands.fontlookupinitialize (name) names.lookup(name) end
+function commands.fontlookupnoffound () context(names.noflookups()) end
+function commands.fontlookupgetkeyofindex(key,index) context(names.getlookupkey(key,index)) end
+function commands.fontlookupgetkey (key) context(names.getlookupkey(key)) end
+
+-- 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
+
+-- 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,tfmdata) -- could be a method of a font instance
+ if unit == "ex" then
+ return (tfmdata and tfmdata.parameters.x_height) or 655360
+ elseif unit == "em" then
+ return (tfmdata and tfmdata.parameters.em_width) or 655360
+ 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.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 quads = hashes.quads
+local xheights = hashes.xheights
+
+setmetatableindex(number.dimenfactors, function(t,k)
+ if k == "ex" then
+ return xheigths[currentfont()]
+ elseif k == "em" then
+ return quads[currentfont()]
+ elseif k == "%" then
+ return dimen.hsize/100
+ else
+ -- error("wrong dimension: " .. (s or "?")) -- better a message
+ return false
+ end
+end)
+
+--[[ldx--
+<p>Before a font is passed to <l n='tex'/> we scale it. Here we also need
+to scale virtual characters.</p>
+--ldx]]--
+
+function constructors.checkvirtualids(tfmdata)
+ -- begin of experiment: we can use { "slot", 0, number } in virtual fonts
+ local fonts = tfmdata.fonts
+ local selfid = font.nextid()
+ if fonts and #fonts > 0 then
+ for i=1,#fonts do
+ if fonts[i][2] == 0 then
+ fonts[i][2] = selfid
+ end
+ end
+ else
+ -- tfmdata.fonts = { "id", selfid } -- conflicts with other next id's (vf math), too late anyway
+ end
+ -- end of experiment
+end
+
+-- function constructors.getvirtualid(tfmdata)
+-- -- since we don't know the id yet, we use 0 as signal
+-- local tf = tfmdata.fonts
+-- if not tf then
+-- local properties = tfmdata.properties
+-- if properties then
+-- properties.virtualized = true
+-- else
+-- tfmdata.properties = { virtualized = true }
+-- end
+-- tf = { }
+-- tfmdata.fonts = tf
+-- end
+-- local ntf = #tf + 1
+-- tf[ntf] = { id = 0 }
+-- return ntf
+-- end
+--
+-- function constructors.checkvirtualid(tfmdata, id) -- will go
+-- local properties = tfmdata.properties
+-- if tfmdata and tfmdata.type == "virtual" or (properties and properties.virtualized) then
+-- local vfonts = tfmdata.fonts
+-- if not vffonts or #vfonts == 0 then
+-- if properties then
+-- properties.virtualized = false
+-- end
+-- tfmdata.fonts = nil
+-- else
+-- for f=1,#vfonts do
+-- local fnt = vfonts[f]
+-- if fnt.id and fnt.id == 0 then
+-- fnt.id = id
+-- end
+-- end
+-- end
+-- end
+-- end
+
+function commands.setfontofid(id)
+ context.getvalue(csnames[id])
+end
+
+-- more interfacing:
+
+commands.definefontfeature = presetcontext
+
+local cache = { }
+
+local hows = {
+ ["+"] = "add",
+ ["-"] = "subtract",
+ ["="] = "replace",
+}
+
+function commands.feature(how,parent,name,font)
+ if not how then
+ if trace_features and texattribute[0] ~= 0 then
+ report_cummulative("font %!font:name!, reset",fontdata[font or true])
+ end
+ texattribute[0] = 0
+ elseif how == true 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
+ texattribute[0] = 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
+ texattribute[0] = done
+ end
+end
+
+function commands.featurelist(...)
+ context(fonts.specifiers.contexttostring(...))
+end
+
+function commands.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
+
+-- a fontkern plug:
+
+local copy_node = node.copy
+local kern = nodes.pool.register(nodes.pool.kern())
+
+node.set_attribute(kern,attributes.private('fontkern'),1) -- we can have several, attributes are shared
+
+nodes.injections.installnewkern(function(k)
+ local c = copy_node(kern)
+ c.kern = k
+ return c
+end)
+
+directives.register("nodes.injections.fontkern", function(v) kern.subtype = v and 0 or 1 end)
+
+-- here
+
+local trace_analyzing = false trackers.register("otf.analyzing", function(v) trace_analyzing = v end)
+
+local otffeatures = fonts.constructors.newfeatures("otf")
+local registerotffeature = otffeatures.register
+
+local analyzers = fonts.analyzers
+local methods = analyzers.methods
+
+local unsetvalue = attributes.unsetvalue
+
+local traverse_by_id = node.traverse_id
+
+local a_color = attributes.private('color')
+local a_colormodel = attributes.private('colormodel')
+local a_state = attributes.private('state')
+local m_color = attributes.list[a_color] or { }
+
+local glyph_code = nodes.nodecodes.glyph
+
+local states = analyzers.states
+
+local names = {
+ [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",
+}
+
+local function markstates(head)
+ if head then
+ local model = head[a_colormodel] or 1
+ for glyph in traverse_by_id(glyph_code,head) do
+ local a = glyph[a_state]
+ if a then
+ local name = names[a]
+ if name then
+ local color = m_color[name]
+ if color then
+ glyph[a_colormodel] = model
+ 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 in traverse_by_id(glyph_code,head) do
+ if not font or n.font == font then
+ n[a_color] = unsetvalue
+ end
+ end
+ return head, true
+end
diff --git a/tex/context/base/font-def.lua b/tex/context/base/font-def.lua
index 7e01c5620..bee02e8dc 100644
--- a/tex/context/base/font-def.lua
+++ b/tex/context/base/font-def.lua
@@ -1,449 +1,449 @@
-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 format, gmatch, match, find, lower, gsub = string.format, string.gmatch, string.match, string.find, string.lower, string.gsub
-local tostring, next = tostring, next
-local lpegmatch = lpeg.match
-
-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")
-trackers.register("fonts.all", "fonts.*", "otf.*", "afm.*", "tfm.*")
-
-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 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 lastdefined = nil -- we don't want this one to end up in s-tra-02
-
-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 splitter, splitspecifiers = nil, "" -- not so nice
-
-local P, C, S, Cc = lpeg.P, lpeg.C, lpeg.S, lpeg.Cc
-
-local left = P("(")
-local right = P(")")
-local colon = P(":")
-local space = 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 = C((1-sub-specification)^1)
- splitter = P((lookup + Cc("")) * name * (sub + Cc("")) * (specification + Cc("")))
-end
-
-local function addlookup(str,default)
- prefixpattern = prefixpattern + P(str)
-end
-
-definers.addlookup = addlookup
-
-addlookup("file")
-addlookup("name")
-addlookup("spec")
-
-local function getspecification(str)
- return lpegmatch(splitter,str)
-end
-
-definers.getspecification = getspecification
-
-function definers.registersplit(symbol,action,verbosename)
- addspecifier(symbol)
- variants[symbol] = action
- if verbosename then
- variants[verbosename] = action
- end
-end
-
-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
-
-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
-
---[[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 = file.suffix(name)
- if fonts.formats[suffix] then
- specification.forced = suffix
- specification.name = file.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 = resolve(specification.name,specification.sub,specification) -- we pass specification for overloaded versions
- if resolved then
- specification.resolved = resolved
- specification.sub = sub
- local suffix = file.suffix(resolved)
- if fonts.formats[suffix] then
- specification.forced = suffix
- specification.name = file.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 = resolvespec(specification.name,specification.sub,specification) -- we pass specification for overloaded versions
- if resolved then
- specification.resolved = resolved
- specification.sub = sub
- specification.forced = file.suffix(resolved)
- specification.name = file.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
- else
- specification.forced = specification.forced
- 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 = format("%s-%s",properties.fullname,extrahash)
- end
- end
- end
- return tfmdata
-end
-
--- function definers.applypostprocessors(tfmdata)
--- return tfmdata
--- end
-
-local function checkembedding(tfmdata)
- local properties = tfmdata.properties
- local embedding
- if directive_embedall then
- embedding = "full"
- elseif properties and properties.filename and constructors.dontembed[properties.filename] then
- embedding = "no"
- else
- embedding = "subset"
- end
- if properties then
- properties.embedding = embedding
- else
- tfmdata.properties = { embedding = embedding }
- end
- tfmdata.embedding = embedding
-end
-
-function definers.loadfont(specification)
- local hash = constructors.hashinstance(specification)
- local tfmdata = loadedfonts[hash] -- hashes by size !
- if not tfmdata then
- local forced = specification.forced or ""
- if forced ~= "" then
- local reader = readers[lower(forced)]
- tfmdata = reader and reader(specification)
- if not tfmdata then
- report_defining("forced type %a of %a not found",forced,specification.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)
- checkembedding(tfmdata) -- todo: general postprocessor
- loadedfonts[hash] = tfmdata
- designsizes[specification.hash] = tfmdata.parameters.designsize
- end
- end
- if not tfmdata then
- report_defining("font with asked name %a is not found using lookup %a",specification.name,specification.lookup)
- end
- return tfmdata
-end
-
-function constructors.checkvirtualids()
- -- dummy in plain version
-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
- constructors.checkvirtualids(tfmdata) -- experiment, will become obsolete when slots can selfreference
- 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.current() -- or maybe current
- return lastdefined
-end
-
-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
- 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
- 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
- lastdefined = tfmdata or id -- todo ! ! ! ! !
- if not tfmdata then -- or id?
- report_defining( "unknown font %a, loading aborted",specification.name)
- elseif trace_defining and type(tfmdata) == "table" then
- local properties = tfmdata.properties or { }
- local parameters = tfmdata.parameters or { }
- report_defining("using %s font with id %a, name %a, size %a, bytes %a, encoding %a, fullname %a, filename %a",
- properties.format, id, properties.name, parameters.size, properties.encodingbytes,
- properties.encodingname, properties.fullname, file.basename(properties.filename))
- end
- statistics.stoptiming(fonts)
- return tfmdata
-end
-
-function font.getfont(id)
- return fontdata[id] -- otherwise issues
-end
-
---[[ldx--
-<p>We overload the <l n='tfm'/> reader.</p>
---ldx]]--
-
-callbacks.register('define_font', definers.read, "definition of fonts (tfmdata preparation)")
+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 format, gmatch, match, find, lower, gsub = string.format, string.gmatch, string.match, string.find, string.lower, string.gsub
+local tostring, next = tostring, next
+local lpegmatch = lpeg.match
+
+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")
+trackers.register("fonts.all", "fonts.*", "otf.*", "afm.*", "tfm.*")
+
+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 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 lastdefined = nil -- we don't want this one to end up in s-tra-02
+
+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 splitter, splitspecifiers = nil, "" -- not so nice
+
+local P, C, S, Cc = lpeg.P, lpeg.C, lpeg.S, lpeg.Cc
+
+local left = P("(")
+local right = P(")")
+local colon = P(":")
+local space = 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 = C((1-sub-specification)^1)
+ splitter = P((lookup + Cc("")) * name * (sub + Cc("")) * (specification + Cc("")))
+end
+
+local function addlookup(str,default)
+ prefixpattern = prefixpattern + P(str)
+end
+
+definers.addlookup = addlookup
+
+addlookup("file")
+addlookup("name")
+addlookup("spec")
+
+local function getspecification(str)
+ return lpegmatch(splitter,str)
+end
+
+definers.getspecification = getspecification
+
+function definers.registersplit(symbol,action,verbosename)
+ addspecifier(symbol)
+ variants[symbol] = action
+ if verbosename then
+ variants[verbosename] = action
+ end
+end
+
+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
+
+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
+
+--[[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 = file.suffix(name)
+ if fonts.formats[suffix] then
+ specification.forced = suffix
+ specification.name = file.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 = resolve(specification.name,specification.sub,specification) -- we pass specification for overloaded versions
+ if resolved then
+ specification.resolved = resolved
+ specification.sub = sub
+ local suffix = file.suffix(resolved)
+ if fonts.formats[suffix] then
+ specification.forced = suffix
+ specification.name = file.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 = resolvespec(specification.name,specification.sub,specification) -- we pass specification for overloaded versions
+ if resolved then
+ specification.resolved = resolved
+ specification.sub = sub
+ specification.forced = file.suffix(resolved)
+ specification.name = file.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
+ else
+ specification.forced = specification.forced
+ 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 = format("%s-%s",properties.fullname,extrahash)
+ end
+ end
+ end
+ return tfmdata
+end
+
+-- function definers.applypostprocessors(tfmdata)
+-- return tfmdata
+-- end
+
+local function checkembedding(tfmdata)
+ local properties = tfmdata.properties
+ local embedding
+ if directive_embedall then
+ embedding = "full"
+ elseif properties and properties.filename and constructors.dontembed[properties.filename] then
+ embedding = "no"
+ else
+ embedding = "subset"
+ end
+ if properties then
+ properties.embedding = embedding
+ else
+ tfmdata.properties = { embedding = embedding }
+ end
+ tfmdata.embedding = embedding
+end
+
+function definers.loadfont(specification)
+ local hash = constructors.hashinstance(specification)
+ local tfmdata = loadedfonts[hash] -- hashes by size !
+ if not tfmdata then
+ local forced = specification.forced or ""
+ if forced ~= "" then
+ local reader = readers[lower(forced)]
+ tfmdata = reader and reader(specification)
+ if not tfmdata then
+ report_defining("forced type %a of %a not found",forced,specification.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)
+ checkembedding(tfmdata) -- todo: general postprocessor
+ loadedfonts[hash] = tfmdata
+ designsizes[specification.hash] = tfmdata.parameters.designsize
+ end
+ end
+ if not tfmdata then
+ report_defining("font with asked name %a is not found using lookup %a",specification.name,specification.lookup)
+ end
+ return tfmdata
+end
+
+function constructors.checkvirtualids()
+ -- dummy in plain version
+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
+ constructors.checkvirtualids(tfmdata) -- experiment, will become obsolete when slots can selfreference
+ 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.current() -- or maybe current
+ return lastdefined
+end
+
+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
+ 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
+ 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
+ lastdefined = tfmdata or id -- todo ! ! ! ! !
+ if not tfmdata then -- or id?
+ report_defining( "unknown font %a, loading aborted",specification.name)
+ elseif trace_defining and type(tfmdata) == "table" then
+ local properties = tfmdata.properties or { }
+ local parameters = tfmdata.parameters or { }
+ report_defining("using %s font with id %a, name %a, size %a, bytes %a, encoding %a, fullname %a, filename %a",
+ properties.format, id, properties.name, parameters.size, properties.encodingbytes,
+ properties.encodingname, properties.fullname, file.basename(properties.filename))
+ end
+ statistics.stoptiming(fonts)
+ return tfmdata
+end
+
+function font.getfont(id)
+ return fontdata[id] -- otherwise issues
+end
+
+--[[ldx--
+<p>We overload the <l n='tfm'/> reader.</p>
+--ldx]]--
+
+callbacks.register('define_font', definers.read, "definition of fonts (tfmdata preparation)")
diff --git a/tex/context/base/font-enc.lua b/tex/context/base/font-enc.lua
index 9795e0948..5305f0736 100644
--- a/tex/context/base/font-enc.lua
+++ b/tex/context/base/font-enc.lua
@@ -1,147 +1,147 @@
-if not modules then modules = { } end modules ['font-enc'] = {
- 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 obsolete
-
-local match, gmatch, gsub = string.match, string.gmatch, string.gsub
-
-local setmetatableindex = table.setmetatableindex
-
---[[ldx--
-<p>Because encodings are going to disappear, we don't bother defining
-them in tables. But we may do so some day, for consistency.</p>
---ldx]]--
-
-local report_encoding = logs.reporter("fonts","encoding")
-
-local encodings = { }
-fonts.encodings = encodings
-
-encodings.version = 1.03
-encodings.cache = containers.define("fonts", "enc", fonts.encodings.version, true)
-encodings.known = utilities.storage.allocate { -- sort of obsolete
- texnansi = true,
- ec = true,
- qx = true,
- t5 = true,
- t2a = true,
- t2b = true,
- t2c = true,
- unicode = true,
-}
-
-function encodings.is_known(encoding)
- return containers.is_valid(encodings.cache,encoding)
-end
-
---[[ldx--
-<p>An encoding file looks like this:</p>
-
-<typing>
-/TeXnANSIEncoding [
-/.notdef
-/Euro
-...
-/ydieresis
-] def
-</typing>
-
-<p>Beware! The generic encoding files don't always apply to the ones that
-ship with fonts. This has to do with the fact that names follow (slightly)
-different standards. However, the fonts where this applies to (for instance
-Latin Modern or <l n='tex'> Gyre) come in OpenType variants too, so these
-will be used.</p>
---ldx]]--
-
-local enccodes = characters.enccodes or { }
-
-function encodings.load(filename)
- local name = file.removesuffix(filename)
- local data = containers.read(encodings.cache,name)
- if data then
- return data
- end
- if name == "unicode" then
- data = encodings.make_unicode_vector() -- special case, no tex file for this
- end
- if data then
- return data
- end
- local vector, tag, hash, unicodes = { }, "", { }, { }
- local foundname = resolvers.findfile(filename,'enc')
- if foundname and foundname ~= "" then
- local ok, encoding, size = resolvers.loadbinfile(foundname)
- if ok and encoding then
- encoding = gsub(encoding,"%%(.-)\n","")
- local tag, vec = match(encoding,"/(%w+)%s*%[(.*)%]%s*def")
- local i = 0
- for ch in gmatch(vec,"/([%a%d%.]+)") do
- if ch ~= ".notdef" then
- vector[i] = ch
- if not hash[ch] then
- hash[ch] = i
- else
- -- duplicate, play safe for tex ligs and take first
- end
- if enccodes[ch] then
- unicodes[enccodes[ch]] = i
- end
- end
- i = i + 1
- end
- end
- end
- local data = {
- name = name,
- tag = tag,
- vector = vector,
- hash = hash,
- unicodes = unicodes
- }
- return containers.write(encodings.cache, name, data)
-end
-
---[[ldx--
-<p>There is no unicode encoding but for practical purposes we define
-one.</p>
---ldx]]--
-
--- maybe make this a function:
-
-function encodings.make_unicode_vector()
- local vector, hash = { }, { }
- for code, v in next, characters.data do
- local name = v.adobename
- if name then
- vector[code] = name
- hash[name] = code
- else
- vector[code] = '.notdef'
- end
- end
- for name, code in next, characters.synonyms do
- vector[code], hash[name] = name, code
- end
- return containers.write(encodings.cache, 'unicode', { name='unicode', tag='unicode', vector=vector, hash=hash })
-end
-
-if not encodings.agl then
-
- -- We delay delay loading this rather big vector that is only needed when a
- -- font is loaded for caching. Once we're further along the route we can also
- -- delay it in the generic version (which doesn't use this file).
-
- encodings.agl = { }
-
- setmetatableindex(encodings.agl, function(t,k)
- report_encoding("loading (extended) adobe glyph list")
- dofile(resolvers.findfile("font-agl.lua"))
- return rawget(encodings.agl,k)
- end)
-
-end
+if not modules then modules = { } end modules ['font-enc'] = {
+ 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 obsolete
+
+local match, gmatch, gsub = string.match, string.gmatch, string.gsub
+
+local setmetatableindex = table.setmetatableindex
+
+--[[ldx--
+<p>Because encodings are going to disappear, we don't bother defining
+them in tables. But we may do so some day, for consistency.</p>
+--ldx]]--
+
+local report_encoding = logs.reporter("fonts","encoding")
+
+local encodings = { }
+fonts.encodings = encodings
+
+encodings.version = 1.03
+encodings.cache = containers.define("fonts", "enc", fonts.encodings.version, true)
+encodings.known = utilities.storage.allocate { -- sort of obsolete
+ texnansi = true,
+ ec = true,
+ qx = true,
+ t5 = true,
+ t2a = true,
+ t2b = true,
+ t2c = true,
+ unicode = true,
+}
+
+function encodings.is_known(encoding)
+ return containers.is_valid(encodings.cache,encoding)
+end
+
+--[[ldx--
+<p>An encoding file looks like this:</p>
+
+<typing>
+/TeXnANSIEncoding [
+/.notdef
+/Euro
+...
+/ydieresis
+] def
+</typing>
+
+<p>Beware! The generic encoding files don't always apply to the ones that
+ship with fonts. This has to do with the fact that names follow (slightly)
+different standards. However, the fonts where this applies to (for instance
+Latin Modern or <l n='tex'> Gyre) come in OpenType variants too, so these
+will be used.</p>
+--ldx]]--
+
+local enccodes = characters.enccodes or { }
+
+function encodings.load(filename)
+ local name = file.removesuffix(filename)
+ local data = containers.read(encodings.cache,name)
+ if data then
+ return data
+ end
+ if name == "unicode" then
+ data = encodings.make_unicode_vector() -- special case, no tex file for this
+ end
+ if data then
+ return data
+ end
+ local vector, tag, hash, unicodes = { }, "", { }, { }
+ local foundname = resolvers.findfile(filename,'enc')
+ if foundname and foundname ~= "" then
+ local ok, encoding, size = resolvers.loadbinfile(foundname)
+ if ok and encoding then
+ encoding = gsub(encoding,"%%(.-)\n","")
+ local tag, vec = match(encoding,"/(%w+)%s*%[(.*)%]%s*def")
+ local i = 0
+ for ch in gmatch(vec,"/([%a%d%.]+)") do
+ if ch ~= ".notdef" then
+ vector[i] = ch
+ if not hash[ch] then
+ hash[ch] = i
+ else
+ -- duplicate, play safe for tex ligs and take first
+ end
+ if enccodes[ch] then
+ unicodes[enccodes[ch]] = i
+ end
+ end
+ i = i + 1
+ end
+ end
+ end
+ local data = {
+ name = name,
+ tag = tag,
+ vector = vector,
+ hash = hash,
+ unicodes = unicodes
+ }
+ return containers.write(encodings.cache, name, data)
+end
+
+--[[ldx--
+<p>There is no unicode encoding but for practical purposes we define
+one.</p>
+--ldx]]--
+
+-- maybe make this a function:
+
+function encodings.make_unicode_vector()
+ local vector, hash = { }, { }
+ for code, v in next, characters.data do
+ local name = v.adobename
+ if name then
+ vector[code] = name
+ hash[name] = code
+ else
+ vector[code] = '.notdef'
+ end
+ end
+ for name, code in next, characters.synonyms do
+ vector[code], hash[name] = name, code
+ end
+ return containers.write(encodings.cache, 'unicode', { name='unicode', tag='unicode', vector=vector, hash=hash })
+end
+
+if not encodings.agl then
+
+ -- We delay delay loading this rather big vector that is only needed when a
+ -- font is loaded for caching. Once we're further along the route we can also
+ -- delay it in the generic version (which doesn't use this file).
+
+ encodings.agl = { }
+
+ setmetatableindex(encodings.agl, function(t,k)
+ report_encoding("loading (extended) adobe glyph list")
+ dofile(resolvers.findfile("font-agl.lua"))
+ return rawget(encodings.agl,k)
+ end)
+
+end
diff --git a/tex/context/base/font-enh.lua b/tex/context/base/font-enh.lua
index cb152083d..2bf0741f5 100644
--- a/tex/context/base/font-enh.lua
+++ b/tex/context/base/font-enh.lua
@@ -1,200 +1,200 @@
-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"
-}
-
-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 tfmfeatures = constructors.newfeatures("tfm")
-local registertfmfeature = tfmfeatures.register
-
-local afmfeatures = fonts.constructors.newfeatures("afm")
-local registerafmfeature = afmfeatures.register
-
-local otffeatures = fonts.constructors.newfeatures("otf")
-local registerotffeature = otffeatures.register
-
--- -- these will become goodies (when needed at all)
---
--- local fontencodings = fonts.encodings
--- fontencodings.remappings = fontencodings.remappings or { }
---
--- local function reencode(tfmdata,encoding)
--- if encoding and fontencodings.known[encoding] then
--- local data = fontencodings.load(encoding)
--- if data then
--- tfmdata.properties.encoding = encoding
--- local characters = tfmdata.characters
--- local original = { }
--- local vector = data.vector
--- for unicode, character in next, characters do
--- character.name = vector[unicode]
--- character.index = unicode, character
--- original[unicode] = character
--- end
--- for newcode, oldcode in next, data.unicodes do
--- if newcode ~= oldcode then
--- if trace_unicoding then
--- report_unicoding("reencoding %U to %U",oldcode,newcode)
--- end
--- characters[newcode] = original[oldcode]
--- end
--- end
--- end
--- end
--- end
---
--- registertfmfeature {
--- name = "reencode",
--- description = "reencode",
--- manipulators = {
--- base = reencode,
--- node = reencode,
--- }
--- }
---
--- local function remap(tfmdata,remapping)
--- local vector = remapping and fontencodings.remappings[remapping]
--- if vector then
--- local characters, original = tfmdata.characters, { }
--- for k, v in next, characters do
--- original[k], characters[k] = v, nil
--- end
--- for k,v in next, vector do
--- if k ~= v then
--- if trace_unicoding then
--- report_unicoding("remapping %U to %U",k,v)
--- end
--- local c = original[k]
--- characters[v] = c
--- c.index = k
--- end
--- end
--- local properties = tfmdata.properties
--- if not properties then
--- properties = { }
--- tfmdata.properties = properties
--- else
--- properties.encodingbytes = 2
--- properties.format = properties.format or 'type1'
--- end
--- end
--- end
---
--- registertfmfeature {
--- name = "remap",
--- description = "remap",
--- manipulators = {
--- base = remap,
--- node = remap,
--- }
--- }
-
--- \definefontfeature[dingbats][goodies=dingbats,unicoding=yes]
-
--- we only add and don't replace
--- we could also add kerns but we asssume symbols
--- todo: complain if not basemode
-
--- remapping = {
--- tounicode = true,
--- unicodes = {
--- a1 = 0x2701,
-
-local tosixteen = fonts.mappings.tounicode16
-
-local function initializeunicoding(tfmdata)
- local goodies = tfmdata.goodies
- local newcoding = nil
- local tounicode = false
- for i=1,#goodies do
- local remapping = goodies[i].remapping
- if remapping and remapping.unicodes then
- newcoding = remapping.unicodes -- names to unicodes
- tounicode = remapping.tounicode
- end
- end
- if newcoding then
- local characters = tfmdata.characters
- local descriptions = tfmdata.descriptions
- local oldcoding = tfmdata.resources.unicodes
- local tounicodes = tfmdata.resources.tounicode -- index to unicode
- 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]
- if original then
- characters [newcode] = original.character
- descriptions[newcode] = original.description
- else
- characters [newcode] = characters [oldcode]
- descriptions[newcode] = descriptions[oldcode]
- end
- else
- oldcoding[name] = newcode
- end
- if tounicode then
- local description = descriptions[newcode]
- if description then
- local index = description.index
- if not tounicodes[index] then
- tounicodes[index] = tosixteen(newcode) -- shared (we could have a metatable)
- end
- end
- 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
-
-registerafmfeature {
- name = "unicoding",
- description = "adapt unicode table",
- initializers = {
- base = initializeunicoding,
- node = initializeunicoding,
- },
- -- manipulators = {
- -- base = finalizeunicoding,
- -- node = finalizeunicoding,
- -- }
-}
-
-registerotffeature {
- name = "unicoding",
- description = "adapt unicode table",
- initializers = {
- base = initializeunicoding,
- node = initializeunicoding,
- },
- -- manipulators = {
- -- base = finalizeunicoding,
- -- node = finalizeunicoding,
- -- }
-}
+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"
+}
+
+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 tfmfeatures = constructors.newfeatures("tfm")
+local registertfmfeature = tfmfeatures.register
+
+local afmfeatures = fonts.constructors.newfeatures("afm")
+local registerafmfeature = afmfeatures.register
+
+local otffeatures = fonts.constructors.newfeatures("otf")
+local registerotffeature = otffeatures.register
+
+-- -- these will become goodies (when needed at all)
+--
+-- local fontencodings = fonts.encodings
+-- fontencodings.remappings = fontencodings.remappings or { }
+--
+-- local function reencode(tfmdata,encoding)
+-- if encoding and fontencodings.known[encoding] then
+-- local data = fontencodings.load(encoding)
+-- if data then
+-- tfmdata.properties.encoding = encoding
+-- local characters = tfmdata.characters
+-- local original = { }
+-- local vector = data.vector
+-- for unicode, character in next, characters do
+-- character.name = vector[unicode]
+-- character.index = unicode, character
+-- original[unicode] = character
+-- end
+-- for newcode, oldcode in next, data.unicodes do
+-- if newcode ~= oldcode then
+-- if trace_unicoding then
+-- report_unicoding("reencoding %U to %U",oldcode,newcode)
+-- end
+-- characters[newcode] = original[oldcode]
+-- end
+-- end
+-- end
+-- end
+-- end
+--
+-- registertfmfeature {
+-- name = "reencode",
+-- description = "reencode",
+-- manipulators = {
+-- base = reencode,
+-- node = reencode,
+-- }
+-- }
+--
+-- local function remap(tfmdata,remapping)
+-- local vector = remapping and fontencodings.remappings[remapping]
+-- if vector then
+-- local characters, original = tfmdata.characters, { }
+-- for k, v in next, characters do
+-- original[k], characters[k] = v, nil
+-- end
+-- for k,v in next, vector do
+-- if k ~= v then
+-- if trace_unicoding then
+-- report_unicoding("remapping %U to %U",k,v)
+-- end
+-- local c = original[k]
+-- characters[v] = c
+-- c.index = k
+-- end
+-- end
+-- local properties = tfmdata.properties
+-- if not properties then
+-- properties = { }
+-- tfmdata.properties = properties
+-- else
+-- properties.encodingbytes = 2
+-- properties.format = properties.format or 'type1'
+-- end
+-- end
+-- end
+--
+-- registertfmfeature {
+-- name = "remap",
+-- description = "remap",
+-- manipulators = {
+-- base = remap,
+-- node = remap,
+-- }
+-- }
+
+-- \definefontfeature[dingbats][goodies=dingbats,unicoding=yes]
+
+-- we only add and don't replace
+-- we could also add kerns but we asssume symbols
+-- todo: complain if not basemode
+
+-- remapping = {
+-- tounicode = true,
+-- unicodes = {
+-- a1 = 0x2701,
+
+local tosixteen = fonts.mappings.tounicode16
+
+local function initializeunicoding(tfmdata)
+ local goodies = tfmdata.goodies
+ local newcoding = nil
+ local tounicode = false
+ for i=1,#goodies do
+ local remapping = goodies[i].remapping
+ if remapping and remapping.unicodes then
+ newcoding = remapping.unicodes -- names to unicodes
+ tounicode = remapping.tounicode
+ end
+ end
+ if newcoding then
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local oldcoding = tfmdata.resources.unicodes
+ local tounicodes = tfmdata.resources.tounicode -- index to unicode
+ 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]
+ if original then
+ characters [newcode] = original.character
+ descriptions[newcode] = original.description
+ else
+ characters [newcode] = characters [oldcode]
+ descriptions[newcode] = descriptions[oldcode]
+ end
+ else
+ oldcoding[name] = newcode
+ end
+ if tounicode then
+ local description = descriptions[newcode]
+ if description then
+ local index = description.index
+ if not tounicodes[index] then
+ tounicodes[index] = tosixteen(newcode) -- shared (we could have a metatable)
+ end
+ end
+ 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
+
+registerafmfeature {
+ name = "unicoding",
+ description = "adapt unicode table",
+ initializers = {
+ base = initializeunicoding,
+ node = initializeunicoding,
+ },
+ -- manipulators = {
+ -- base = finalizeunicoding,
+ -- node = finalizeunicoding,
+ -- }
+}
+
+registerotffeature {
+ name = "unicoding",
+ description = "adapt unicode table",
+ initializers = {
+ base = initializeunicoding,
+ node = initializeunicoding,
+ },
+ -- manipulators = {
+ -- base = finalizeunicoding,
+ -- node = finalizeunicoding,
+ -- }
+}
diff --git a/tex/context/base/font-ext.lua b/tex/context/base/font-ext.lua
index d2bc21837..89d5927d4 100644
--- a/tex/context/base/font-ext.lua
+++ b/tex/context/base/font-ext.lua
@@ -1,946 +1,946 @@
-if not modules then modules = { } end modules ['font-ext'] = {
- 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, byte = next, type, string.byte
-local gmatch, concat, format = string.gmatch, table.concat, string.format
-local utfchar = utf.char
-
-local commands, context = commands, context
-local fonts, utilities = fonts, utilities
-
-local trace_protrusion = false trackers.register("fonts.protrusion", function(v) trace_protrusion = v end)
-local trace_expansion = false trackers.register("fonts.expansion", function(v) trace_expansion = v end)
-
-local report_expansions = logs.reporter("fonts","expansions")
-local report_protrusions = logs.reporter("fonts","protrusions")
-
--- todo: byte(..) => 0xHHHH
-
---[[ldx--
-<p>When we implement functions that deal with features, most of them
-will depend of the font format. Here we define the few that are kind
-of neutral.</p>
---ldx]]--
-
-local handlers = fonts.handlers
-local hashes = fonts.hashes
-local otf = handlers.otf
-
-local registerotffeature = handlers.otf.features.register
-local registerafmfeature = handlers.afm.features.register
-
-local fontdata = hashes.identifiers
-
-local allocate = utilities.storage.allocate
-local settings_to_array = utilities.parsers.settings_to_array
-local getparameters = utilities.parsers.getparameters
-
-local setmetatableindex = table.setmetatableindex
-
--- -- -- -- -- --
--- shared
--- -- -- -- -- --
-
-local function get_class_and_vector(tfmdata,value,where) -- "expansions"
- local g_where = tfmdata.goodies and tfmdata.goodies[where]
- local f_where = fonts[where]
- local g_classes = g_where and g_where.classes
- local f_classes = f_where and f_where.classes
- local class = (g_classes and g_classes[value]) or (f_classes and f_classes[value])
- if class then
- local class_vector = class.vector
- local g_vectors = g_where and g_where.vectors
- local f_vectors = f_where and f_where.vectors
- local vector = (g_vectors and g_vectors[class_vector]) or (f_vectors and f_vectors[class_vector])
- return class, vector
- end
-end
-
--- -- -- -- -- --
--- expansion (hz)
--- -- -- -- -- --
-
-local expansions = fonts.expansions or allocate()
-
-fonts.expansions = expansions
-
-local classes = expansions.classes or allocate()
-local vectors = expansions.vectors or allocate()
-
-expansions.classes = classes
-expansions.vectors = vectors
-
--- beware, pdftex itself uses percentages * 10
-
-classes.preset = { stretch = 2, shrink = 2, step = .5, factor = 1 }
-
-function commands.setupfontexpansion(class,settings)
- getparameters(classes,class,'preset',settings)
-end
-
-classes['quality'] = {
- stretch = 2, shrink = 2, step = .5, vector = 'default', factor = 1
-}
-
-vectors['default'] = {
- [byte('A')] = 0.5, [byte('B')] = 0.7, [byte('C')] = 0.7, [byte('D')] = 0.5, [byte('E')] = 0.7,
- [byte('F')] = 0.7, [byte('G')] = 0.5, [byte('H')] = 0.7, [byte('K')] = 0.7, [byte('M')] = 0.7,
- [byte('N')] = 0.7, [byte('O')] = 0.5, [byte('P')] = 0.7, [byte('Q')] = 0.5, [byte('R')] = 0.7,
- [byte('S')] = 0.7, [byte('U')] = 0.7, [byte('W')] = 0.7, [byte('Z')] = 0.7,
- [byte('a')] = 0.7, [byte('b')] = 0.7, [byte('c')] = 0.7, [byte('d')] = 0.7, [byte('e')] = 0.7,
- [byte('g')] = 0.7, [byte('h')] = 0.7, [byte('k')] = 0.7, [byte('m')] = 0.7, [byte('n')] = 0.7,
- [byte('o')] = 0.7, [byte('p')] = 0.7, [byte('q')] = 0.7, [byte('s')] = 0.7, [byte('u')] = 0.7,
- [byte('w')] = 0.7, [byte('z')] = 0.7,
- [byte('2')] = 0.7, [byte('3')] = 0.7, [byte('6')] = 0.7, [byte('8')] = 0.7, [byte('9')] = 0.7,
-}
-
-vectors['quality'] = vectors['default'] -- metatable ?
-
-local function initializeexpansion(tfmdata,value)
- if value then
- local class, vector = get_class_and_vector(tfmdata,value,"expansions")
- if class then
- if vector then
- local stretch = class.stretch or 0
- local shrink = class.shrink or 0
- local step = class.step or 0
- local factor = class.factor or 1
- if trace_expansion then
- report_expansions("setting class %a, vector %a, factor %a, stretch %a, shrink %a, step %a",
- value,class.vector,factor,stretch,shrink,step)
- end
- tfmdata.parameters.expansion = {
- stretch = 10 * stretch,
- shrink = 10 * shrink,
- step = 10 * step,
- factor = factor,
- auto = true,
- }
- local data = characters and characters.data
- for i, chr in next, tfmdata.characters do
- local v = vector[i]
- if data and not v then -- we could move the data test outside (needed for plain)
- local d = data[i]
- if d then
- local s = d.shcode
- if not s then
- -- sorry
- elseif type(s) == "table" then
- v = ((vector[s[1]] or 0) + (vector[s[#s]] or 0)) / 2
- else
- v = vector[s] or 0
- end
- end
- end
- if v and v ~= 0 then
- chr.expansion_factor = v*factor
- else -- can be option
- chr.expansion_factor = factor
- end
- end
- elseif trace_expansion then
- report_expansions("unknown vector %a in class %a",class.vector,value)
- end
- elseif trace_expansion then
- report_expansions("unknown class %a",value)
- end
- end
-end
-
-registerotffeature {
- name = "expansion",
- description = "apply hz optimization",
- initializers = {
- base = initializeexpansion,
- node = initializeexpansion,
- }
-}
-
-registerafmfeature {
- name = "expansion",
- description = "apply hz optimization",
- initializers = {
- base = initializeexpansion,
- node = initializeexpansion,
- }
-}
-
-fonts.goodies.register("expansions", function(...) return fonts.goodies.report("expansions", trace_expansion, ...) end)
-
-local report_opbd = logs.reporter("fonts","otf opbd")
-
--- -- -- -- -- --
--- protrusion
--- -- -- -- -- --
-
-fonts.protrusions = allocate()
-local protrusions = fonts.protrusions
-
-protrusions.classes = allocate()
-protrusions.vectors = allocate()
-
-local classes = protrusions.classes
-local vectors = protrusions.vectors
-
--- the values need to be revisioned
-
-classes.preset = { factor = 1, left = 1, right = 1 }
-
-function commands.setupfontprotrusion(class,settings)
- getparameters(classes,class,'preset',settings)
-end
-
-classes['pure'] = {
- vector = 'pure', factor = 1
-}
-classes['punctuation'] = {
- vector = 'punctuation', factor = 1
-}
-classes['alpha'] = {
- vector = 'alpha', factor = 1
-}
-classes['quality'] = {
- vector = 'quality', factor = 1
-}
-
-vectors['pure'] = {
-
- [0x002C] = { 0, 1 }, -- comma
- [0x002E] = { 0, 1 }, -- period
- [0x003A] = { 0, 1 }, -- colon
- [0x003B] = { 0, 1 }, -- semicolon
- [0x002D] = { 0, 1 }, -- hyphen
- [0x00AD] = { 0, 1 }, -- also hyphen
- [0x2013] = { 0, 0.50 }, -- endash
- [0x2014] = { 0, 0.33 }, -- emdash
- [0x3001] = { 0, 1 }, -- ideographic comma 、
- [0x3002] = { 0, 1 }, -- ideographic full stop 。
- [0x060C] = { 0, 1 }, -- arabic comma ،
- [0x061B] = { 0, 1 }, -- arabic semicolon ؛
- [0x06D4] = { 0, 1 }, -- arabic full stop ۔
-
-}
-
-vectors['punctuation'] = {
-
- [0x003F] = { 0, 0.20 }, -- ?
- [0x00BF] = { 0, 0.20 }, -- ¿
- [0x0021] = { 0, 0.20 }, -- !
- [0x00A1] = { 0, 0.20 }, -- ¡
- [0x0028] = { 0.05, 0 }, -- (
- [0x0029] = { 0, 0.05 }, -- )
- [0x005B] = { 0.05, 0 }, -- [
- [0x005D] = { 0, 0.05 }, -- ]
- [0x002C] = { 0, 0.70 }, -- comma
- [0x002E] = { 0, 0.70 }, -- period
- [0x003A] = { 0, 0.50 }, -- colon
- [0x003B] = { 0, 0.50 }, -- semicolon
- [0x002D] = { 0, 0.70 }, -- hyphen
- [0x00AD] = { 0, 0.70 }, -- also hyphen
- [0x2013] = { 0, 0.30 }, -- endash
- [0x2014] = { 0, 0.20 }, -- emdash
- [0x060C] = { 0, 0.70 }, -- arabic comma
- [0x061B] = { 0, 0.50 }, -- arabic semicolon
- [0x06D4] = { 0, 0.70 }, -- arabic full stop
- [0x061F] = { 0, 0.20 }, -- ؟
-
- -- todo: left and right quotes: .5 double, .7 single
-
- [0x2039] = { 0.70, 0.70 }, -- left single guillemet ‹
- [0x203A] = { 0.70, 0.70 }, -- right single guillemet ›
- [0x00AB] = { 0.50, 0.50 }, -- left guillemet «
- [0x00BB] = { 0.50, 0.50 }, -- right guillemet »
-
- [0x2018] = { 0.70, 0.70 }, -- left single quotation mark ‘
- [0x2019] = { 0, 0.70 }, -- right single quotation mark ’
- [0x201A] = { 0.70, 0 }, -- single low-9 quotation mark ,
- [0x201B] = { 0.70, 0 }, -- single high-reversed-9 quotation mark ‛
- [0x201C] = { 0.50, 0.50 }, -- left double quotation mark “
- [0x201D] = { 0, 0.50 }, -- right double quotation mark ”
- [0x201E] = { 0.50, 0 }, -- double low-9 quotation mark „
- [0x201F] = { 0.50, 0 }, -- double high-reversed-9 quotation mark ‟
-
-}
-
-vectors['alpha'] = {
-
- [byte("A")] = { .05, .05 },
- [byte("F")] = { 0, .05 },
- [byte("J")] = { .05, 0 },
- [byte("K")] = { 0, .05 },
- [byte("L")] = { 0, .05 },
- [byte("T")] = { .05, .05 },
- [byte("V")] = { .05, .05 },
- [byte("W")] = { .05, .05 },
- [byte("X")] = { .05, .05 },
- [byte("Y")] = { .05, .05 },
-
- [byte("k")] = { 0, .05 },
- [byte("r")] = { 0, .05 },
- [byte("t")] = { 0, .05 },
- [byte("v")] = { .05, .05 },
- [byte("w")] = { .05, .05 },
- [byte("x")] = { .05, .05 },
- [byte("y")] = { .05, .05 },
-
-}
-
-vectors['quality'] = table.merged(
- vectors['punctuation'],
- vectors['alpha']
-)
-
--- As this is experimental code, users should not depend on it. The
--- implications are still discussed on the ConTeXt Dev List and we're
--- not sure yet what exactly the spec is (the next code is tested with
--- a gyre font patched by / fea file made by Khaled Hosny). The double
--- trick should not be needed it proper hanging punctuation is used in
--- which case values < 1 can be used.
---
--- preferred (in context, usine vectors):
---
--- \definefontfeature[whatever][default][mode=node,protrusion=quality]
---
--- using lfbd and rtbd, with possibibility to enable only one side :
---
--- \definefontfeature[whocares][default][mode=node,protrusion=yes, opbd=yes,script=latn]
--- \definefontfeature[whocares][default][mode=node,protrusion=right,opbd=yes,script=latn]
---
--- idem, using multiplier
---
--- \definefontfeature[whocares][default][mode=node,protrusion=2,opbd=yes,script=latn]
--- \definefontfeature[whocares][default][mode=node,protrusion=double,opbd=yes,script=latn]
---
--- idem, using named feature file (less frozen):
---
--- \definefontfeature[whocares][default][mode=node,protrusion=2,opbd=yes,script=latn,featurefile=texgyrepagella-regularxx.fea]
-
-classes['double'] = { -- for testing opbd
- factor = 2, left = 1, right = 1,
-}
-
-local function map_opbd_onto_protrusion(tfmdata,value,opbd)
- local characters = tfmdata.characters
- local descriptions = tfmdata.descriptions
- local properties = tfmdata.properties
- local rawdata = tfmdata.shared.rawdata
- local lookuphash = rawdata.lookuphash
- local script = properties.script
- local language = properties.language
- local done, factor, left, right = false, 1, 1, 1
- local class = classes[value]
- if class then
- factor = class.factor or 1
- left = class.left or 1
- right = class.right or 1
- else
- factor = tonumber(value) or 1
- end
- if opbd ~= "right" then
- local validlookups, lookuplist = otf.collectlookups(rawdata,"lfbd",script,language)
- if validlookups then
- for i=1,#lookuplist do
- local lookup = lookuplist[i]
- local data = lookuphash[lookup]
- if data then
- if trace_protrusion then
- report_protrusions("setting left using lfbd lookup %a",lookup)
- end
- for k, v in next, data do
- -- local p = - v[3] / descriptions[k].width-- or 1 ~= 0 too but the same
- local p = - (v[1] / 1000) * factor * left
- characters[k].left_protruding = p
- if trace_protrusion then
- report_protrusions("lfbd -> %s -> %C -> %0.03f (% t)",lookup,k,p,v)
- end
- end
- done = true
- end
- end
- end
- end
- if opbd ~= "left" then
- local validlookups, lookuplist = otf.collectlookups(rawdata,"rtbd",script,language)
- if validlookups then
- for i=1,#lookuplist do
- local lookup = lookuplist[i]
- local data = lookuphash[lookup]
- if data then
- if trace_protrusion then
- report_protrusions("setting right using rtbd lookup %a",lookup)
- end
- for k, v in next, data do
- -- local p = v[3] / descriptions[k].width -- or 3
- local p = (v[1] / 1000) * factor * right
- characters[k].right_protruding = p
- if trace_protrusion then
- report_protrusions("rtbd -> %s -> %C -> %0.03f (% t)",lookup,k,p,v)
- end
- end
- end
- done = true
- end
- end
- end
- local parameters = tfmdata.parameters
- local protrusion = tfmdata.protrusion
- if not protrusion then
- parameters.protrusion = {
- auto = true
- }
- else
- protrusion.auto = true
- end
-end
-
--- The opbd test is just there because it was discussed on the
--- context development list. However, the mentioned fxlbi.otf font
--- only has some kerns for digits. So, consider this feature not
--- supported till we have a proper test font.
-
-local function initializeprotrusion(tfmdata,value)
- if value then
- local opbd = tfmdata.shared.features.opbd
- if opbd then
- -- possible values: left right both yes no (experimental)
- map_opbd_onto_protrusion(tfmdata,value,opbd)
- else
- local class, vector = get_class_and_vector(tfmdata,value,"protrusions")
- if class then
- if vector then
- local factor = class.factor or 1
- local left = class.left or 1
- local right = class.right or 1
- if trace_protrusion then
- report_protrusions("setting class %a, vector %a, factor %a, left %a, right %a",
- value,class.vector,factor,left,right)
- end
- local data = characters.data
- local emwidth = tfmdata.parameters.quad
- tfmdata.parameters.protrusion = {
- factor = factor,
- left = left,
- right = right,
- auto = true,
- }
- for i, chr in next, tfmdata.characters do
- local v, pl, pr = vector[i], nil, nil
- if v then
- pl, pr = v[1], v[2]
- else
- local d = data[i]
- if d then
- local s = d.shcode
- if not s then
- -- sorry
- elseif type(s) == "table" then
- local vl, vr = vector[s[1]], vector[s[#s]]
- if vl then pl = vl[1] end
- if vr then pr = vr[2] end
- else
- v = vector[s]
- if v then
- pl, pr = v[1], v[2]
- end
- end
- end
- end
- if pl and pl ~= 0 then
- chr.left_protruding = left *pl*factor
- end
- if pr and pr ~= 0 then
- chr.right_protruding = right*pr*factor
- end
- end
- elseif trace_protrusion then
- report_protrusions("unknown vector %a in class %a",class.vector,value)
- end
- elseif trace_protrusion then
- report_protrusions("unknown class %a",value)
- end
- end
- end
-end
-
-registerotffeature {
- name = "protrusion",
- description = "shift characters into the left and or right margin",
- initializers = {
- base = initializeprotrusion,
- node = initializeprotrusion,
- }
-}
-
-registerafmfeature {
- name = "protrusion",
- description = "shift characters into the left and or right margin",
- initializers = {
- base = initializeprotrusion,
- node = initializeprotrusion,
- }
-}
-
-fonts.goodies.register("protrusions", function(...) return fonts.goodies.report("protrusions", trace_protrusion, ...) end)
-
--- -- --
-
-local function initializenostackmath(tfmdata,value)
- tfmdata.properties.nostackmath = value and true
-end
-
-registerotffeature {
- name = "nostackmath",
- description = "disable math stacking mechanism",
- initializers = {
- base = initializenostackmath,
- node = initializenostackmath,
- }
-}
-
-local function initializeitlc(tfmdata,value) -- hm, always value
- if value then
- -- the magic 40 and it formula come from Dohyun Kim but we might need another guess
- local parameters = tfmdata.parameters
- local italicangle = parameters.italicangle
- if italicangle and italicangle ~= 0 then
- local properties = tfmdata.properties
- local factor = tonumber(value) or 1
- properties.hasitalics = true
- properties.autoitalicamount = factor * (parameters.uwidth or 40)/2
- end
- end
-end
-
-registerotffeature {
- name = "itlc",
- description = "italic correction",
- initializers = {
- base = initializeitlc,
- node = initializeitlc,
- }
-}
-
-registerafmfeature {
- name = "itlc",
- description = "italic correction",
- initializers = {
- base = initializeitlc,
- node = initializeitlc,
- }
-}
-
-local function initializetextitalics(tfmdata,value) -- yes no delay
- local delay = value == "delay"
- tfmdata.properties.textitalics = delay and true or value
- tfmdata.properties.delaytextitalics = delay
-end
-
-registerotffeature {
- name = "textitalics",
- description = "use alternative text italic correction",
- initializers = {
- base = initializetextitalics,
- node = initializetextitalics,
- }
-}
-
-registerafmfeature {
- name = "textitalics",
- description = "use alternative text italic correction",
- initializers = {
- base = initializetextitalics,
- node = initializetextitalics,
- }
-}
-
--- slanting
-
-local function initializeslant(tfmdata,value)
- value = tonumber(value)
- if not value then
- value = 0
- elseif value > 1 then
- value = 1
- elseif value < -1 then
- value = -1
- end
- tfmdata.parameters.slantfactor = value
-end
-
-registerotffeature {
- name = "slant",
- description = "slant glyphs",
- initializers = {
- base = initializeslant,
- node = initializeslant,
- }
-}
-
-registerafmfeature {
- name = "slant",
- description = "slant glyphs",
- initializers = {
- base = initializeslant,
- node = initializeslant,
- }
-}
-
-local function initializeextend(tfmdata,value)
- value = tonumber(value)
- if not value then
- value = 0
- elseif value > 10 then
- value = 10
- elseif value < -10 then
- value = -10
- end
- tfmdata.parameters.extendfactor = value
-end
-
-registerotffeature {
- name = "extend",
- description = "scale glyphs horizontally",
- initializers = {
- base = initializeextend,
- node = initializeextend,
- }
-}
-
-registerafmfeature {
- name = "extend",
- description = "scale glyphs horizontally",
- initializers = {
- base = initializeextend,
- node = initializeextend,
- }
-}
-
--- For Wolfgang Schuster:
---
--- \definefontfeature[thisway][default][script=hang,language=zhs,dimensions={2,2,2}]
--- \definedfont[file:kozminpr6nregular*thisway]
---
--- For the moment we don't mess with the descriptions.
-
-local function manipulatedimensions(tfmdata,key,value)
- if type(value) == "string" and value ~= "" then
- local characters = tfmdata.characters
- local parameters = tfmdata.parameters
- local emwidth = parameters.quad
- local exheight = parameters.xheight
- local spec = settings_to_array(value)
- local width = (spec[1] or 0) * emwidth
- local height = (spec[2] or 0) * exheight
- local depth = (spec[3] or 0) * exheight
- if width > 0 then
- local resources = tfmdata.resources
- local additions = { }
- local private = resources.private
- for unicode, old_c in next, characters do
- local oldwidth = old_c.width
- if oldwidth ~= width then
- -- Defining the tables in one step is more efficient
- -- than adding fields later.
- private = private + 1
- local new_c
- local commands = {
- { "right", (width - oldwidth) / 2 },
- { "slot", 1, private },
- }
- if height > 0 then
- if depth > 0 then
- new_c = {
- width = width,
- height = height,
- depth = depth,
- commands = commands,
- }
- else
- new_c = {
- width = width,
- height = height,
- commands = commands,
- }
- end
- else
- if depth > 0 then
- new_c = {
- width = width,
- depth = depth,
- commands = commands,
- }
- else
- new_c = {
- width = width,
- commands = commands,
- }
- end
- end
- setmetatableindex(new_c,old_c)
- characters[unicode] = new_c
- additions[private] = old_c
- end
- end
- for k, v in next, additions do
- characters[k] = v
- end
- resources.private = private
- elseif height > 0 and depth > 0 then
- for unicode, old_c in next, characters do
- old_c.height = height
- old_c.depth = depth
- end
- elseif height > 0 then
- for unicode, old_c in next, characters do
- old_c.height = height
- end
- elseif depth > 0 then
- for unicode, old_c in next, characters do
- old_c.depth = depth
- end
- end
- end
-end
-
-registerotffeature {
- name = "dimensions",
- description = "force dimensions",
- manipulators = {
- base = manipulatedimensions,
- node = manipulatedimensions,
- }
-}
-
--- for zhichu chen (see mailing list archive): we might add a few more variants
--- in due time
---
--- \definefontfeature[boxed][default][boundingbox=yes] % paleblue
---
--- maybe:
---
--- \definecolor[DummyColor][s=.75,t=.5,a=1] {\DummyColor test} \nopdfcompression
---
--- local gray = { "special", "pdf: /Tr1 gs .75 g" }
--- local black = { "special", "pdf: /Tr0 gs 0 g" }
-
-local push = { "push" }
-local pop = { "pop" }
-local gray = { "special", "pdf: .75 g" }
-local black = { "special", "pdf: 0 g" }
-
-local downcache = { } -- handy for huge cjk fonts
-local rulecache = { } -- handy for huge cjk fonts
-
-setmetatableindex(downcache,function(t,d)
- local v = { "down", d }
- t[d] = v
- return v
-end)
-
-setmetatableindex(rulecache,function(t,h)
- local v = { }
- t[h] = v
- setmetatableindex(v,function(t,w)
- local v = { "rule", h, w }
- t[w] = v
- return v
- end)
- return v
-end)
-
-local function showboundingbox(tfmdata,key,value)
- if value then
- local vfspecials = backends.pdf.tables.vfspecials
- local gray = vfspecials and (vfspecials.rulecolors[value] or vfspecials.rulecolors.palegray) or gray
- local characters = tfmdata.characters
- local resources = tfmdata.resources
- local additions = { }
- local private = resources.private
- for unicode, old_c in next, characters do
- private = private + 1
- local width = old_c.width or 0
- local height = old_c.height or 0
- local depth = old_c.depth or 0
- local new_c
- if depth == 0 then
- new_c = {
- width = width,
- height = height,
- commands = {
- push,
- gray,
- rulecache[height][width],
- black,
- pop,
- { "slot", 1, private },
- }
- }
- else
- new_c = {
- width = width,
- height = height,
- depth = depth,
- commands = {
- push,
- downcache[depth],
- gray,
- rulecache[height+depth][width],
- black,
- pop,
- { "slot", 1, private },
- }
- }
- end
- setmetatableindex(new_c,old_c)
- characters[unicode] = new_c
- additions[private] = old_c
- end
- for k, v in next, additions do
- characters[k] = v
- end
- resources.private = private
- end
-end
-
-registerotffeature {
- name = "boundingbox",
- description = "show boundingbox",
- manipulators = {
- base = showboundingbox,
- node = showboundingbox,
- }
-}
-
--- -- historic stuff, move from font-ota (handled differently, typo-rep)
---
--- local delete_node = nodes.delete
--- local fontdata = fonts.hashes.identifiers
---
--- local nodecodes = nodes.nodecodes
--- local glyph_code = nodecodes.glyph
---
--- local strippables = allocate()
--- fonts.strippables = strippables
---
--- strippables.joiners = table.tohash {
--- 0x200C, -- zwnj
--- 0x200D, -- zwj
--- }
---
--- strippables.all = table.tohash {
--- 0x000AD, 0x017B4, 0x017B5, 0x0200B, 0x0200C, 0x0200D, 0x0200E, 0x0200F, 0x0202A, 0x0202B,
--- 0x0202C, 0x0202D, 0x0202E, 0x02060, 0x02061, 0x02062, 0x02063, 0x0206A, 0x0206B, 0x0206C,
--- 0x0206D, 0x0206E, 0x0206F, 0x0FEFF, 0x1D173, 0x1D174, 0x1D175, 0x1D176, 0x1D177, 0x1D178,
--- 0x1D179, 0x1D17A, 0xE0001, 0xE0020, 0xE0021, 0xE0022, 0xE0023, 0xE0024, 0xE0025, 0xE0026,
--- 0xE0027, 0xE0028, 0xE0029, 0xE002A, 0xE002B, 0xE002C, 0xE002D, 0xE002E, 0xE002F, 0xE0030,
--- 0xE0031, 0xE0032, 0xE0033, 0xE0034, 0xE0035, 0xE0036, 0xE0037, 0xE0038, 0xE0039, 0xE003A,
--- 0xE003B, 0xE003C, 0xE003D, 0xE003E, 0xE003F, 0xE0040, 0xE0041, 0xE0042, 0xE0043, 0xE0044,
--- 0xE0045, 0xE0046, 0xE0047, 0xE0048, 0xE0049, 0xE004A, 0xE004B, 0xE004C, 0xE004D, 0xE004E,
--- 0xE004F, 0xE0050, 0xE0051, 0xE0052, 0xE0053, 0xE0054, 0xE0055, 0xE0056, 0xE0057, 0xE0058,
--- 0xE0059, 0xE005A, 0xE005B, 0xE005C, 0xE005D, 0xE005E, 0xE005F, 0xE0060, 0xE0061, 0xE0062,
--- 0xE0063, 0xE0064, 0xE0065, 0xE0066, 0xE0067, 0xE0068, 0xE0069, 0xE006A, 0xE006B, 0xE006C,
--- 0xE006D, 0xE006E, 0xE006F, 0xE0070, 0xE0071, 0xE0072, 0xE0073, 0xE0074, 0xE0075, 0xE0076,
--- 0xE0077, 0xE0078, 0xE0079, 0xE007A, 0xE007B, 0xE007C, 0xE007D, 0xE007E, 0xE007F,
--- }
---
--- strippables[true] = strippables.joiners
---
--- local function processformatters(head,font)
--- local subset = fontdata[font].shared.features.formatters
--- local vector = subset and strippables[subset]
--- if vector then
--- local current, done = head, false
--- while current do
--- if current.id == glyph_code and current.subtype<256 and current.font == font then
--- local char = current.char
--- if vector[char] then
--- head, current = delete_node(head,current)
--- done = true
--- else
--- current = current.next
--- end
--- else
--- current = current.next
--- end
--- end
--- return head, done
--- else
--- return head, false
--- end
--- end
---
--- registerotffeature {
--- name = "formatters",
--- description = "hide formatting characters",
--- methods = {
--- base = processformatters,
--- node = processformatters,
--- }
--- }
-
--- a handy helper (might change or be moved to another namespace)
-
-local new_special = nodes.pool.special
-local new_glyph = nodes.pool.glyph
-local hpack_node = node.hpack
-
-function fonts.helpers.addprivate(tfmdata,name,characterdata)
- local properties = tfmdata.properties
- local privates = properties.privates
- local lastprivate = properties.lastprivate
- if lastprivate then
- lastprivate = lastprivate + 1
- else
- lastprivate = 0xE000
- end
- if not privates then
- privates = { }
- properties.privates = privates
- end
- if name then
- privates[name] = lastprivate
- end
- properties.lastprivate = lastprivate
- tfmdata.characters[lastprivate] = characterdata
- if properties.finalized then
- properties.lateprivates = true
- end
- return lastprivate
-end
-
-function fonts.helpers.getprivatenode(tfmdata,name)
- local properties = tfmdata.properties
- local privates = properties and properties.privates
- if privates then
- local p = privates[name]
- if p then
- local char = tfmdata.characters[p]
- local commands = char.commands
- if commands then
- local fake = hpack_node(new_special(commands[1][2]))
- fake.width = char.width
- fake.height = char.height
- fake.depth = char.depth
- return fake
- else
- -- todo: set current attribibutes
- return new_glyph(properties.id,p)
- end
- end
- end
-end
-
-function fonts.helpers.hasprivate(tfmdata,name)
- local properties = tfmdata.properties
- local privates = properties and properties.privates
- return privates and privates[name] or false
-end
-
-function commands.getprivatechar(name)
- context(fonts.helpers.getprivatenode(fontdata[font.current()],name))
-end
+if not modules then modules = { } end modules ['font-ext'] = {
+ 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, byte = next, type, string.byte
+local gmatch, concat, format = string.gmatch, table.concat, string.format
+local utfchar = utf.char
+
+local commands, context = commands, context
+local fonts, utilities = fonts, utilities
+
+local trace_protrusion = false trackers.register("fonts.protrusion", function(v) trace_protrusion = v end)
+local trace_expansion = false trackers.register("fonts.expansion", function(v) trace_expansion = v end)
+
+local report_expansions = logs.reporter("fonts","expansions")
+local report_protrusions = logs.reporter("fonts","protrusions")
+
+-- todo: byte(..) => 0xHHHH
+
+--[[ldx--
+<p>When we implement functions that deal with features, most of them
+will depend of the font format. Here we define the few that are kind
+of neutral.</p>
+--ldx]]--
+
+local handlers = fonts.handlers
+local hashes = fonts.hashes
+local otf = handlers.otf
+
+local registerotffeature = handlers.otf.features.register
+local registerafmfeature = handlers.afm.features.register
+
+local fontdata = hashes.identifiers
+
+local allocate = utilities.storage.allocate
+local settings_to_array = utilities.parsers.settings_to_array
+local getparameters = utilities.parsers.getparameters
+
+local setmetatableindex = table.setmetatableindex
+
+-- -- -- -- -- --
+-- shared
+-- -- -- -- -- --
+
+local function get_class_and_vector(tfmdata,value,where) -- "expansions"
+ local g_where = tfmdata.goodies and tfmdata.goodies[where]
+ local f_where = fonts[where]
+ local g_classes = g_where and g_where.classes
+ local f_classes = f_where and f_where.classes
+ local class = (g_classes and g_classes[value]) or (f_classes and f_classes[value])
+ if class then
+ local class_vector = class.vector
+ local g_vectors = g_where and g_where.vectors
+ local f_vectors = f_where and f_where.vectors
+ local vector = (g_vectors and g_vectors[class_vector]) or (f_vectors and f_vectors[class_vector])
+ return class, vector
+ end
+end
+
+-- -- -- -- -- --
+-- expansion (hz)
+-- -- -- -- -- --
+
+local expansions = fonts.expansions or allocate()
+
+fonts.expansions = expansions
+
+local classes = expansions.classes or allocate()
+local vectors = expansions.vectors or allocate()
+
+expansions.classes = classes
+expansions.vectors = vectors
+
+-- beware, pdftex itself uses percentages * 10
+
+classes.preset = { stretch = 2, shrink = 2, step = .5, factor = 1 }
+
+function commands.setupfontexpansion(class,settings)
+ getparameters(classes,class,'preset',settings)
+end
+
+classes['quality'] = {
+ stretch = 2, shrink = 2, step = .5, vector = 'default', factor = 1
+}
+
+vectors['default'] = {
+ [byte('A')] = 0.5, [byte('B')] = 0.7, [byte('C')] = 0.7, [byte('D')] = 0.5, [byte('E')] = 0.7,
+ [byte('F')] = 0.7, [byte('G')] = 0.5, [byte('H')] = 0.7, [byte('K')] = 0.7, [byte('M')] = 0.7,
+ [byte('N')] = 0.7, [byte('O')] = 0.5, [byte('P')] = 0.7, [byte('Q')] = 0.5, [byte('R')] = 0.7,
+ [byte('S')] = 0.7, [byte('U')] = 0.7, [byte('W')] = 0.7, [byte('Z')] = 0.7,
+ [byte('a')] = 0.7, [byte('b')] = 0.7, [byte('c')] = 0.7, [byte('d')] = 0.7, [byte('e')] = 0.7,
+ [byte('g')] = 0.7, [byte('h')] = 0.7, [byte('k')] = 0.7, [byte('m')] = 0.7, [byte('n')] = 0.7,
+ [byte('o')] = 0.7, [byte('p')] = 0.7, [byte('q')] = 0.7, [byte('s')] = 0.7, [byte('u')] = 0.7,
+ [byte('w')] = 0.7, [byte('z')] = 0.7,
+ [byte('2')] = 0.7, [byte('3')] = 0.7, [byte('6')] = 0.7, [byte('8')] = 0.7, [byte('9')] = 0.7,
+}
+
+vectors['quality'] = vectors['default'] -- metatable ?
+
+local function initializeexpansion(tfmdata,value)
+ if value then
+ local class, vector = get_class_and_vector(tfmdata,value,"expansions")
+ if class then
+ if vector then
+ local stretch = class.stretch or 0
+ local shrink = class.shrink or 0
+ local step = class.step or 0
+ local factor = class.factor or 1
+ if trace_expansion then
+ report_expansions("setting class %a, vector %a, factor %a, stretch %a, shrink %a, step %a",
+ value,class.vector,factor,stretch,shrink,step)
+ end
+ tfmdata.parameters.expansion = {
+ stretch = 10 * stretch,
+ shrink = 10 * shrink,
+ step = 10 * step,
+ factor = factor,
+ auto = true,
+ }
+ local data = characters and characters.data
+ for i, chr in next, tfmdata.characters do
+ local v = vector[i]
+ if data and not v then -- we could move the data test outside (needed for plain)
+ local d = data[i]
+ if d then
+ local s = d.shcode
+ if not s then
+ -- sorry
+ elseif type(s) == "table" then
+ v = ((vector[s[1]] or 0) + (vector[s[#s]] or 0)) / 2
+ else
+ v = vector[s] or 0
+ end
+ end
+ end
+ if v and v ~= 0 then
+ chr.expansion_factor = v*factor
+ else -- can be option
+ chr.expansion_factor = factor
+ end
+ end
+ elseif trace_expansion then
+ report_expansions("unknown vector %a in class %a",class.vector,value)
+ end
+ elseif trace_expansion then
+ report_expansions("unknown class %a",value)
+ end
+ end
+end
+
+registerotffeature {
+ name = "expansion",
+ description = "apply hz optimization",
+ initializers = {
+ base = initializeexpansion,
+ node = initializeexpansion,
+ }
+}
+
+registerafmfeature {
+ name = "expansion",
+ description = "apply hz optimization",
+ initializers = {
+ base = initializeexpansion,
+ node = initializeexpansion,
+ }
+}
+
+fonts.goodies.register("expansions", function(...) return fonts.goodies.report("expansions", trace_expansion, ...) end)
+
+local report_opbd = logs.reporter("fonts","otf opbd")
+
+-- -- -- -- -- --
+-- protrusion
+-- -- -- -- -- --
+
+fonts.protrusions = allocate()
+local protrusions = fonts.protrusions
+
+protrusions.classes = allocate()
+protrusions.vectors = allocate()
+
+local classes = protrusions.classes
+local vectors = protrusions.vectors
+
+-- the values need to be revisioned
+
+classes.preset = { factor = 1, left = 1, right = 1 }
+
+function commands.setupfontprotrusion(class,settings)
+ getparameters(classes,class,'preset',settings)
+end
+
+classes['pure'] = {
+ vector = 'pure', factor = 1
+}
+classes['punctuation'] = {
+ vector = 'punctuation', factor = 1
+}
+classes['alpha'] = {
+ vector = 'alpha', factor = 1
+}
+classes['quality'] = {
+ vector = 'quality', factor = 1
+}
+
+vectors['pure'] = {
+
+ [0x002C] = { 0, 1 }, -- comma
+ [0x002E] = { 0, 1 }, -- period
+ [0x003A] = { 0, 1 }, -- colon
+ [0x003B] = { 0, 1 }, -- semicolon
+ [0x002D] = { 0, 1 }, -- hyphen
+ [0x00AD] = { 0, 1 }, -- also hyphen
+ [0x2013] = { 0, 0.50 }, -- endash
+ [0x2014] = { 0, 0.33 }, -- emdash
+ [0x3001] = { 0, 1 }, -- ideographic comma 、
+ [0x3002] = { 0, 1 }, -- ideographic full stop 。
+ [0x060C] = { 0, 1 }, -- arabic comma ،
+ [0x061B] = { 0, 1 }, -- arabic semicolon ؛
+ [0x06D4] = { 0, 1 }, -- arabic full stop ۔
+
+}
+
+vectors['punctuation'] = {
+
+ [0x003F] = { 0, 0.20 }, -- ?
+ [0x00BF] = { 0, 0.20 }, -- ¿
+ [0x0021] = { 0, 0.20 }, -- !
+ [0x00A1] = { 0, 0.20 }, -- ¡
+ [0x0028] = { 0.05, 0 }, -- (
+ [0x0029] = { 0, 0.05 }, -- )
+ [0x005B] = { 0.05, 0 }, -- [
+ [0x005D] = { 0, 0.05 }, -- ]
+ [0x002C] = { 0, 0.70 }, -- comma
+ [0x002E] = { 0, 0.70 }, -- period
+ [0x003A] = { 0, 0.50 }, -- colon
+ [0x003B] = { 0, 0.50 }, -- semicolon
+ [0x002D] = { 0, 0.70 }, -- hyphen
+ [0x00AD] = { 0, 0.70 }, -- also hyphen
+ [0x2013] = { 0, 0.30 }, -- endash
+ [0x2014] = { 0, 0.20 }, -- emdash
+ [0x060C] = { 0, 0.70 }, -- arabic comma
+ [0x061B] = { 0, 0.50 }, -- arabic semicolon
+ [0x06D4] = { 0, 0.70 }, -- arabic full stop
+ [0x061F] = { 0, 0.20 }, -- ؟
+
+ -- todo: left and right quotes: .5 double, .7 single
+
+ [0x2039] = { 0.70, 0.70 }, -- left single guillemet ‹
+ [0x203A] = { 0.70, 0.70 }, -- right single guillemet ›
+ [0x00AB] = { 0.50, 0.50 }, -- left guillemet «
+ [0x00BB] = { 0.50, 0.50 }, -- right guillemet »
+
+ [0x2018] = { 0.70, 0.70 }, -- left single quotation mark ‘
+ [0x2019] = { 0, 0.70 }, -- right single quotation mark ’
+ [0x201A] = { 0.70, 0 }, -- single low-9 quotation mark ,
+ [0x201B] = { 0.70, 0 }, -- single high-reversed-9 quotation mark ‛
+ [0x201C] = { 0.50, 0.50 }, -- left double quotation mark “
+ [0x201D] = { 0, 0.50 }, -- right double quotation mark ”
+ [0x201E] = { 0.50, 0 }, -- double low-9 quotation mark „
+ [0x201F] = { 0.50, 0 }, -- double high-reversed-9 quotation mark ‟
+
+}
+
+vectors['alpha'] = {
+
+ [byte("A")] = { .05, .05 },
+ [byte("F")] = { 0, .05 },
+ [byte("J")] = { .05, 0 },
+ [byte("K")] = { 0, .05 },
+ [byte("L")] = { 0, .05 },
+ [byte("T")] = { .05, .05 },
+ [byte("V")] = { .05, .05 },
+ [byte("W")] = { .05, .05 },
+ [byte("X")] = { .05, .05 },
+ [byte("Y")] = { .05, .05 },
+
+ [byte("k")] = { 0, .05 },
+ [byte("r")] = { 0, .05 },
+ [byte("t")] = { 0, .05 },
+ [byte("v")] = { .05, .05 },
+ [byte("w")] = { .05, .05 },
+ [byte("x")] = { .05, .05 },
+ [byte("y")] = { .05, .05 },
+
+}
+
+vectors['quality'] = table.merged(
+ vectors['punctuation'],
+ vectors['alpha']
+)
+
+-- As this is experimental code, users should not depend on it. The
+-- implications are still discussed on the ConTeXt Dev List and we're
+-- not sure yet what exactly the spec is (the next code is tested with
+-- a gyre font patched by / fea file made by Khaled Hosny). The double
+-- trick should not be needed it proper hanging punctuation is used in
+-- which case values < 1 can be used.
+--
+-- preferred (in context, usine vectors):
+--
+-- \definefontfeature[whatever][default][mode=node,protrusion=quality]
+--
+-- using lfbd and rtbd, with possibibility to enable only one side :
+--
+-- \definefontfeature[whocares][default][mode=node,protrusion=yes, opbd=yes,script=latn]
+-- \definefontfeature[whocares][default][mode=node,protrusion=right,opbd=yes,script=latn]
+--
+-- idem, using multiplier
+--
+-- \definefontfeature[whocares][default][mode=node,protrusion=2,opbd=yes,script=latn]
+-- \definefontfeature[whocares][default][mode=node,protrusion=double,opbd=yes,script=latn]
+--
+-- idem, using named feature file (less frozen):
+--
+-- \definefontfeature[whocares][default][mode=node,protrusion=2,opbd=yes,script=latn,featurefile=texgyrepagella-regularxx.fea]
+
+classes['double'] = { -- for testing opbd
+ factor = 2, left = 1, right = 1,
+}
+
+local function map_opbd_onto_protrusion(tfmdata,value,opbd)
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local properties = tfmdata.properties
+ local rawdata = tfmdata.shared.rawdata
+ local lookuphash = rawdata.lookuphash
+ local script = properties.script
+ local language = properties.language
+ local done, factor, left, right = false, 1, 1, 1
+ local class = classes[value]
+ if class then
+ factor = class.factor or 1
+ left = class.left or 1
+ right = class.right or 1
+ else
+ factor = tonumber(value) or 1
+ end
+ if opbd ~= "right" then
+ local validlookups, lookuplist = otf.collectlookups(rawdata,"lfbd",script,language)
+ if validlookups then
+ for i=1,#lookuplist do
+ local lookup = lookuplist[i]
+ local data = lookuphash[lookup]
+ if data then
+ if trace_protrusion then
+ report_protrusions("setting left using lfbd lookup %a",lookup)
+ end
+ for k, v in next, data do
+ -- local p = - v[3] / descriptions[k].width-- or 1 ~= 0 too but the same
+ local p = - (v[1] / 1000) * factor * left
+ characters[k].left_protruding = p
+ if trace_protrusion then
+ report_protrusions("lfbd -> %s -> %C -> %0.03f (% t)",lookup,k,p,v)
+ end
+ end
+ done = true
+ end
+ end
+ end
+ end
+ if opbd ~= "left" then
+ local validlookups, lookuplist = otf.collectlookups(rawdata,"rtbd",script,language)
+ if validlookups then
+ for i=1,#lookuplist do
+ local lookup = lookuplist[i]
+ local data = lookuphash[lookup]
+ if data then
+ if trace_protrusion then
+ report_protrusions("setting right using rtbd lookup %a",lookup)
+ end
+ for k, v in next, data do
+ -- local p = v[3] / descriptions[k].width -- or 3
+ local p = (v[1] / 1000) * factor * right
+ characters[k].right_protruding = p
+ if trace_protrusion then
+ report_protrusions("rtbd -> %s -> %C -> %0.03f (% t)",lookup,k,p,v)
+ end
+ end
+ end
+ done = true
+ end
+ end
+ end
+ local parameters = tfmdata.parameters
+ local protrusion = tfmdata.protrusion
+ if not protrusion then
+ parameters.protrusion = {
+ auto = true
+ }
+ else
+ protrusion.auto = true
+ end
+end
+
+-- The opbd test is just there because it was discussed on the
+-- context development list. However, the mentioned fxlbi.otf font
+-- only has some kerns for digits. So, consider this feature not
+-- supported till we have a proper test font.
+
+local function initializeprotrusion(tfmdata,value)
+ if value then
+ local opbd = tfmdata.shared.features.opbd
+ if opbd then
+ -- possible values: left right both yes no (experimental)
+ map_opbd_onto_protrusion(tfmdata,value,opbd)
+ else
+ local class, vector = get_class_and_vector(tfmdata,value,"protrusions")
+ if class then
+ if vector then
+ local factor = class.factor or 1
+ local left = class.left or 1
+ local right = class.right or 1
+ if trace_protrusion then
+ report_protrusions("setting class %a, vector %a, factor %a, left %a, right %a",
+ value,class.vector,factor,left,right)
+ end
+ local data = characters.data
+ local emwidth = tfmdata.parameters.quad
+ tfmdata.parameters.protrusion = {
+ factor = factor,
+ left = left,
+ right = right,
+ auto = true,
+ }
+ for i, chr in next, tfmdata.characters do
+ local v, pl, pr = vector[i], nil, nil
+ if v then
+ pl, pr = v[1], v[2]
+ else
+ local d = data[i]
+ if d then
+ local s = d.shcode
+ if not s then
+ -- sorry
+ elseif type(s) == "table" then
+ local vl, vr = vector[s[1]], vector[s[#s]]
+ if vl then pl = vl[1] end
+ if vr then pr = vr[2] end
+ else
+ v = vector[s]
+ if v then
+ pl, pr = v[1], v[2]
+ end
+ end
+ end
+ end
+ if pl and pl ~= 0 then
+ chr.left_protruding = left *pl*factor
+ end
+ if pr and pr ~= 0 then
+ chr.right_protruding = right*pr*factor
+ end
+ end
+ elseif trace_protrusion then
+ report_protrusions("unknown vector %a in class %a",class.vector,value)
+ end
+ elseif trace_protrusion then
+ report_protrusions("unknown class %a",value)
+ end
+ end
+ end
+end
+
+registerotffeature {
+ name = "protrusion",
+ description = "shift characters into the left and or right margin",
+ initializers = {
+ base = initializeprotrusion,
+ node = initializeprotrusion,
+ }
+}
+
+registerafmfeature {
+ name = "protrusion",
+ description = "shift characters into the left and or right margin",
+ initializers = {
+ base = initializeprotrusion,
+ node = initializeprotrusion,
+ }
+}
+
+fonts.goodies.register("protrusions", function(...) return fonts.goodies.report("protrusions", trace_protrusion, ...) end)
+
+-- -- --
+
+local function initializenostackmath(tfmdata,value)
+ tfmdata.properties.nostackmath = value and true
+end
+
+registerotffeature {
+ name = "nostackmath",
+ description = "disable math stacking mechanism",
+ initializers = {
+ base = initializenostackmath,
+ node = initializenostackmath,
+ }
+}
+
+local function initializeitlc(tfmdata,value) -- hm, always value
+ if value then
+ -- the magic 40 and it formula come from Dohyun Kim but we might need another guess
+ local parameters = tfmdata.parameters
+ local italicangle = parameters.italicangle
+ if italicangle and italicangle ~= 0 then
+ local properties = tfmdata.properties
+ local factor = tonumber(value) or 1
+ properties.hasitalics = true
+ properties.autoitalicamount = factor * (parameters.uwidth or 40)/2
+ end
+ end
+end
+
+registerotffeature {
+ name = "itlc",
+ description = "italic correction",
+ initializers = {
+ base = initializeitlc,
+ node = initializeitlc,
+ }
+}
+
+registerafmfeature {
+ name = "itlc",
+ description = "italic correction",
+ initializers = {
+ base = initializeitlc,
+ node = initializeitlc,
+ }
+}
+
+local function initializetextitalics(tfmdata,value) -- yes no delay
+ local delay = value == "delay"
+ tfmdata.properties.textitalics = delay and true or value
+ tfmdata.properties.delaytextitalics = delay
+end
+
+registerotffeature {
+ name = "textitalics",
+ description = "use alternative text italic correction",
+ initializers = {
+ base = initializetextitalics,
+ node = initializetextitalics,
+ }
+}
+
+registerafmfeature {
+ name = "textitalics",
+ description = "use alternative text italic correction",
+ initializers = {
+ base = initializetextitalics,
+ node = initializetextitalics,
+ }
+}
+
+-- slanting
+
+local function initializeslant(tfmdata,value)
+ value = tonumber(value)
+ if not value then
+ value = 0
+ elseif value > 1 then
+ value = 1
+ elseif value < -1 then
+ value = -1
+ end
+ tfmdata.parameters.slantfactor = value
+end
+
+registerotffeature {
+ name = "slant",
+ description = "slant glyphs",
+ initializers = {
+ base = initializeslant,
+ node = initializeslant,
+ }
+}
+
+registerafmfeature {
+ name = "slant",
+ description = "slant glyphs",
+ initializers = {
+ base = initializeslant,
+ node = initializeslant,
+ }
+}
+
+local function initializeextend(tfmdata,value)
+ value = tonumber(value)
+ if not value then
+ value = 0
+ elseif value > 10 then
+ value = 10
+ elseif value < -10 then
+ value = -10
+ end
+ tfmdata.parameters.extendfactor = value
+end
+
+registerotffeature {
+ name = "extend",
+ description = "scale glyphs horizontally",
+ initializers = {
+ base = initializeextend,
+ node = initializeextend,
+ }
+}
+
+registerafmfeature {
+ name = "extend",
+ description = "scale glyphs horizontally",
+ initializers = {
+ base = initializeextend,
+ node = initializeextend,
+ }
+}
+
+-- For Wolfgang Schuster:
+--
+-- \definefontfeature[thisway][default][script=hang,language=zhs,dimensions={2,2,2}]
+-- \definedfont[file:kozminpr6nregular*thisway]
+--
+-- For the moment we don't mess with the descriptions.
+
+local function manipulatedimensions(tfmdata,key,value)
+ if type(value) == "string" and value ~= "" then
+ local characters = tfmdata.characters
+ local parameters = tfmdata.parameters
+ local emwidth = parameters.quad
+ local exheight = parameters.xheight
+ local spec = settings_to_array(value)
+ local width = (spec[1] or 0) * emwidth
+ local height = (spec[2] or 0) * exheight
+ local depth = (spec[3] or 0) * exheight
+ if width > 0 then
+ local resources = tfmdata.resources
+ local additions = { }
+ local private = resources.private
+ for unicode, old_c in next, characters do
+ local oldwidth = old_c.width
+ if oldwidth ~= width then
+ -- Defining the tables in one step is more efficient
+ -- than adding fields later.
+ private = private + 1
+ local new_c
+ local commands = {
+ { "right", (width - oldwidth) / 2 },
+ { "slot", 1, private },
+ }
+ if height > 0 then
+ if depth > 0 then
+ new_c = {
+ width = width,
+ height = height,
+ depth = depth,
+ commands = commands,
+ }
+ else
+ new_c = {
+ width = width,
+ height = height,
+ commands = commands,
+ }
+ end
+ else
+ if depth > 0 then
+ new_c = {
+ width = width,
+ depth = depth,
+ commands = commands,
+ }
+ else
+ new_c = {
+ width = width,
+ commands = commands,
+ }
+ end
+ end
+ setmetatableindex(new_c,old_c)
+ characters[unicode] = new_c
+ additions[private] = old_c
+ end
+ end
+ for k, v in next, additions do
+ characters[k] = v
+ end
+ resources.private = private
+ elseif height > 0 and depth > 0 then
+ for unicode, old_c in next, characters do
+ old_c.height = height
+ old_c.depth = depth
+ end
+ elseif height > 0 then
+ for unicode, old_c in next, characters do
+ old_c.height = height
+ end
+ elseif depth > 0 then
+ for unicode, old_c in next, characters do
+ old_c.depth = depth
+ end
+ end
+ end
+end
+
+registerotffeature {
+ name = "dimensions",
+ description = "force dimensions",
+ manipulators = {
+ base = manipulatedimensions,
+ node = manipulatedimensions,
+ }
+}
+
+-- for zhichu chen (see mailing list archive): we might add a few more variants
+-- in due time
+--
+-- \definefontfeature[boxed][default][boundingbox=yes] % paleblue
+--
+-- maybe:
+--
+-- \definecolor[DummyColor][s=.75,t=.5,a=1] {\DummyColor test} \nopdfcompression
+--
+-- local gray = { "special", "pdf: /Tr1 gs .75 g" }
+-- local black = { "special", "pdf: /Tr0 gs 0 g" }
+
+local push = { "push" }
+local pop = { "pop" }
+local gray = { "special", "pdf: .75 g" }
+local black = { "special", "pdf: 0 g" }
+
+local downcache = { } -- handy for huge cjk fonts
+local rulecache = { } -- handy for huge cjk fonts
+
+setmetatableindex(downcache,function(t,d)
+ local v = { "down", d }
+ t[d] = v
+ return v
+end)
+
+setmetatableindex(rulecache,function(t,h)
+ local v = { }
+ t[h] = v
+ setmetatableindex(v,function(t,w)
+ local v = { "rule", h, w }
+ t[w] = v
+ return v
+ end)
+ return v
+end)
+
+local function showboundingbox(tfmdata,key,value)
+ if value then
+ local vfspecials = backends.pdf.tables.vfspecials
+ local gray = vfspecials and (vfspecials.rulecolors[value] or vfspecials.rulecolors.palegray) or gray
+ local characters = tfmdata.characters
+ local resources = tfmdata.resources
+ local additions = { }
+ local private = resources.private
+ for unicode, old_c in next, characters do
+ private = private + 1
+ local width = old_c.width or 0
+ local height = old_c.height or 0
+ local depth = old_c.depth or 0
+ local new_c
+ if depth == 0 then
+ new_c = {
+ width = width,
+ height = height,
+ commands = {
+ push,
+ gray,
+ rulecache[height][width],
+ black,
+ pop,
+ { "slot", 1, private },
+ }
+ }
+ else
+ new_c = {
+ width = width,
+ height = height,
+ depth = depth,
+ commands = {
+ push,
+ downcache[depth],
+ gray,
+ rulecache[height+depth][width],
+ black,
+ pop,
+ { "slot", 1, private },
+ }
+ }
+ end
+ setmetatableindex(new_c,old_c)
+ characters[unicode] = new_c
+ additions[private] = old_c
+ end
+ for k, v in next, additions do
+ characters[k] = v
+ end
+ resources.private = private
+ end
+end
+
+registerotffeature {
+ name = "boundingbox",
+ description = "show boundingbox",
+ manipulators = {
+ base = showboundingbox,
+ node = showboundingbox,
+ }
+}
+
+-- -- historic stuff, move from font-ota (handled differently, typo-rep)
+--
+-- local delete_node = nodes.delete
+-- local fontdata = fonts.hashes.identifiers
+--
+-- local nodecodes = nodes.nodecodes
+-- local glyph_code = nodecodes.glyph
+--
+-- local strippables = allocate()
+-- fonts.strippables = strippables
+--
+-- strippables.joiners = table.tohash {
+-- 0x200C, -- zwnj
+-- 0x200D, -- zwj
+-- }
+--
+-- strippables.all = table.tohash {
+-- 0x000AD, 0x017B4, 0x017B5, 0x0200B, 0x0200C, 0x0200D, 0x0200E, 0x0200F, 0x0202A, 0x0202B,
+-- 0x0202C, 0x0202D, 0x0202E, 0x02060, 0x02061, 0x02062, 0x02063, 0x0206A, 0x0206B, 0x0206C,
+-- 0x0206D, 0x0206E, 0x0206F, 0x0FEFF, 0x1D173, 0x1D174, 0x1D175, 0x1D176, 0x1D177, 0x1D178,
+-- 0x1D179, 0x1D17A, 0xE0001, 0xE0020, 0xE0021, 0xE0022, 0xE0023, 0xE0024, 0xE0025, 0xE0026,
+-- 0xE0027, 0xE0028, 0xE0029, 0xE002A, 0xE002B, 0xE002C, 0xE002D, 0xE002E, 0xE002F, 0xE0030,
+-- 0xE0031, 0xE0032, 0xE0033, 0xE0034, 0xE0035, 0xE0036, 0xE0037, 0xE0038, 0xE0039, 0xE003A,
+-- 0xE003B, 0xE003C, 0xE003D, 0xE003E, 0xE003F, 0xE0040, 0xE0041, 0xE0042, 0xE0043, 0xE0044,
+-- 0xE0045, 0xE0046, 0xE0047, 0xE0048, 0xE0049, 0xE004A, 0xE004B, 0xE004C, 0xE004D, 0xE004E,
+-- 0xE004F, 0xE0050, 0xE0051, 0xE0052, 0xE0053, 0xE0054, 0xE0055, 0xE0056, 0xE0057, 0xE0058,
+-- 0xE0059, 0xE005A, 0xE005B, 0xE005C, 0xE005D, 0xE005E, 0xE005F, 0xE0060, 0xE0061, 0xE0062,
+-- 0xE0063, 0xE0064, 0xE0065, 0xE0066, 0xE0067, 0xE0068, 0xE0069, 0xE006A, 0xE006B, 0xE006C,
+-- 0xE006D, 0xE006E, 0xE006F, 0xE0070, 0xE0071, 0xE0072, 0xE0073, 0xE0074, 0xE0075, 0xE0076,
+-- 0xE0077, 0xE0078, 0xE0079, 0xE007A, 0xE007B, 0xE007C, 0xE007D, 0xE007E, 0xE007F,
+-- }
+--
+-- strippables[true] = strippables.joiners
+--
+-- local function processformatters(head,font)
+-- local subset = fontdata[font].shared.features.formatters
+-- local vector = subset and strippables[subset]
+-- if vector then
+-- local current, done = head, false
+-- while current do
+-- if current.id == glyph_code and current.subtype<256 and current.font == font then
+-- local char = current.char
+-- if vector[char] then
+-- head, current = delete_node(head,current)
+-- done = true
+-- else
+-- current = current.next
+-- end
+-- else
+-- current = current.next
+-- end
+-- end
+-- return head, done
+-- else
+-- return head, false
+-- end
+-- end
+--
+-- registerotffeature {
+-- name = "formatters",
+-- description = "hide formatting characters",
+-- methods = {
+-- base = processformatters,
+-- node = processformatters,
+-- }
+-- }
+
+-- a handy helper (might change or be moved to another namespace)
+
+local new_special = nodes.pool.special
+local new_glyph = nodes.pool.glyph
+local hpack_node = node.hpack
+
+function fonts.helpers.addprivate(tfmdata,name,characterdata)
+ local properties = tfmdata.properties
+ local privates = properties.privates
+ local lastprivate = properties.lastprivate
+ if lastprivate then
+ lastprivate = lastprivate + 1
+ else
+ lastprivate = 0xE000
+ end
+ if not privates then
+ privates = { }
+ properties.privates = privates
+ end
+ if name then
+ privates[name] = lastprivate
+ end
+ properties.lastprivate = lastprivate
+ tfmdata.characters[lastprivate] = characterdata
+ if properties.finalized then
+ properties.lateprivates = true
+ end
+ return lastprivate
+end
+
+function fonts.helpers.getprivatenode(tfmdata,name)
+ local properties = tfmdata.properties
+ local privates = properties and properties.privates
+ if privates then
+ local p = privates[name]
+ if p then
+ local char = tfmdata.characters[p]
+ local commands = char.commands
+ if commands then
+ local fake = hpack_node(new_special(commands[1][2]))
+ fake.width = char.width
+ fake.height = char.height
+ fake.depth = char.depth
+ return fake
+ else
+ -- todo: set current attribibutes
+ return new_glyph(properties.id,p)
+ end
+ end
+ end
+end
+
+function fonts.helpers.hasprivate(tfmdata,name)
+ local properties = tfmdata.properties
+ local privates = properties and properties.privates
+ return privates and privates[name] or false
+end
+
+function commands.getprivatechar(name)
+ context(fonts.helpers.getprivatenode(fontdata[font.current()],name))
+end
diff --git a/tex/context/base/font-fbk.lua b/tex/context/base/font-fbk.lua
index 32e5d16de..48e2167e6 100644
--- a/tex/context/base/font-fbk.lua
+++ b/tex/context/base/font-fbk.lua
@@ -1,304 +1,304 @@
-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
-
---[[ldx--
-<p>This is very experimental code!</p>
---ldx]]--
-
-local trace_combining_visualize = false trackers.register("fonts.composing.visualize", function(v) trace_combining_visualize = v end)
-local trace_combining_define = false trackers.register("fonts.composing.define", function(v) trace_combining_define = v end)
-
-trackers.register("fonts.combining", "fonts.composing.define") -- for old times sake (and manuals)
-trackers.register("fonts.combining.all", "fonts.composing.*") -- for old times sake (and manuals)
-
-local report_combining = logs.reporter("fonts","combining")
-
-local force_combining = false -- just for demo purposes (see mk)
-
-local allocate = utilities.storage.allocate
-
-local fonts = fonts
-local handlers = fonts.handlers
-local constructors = fonts.constructors
-
-local registerotffeature = handlers.otf.features.register
-local registerafmfeature = handlers.afm.features.register
-
-local unicodecharacters = characters.data
-local unicodefallbacks = characters.fallbacks
-
-local vf = handlers.vf
-local commands = vf.combiner.commands
-local push = vf.predefined.push
-local pop = vf.predefined.pop
-
-local force_composed = false
-local cache = { } -- we could make these weak
-local fraction = 0.15 -- 30 units for lucida
-
-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 vfspecials = backends.tables.vfspecials --brr
- local red, green, blue, black
- if trace_combining_visualize then
- red = vfspecials.red
- green = vfspecials.green
- blue = vfspecials.blue
- black = vfspecials.black
- end
- local compose = fonts.goodies.getcompositions(tfmdata)
- if compose and trace_combining_visualize then
- report_combining("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 = cache[chr]
- if not chr_t then
- chr_t = {"slot", 1, chr}
- cache[chr] = chr_t
- end
- if charsacc then
- if trace_combining_define then
- report_combining("composed %C, base %C, accent %C",i,chr,acc)
- end
- local acc_t = cache[acc]
- if not acc_t then
- acc_t = {"slot", 1, acc}
- cache[acc] = acc_t
- end
- local cb = descriptions[chr].boundingbox
- local ab = descriptions[acc].boundingbox
- -- todo: adapt height
- if cb and ab then
- local c_llx, c_lly, c_urx, c_ury = scale*cb[1], scale*cb[2], scale*cb[3], scale*cb[4]
- local a_llx, a_lly, a_urx, a_ury = scale*ab[1], scale*ab[2], scale*ab[3], 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_combining_define then
- report_combining("building %C from %C and %C",i,chr,acc)
- report_combining(" boundingbox:")
- report_combining(" chr: %3i %3i %3i %3i",unpack(cb))
- report_combining(" acc: %3i %3i %3i %3i",unpack(ab))
- report_combining(" anchors:")
- report_combining(" chr: %3i %3i",cx,cy)
- report_combining(" acc: %3i %3i",ax,ay)
- report_combining(" delta:")
- report_combining(" %s: %3i %3i",i_anchored,dx,dy)
- end
- if trace_combining_visualize then
- t.commands = { push, {"right", scale*dx}, {"down",-scale*dy}, green, acc_t, black, pop, chr_t }
- -- t.commands = {
- -- push, {"right", scale*cx}, {"down", -scale*cy}, red, {"rule",10000,10000,10000}, pop,
- -- push, {"right", scale*ax}, {"down", -scale*ay}, blue, {"rule",10000,10000,10000}, pop,
- -- push, {"right", scale*dx}, {"down", -scale*dy}, green, acc_t, black, pop, chr_t
- -- }
- else
- t.commands = { push, {"right", scale*dx}, {"down",-scale*dy}, 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
- if trace_combining_visualize then
- t.commands = { push, {"right", dx-dd}, red, acc_t, black, pop, chr_t }
- else
- t.commands = { push, {"right", dx-dd}, acc_t, pop, chr_t }
- end
- 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
- if trace_combining_visualize then
- t.commands = { push, {"right", dx+dd}, {"down", dy}, green, acc_t, black, pop, chr_t }
- else
- t.commands = { push, {"right", dx+dd}, {"down", dy}, acc_t, pop, chr_t }
- end
- else
- if trace_combining_visualize then
- t.commands = { push, {"right", dx+dd}, blue, acc_t, black, pop, chr_t }
- else
- t.commands = { push, {"right", dx+dd}, acc_t, pop, chr_t }
- end
- end
- end
- else
- t.commands = { chr_t } -- else index mess
- end
- else
- if trace_combining_define then
- report_combining("%C becomes simplfied %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
- if done then
- properties.virtualized = true
- end
- end
-end
-
-registerotffeature {
- name = "compose",
- description = "additional composed characters",
- manipulators = {
- base = composecharacters,
- node = composecharacters,
- }
-}
-
-registerafmfeature {
- name = "compose",
- description = "additional composed characters",
- manipulators = {
- base = composecharacters,
- node = composecharacters,
- }
-}
-
-vf.helpers.composecharacters = composecharacters
-
--- This installs the builder into the regular virtual font builder,
--- which only makes sense as demo.
-
-commands["compose.trace.enable"] = function()
- trace_combining_visualize = true
-end
-
-commands["compose.trace.disable"] = function()
- trace_combining_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_combining_visualize = true
- else
- trace_combining_visualize = v[2]
- end
-end
-
-commands["compose.apply"] = function(g,v)
- composecharacters(g)
-end
-
--- vf builder
-
--- {'special', 'pdf: q ' .. s .. ' 0 0 '.. s .. ' 0 0 cm'},
--- {'special', 'pdf: q 1 0 0 1 ' .. -w .. ' ' .. -h .. ' cm'},
--- {'special', 'pdf: /Fm\XX\space Do'},
--- {'special', 'pdf: Q'},
--- {'special', 'pdf: Q'},
+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
+
+--[[ldx--
+<p>This is very experimental code!</p>
+--ldx]]--
+
+local trace_combining_visualize = false trackers.register("fonts.composing.visualize", function(v) trace_combining_visualize = v end)
+local trace_combining_define = false trackers.register("fonts.composing.define", function(v) trace_combining_define = v end)
+
+trackers.register("fonts.combining", "fonts.composing.define") -- for old times sake (and manuals)
+trackers.register("fonts.combining.all", "fonts.composing.*") -- for old times sake (and manuals)
+
+local report_combining = logs.reporter("fonts","combining")
+
+local force_combining = false -- just for demo purposes (see mk)
+
+local allocate = utilities.storage.allocate
+
+local fonts = fonts
+local handlers = fonts.handlers
+local constructors = fonts.constructors
+
+local registerotffeature = handlers.otf.features.register
+local registerafmfeature = handlers.afm.features.register
+
+local unicodecharacters = characters.data
+local unicodefallbacks = characters.fallbacks
+
+local vf = handlers.vf
+local commands = vf.combiner.commands
+local push = vf.predefined.push
+local pop = vf.predefined.pop
+
+local force_composed = false
+local cache = { } -- we could make these weak
+local fraction = 0.15 -- 30 units for lucida
+
+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 vfspecials = backends.tables.vfspecials --brr
+ local red, green, blue, black
+ if trace_combining_visualize then
+ red = vfspecials.red
+ green = vfspecials.green
+ blue = vfspecials.blue
+ black = vfspecials.black
+ end
+ local compose = fonts.goodies.getcompositions(tfmdata)
+ if compose and trace_combining_visualize then
+ report_combining("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 = cache[chr]
+ if not chr_t then
+ chr_t = {"slot", 1, chr}
+ cache[chr] = chr_t
+ end
+ if charsacc then
+ if trace_combining_define then
+ report_combining("composed %C, base %C, accent %C",i,chr,acc)
+ end
+ local acc_t = cache[acc]
+ if not acc_t then
+ acc_t = {"slot", 1, acc}
+ cache[acc] = acc_t
+ end
+ local cb = descriptions[chr].boundingbox
+ local ab = descriptions[acc].boundingbox
+ -- todo: adapt height
+ if cb and ab then
+ local c_llx, c_lly, c_urx, c_ury = scale*cb[1], scale*cb[2], scale*cb[3], scale*cb[4]
+ local a_llx, a_lly, a_urx, a_ury = scale*ab[1], scale*ab[2], scale*ab[3], 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_combining_define then
+ report_combining("building %C from %C and %C",i,chr,acc)
+ report_combining(" boundingbox:")
+ report_combining(" chr: %3i %3i %3i %3i",unpack(cb))
+ report_combining(" acc: %3i %3i %3i %3i",unpack(ab))
+ report_combining(" anchors:")
+ report_combining(" chr: %3i %3i",cx,cy)
+ report_combining(" acc: %3i %3i",ax,ay)
+ report_combining(" delta:")
+ report_combining(" %s: %3i %3i",i_anchored,dx,dy)
+ end
+ if trace_combining_visualize then
+ t.commands = { push, {"right", scale*dx}, {"down",-scale*dy}, green, acc_t, black, pop, chr_t }
+ -- t.commands = {
+ -- push, {"right", scale*cx}, {"down", -scale*cy}, red, {"rule",10000,10000,10000}, pop,
+ -- push, {"right", scale*ax}, {"down", -scale*ay}, blue, {"rule",10000,10000,10000}, pop,
+ -- push, {"right", scale*dx}, {"down", -scale*dy}, green, acc_t, black, pop, chr_t
+ -- }
+ else
+ t.commands = { push, {"right", scale*dx}, {"down",-scale*dy}, 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
+ if trace_combining_visualize then
+ t.commands = { push, {"right", dx-dd}, red, acc_t, black, pop, chr_t }
+ else
+ t.commands = { push, {"right", dx-dd}, acc_t, pop, chr_t }
+ end
+ 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
+ if trace_combining_visualize then
+ t.commands = { push, {"right", dx+dd}, {"down", dy}, green, acc_t, black, pop, chr_t }
+ else
+ t.commands = { push, {"right", dx+dd}, {"down", dy}, acc_t, pop, chr_t }
+ end
+ else
+ if trace_combining_visualize then
+ t.commands = { push, {"right", dx+dd}, blue, acc_t, black, pop, chr_t }
+ else
+ t.commands = { push, {"right", dx+dd}, acc_t, pop, chr_t }
+ end
+ end
+ end
+ else
+ t.commands = { chr_t } -- else index mess
+ end
+ else
+ if trace_combining_define then
+ report_combining("%C becomes simplfied %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
+ if done then
+ properties.virtualized = true
+ end
+ end
+end
+
+registerotffeature {
+ name = "compose",
+ description = "additional composed characters",
+ manipulators = {
+ base = composecharacters,
+ node = composecharacters,
+ }
+}
+
+registerafmfeature {
+ name = "compose",
+ description = "additional composed characters",
+ manipulators = {
+ base = composecharacters,
+ node = composecharacters,
+ }
+}
+
+vf.helpers.composecharacters = composecharacters
+
+-- This installs the builder into the regular virtual font builder,
+-- which only makes sense as demo.
+
+commands["compose.trace.enable"] = function()
+ trace_combining_visualize = true
+end
+
+commands["compose.trace.disable"] = function()
+ trace_combining_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_combining_visualize = true
+ else
+ trace_combining_visualize = v[2]
+ end
+end
+
+commands["compose.apply"] = function(g,v)
+ composecharacters(g)
+end
+
+-- vf builder
+
+-- {'special', 'pdf: q ' .. s .. ' 0 0 '.. s .. ' 0 0 cm'},
+-- {'special', 'pdf: q 1 0 0 1 ' .. -w .. ' ' .. -h .. ' cm'},
+-- {'special', 'pdf: /Fm\XX\space Do'},
+-- {'special', 'pdf: Q'},
+-- {'special', 'pdf: Q'},
diff --git a/tex/context/base/font-gds.lua b/tex/context/base/font-gds.lua
index 4eb57bfa4..6332f40b0 100644
--- a/tex/context/base/font-gds.lua
+++ b/tex/context/base/font-gds.lua
@@ -1,752 +1,752 @@
-if not modules then modules = { } end modules ['font-gds'] = {
- version = 1.000,
- comment = "companion to font-gds.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- depends on ctx
-
-local type, next, tonumber = type, next, tonumber
-local gmatch, format, lower, find, splitup = string.gmatch, string.format, string.lower, string.find, string.splitup
-local texsp = tex.sp
-
-local fonts, nodes, attributes, node = fonts, nodes, attributes, node
-
-local trace_goodies = false trackers.register("fonts.goodies", function(v) trace_goodies = v end)
-local report_goodies = logs.reporter("fonts","goodies")
-
-local allocate = utilities.storage.allocate
-
-local otf = fonts.handlers.otf
-local afm = fonts.handlers.afm
-local tfm = fonts.handlers.tfm
-
-local registerotffeature = otf.features.register
-local registerafmfeature = afm.features.register
-local registertfmfeature = tfm.features.register
-
-local fontgoodies = fonts.goodies or { }
-fonts.goodies = fontgoodies
-
-local typefaces = fonts.typefaces or allocate()
-fonts.typefaces = typefaces
-
-local data = fontgoodies.data or allocate()
-fontgoodies.data = data
-
-local list = fontgoodies.list or { }
-fontgoodies.list = list -- no allocate as we want to see what is there
-
-local addotffeature = otf.enhancers.addfeature
-
-local findfile = resolvers.findfile
-
-function fontgoodies.report(what,trace,goodies)
- if trace_goodies or trace then
- local whatever = goodies[what]
- if whatever then
- report_goodies("goodie %a found in %a",what,goodies.name)
- end
- end
-end
-
-local function loadgoodies(filename) -- maybe a merge is better
- local goodies = data[filename] -- we assume no suffix is given
- if goodies ~= nil then
- -- found or tagged unfound
- elseif type(filename) == "string" then
- local fullname = findfile(file.addsuffix(filename,"lfg")) or "" -- prefered suffix
- if fullname == "" then
- fullname = findfile(file.addsuffix(filename,"lua")) or "" -- fallback suffix
- end
- if fullname == "" then
- report_goodies("goodie file '%s.lfg' is not found",filename)
- data[filename] = false -- signal for not found
- else
- goodies = dofile(fullname) or false
- if not goodies then
- report_goodies("goodie file %a is invalid",fullname)
- return nil
- elseif trace_goodies then
- report_goodies("goodie file %a is loaded",fullname)
- end
- goodies.name = goodies.name or "no name"
- for name, fnc in next, list do
- fnc(goodies)
- end
- goodies.initialized = true
- data[filename] = goodies
- end
- end
- return goodies
-end
-
-function fontgoodies.register(name,fnc) -- will be a proper sequencer
- list[name] = fnc
-end
-
-fontgoodies.load = loadgoodies
-
--- register goodies file
-
-local function setgoodies(tfmdata,value)
- local goodies = tfmdata.goodies
- if not goodies then -- actually an error
- goodies = { }
- tfmdata.goodies = goodies
- end
- for filename in gmatch(value,"[^, ]+") do
- -- we need to check for duplicates
- local ok = loadgoodies(filename)
- if ok then
- if trace_goodies then
- report_goodies("assigning goodie %a",filename)
- end
- goodies[#goodies+1] = ok
- end
- end
-end
-
--- this will be split into good-* files and this file might become good-ini.lua
-
--- featuresets
-
-local function flattenedfeatures(t,tt)
- -- first set value dominates
- local tt = tt or { }
- for i=1,#t do
- local ti = t[i]
- if type(ti) == "table" then
- flattenedfeatures(ti,tt)
- elseif tt[ti] == nil then
- tt[ti] = true
- end
- end
- for k, v in next, t do
- if type(k) ~= "number" then -- not tonumber(k)
- if type(v) == "table" then
- flattenedfeatures(v,tt)
- elseif tt[k] == nil then
- tt[k] = v
- end
- end
- end
- return tt
-end
-
--- fonts.features.flattened = flattenedfeatures
-
-local function prepare_features(goodies,name,set)
- if set then
- local ff = flattenedfeatures(set)
- local fullname = goodies.name .. "::" .. name
- local n, s = fonts.specifiers.presetcontext(fullname,"",ff)
- goodies.featuresets[name] = s -- set
- if trace_goodies then
- report_goodies("feature set %a gets number %a and name %a",name,n,fullname)
- end
- return n
- end
-end
-
-fontgoodies.prepare_features = prepare_features
-
-local function initialize(goodies,tfmdata)
- local featuresets = goodies.featuresets
- local goodiesname = goodies.name
- if featuresets then
- if trace_goodies then
- report_goodies("checking featuresets in %a",goodies.name)
- end
- for name, set in next, featuresets do
- prepare_features(goodies,name,set)
- end
- end
-end
-
-fontgoodies.register("featureset",initialize)
-
-local function setfeatureset(tfmdata,set,features)
- local goodies = tfmdata.goodies -- shared ?
- if goodies then
- local properties = tfmdata.properties
- local what
- for i=1,#goodies do
- -- last one wins
- local g = goodies[i]
- what = g.featuresets and g.featuresets[set] or what
- end
- if what then
- for feature, value in next, what do
- if features[feature] == nil then
- features[feature] = value
- end
- end
- properties.mode = what.mode or properties.mode
- end
- end
-end
-
--- postprocessors (we could hash processor and share code)
-
-function fontgoodies.registerpostprocessor(tfmdata,f,prepend)
- local postprocessors = tfmdata.postprocessors
- if not postprocessors then
- tfmdata.postprocessors = { f }
- elseif prepend then
- table.insert(postprocessors,f,1)
- else
- table.insert(postprocessors,f)
- end
-end
-
-local function setpostprocessor(tfmdata,processor)
- local goodies = tfmdata.goodies
- if goodies and type(processor) == "string" then
- local found = { }
- local asked = utilities.parsers.settings_to_array(processor)
- for i=1,#goodies do
- local g = goodies[i]
- local p = g.postprocessors
- if p then
- for i=1,#asked do
- local a = asked[i]
- local f = p[a]
- if type(f) == "function" then
- found[a] = f
- end
- end
- end
- end
- local postprocessors = tfmdata.postprocessors or { }
- for i=1,#asked do
- local a = asked[i]
- local f = found[a]
- if f then
- postprocessors[#postprocessors+1] = f
- end
- end
- if #postprocessors > 0 then
- tfmdata.postprocessors = postprocessors
- end
- end
-end
-
--- colorschemes
-
-local colorschemes = fontgoodies.colorschemes or allocate { }
-fontgoodies.colorschemes = colorschemes
-colorschemes.data = colorschemes.data or { }
-
-local function setcolorscheme(tfmdata,scheme)
- if type(scheme) == "string" then
- local goodies = tfmdata.goodies
- -- todo : check for already defined in shared
- if goodies then
- local what
- for i=1,#goodies do
- -- last one counts
- local g = goodies[i]
- what = g.colorschemes and g.colorschemes[scheme] or what
- end
- if type(what) == "table" then
- -- this is font bound but we can share them if needed
- -- just as we could hash the conversions (per font)
- local hash = tfmdata.resources.unicodes
- local reverse = { }
- local characters = tfmdata.characters
- for i=1,#what do
- local w = what[i]
- for j=1,#w do
- local name = w[j]
- if name == "*" then
- -- inefficient but only used for tracing anyway
- for _, unicode in next, hash do
- reverse[unicode] = i
- end
- elseif type(name) == "number" then
- reverse[name] = i
- elseif find(name,":") then
- local start, stop = splitup(name,":")
- start = tonumber(start)
- stop = tonumber(stop)
- if start and stop then
- -- limited usage: we only deal with non reassigned
- -- maybe some day I'll also support the ones with a
- -- tounicode in this range
- for unicode=start,stop do
- if characters[unicode] then
- reverse[unicode] = i
- end
- end
- end
- else
- local unicode = hash[name]
- if unicode then
- reverse[unicode] = i
- end
- end
- end
- end
- tfmdata.properties.colorscheme = reverse
- return
- end
- end
- end
- tfmdata.properties.colorscheme = false
-end
-
-local fontdata = fonts.hashes.identifiers
-local setnodecolor = nodes.tracers.colors.set
-local traverse_id = node.traverse_id
-local a_colorscheme = attributes.private('colorscheme')
-local glyph = node.id("glyph")
-
-function colorschemes.coloring(head)
- local lastfont, lastscheme
- local done = false
- for n in traverse_id(glyph,head) do
- local a = n[a_colorscheme]
- if a then
- local f = n.font
- if f ~= lastfont then
- lastscheme, lastfont = fontdata[f].properties.colorscheme, f
- end
- if lastscheme then
- local sc = lastscheme[n.char]
- if sc then
- done = true
- setnodecolor(n,"colorscheme:"..a..":"..sc) -- slow
- end
- end
- end
- end
- return head, done
-end
-
-function colorschemes.enable()
- nodes.tasks.appendaction("processors","fonts","fonts.goodies.colorschemes.coloring")
- function colorschemes.enable() end
-end
-
-local function setextrafeatures(tfmdata)
- local goodies = tfmdata.goodies
- if goodies then
- for i=1,#goodies do
- local g = goodies[i]
- local f = g.features
- if f then
- for feature, specification in next, f do
- addotffeature(tfmdata.shared.rawdata,feature,specification)
- registerotffeature {
- name = feature,
- description = format("extra: %s",feature)
- }
- end
- end
- end
- end
-end
-
--- installation (collected to keep the overview) -- also for type 1
-
-registerotffeature {
- name = "goodies",
- description = "goodies on top of built in features",
- initializers = {
- position = 1,
- base = setgoodies,
- node = setgoodies,
- }
-}
-
-registerotffeature {
- name = "extrafeatures",
- description = "extra features",
- default = true,
- initializers = {
- position = 2,
- base = setextrafeatures,
- node = setextrafeatures,
- }
-}
-
-registerotffeature {
- name = "featureset",
- description = "goodie feature set",
- initializers = {
- position = 3,
- base = setfeatureset,
- node = setfeatureset,
- }
-}
-
-registerotffeature {
- name = "colorscheme",
- description = "goodie color scheme",
- initializers = {
- base = setcolorscheme,
- node = setcolorscheme,
- }
-}
-
-registerotffeature {
- name = "postprocessor",
- description = "goodie postprocessor",
- initializers = {
- base = setpostprocessor,
- node = setpostprocessor,
- }
-}
-
--- afm
-
-registerafmfeature {
- name = "goodies",
- description = "goodies on top of built in features",
- initializers = {
- position = 1,
- base = setgoodies,
- node = setgoodies,
- }
-}
-
--- tfm
-
-registertfmfeature {
- name = "goodies",
- description = "goodies on top of built in features",
- initializers = {
- position = 1,
- base = setgoodies,
- node = setgoodies,
- }
-}
-
--- experiment, we have to load the definitions immediately as they precede
--- the definition so they need to be initialized in the typescript
-
-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
- 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)
-
--- the following takes care of explicit file specifications
---
--- files = {
--- name = "antykwapoltawskiego",
--- list = {
--- ["AntPoltLtCond-Regular.otf"] = {
--- -- name = "antykwapoltawskiego",
--- style = "regular",
--- weight = "light",
--- width = "condensed",
--- },
--- },
--- }
-
--- math italics
-
--- 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
- local mathitalics = mathgoodies and mathgoodies.italics
- if mathitalics then
- local properties = tfmdata.properties
- 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
- local disableengine = mathitalics.disableengine
- properties.hasitalics = true
- properties.mathitalic_defaultfactor = defaultfactor -- we inherit outer one anyway (name will change)
- if properties.mathitalics == nil then
- properties.mathitalics = disableengine
- end
- if corrections then
- -- As we want to set italic_correction (the context one) we need a
- -- postprocessor instead of messing with the (unscaled) descriptions.
- 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.hasitalics = true
- properties.mathitalic_defaultfactor = defaultfactor
- properties.mathitalic_defaultvalue = defaultfactor * parameters.quad
- if properties.mathitalics == nil then
- properties.mathitalics = disableengine
- end
- if trace_goodies then
- report_goodies("assigning mathitalics for font %a",properties.name)
- end
- local mathitalics = properties.mathitalics
- local quad = parameters.quad
- local hfactor = parameters.hfactor
- for k, v in next, corrections do
- local c = characters[k]
- if v > -1 and v < 1 then
- v = v * quad
- else
- v = v * hfactor
- end
- c.italic_correction = v -- for context
- if mathitalics then
- c.italic = v -- for tex
- else
- c.italic = nil
- end
- end
- end)
- end
- return -- maybe not as these can accumulate
- end
- end
- end
- end
-end
-
-registerotffeature {
- name = "mathitalics",
- description = "additional math italic corrections",
- -- default = true,
- initializers = {
- base = initialize,
- node = initialize,
- }
-}
-
--- fontgoodies.register("mathitalics", initialize)
-
--- files
-
-local function initialize(goodies)
- local files = goodies.files
- if files then
- fonts.names.register(files)
- end
-end
-
-fontgoodies.register("files", initialize)
-
--- some day we will have a define command and then we can also do some
--- proper tracing
---
--- fonts.typefaces["antykwapoltawskiego-condensed"] = {
--- shortcut = "rm",
--- shape = "serif",
--- fontname = "antykwapoltawskiego",
--- normalweight = "light",
--- boldweight = "medium",
--- width = "condensed",
--- size = "default",
--- features = "default",
--- }
-
-local function initialize(goodies)
- local typefaces = goodies.typefaces
- if typefaces then
- local ft = fonts.typefaces
- for k, v in next, typefaces do
- ft[k] = v
- end
- end
-end
-
-fontgoodies.register("typefaces", initialize)
-
-local compositions = { }
-
-function fontgoodies.getcompositions(tfmdata)
- return compositions[file.nameonly(tfmdata.properties.filename or "")]
-end
-
-local function initialize(goodies)
- local gc = goodies.compositions
- if gc then
- for k, v in next, gc do
- compositions[k] = v
- end
- end
-end
-
-fontgoodies.register("compositions", initialize)
-
--- extra treatments (on top of defaults): \loadfontgoodies[mytreatments]
-
-local treatmentdata = fonts.treatments.data
-
-local function initialize(goodies)
- local treatments = goodies.treatments
- if treatments then
- for name, data in next, treatments do
- treatmentdata[name] = data -- always wins
- end
- end
-end
-
-fontgoodies.register("treatments", initialize)
-
-local filenames = fontgoodies.filenames or allocate()
-fontgoodies.filenames = filenames
-
-local filedata = filenames.data or allocate()
-filenames.data = filedata
-
-local function initialize(goodies) -- design sizes are registered global
- local fn = goodies.filenames
- if fn then
- for usedname, alternativenames in next, fn do
- filedata[usedname] = alternativenames
- end
- end
-end
-
-fontgoodies.register("filenames", initialize)
-
-function fontgoodies.filenames.resolve(name)
- local fd = filedata[name]
- if fd and findfile(name) == "" then
- for i=1,#fd do
- local fn = fd[i]
- if findfile(fn) ~= "" then
- return fn
- end
- end
- else
- -- no lookup, just use the regular mechanism
- end
- return name
-end
-
-local designsizes = fontgoodies.designsizes or allocate()
-fontgoodies.designsizes = designsizes
-
-local designdata = designsizes.data or allocate()
-designsizes.data = designdata
-
-local function initialize(goodies) -- design sizes are registered global
- local gd = goodies.designsizes
- if gd then
- for name, data in next, gd do
- local ranges = { }
- for size, file in next, data do
- if size ~= "default" then
- ranges[#ranges+1] = { texsp(size), file } -- also lower(file)
- end
- end
- table.sort(ranges,function(a,b) return a[1] < b[1] end)
- designdata[lower(name)] = { -- overloads, doesn't merge!
- default = data.default,
- ranges = ranges,
- }
- end
- end
-end
-
-fontgoodies.register("designsizes", initialize)
-
-function fontgoodies.designsizes.register(name,size,specification)
- local d = designdata[name]
- if not d then
- d = {
- ranges = { },
- default = nil, -- so we have no default set
- }
- designdata[name] = d
- end
- if size == "default" then
- d.default = specification
- else
- if type(size) == "string" then
- size = texsp(size)
- end
- local ranges = d.ranges
- ranges[#ranges+1] = { size, specification }
- end
-end
-
-function fontgoodies.designsizes.filename(name,spec,size) -- returns nil of no match
- if spec and spec ~= "" then
- local data = designdata[lower(name)]
- if data then
- if spec == "default" then
- return data.default
- elseif spec == "auto" then
- local ranges = data.ranges
- if ranges then
- for i=1,#ranges do
- local r = ranges[i]
- if r[1] >= size then -- todo: rounding so maybe size - 100
- return r[2]
- end
- end
- end
- return data.default or (ranges and ranges[#ranges][2])
- end
- end
- end
-end
-
--- The following file (husayni.lfg) is the experimental setup that we used
--- for Idris font. For the moment we don't store this in the cache and quite
--- probably these files sit in one of the paths:
---
--- tex/context/fonts/goodies
--- tex/fonts/goodies/context
--- tex/fonts/data/foundry/collection
---
--- see lfg files in distribution
-
--- interface
-
-commands.loadfontgoodies = fontgoodies.load
-commands.enablefontcolorschemes = colorschemes.enable
-
--- weird place ... depends on math
-
-local function finalize(tfmdata,feature,value)
- mathematics.overloaddimensions(tfmdata,tfmdata,value)
-end
-
-registerotffeature {
- name = "mathdimensions",
- description = "manipulate math dimensions",
- -- default = true,
- manipulators = {
- base = finalize,
- node = finalize,
- }
-}
+if not modules then modules = { } end modules ['font-gds'] = {
+ version = 1.000,
+ comment = "companion to font-gds.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- depends on ctx
+
+local type, next, tonumber = type, next, tonumber
+local gmatch, format, lower, find, splitup = string.gmatch, string.format, string.lower, string.find, string.splitup
+local texsp = tex.sp
+
+local fonts, nodes, attributes, node = fonts, nodes, attributes, node
+
+local trace_goodies = false trackers.register("fonts.goodies", function(v) trace_goodies = v end)
+local report_goodies = logs.reporter("fonts","goodies")
+
+local allocate = utilities.storage.allocate
+
+local otf = fonts.handlers.otf
+local afm = fonts.handlers.afm
+local tfm = fonts.handlers.tfm
+
+local registerotffeature = otf.features.register
+local registerafmfeature = afm.features.register
+local registertfmfeature = tfm.features.register
+
+local fontgoodies = fonts.goodies or { }
+fonts.goodies = fontgoodies
+
+local typefaces = fonts.typefaces or allocate()
+fonts.typefaces = typefaces
+
+local data = fontgoodies.data or allocate()
+fontgoodies.data = data
+
+local list = fontgoodies.list or { }
+fontgoodies.list = list -- no allocate as we want to see what is there
+
+local addotffeature = otf.enhancers.addfeature
+
+local findfile = resolvers.findfile
+
+function fontgoodies.report(what,trace,goodies)
+ if trace_goodies or trace then
+ local whatever = goodies[what]
+ if whatever then
+ report_goodies("goodie %a found in %a",what,goodies.name)
+ end
+ end
+end
+
+local function loadgoodies(filename) -- maybe a merge is better
+ local goodies = data[filename] -- we assume no suffix is given
+ if goodies ~= nil then
+ -- found or tagged unfound
+ elseif type(filename) == "string" then
+ local fullname = findfile(file.addsuffix(filename,"lfg")) or "" -- prefered suffix
+ if fullname == "" then
+ fullname = findfile(file.addsuffix(filename,"lua")) or "" -- fallback suffix
+ end
+ if fullname == "" then
+ report_goodies("goodie file '%s.lfg' is not found",filename)
+ data[filename] = false -- signal for not found
+ else
+ goodies = dofile(fullname) or false
+ if not goodies then
+ report_goodies("goodie file %a is invalid",fullname)
+ return nil
+ elseif trace_goodies then
+ report_goodies("goodie file %a is loaded",fullname)
+ end
+ goodies.name = goodies.name or "no name"
+ for name, fnc in next, list do
+ fnc(goodies)
+ end
+ goodies.initialized = true
+ data[filename] = goodies
+ end
+ end
+ return goodies
+end
+
+function fontgoodies.register(name,fnc) -- will be a proper sequencer
+ list[name] = fnc
+end
+
+fontgoodies.load = loadgoodies
+
+-- register goodies file
+
+local function setgoodies(tfmdata,value)
+ local goodies = tfmdata.goodies
+ if not goodies then -- actually an error
+ goodies = { }
+ tfmdata.goodies = goodies
+ end
+ for filename in gmatch(value,"[^, ]+") do
+ -- we need to check for duplicates
+ local ok = loadgoodies(filename)
+ if ok then
+ if trace_goodies then
+ report_goodies("assigning goodie %a",filename)
+ end
+ goodies[#goodies+1] = ok
+ end
+ end
+end
+
+-- this will be split into good-* files and this file might become good-ini.lua
+
+-- featuresets
+
+local function flattenedfeatures(t,tt)
+ -- first set value dominates
+ local tt = tt or { }
+ for i=1,#t do
+ local ti = t[i]
+ if type(ti) == "table" then
+ flattenedfeatures(ti,tt)
+ elseif tt[ti] == nil then
+ tt[ti] = true
+ end
+ end
+ for k, v in next, t do
+ if type(k) ~= "number" then -- not tonumber(k)
+ if type(v) == "table" then
+ flattenedfeatures(v,tt)
+ elseif tt[k] == nil then
+ tt[k] = v
+ end
+ end
+ end
+ return tt
+end
+
+-- fonts.features.flattened = flattenedfeatures
+
+local function prepare_features(goodies,name,set)
+ if set then
+ local ff = flattenedfeatures(set)
+ local fullname = goodies.name .. "::" .. name
+ local n, s = fonts.specifiers.presetcontext(fullname,"",ff)
+ goodies.featuresets[name] = s -- set
+ if trace_goodies then
+ report_goodies("feature set %a gets number %a and name %a",name,n,fullname)
+ end
+ return n
+ end
+end
+
+fontgoodies.prepare_features = prepare_features
+
+local function initialize(goodies,tfmdata)
+ local featuresets = goodies.featuresets
+ local goodiesname = goodies.name
+ if featuresets then
+ if trace_goodies then
+ report_goodies("checking featuresets in %a",goodies.name)
+ end
+ for name, set in next, featuresets do
+ prepare_features(goodies,name,set)
+ end
+ end
+end
+
+fontgoodies.register("featureset",initialize)
+
+local function setfeatureset(tfmdata,set,features)
+ local goodies = tfmdata.goodies -- shared ?
+ if goodies then
+ local properties = tfmdata.properties
+ local what
+ for i=1,#goodies do
+ -- last one wins
+ local g = goodies[i]
+ what = g.featuresets and g.featuresets[set] or what
+ end
+ if what then
+ for feature, value in next, what do
+ if features[feature] == nil then
+ features[feature] = value
+ end
+ end
+ properties.mode = what.mode or properties.mode
+ end
+ end
+end
+
+-- postprocessors (we could hash processor and share code)
+
+function fontgoodies.registerpostprocessor(tfmdata,f,prepend)
+ local postprocessors = tfmdata.postprocessors
+ if not postprocessors then
+ tfmdata.postprocessors = { f }
+ elseif prepend then
+ table.insert(postprocessors,f,1)
+ else
+ table.insert(postprocessors,f)
+ end
+end
+
+local function setpostprocessor(tfmdata,processor)
+ local goodies = tfmdata.goodies
+ if goodies and type(processor) == "string" then
+ local found = { }
+ local asked = utilities.parsers.settings_to_array(processor)
+ for i=1,#goodies do
+ local g = goodies[i]
+ local p = g.postprocessors
+ if p then
+ for i=1,#asked do
+ local a = asked[i]
+ local f = p[a]
+ if type(f) == "function" then
+ found[a] = f
+ end
+ end
+ end
+ end
+ local postprocessors = tfmdata.postprocessors or { }
+ for i=1,#asked do
+ local a = asked[i]
+ local f = found[a]
+ if f then
+ postprocessors[#postprocessors+1] = f
+ end
+ end
+ if #postprocessors > 0 then
+ tfmdata.postprocessors = postprocessors
+ end
+ end
+end
+
+-- colorschemes
+
+local colorschemes = fontgoodies.colorschemes or allocate { }
+fontgoodies.colorschemes = colorschemes
+colorschemes.data = colorschemes.data or { }
+
+local function setcolorscheme(tfmdata,scheme)
+ if type(scheme) == "string" then
+ local goodies = tfmdata.goodies
+ -- todo : check for already defined in shared
+ if goodies then
+ local what
+ for i=1,#goodies do
+ -- last one counts
+ local g = goodies[i]
+ what = g.colorschemes and g.colorschemes[scheme] or what
+ end
+ if type(what) == "table" then
+ -- this is font bound but we can share them if needed
+ -- just as we could hash the conversions (per font)
+ local hash = tfmdata.resources.unicodes
+ local reverse = { }
+ local characters = tfmdata.characters
+ for i=1,#what do
+ local w = what[i]
+ for j=1,#w do
+ local name = w[j]
+ if name == "*" then
+ -- inefficient but only used for tracing anyway
+ for _, unicode in next, hash do
+ reverse[unicode] = i
+ end
+ elseif type(name) == "number" then
+ reverse[name] = i
+ elseif find(name,":") then
+ local start, stop = splitup(name,":")
+ start = tonumber(start)
+ stop = tonumber(stop)
+ if start and stop then
+ -- limited usage: we only deal with non reassigned
+ -- maybe some day I'll also support the ones with a
+ -- tounicode in this range
+ for unicode=start,stop do
+ if characters[unicode] then
+ reverse[unicode] = i
+ end
+ end
+ end
+ else
+ local unicode = hash[name]
+ if unicode then
+ reverse[unicode] = i
+ end
+ end
+ end
+ end
+ tfmdata.properties.colorscheme = reverse
+ return
+ end
+ end
+ end
+ tfmdata.properties.colorscheme = false
+end
+
+local fontdata = fonts.hashes.identifiers
+local setnodecolor = nodes.tracers.colors.set
+local traverse_id = node.traverse_id
+local a_colorscheme = attributes.private('colorscheme')
+local glyph = node.id("glyph")
+
+function colorschemes.coloring(head)
+ local lastfont, lastscheme
+ local done = false
+ for n in traverse_id(glyph,head) do
+ local a = n[a_colorscheme]
+ if a then
+ local f = n.font
+ if f ~= lastfont then
+ lastscheme, lastfont = fontdata[f].properties.colorscheme, f
+ end
+ if lastscheme then
+ local sc = lastscheme[n.char]
+ if sc then
+ done = true
+ setnodecolor(n,"colorscheme:"..a..":"..sc) -- slow
+ end
+ end
+ end
+ end
+ return head, done
+end
+
+function colorschemes.enable()
+ nodes.tasks.appendaction("processors","fonts","fonts.goodies.colorschemes.coloring")
+ function colorschemes.enable() end
+end
+
+local function setextrafeatures(tfmdata)
+ local goodies = tfmdata.goodies
+ if goodies then
+ for i=1,#goodies do
+ local g = goodies[i]
+ local f = g.features
+ if f then
+ for feature, specification in next, f do
+ addotffeature(tfmdata.shared.rawdata,feature,specification)
+ registerotffeature {
+ name = feature,
+ description = format("extra: %s",feature)
+ }
+ end
+ end
+ end
+ end
+end
+
+-- installation (collected to keep the overview) -- also for type 1
+
+registerotffeature {
+ name = "goodies",
+ description = "goodies on top of built in features",
+ initializers = {
+ position = 1,
+ base = setgoodies,
+ node = setgoodies,
+ }
+}
+
+registerotffeature {
+ name = "extrafeatures",
+ description = "extra features",
+ default = true,
+ initializers = {
+ position = 2,
+ base = setextrafeatures,
+ node = setextrafeatures,
+ }
+}
+
+registerotffeature {
+ name = "featureset",
+ description = "goodie feature set",
+ initializers = {
+ position = 3,
+ base = setfeatureset,
+ node = setfeatureset,
+ }
+}
+
+registerotffeature {
+ name = "colorscheme",
+ description = "goodie color scheme",
+ initializers = {
+ base = setcolorscheme,
+ node = setcolorscheme,
+ }
+}
+
+registerotffeature {
+ name = "postprocessor",
+ description = "goodie postprocessor",
+ initializers = {
+ base = setpostprocessor,
+ node = setpostprocessor,
+ }
+}
+
+-- afm
+
+registerafmfeature {
+ name = "goodies",
+ description = "goodies on top of built in features",
+ initializers = {
+ position = 1,
+ base = setgoodies,
+ node = setgoodies,
+ }
+}
+
+-- tfm
+
+registertfmfeature {
+ name = "goodies",
+ description = "goodies on top of built in features",
+ initializers = {
+ position = 1,
+ base = setgoodies,
+ node = setgoodies,
+ }
+}
+
+-- experiment, we have to load the definitions immediately as they precede
+-- the definition so they need to be initialized in the typescript
+
+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
+ 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)
+
+-- the following takes care of explicit file specifications
+--
+-- files = {
+-- name = "antykwapoltawskiego",
+-- list = {
+-- ["AntPoltLtCond-Regular.otf"] = {
+-- -- name = "antykwapoltawskiego",
+-- style = "regular",
+-- weight = "light",
+-- width = "condensed",
+-- },
+-- },
+-- }
+
+-- math italics
+
+-- 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
+ local mathitalics = mathgoodies and mathgoodies.italics
+ if mathitalics then
+ local properties = tfmdata.properties
+ 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
+ local disableengine = mathitalics.disableengine
+ properties.hasitalics = true
+ properties.mathitalic_defaultfactor = defaultfactor -- we inherit outer one anyway (name will change)
+ if properties.mathitalics == nil then
+ properties.mathitalics = disableengine
+ end
+ if corrections then
+ -- As we want to set italic_correction (the context one) we need a
+ -- postprocessor instead of messing with the (unscaled) descriptions.
+ 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.hasitalics = true
+ properties.mathitalic_defaultfactor = defaultfactor
+ properties.mathitalic_defaultvalue = defaultfactor * parameters.quad
+ if properties.mathitalics == nil then
+ properties.mathitalics = disableengine
+ end
+ if trace_goodies then
+ report_goodies("assigning mathitalics for font %a",properties.name)
+ end
+ local mathitalics = properties.mathitalics
+ local quad = parameters.quad
+ local hfactor = parameters.hfactor
+ for k, v in next, corrections do
+ local c = characters[k]
+ if v > -1 and v < 1 then
+ v = v * quad
+ else
+ v = v * hfactor
+ end
+ c.italic_correction = v -- for context
+ if mathitalics then
+ c.italic = v -- for tex
+ else
+ c.italic = nil
+ end
+ end
+ end)
+ end
+ return -- maybe not as these can accumulate
+ end
+ end
+ end
+ end
+end
+
+registerotffeature {
+ name = "mathitalics",
+ description = "additional math italic corrections",
+ -- default = true,
+ initializers = {
+ base = initialize,
+ node = initialize,
+ }
+}
+
+-- fontgoodies.register("mathitalics", initialize)
+
+-- files
+
+local function initialize(goodies)
+ local files = goodies.files
+ if files then
+ fonts.names.register(files)
+ end
+end
+
+fontgoodies.register("files", initialize)
+
+-- some day we will have a define command and then we can also do some
+-- proper tracing
+--
+-- fonts.typefaces["antykwapoltawskiego-condensed"] = {
+-- shortcut = "rm",
+-- shape = "serif",
+-- fontname = "antykwapoltawskiego",
+-- normalweight = "light",
+-- boldweight = "medium",
+-- width = "condensed",
+-- size = "default",
+-- features = "default",
+-- }
+
+local function initialize(goodies)
+ local typefaces = goodies.typefaces
+ if typefaces then
+ local ft = fonts.typefaces
+ for k, v in next, typefaces do
+ ft[k] = v
+ end
+ end
+end
+
+fontgoodies.register("typefaces", initialize)
+
+local compositions = { }
+
+function fontgoodies.getcompositions(tfmdata)
+ return compositions[file.nameonly(tfmdata.properties.filename or "")]
+end
+
+local function initialize(goodies)
+ local gc = goodies.compositions
+ if gc then
+ for k, v in next, gc do
+ compositions[k] = v
+ end
+ end
+end
+
+fontgoodies.register("compositions", initialize)
+
+-- extra treatments (on top of defaults): \loadfontgoodies[mytreatments]
+
+local treatmentdata = fonts.treatments.data
+
+local function initialize(goodies)
+ local treatments = goodies.treatments
+ if treatments then
+ for name, data in next, treatments do
+ treatmentdata[name] = data -- always wins
+ end
+ end
+end
+
+fontgoodies.register("treatments", initialize)
+
+local filenames = fontgoodies.filenames or allocate()
+fontgoodies.filenames = filenames
+
+local filedata = filenames.data or allocate()
+filenames.data = filedata
+
+local function initialize(goodies) -- design sizes are registered global
+ local fn = goodies.filenames
+ if fn then
+ for usedname, alternativenames in next, fn do
+ filedata[usedname] = alternativenames
+ end
+ end
+end
+
+fontgoodies.register("filenames", initialize)
+
+function fontgoodies.filenames.resolve(name)
+ local fd = filedata[name]
+ if fd and findfile(name) == "" then
+ for i=1,#fd do
+ local fn = fd[i]
+ if findfile(fn) ~= "" then
+ return fn
+ end
+ end
+ else
+ -- no lookup, just use the regular mechanism
+ end
+ return name
+end
+
+local designsizes = fontgoodies.designsizes or allocate()
+fontgoodies.designsizes = designsizes
+
+local designdata = designsizes.data or allocate()
+designsizes.data = designdata
+
+local function initialize(goodies) -- design sizes are registered global
+ local gd = goodies.designsizes
+ if gd then
+ for name, data in next, gd do
+ local ranges = { }
+ for size, file in next, data do
+ if size ~= "default" then
+ ranges[#ranges+1] = { texsp(size), file } -- also lower(file)
+ end
+ end
+ table.sort(ranges,function(a,b) return a[1] < b[1] end)
+ designdata[lower(name)] = { -- overloads, doesn't merge!
+ default = data.default,
+ ranges = ranges,
+ }
+ end
+ end
+end
+
+fontgoodies.register("designsizes", initialize)
+
+function fontgoodies.designsizes.register(name,size,specification)
+ local d = designdata[name]
+ if not d then
+ d = {
+ ranges = { },
+ default = nil, -- so we have no default set
+ }
+ designdata[name] = d
+ end
+ if size == "default" then
+ d.default = specification
+ else
+ if type(size) == "string" then
+ size = texsp(size)
+ end
+ local ranges = d.ranges
+ ranges[#ranges+1] = { size, specification }
+ end
+end
+
+function fontgoodies.designsizes.filename(name,spec,size) -- returns nil of no match
+ if spec and spec ~= "" then
+ local data = designdata[lower(name)]
+ if data then
+ if spec == "default" then
+ return data.default
+ elseif spec == "auto" then
+ local ranges = data.ranges
+ if ranges then
+ for i=1,#ranges do
+ local r = ranges[i]
+ if r[1] >= size then -- todo: rounding so maybe size - 100
+ return r[2]
+ end
+ end
+ end
+ return data.default or (ranges and ranges[#ranges][2])
+ end
+ end
+ end
+end
+
+-- The following file (husayni.lfg) is the experimental setup that we used
+-- for Idris font. For the moment we don't store this in the cache and quite
+-- probably these files sit in one of the paths:
+--
+-- tex/context/fonts/goodies
+-- tex/fonts/goodies/context
+-- tex/fonts/data/foundry/collection
+--
+-- see lfg files in distribution
+
+-- interface
+
+commands.loadfontgoodies = fontgoodies.load
+commands.enablefontcolorschemes = colorschemes.enable
+
+-- weird place ... depends on math
+
+local function finalize(tfmdata,feature,value)
+ mathematics.overloaddimensions(tfmdata,tfmdata,value)
+end
+
+registerotffeature {
+ name = "mathdimensions",
+ description = "manipulate math dimensions",
+ -- default = true,
+ manipulators = {
+ base = finalize,
+ node = finalize,
+ }
+}
diff --git a/tex/context/base/font-hsh.lua b/tex/context/base/font-hsh.lua
index c11f9a721..f5c80d705 100644
--- a/tex/context/base/font-hsh.lua
+++ b/tex/context/base/font-hsh.lua
@@ -1,226 +1,226 @@
-if not modules then modules = { } end modules ['font-hsh'] = {
- 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 setmetatableindex = table.setmetatableindex
-local currentfont = font.current
-local allocate = utilities.storage.allocate
-
-local fonts = fonts
-local hashes = fonts.hashes or allocate()
-fonts.hashes = hashes
-
--- todo: autoallocate ... just create on the fly .. use constructors.keys (problem: plurals)
-
-local identifiers = hashes.identifiers or allocate()
-local characters = hashes.characters or allocate() -- chardata
-local descriptions = hashes.descriptions or allocate()
-local parameters = hashes.parameters or allocate()
-local properties = hashes.properties or allocate()
-local resources = hashes.resources or allocate()
-local spacings = hashes.spacings or allocate()
-local spaces = hashes.spaces or allocate()
-local quads = hashes.quads or allocate() -- maybe also spacedata
-local xheights = hashes.xheights or allocate()
-local csnames = hashes.csnames or allocate() -- namedata
-local marks = hashes.marks or allocate()
-local italics = hashes.italics or allocate()
-local lastmathids = hashes.lastmathids or allocate()
-local dynamics = hashes.dynamics or allocate()
-
-hashes.characters = characters
-hashes.descriptions = descriptions
-hashes.parameters = parameters
-hashes.properties = properties
-hashes.resources = resources
-hashes.spacings = spacings
-hashes.spaces = spaces
-hashes.quads = quads hashes.emwidths = quads
-hashes.xheights = xheights hashes.exheights = xheights
-hashes.csnames = csnames
-hashes.marks = marks
-hashes.italics = italics
-hashes.lastmathids = lastmathids
-hashes.dynamics = dynamics
-
-local nulldata = allocate {
- name = "nullfont",
- characters = { },
- descriptions = { },
- properties = { },
- parameters = { -- lmromanregular @ 12pt
- slantperpoint = 0,
- spacing = {
- width = 256377,
- stretch = 128188,
- shrink = 85459,
- extra = 85459,
- },
- quad = 786432,
- xheight = 338952,
- -- compatibility:
- slant = 0, -- 1
- space = 256377, -- 2
- space_stretch = 128188, -- 3
- space_shrink = 85459, -- 4
- x_height = 338952, -- 5
- quad = 786432, -- 6
- extra_space = 85459, -- 7
- },
-}
-
-fonts.nulldata = nulldata
-
-fonts.constructors.enhanceparameters(nulldata.parameters) -- official copies for us
-
-setmetatableindex(identifiers, function(t,k)
- return k == true and identifiers[currentfont()] or nulldata
-end)
-
-setmetatableindex(characters, function(t,k)
- if k == true then
- return characters[currentfont()]
- else
- local characters = identifiers[k].characters
- t[k] = characters
- return characters
- end
-end)
-
-setmetatableindex(descriptions, function(t,k)
- if k == true then
- return descriptions[currentfont()]
- else
- local descriptions = identifiers[k].descriptions
- t[k] = descriptions
- return descriptions
- end
-end)
-
-setmetatableindex(parameters, function(t,k)
- if k == true then
- return parameters[currentfont()]
- else
- local parameters = identifiers[k].parameters
- t[k] = parameters
- return parameters
- end
-end)
-
-setmetatableindex(properties, function(t,k)
- if k == true then
- return properties[currentfont()]
- else
- local properties = identifiers[k].properties
- t[k] = properties
- return properties
- end
-end)
-
-setmetatableindex(resources, function(t,k)
- if k == true then
- return resources[currentfont()]
- else
- local shared = identifiers[k].shared
- local rawdata = shared and shared.rawdata
- local resources = rawdata and rawdata.resources
- t[k] = resources or false -- better than resolving each time
- return resources
- end
-end)
-
-setmetatableindex(quads, function(t,k)
- if k == true then
- return quads[currentfont()]
- else
- local parameters = parameters[k]
- local quad = parameters and parameters.quad or 0
- t[k] = quad
- return quad
- end
-end)
-
-local nospacing = {
- width = 0,
- stretch = 0,
- shrink = 0,
- extra = 0,
-}
-
-setmetatableindex(spacings, function(t,k)
- if k == true then
- return spacings[currentfont()]
- else
- local parameters = parameters[k]
- local spacing = parameters and parameters.spacing or nospacing
- t[k] = spacing
- return spacing
- end
-end)
-
-setmetatableindex(spaces, function(t,k)
- if k == true then
- return spaces[currentfont()]
- else
- local space = spacings[k].width
- t[k] = space
- return space
- end
-end)
-
-setmetatableindex(marks, function(t,k)
- if k == true then
- return marks[currentfont()]
- else
- local resources = identifiers[k].resources or { }
- local marks = resources.marks or { }
- t[k] = marks
- return marks
- end
-end)
-
-setmetatableindex(xheights, function(t,k)
- if k == true then
- return xheights[currentfont()]
- else
- local parameters = parameters[k]
- local xheight = parameters and parameters.xheight or 0
- t[k] = xheight
- return xheight
- end
-end)
-
-setmetatableindex(italics, function(t,k) -- is test !
- if k == true then
- return italics[currentfont()]
- else
- local properties = identifiers[k].properties
- local hasitalics = properties and properties.hasitalics
- if hasitalics then
- hasitalics = characters[k] -- convenient return
- else
- hasitalics = false
- end
- t[k] = hasitalics
- return hasitalics
- end
-end)
-
-setmetatableindex(dynamics, function(t,k)
- if k == true then
- return dynamics[currentfont()]
- else
- local shared = identifiers[k].shared
- local dynamics = shared and shared.dynamics or false
- t[k] = dynamics
- return dynamics
- end
-end)
-
-function font.getfont(id)
- return identifiers[id]
-end
+if not modules then modules = { } end modules ['font-hsh'] = {
+ 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 setmetatableindex = table.setmetatableindex
+local currentfont = font.current
+local allocate = utilities.storage.allocate
+
+local fonts = fonts
+local hashes = fonts.hashes or allocate()
+fonts.hashes = hashes
+
+-- todo: autoallocate ... just create on the fly .. use constructors.keys (problem: plurals)
+
+local identifiers = hashes.identifiers or allocate()
+local characters = hashes.characters or allocate() -- chardata
+local descriptions = hashes.descriptions or allocate()
+local parameters = hashes.parameters or allocate()
+local properties = hashes.properties or allocate()
+local resources = hashes.resources or allocate()
+local spacings = hashes.spacings or allocate()
+local spaces = hashes.spaces or allocate()
+local quads = hashes.quads or allocate() -- maybe also spacedata
+local xheights = hashes.xheights or allocate()
+local csnames = hashes.csnames or allocate() -- namedata
+local marks = hashes.marks or allocate()
+local italics = hashes.italics or allocate()
+local lastmathids = hashes.lastmathids or allocate()
+local dynamics = hashes.dynamics or allocate()
+
+hashes.characters = characters
+hashes.descriptions = descriptions
+hashes.parameters = parameters
+hashes.properties = properties
+hashes.resources = resources
+hashes.spacings = spacings
+hashes.spaces = spaces
+hashes.quads = quads hashes.emwidths = quads
+hashes.xheights = xheights hashes.exheights = xheights
+hashes.csnames = csnames
+hashes.marks = marks
+hashes.italics = italics
+hashes.lastmathids = lastmathids
+hashes.dynamics = dynamics
+
+local nulldata = allocate {
+ name = "nullfont",
+ characters = { },
+ descriptions = { },
+ properties = { },
+ parameters = { -- lmromanregular @ 12pt
+ slantperpoint = 0,
+ spacing = {
+ width = 256377,
+ stretch = 128188,
+ shrink = 85459,
+ extra = 85459,
+ },
+ quad = 786432,
+ xheight = 338952,
+ -- compatibility:
+ slant = 0, -- 1
+ space = 256377, -- 2
+ space_stretch = 128188, -- 3
+ space_shrink = 85459, -- 4
+ x_height = 338952, -- 5
+ quad = 786432, -- 6
+ extra_space = 85459, -- 7
+ },
+}
+
+fonts.nulldata = nulldata
+
+fonts.constructors.enhanceparameters(nulldata.parameters) -- official copies for us
+
+setmetatableindex(identifiers, function(t,k)
+ return k == true and identifiers[currentfont()] or nulldata
+end)
+
+setmetatableindex(characters, function(t,k)
+ if k == true then
+ return characters[currentfont()]
+ else
+ local characters = identifiers[k].characters
+ t[k] = characters
+ return characters
+ end
+end)
+
+setmetatableindex(descriptions, function(t,k)
+ if k == true then
+ return descriptions[currentfont()]
+ else
+ local descriptions = identifiers[k].descriptions
+ t[k] = descriptions
+ return descriptions
+ end
+end)
+
+setmetatableindex(parameters, function(t,k)
+ if k == true then
+ return parameters[currentfont()]
+ else
+ local parameters = identifiers[k].parameters
+ t[k] = parameters
+ return parameters
+ end
+end)
+
+setmetatableindex(properties, function(t,k)
+ if k == true then
+ return properties[currentfont()]
+ else
+ local properties = identifiers[k].properties
+ t[k] = properties
+ return properties
+ end
+end)
+
+setmetatableindex(resources, function(t,k)
+ if k == true then
+ return resources[currentfont()]
+ else
+ local shared = identifiers[k].shared
+ local rawdata = shared and shared.rawdata
+ local resources = rawdata and rawdata.resources
+ t[k] = resources or false -- better than resolving each time
+ return resources
+ end
+end)
+
+setmetatableindex(quads, function(t,k)
+ if k == true then
+ return quads[currentfont()]
+ else
+ local parameters = parameters[k]
+ local quad = parameters and parameters.quad or 0
+ t[k] = quad
+ return quad
+ end
+end)
+
+local nospacing = {
+ width = 0,
+ stretch = 0,
+ shrink = 0,
+ extra = 0,
+}
+
+setmetatableindex(spacings, function(t,k)
+ if k == true then
+ return spacings[currentfont()]
+ else
+ local parameters = parameters[k]
+ local spacing = parameters and parameters.spacing or nospacing
+ t[k] = spacing
+ return spacing
+ end
+end)
+
+setmetatableindex(spaces, function(t,k)
+ if k == true then
+ return spaces[currentfont()]
+ else
+ local space = spacings[k].width
+ t[k] = space
+ return space
+ end
+end)
+
+setmetatableindex(marks, function(t,k)
+ if k == true then
+ return marks[currentfont()]
+ else
+ local resources = identifiers[k].resources or { }
+ local marks = resources.marks or { }
+ t[k] = marks
+ return marks
+ end
+end)
+
+setmetatableindex(xheights, function(t,k)
+ if k == true then
+ return xheights[currentfont()]
+ else
+ local parameters = parameters[k]
+ local xheight = parameters and parameters.xheight or 0
+ t[k] = xheight
+ return xheight
+ end
+end)
+
+setmetatableindex(italics, function(t,k) -- is test !
+ if k == true then
+ return italics[currentfont()]
+ else
+ local properties = identifiers[k].properties
+ local hasitalics = properties and properties.hasitalics
+ if hasitalics then
+ hasitalics = characters[k] -- convenient return
+ else
+ hasitalics = false
+ end
+ t[k] = hasitalics
+ return hasitalics
+ end
+end)
+
+setmetatableindex(dynamics, function(t,k)
+ if k == true then
+ return dynamics[currentfont()]
+ else
+ local shared = identifiers[k].shared
+ local dynamics = shared and shared.dynamics or false
+ t[k] = dynamics
+ return dynamics
+ end
+end)
+
+function font.getfont(id)
+ return identifiers[id]
+end
diff --git a/tex/context/base/font-ini.lua b/tex/context/base/font-ini.lua
index e902eca03..884b22474 100644
--- a/tex/context/base/font-ini.lua
+++ b/tex/context/base/font-ini.lua
@@ -1,32 +1,32 @@
-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 report_defining = logs.reporter("fonts","defining")
-
-fonts = fonts or { }
-local fonts = fonts
-
-fonts.hashes = { identifiers = allocate() }
-
-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 }
-
-fontloader.totable = fontloader.to_table
+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 report_defining = logs.reporter("fonts","defining")
+
+fonts = fonts or { }
+local fonts = fonts
+
+fonts.hashes = { identifiers = allocate() }
+
+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 }
+
+fontloader.totable = fontloader.to_table
diff --git a/tex/context/base/font-ldr.lua b/tex/context/base/font-ldr.lua
index 46cd396f8..175b4d0cc 100644
--- a/tex/context/base/font-ldr.lua
+++ b/tex/context/base/font-ldr.lua
@@ -1,70 +1,70 @@
-if not modules then modules = { } end modules ['font-ldr'] = {
- 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 provides an experimental replacement for fontloader.to_table
--- but is not used that much.
-
-local fields = fontloader.fields
-
-if fields then
-
- local glyphfields
-
- local function get_glyphs(r)
- local t = { }
- local g = r.glyphs
- for i=1,r.glyphmax-1 do
- local gi = g[i]
- if gi then
- if not glyphfields then
- glyphfields = fields(gi)
- end
- local h = { }
- for i=1,#glyphfields do
- local s = glyphfields[i]
- h[s] = gi[s]
- end
- t[i] = h
- end
- end
- return t
- end
-
- local function to_table(r)
- local f = fields(r)
- if f then
- local t = { }
- for i=1,#f do
- local fi = f[i]
- local ri = r[fi]
- if not ri then
- -- skip
- elseif fi == "glyphs" then
- t.glyphs = get_glyphs(r)
- elseif fi == "subfonts" then
- t[fi] = ri
- ri.glyphs = get_glyphs(ri)
- else
- t[fi] = r[fi]
- end
- end
- return t
- end
- end
-
- -- currently glyphs, subfont-glyphs and the main table are userdata
-
- function fonts.to_table(raw)
- return to_table(raw)
- end
-
-else
-
- fonts.to_table = fontloader.to_table
-
-end
+if not modules then modules = { } end modules ['font-ldr'] = {
+ 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 provides an experimental replacement for fontloader.to_table
+-- but is not used that much.
+
+local fields = fontloader.fields
+
+if fields then
+
+ local glyphfields
+
+ local function get_glyphs(r)
+ local t = { }
+ local g = r.glyphs
+ for i=1,r.glyphmax-1 do
+ local gi = g[i]
+ if gi then
+ if not glyphfields then
+ glyphfields = fields(gi)
+ end
+ local h = { }
+ for i=1,#glyphfields do
+ local s = glyphfields[i]
+ h[s] = gi[s]
+ end
+ t[i] = h
+ end
+ end
+ return t
+ end
+
+ local function to_table(r)
+ local f = fields(r)
+ if f then
+ local t = { }
+ for i=1,#f do
+ local fi = f[i]
+ local ri = r[fi]
+ if not ri then
+ -- skip
+ elseif fi == "glyphs" then
+ t.glyphs = get_glyphs(r)
+ elseif fi == "subfonts" then
+ t[fi] = ri
+ ri.glyphs = get_glyphs(ri)
+ else
+ t[fi] = r[fi]
+ end
+ end
+ return t
+ end
+ end
+
+ -- currently glyphs, subfont-glyphs and the main table are userdata
+
+ function fonts.to_table(raw)
+ return to_table(raw)
+ end
+
+else
+
+ fonts.to_table = fontloader.to_table
+
+end
diff --git a/tex/context/base/font-log.lua b/tex/context/base/font-log.lua
index 3a2a1c5de..41da75378 100644
--- a/tex/context/base/font-log.lua
+++ b/tex/context/base/font-log.lua
@@ -1,86 +1,86 @@
-if not modules then modules = { } end modules ['font-log'] = {
- 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 next, format, lower, concat = next, string.format, string.lower, table.concat
-
-local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end)
-local report_defining = logs.reporter("fonts","defining")
-
-local basename = file.basename
-
-local fonts = fonts
-local loggers = { }
-fonts.loggers = loggers
-local usedfonts = utilities.storage.allocate()
------ loadedfonts = utilities.storage.allocate()
-
---[[ldx--
-<p>The following functions are used for reporting about the fonts
-used. The message itself is not that useful in regular runs but since
-we now have several readers it may be handy to know what reader is
-used for which font.</p>
---ldx]]--
-
-function loggers.onetimemessage(font,char,message,reporter)
- local tfmdata = fonts.hashes.identifiers[font]
- local shared = tfmdata.shared
- 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 not category[char] then
- if not reporter then
- reporter = report_defining
- end
- reporter("char %U in font %a with id %s: %s",char,tfmdata.properties.fullname,font,message)
- category[char] = true
- end
-end
-
-function loggers.register(tfmdata,source,specification) -- save file name in spec here ! ! ! ! ! !
- if tfmdata and specification and specification.specification then
- local name = lower(specification.name)
- if trace_defining and not usedfonts[name] then
- report_defining("registering %a as %a, used %a",file.basename(specification.name),source,file.basename(specification.filename))
- end
- specification.source = source
- -- loadedfonts[lower(specification.specification)] = specification
- usedfonts[lower(specification.filename or specification.name)] = source
- end
-end
-
-function loggers.format(name) -- should be avoided
- return usedfonts[name] or "unknown"
-end
-
-statistics.register("loaded fonts", function()
- if next(usedfonts) then
- local t, n = { }, 0
- local treatmentdata = fonts.treatments.data
- for name, used in table.sortedhash(usedfonts) do
- n = n + 1
- local base = basename(name)
- if complete then
- t[n] = format("%s -> %s",used,base)
- else
- t[n] = base
- end
- local treatment = treatmentdata[base]
- if treatment and treatment.comment then
- t[n] = format("%s (%s)",t[n],treatment.comment)
- end
- end
- return n > 0 and format("%s files: %s",n,concat(t,", ")) or "none"
- end
-end)
+if not modules then modules = { } end modules ['font-log'] = {
+ 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 next, format, lower, concat = next, string.format, string.lower, table.concat
+
+local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end)
+local report_defining = logs.reporter("fonts","defining")
+
+local basename = file.basename
+
+local fonts = fonts
+local loggers = { }
+fonts.loggers = loggers
+local usedfonts = utilities.storage.allocate()
+----- loadedfonts = utilities.storage.allocate()
+
+--[[ldx--
+<p>The following functions are used for reporting about the fonts
+used. The message itself is not that useful in regular runs but since
+we now have several readers it may be handy to know what reader is
+used for which font.</p>
+--ldx]]--
+
+function loggers.onetimemessage(font,char,message,reporter)
+ local tfmdata = fonts.hashes.identifiers[font]
+ local shared = tfmdata.shared
+ 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 not category[char] then
+ if not reporter then
+ reporter = report_defining
+ end
+ reporter("char %U in font %a with id %s: %s",char,tfmdata.properties.fullname,font,message)
+ category[char] = true
+ end
+end
+
+function loggers.register(tfmdata,source,specification) -- save file name in spec here ! ! ! ! ! !
+ if tfmdata and specification and specification.specification then
+ local name = lower(specification.name)
+ if trace_defining and not usedfonts[name] then
+ report_defining("registering %a as %a, used %a",file.basename(specification.name),source,file.basename(specification.filename))
+ end
+ specification.source = source
+ -- loadedfonts[lower(specification.specification)] = specification
+ usedfonts[lower(specification.filename or specification.name)] = source
+ end
+end
+
+function loggers.format(name) -- should be avoided
+ return usedfonts[name] or "unknown"
+end
+
+statistics.register("loaded fonts", function()
+ if next(usedfonts) then
+ local t, n = { }, 0
+ local treatmentdata = fonts.treatments.data
+ for name, used in table.sortedhash(usedfonts) do
+ n = n + 1
+ local base = basename(name)
+ if complete then
+ t[n] = format("%s -> %s",used,base)
+ else
+ t[n] = base
+ end
+ local treatment = treatmentdata[base]
+ if treatment and treatment.comment then
+ t[n] = format("%s (%s)",t[n],treatment.comment)
+ end
+ end
+ return n > 0 and format("%s files: %s",n,concat(t,", ")) or "none"
+ end
+end)
diff --git a/tex/context/base/font-lua.lua b/tex/context/base/font-lua.lua
index 27b40e5b8..6fbbcf17e 100644
--- a/tex/context/base/font-lua.lua
+++ b/tex/context/base/font-lua.lua
@@ -1,46 +1,46 @@
-if not modules then modules = { } end modules ['font-lua'] = {
- 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 trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end)
-
-local report_lua = logs.reporter("fonts","lua loading")
-
-local fonts = fonts
-local readers = fonts.readers
-fonts.formats.lua = "lua"
-
--- we could add support for features here
-
-local function check_lua(specification,fullname)
- -- standard tex file lookup
- local fullname = resolvers.findfile(fullname) or ""
- if fullname ~= "" then
- local loader = loadfile(fullname)
- loader = loader and loader()
- return loader and loader(specification)
- end
-end
-
-readers.check_lua = check_lua
-
-function readers.lua(specification)
- local original = specification.specification
- if trace_defining then
- report_lua("using lua reader for %a",original)
- end
- local fullname = specification.filename or ""
- if fullname == "" then
- local forced = specification.forced or ""
- if forced ~= "" then
- fullname = specification.name .. "." .. forced
- else
- fullname = specification.name
- end
- end
- return check_lua(specification,fullname)
-end
+if not modules then modules = { } end modules ['font-lua'] = {
+ 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 trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end)
+
+local report_lua = logs.reporter("fonts","lua loading")
+
+local fonts = fonts
+local readers = fonts.readers
+fonts.formats.lua = "lua"
+
+-- we could add support for features here
+
+local function check_lua(specification,fullname)
+ -- standard tex file lookup
+ local fullname = resolvers.findfile(fullname) or ""
+ if fullname ~= "" then
+ local loader = loadfile(fullname)
+ loader = loader and loader()
+ return loader and loader(specification)
+ end
+end
+
+readers.check_lua = check_lua
+
+function readers.lua(specification)
+ local original = specification.specification
+ if trace_defining then
+ report_lua("using lua reader for %a",original)
+ end
+ local fullname = specification.filename or ""
+ if fullname == "" then
+ local forced = specification.forced or ""
+ if forced ~= "" then
+ fullname = specification.name .. "." .. forced
+ else
+ fullname = specification.name
+ end
+ end
+ return check_lua(specification,fullname)
+end
diff --git a/tex/context/base/font-map.lua b/tex/context/base/font-map.lua
index 864b43c24..6988b9b9e 100644
--- a/tex/context/base/font-map.lua
+++ b/tex/context/base/font-map.lua
@@ -1,329 +1,329 @@
-if not modules then modules = { } end modules ['font-map'] = {
- 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 = tonumber
-
-local match, format, find, concat, gsub, lower = string.match, string.format, string.find, table.concat, string.gsub, string.lower
-local P, R, S, C, Ct, Cc, lpegmatch = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.match
-local utfbyte = utf.byte
-local floor = math.floor
-
-local trace_loading = false trackers.register("fonts.loading", function(v) trace_loading = v end)
-local trace_mapping = false trackers.register("fonts.mapping", function(v) trace_unimapping = v end)
-
-local report_fonts = logs.reporter("fonts","loading") -- not otf only
-
-local fonts = fonts or { }
-local mappings = fonts.mappings or { }
-fonts.mappings = mappings
-
---[[ldx--
-<p>Eventually this code will disappear because map files are kind
-of obsolete. Some code may move to runtime or auxiliary modules.</p>
-<p>The name to unciode related code will stay of course.</p>
---ldx]]--
-
-local function loadlumtable(filename) -- will move to font goodies
- local lumname = file.replacesuffix(file.basename(filename),"lum")
- local lumfile = resolvers.findfile(lumname,"map") or ""
- if lumfile ~= "" and lfs.isfile(lumfile) then
- if trace_loading or trace_mapping then
- report_fonts("loading map table %a",lumfile)
- end
- lumunic = dofile(lumfile)
- return lumunic, lumfile
- end
-end
-
-local hex = R("AF","09")
-local hexfour = (hex*hex*hex*hex) / function(s) return tonumber(s,16) end
-local hexsix = (hex*hex*hex*hex*hex*hex) / function(s) return tonumber(s,16) end
-local dec = (R("09")^1) / tonumber
-local period = P(".")
-local unicode = P("uni") * (hexfour * (period + P(-1)) * Cc(false) + Ct(hexfour^1) * Cc(true))
-local ucode = P("u") * (hexsix * (period + P(-1)) * Cc(false) + Ct(hexsix ^1) * Cc(true))
-local index = P("index") * dec * Cc(false)
-
-local parser = unicode + ucode + index
-
-local parsers = { }
-
-local function makenameparser(str)
- if not str or str == "" then
- return parser
- else
- local p = parsers[str]
- if not p then
- p = P(str) * period * dec * Cc(false)
- parsers[str] = p
- end
- return p
- end
-end
-
--- local parser = makenameparser("Japan1")
--- local parser = makenameparser()
--- local function test(str)
--- local b, a = lpegmatch(parser,str)
--- print((a and table.serialize(b)) or b)
--- end
--- test("a.sc")
--- test("a")
--- test("uni1234")
--- test("uni1234.xx")
--- test("uni12349876")
--- test("u123400987600")
--- test("index1234")
--- test("Japan1.123")
-
-local function tounicode16(unicode,name)
- if unicode < 0x10000 then
- return format("%04X",unicode)
- elseif unicode < 0x1FFFFFFFFF then
- return format("%04X%04X",floor(unicode/1024),unicode%1024+0xDC00)
- else
- report_fonts("can't convert %a in %a into tounicode",unicode,name)
- end
-end
-
-local function tounicode16sequence(unicodes,name)
- local t = { }
- for l=1,#unicodes do
- local unicode = unicodes[l]
- if unicode < 0x10000 then
- t[l] = format("%04X",unicode)
- elseif unicode < 0x1FFFFFFFFF then
- t[l] = format("%04X%04X",floor(unicode/1024),unicode%1024+0xDC00)
- else
- report_fonts ("can't convert %a in %a into tounicode",unicode,name)
- end
- end
- return concat(t)
-end
-
-local function fromunicode16(str)
- if #str == 4 then
- return tonumber(str,16)
- else
- local l, r = match(str,"(....)(....)")
- return (tonumber(l,16))*0x400 + tonumber(r,16) - 0xDC00
- end
-end
-
--- Slightly slower:
---
--- local p = C(4) * (C(4)^-1) / function(l,r)
--- if r then
--- return (tonumber(l,16))*0x400 + tonumber(r,16) - 0xDC00
--- else
--- return tonumber(l,16)
--- end
--- end
---
--- local function fromunicode16(str)
--- return lpegmatch(p,str)
--- end
-
--- This is quite a bit faster but at the cost of some memory but if we
--- do this we will also use it elsewhere so let's not follow this route
--- now. I might use this method in the plain variant (no caching there)
--- but then I need a flag that distinguishes between code branches.
---
--- local cache = { }
---
--- function mappings.tounicode16(unicode)
--- local s = cache[unicode]
--- if not s then
--- if unicode < 0x10000 then
--- s = format("%04X",unicode)
--- else
--- s = format("%04X%04X",unicode/0x400+0xD800,unicode%0x400+0xDC00)
--- end
--- cache[unicode] = s
--- end
--- return s
--- end
-
-mappings.loadlumtable = loadlumtable
-mappings.makenameparser = makenameparser
-mappings.tounicode16 = tounicode16
-mappings.tounicode16sequence = tounicode16sequence
-mappings.fromunicode16 = fromunicode16
-
-local separator = S("_.")
-local other = C((1 - separator)^1)
-local ligsplitter = Ct(other * (separator * other)^0)
-
---~ print(table.serialize(lpegmatch(ligsplitter,"this")))
---~ print(table.serialize(lpegmatch(ligsplitter,"this.that")))
---~ print(table.serialize(lpegmatch(ligsplitter,"japan1.123")))
---~ print(table.serialize(lpegmatch(ligsplitter,"such_so_more")))
---~ print(table.serialize(lpegmatch(ligsplitter,"such_so_more.that")))
-
-function mappings.addtounicode(data,filename)
- local resources = data.resources
- local properties = data.properties
- local descriptions = data.descriptions
- local unicodes = resources.unicodes
- if not unicodes then
- return
- end
- -- we need to move this code
- unicodes['space'] = unicodes['space'] or 32
- unicodes['hyphen'] = unicodes['hyphen'] or 45
- unicodes['zwj'] = unicodes['zwj'] or 0x200D
- unicodes['zwnj'] = unicodes['zwnj'] or 0x200C
- -- the tounicode mapping is sparse and only needed for alternatives
- local private = fonts.constructors.privateoffset
- local unknown = format("%04X",utfbyte("?"))
- local unicodevector = fonts.encodings.agl.unicodes -- loaded runtime in context
- local tounicode = { }
- local originals = { }
- resources.tounicode = tounicode
- resources.originals = originals
- local lumunic, uparser, oparser
- local cidinfo, cidnames, cidcodes, usedmap
- if false then -- will become an option
- lumunic = loadlumtable(filename)
- lumunic = lumunic and lumunic.tounicode
- end
- --
- cidinfo = properties.cidinfo
- usedmap = cidinfo and fonts.cid.getmap(cidinfo)
- --
- if usedmap then
- oparser = usedmap and makenameparser(cidinfo.ordering)
- cidnames = usedmap.names
- cidcodes = usedmap.unicodes
- end
- uparser = makenameparser()
- local ns, nl = 0, 0
- for unic, glyph in next, descriptions do
- local index = glyph.index
- local name = glyph.name
- if unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then
- local unicode = lumunic and lumunic[name] or unicodevector[name]
- if unicode then
- originals[index] = unicode
- tounicode[index] = tounicode16(unicode,name)
- ns = ns + 1
- end
- -- cidmap heuristics, beware, there is no guarantee for a match unless
- -- the chain resolves
- if (not unicode) and usedmap then
- local foundindex = lpegmatch(oparser,name)
- if foundindex then
- unicode = cidcodes[foundindex] -- name to number
- if unicode then
- originals[index] = unicode
- tounicode[index] = tounicode16(unicode,name)
- ns = ns + 1
- else
- local reference = cidnames[foundindex] -- number to name
- if reference then
- local foundindex = lpegmatch(oparser,reference)
- if foundindex then
- unicode = cidcodes[foundindex]
- if unicode then
- originals[index] = unicode
- tounicode[index] = tounicode16(unicode,name)
- ns = ns + 1
- end
- end
- if not unicode or unicode == "" then
- local foundcodes, multiple = lpegmatch(uparser,reference)
- if foundcodes then
- originals[index] = foundcodes
- if multiple then
- tounicode[index] = tounicode16sequence(foundcodes)
- nl = nl + 1
- unicode = true
- else
- tounicode[index] = tounicode16(foundcodes,name)
- ns = ns + 1
- unicode = foundcodes
- end
- end
- end
- end
- end
- end
- end
- -- a.whatever or a_b_c.whatever or a_b_c (no numbers)
- if not unicode or unicode == "" then
- local split = lpegmatch(ligsplitter,name)
- local nplit = split and #split or 0
- if nplit >= 2 then
- local t, n = { }, 0
- for l=1,nplit do
- local base = split[l]
- local u = unicodes[base] or unicodevector[base]
- if not u then
- break
- elseif type(u) == "table" then
- n = n + 1
- t[n] = u[1]
- else
- n = n + 1
- t[n] = u
- end
- end
- if n == 0 then -- done then
- -- nothing
- elseif n == 1 then
- originals[index] = t[1]
- tounicode[index] = tounicode16(t[1],name)
- else
- originals[index] = t
- tounicode[index] = tounicode16sequence(t)
- end
- nl = nl + 1
- unicode = true
- else
- -- skip: already checked and we don't want privates here
- end
- end
- -- last resort (we might need to catch private here as well)
- if not unicode or unicode == "" then
- local foundcodes, multiple = lpegmatch(uparser,name)
- if foundcodes then
- if multiple then
- originals[index] = foundcodes
- tounicode[index] = tounicode16sequence(foundcodes,name)
- nl = nl + 1
- unicode = true
- else
- originals[index] = foundcodes
- tounicode[index] = tounicode16(foundcodes,name)
- ns = ns + 1
- unicode = foundcodes
- end
- end
- end
- -- if not unicode then
- -- originals[index] = 0xFFFD
- -- tounicode[index] = "FFFD"
- -- end
- end
- end
- if trace_mapping then
- for unic, glyph in table.sortedhash(descriptions) do
- local name = glyph.name
- local index = glyph.index
- local toun = tounicode[index]
- if toun then
- report_fonts("internal slot %U, name %a, unicode %U, tounicode %a",index,name,unic,toun)
- else
- report_fonts("internal slot %U, name %a, unicode %U",index,name,unic)
- end
- end
- end
- if trace_loading and (ns > 0 or nl > 0) then
- report_fonts("%s tounicode entries added, ligatures %s",nl+ns,ns)
- end
-end
+if not modules then modules = { } end modules ['font-map'] = {
+ 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 = tonumber
+
+local match, format, find, concat, gsub, lower = string.match, string.format, string.find, table.concat, string.gsub, string.lower
+local P, R, S, C, Ct, Cc, lpegmatch = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.match
+local utfbyte = utf.byte
+local floor = math.floor
+
+local trace_loading = false trackers.register("fonts.loading", function(v) trace_loading = v end)
+local trace_mapping = false trackers.register("fonts.mapping", function(v) trace_unimapping = v end)
+
+local report_fonts = logs.reporter("fonts","loading") -- not otf only
+
+local fonts = fonts or { }
+local mappings = fonts.mappings or { }
+fonts.mappings = mappings
+
+--[[ldx--
+<p>Eventually this code will disappear because map files are kind
+of obsolete. Some code may move to runtime or auxiliary modules.</p>
+<p>The name to unciode related code will stay of course.</p>
+--ldx]]--
+
+local function loadlumtable(filename) -- will move to font goodies
+ local lumname = file.replacesuffix(file.basename(filename),"lum")
+ local lumfile = resolvers.findfile(lumname,"map") or ""
+ if lumfile ~= "" and lfs.isfile(lumfile) then
+ if trace_loading or trace_mapping then
+ report_fonts("loading map table %a",lumfile)
+ end
+ lumunic = dofile(lumfile)
+ return lumunic, lumfile
+ end
+end
+
+local hex = R("AF","09")
+local hexfour = (hex*hex*hex*hex) / function(s) return tonumber(s,16) end
+local hexsix = (hex*hex*hex*hex*hex*hex) / function(s) return tonumber(s,16) end
+local dec = (R("09")^1) / tonumber
+local period = P(".")
+local unicode = P("uni") * (hexfour * (period + P(-1)) * Cc(false) + Ct(hexfour^1) * Cc(true))
+local ucode = P("u") * (hexsix * (period + P(-1)) * Cc(false) + Ct(hexsix ^1) * Cc(true))
+local index = P("index") * dec * Cc(false)
+
+local parser = unicode + ucode + index
+
+local parsers = { }
+
+local function makenameparser(str)
+ if not str or str == "" then
+ return parser
+ else
+ local p = parsers[str]
+ if not p then
+ p = P(str) * period * dec * Cc(false)
+ parsers[str] = p
+ end
+ return p
+ end
+end
+
+-- local parser = makenameparser("Japan1")
+-- local parser = makenameparser()
+-- local function test(str)
+-- local b, a = lpegmatch(parser,str)
+-- print((a and table.serialize(b)) or b)
+-- end
+-- test("a.sc")
+-- test("a")
+-- test("uni1234")
+-- test("uni1234.xx")
+-- test("uni12349876")
+-- test("u123400987600")
+-- test("index1234")
+-- test("Japan1.123")
+
+local function tounicode16(unicode,name)
+ if unicode < 0x10000 then
+ return format("%04X",unicode)
+ elseif unicode < 0x1FFFFFFFFF then
+ return format("%04X%04X",floor(unicode/1024),unicode%1024+0xDC00)
+ else
+ report_fonts("can't convert %a in %a into tounicode",unicode,name)
+ end
+end
+
+local function tounicode16sequence(unicodes,name)
+ local t = { }
+ for l=1,#unicodes do
+ local unicode = unicodes[l]
+ if unicode < 0x10000 then
+ t[l] = format("%04X",unicode)
+ elseif unicode < 0x1FFFFFFFFF then
+ t[l] = format("%04X%04X",floor(unicode/1024),unicode%1024+0xDC00)
+ else
+ report_fonts ("can't convert %a in %a into tounicode",unicode,name)
+ end
+ end
+ return concat(t)
+end
+
+local function fromunicode16(str)
+ if #str == 4 then
+ return tonumber(str,16)
+ else
+ local l, r = match(str,"(....)(....)")
+ return (tonumber(l,16))*0x400 + tonumber(r,16) - 0xDC00
+ end
+end
+
+-- Slightly slower:
+--
+-- local p = C(4) * (C(4)^-1) / function(l,r)
+-- if r then
+-- return (tonumber(l,16))*0x400 + tonumber(r,16) - 0xDC00
+-- else
+-- return tonumber(l,16)
+-- end
+-- end
+--
+-- local function fromunicode16(str)
+-- return lpegmatch(p,str)
+-- end
+
+-- This is quite a bit faster but at the cost of some memory but if we
+-- do this we will also use it elsewhere so let's not follow this route
+-- now. I might use this method in the plain variant (no caching there)
+-- but then I need a flag that distinguishes between code branches.
+--
+-- local cache = { }
+--
+-- function mappings.tounicode16(unicode)
+-- local s = cache[unicode]
+-- if not s then
+-- if unicode < 0x10000 then
+-- s = format("%04X",unicode)
+-- else
+-- s = format("%04X%04X",unicode/0x400+0xD800,unicode%0x400+0xDC00)
+-- end
+-- cache[unicode] = s
+-- end
+-- return s
+-- end
+
+mappings.loadlumtable = loadlumtable
+mappings.makenameparser = makenameparser
+mappings.tounicode16 = tounicode16
+mappings.tounicode16sequence = tounicode16sequence
+mappings.fromunicode16 = fromunicode16
+
+local separator = S("_.")
+local other = C((1 - separator)^1)
+local ligsplitter = Ct(other * (separator * other)^0)
+
+--~ print(table.serialize(lpegmatch(ligsplitter,"this")))
+--~ print(table.serialize(lpegmatch(ligsplitter,"this.that")))
+--~ print(table.serialize(lpegmatch(ligsplitter,"japan1.123")))
+--~ print(table.serialize(lpegmatch(ligsplitter,"such_so_more")))
+--~ print(table.serialize(lpegmatch(ligsplitter,"such_so_more.that")))
+
+function mappings.addtounicode(data,filename)
+ local resources = data.resources
+ local properties = data.properties
+ local descriptions = data.descriptions
+ local unicodes = resources.unicodes
+ if not unicodes then
+ return
+ end
+ -- we need to move this code
+ unicodes['space'] = unicodes['space'] or 32
+ unicodes['hyphen'] = unicodes['hyphen'] or 45
+ unicodes['zwj'] = unicodes['zwj'] or 0x200D
+ unicodes['zwnj'] = unicodes['zwnj'] or 0x200C
+ -- the tounicode mapping is sparse and only needed for alternatives
+ local private = fonts.constructors.privateoffset
+ local unknown = format("%04X",utfbyte("?"))
+ local unicodevector = fonts.encodings.agl.unicodes -- loaded runtime in context
+ local tounicode = { }
+ local originals = { }
+ resources.tounicode = tounicode
+ resources.originals = originals
+ local lumunic, uparser, oparser
+ local cidinfo, cidnames, cidcodes, usedmap
+ if false then -- will become an option
+ lumunic = loadlumtable(filename)
+ lumunic = lumunic and lumunic.tounicode
+ end
+ --
+ cidinfo = properties.cidinfo
+ usedmap = cidinfo and fonts.cid.getmap(cidinfo)
+ --
+ if usedmap then
+ oparser = usedmap and makenameparser(cidinfo.ordering)
+ cidnames = usedmap.names
+ cidcodes = usedmap.unicodes
+ end
+ uparser = makenameparser()
+ local ns, nl = 0, 0
+ for unic, glyph in next, descriptions do
+ local index = glyph.index
+ local name = glyph.name
+ if unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then
+ local unicode = lumunic and lumunic[name] or unicodevector[name]
+ if unicode then
+ originals[index] = unicode
+ tounicode[index] = tounicode16(unicode,name)
+ ns = ns + 1
+ end
+ -- cidmap heuristics, beware, there is no guarantee for a match unless
+ -- the chain resolves
+ if (not unicode) and usedmap then
+ local foundindex = lpegmatch(oparser,name)
+ if foundindex then
+ unicode = cidcodes[foundindex] -- name to number
+ if unicode then
+ originals[index] = unicode
+ tounicode[index] = tounicode16(unicode,name)
+ ns = ns + 1
+ else
+ local reference = cidnames[foundindex] -- number to name
+ if reference then
+ local foundindex = lpegmatch(oparser,reference)
+ if foundindex then
+ unicode = cidcodes[foundindex]
+ if unicode then
+ originals[index] = unicode
+ tounicode[index] = tounicode16(unicode,name)
+ ns = ns + 1
+ end
+ end
+ if not unicode or unicode == "" then
+ local foundcodes, multiple = lpegmatch(uparser,reference)
+ if foundcodes then
+ originals[index] = foundcodes
+ if multiple then
+ tounicode[index] = tounicode16sequence(foundcodes)
+ nl = nl + 1
+ unicode = true
+ else
+ tounicode[index] = tounicode16(foundcodes,name)
+ ns = ns + 1
+ unicode = foundcodes
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ -- a.whatever or a_b_c.whatever or a_b_c (no numbers)
+ if not unicode or unicode == "" then
+ local split = lpegmatch(ligsplitter,name)
+ local nplit = split and #split or 0
+ if nplit >= 2 then
+ local t, n = { }, 0
+ for l=1,nplit do
+ local base = split[l]
+ local u = unicodes[base] or unicodevector[base]
+ if not u then
+ break
+ elseif type(u) == "table" then
+ n = n + 1
+ t[n] = u[1]
+ else
+ n = n + 1
+ t[n] = u
+ end
+ end
+ if n == 0 then -- done then
+ -- nothing
+ elseif n == 1 then
+ originals[index] = t[1]
+ tounicode[index] = tounicode16(t[1],name)
+ else
+ originals[index] = t
+ tounicode[index] = tounicode16sequence(t)
+ end
+ nl = nl + 1
+ unicode = true
+ else
+ -- skip: already checked and we don't want privates here
+ end
+ end
+ -- last resort (we might need to catch private here as well)
+ if not unicode or unicode == "" then
+ local foundcodes, multiple = lpegmatch(uparser,name)
+ if foundcodes then
+ if multiple then
+ originals[index] = foundcodes
+ tounicode[index] = tounicode16sequence(foundcodes,name)
+ nl = nl + 1
+ unicode = true
+ else
+ originals[index] = foundcodes
+ tounicode[index] = tounicode16(foundcodes,name)
+ ns = ns + 1
+ unicode = foundcodes
+ end
+ end
+ end
+ -- if not unicode then
+ -- originals[index] = 0xFFFD
+ -- tounicode[index] = "FFFD"
+ -- end
+ end
+ end
+ if trace_mapping then
+ for unic, glyph in table.sortedhash(descriptions) do
+ local name = glyph.name
+ local index = glyph.index
+ local toun = tounicode[index]
+ if toun then
+ report_fonts("internal slot %U, name %a, unicode %U, tounicode %a",index,name,unic,toun)
+ else
+ report_fonts("internal slot %U, name %a, unicode %U",index,name,unic)
+ end
+ end
+ end
+ if trace_loading and (ns > 0 or nl > 0) then
+ report_fonts("%s tounicode entries added, ligatures %s",nl+ns,ns)
+ end
+end
diff --git a/tex/context/base/font-mis.lua b/tex/context/base/font-mis.lua
index 1915f7a82..83df65341 100644
--- a/tex/context/base/font-mis.lua
+++ b/tex/context/base/font-mis.lua
@@ -1,111 +1,111 @@
-if not modules then modules = { } end modules ['font-mis'] = {
- version = 1.001,
- comment = "companion to mtx-fonts",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local next = next
-local lower, strip = string.lower, string.strip
-
--- also used in other scripts so we need to check some tables:
-
-fonts = fonts or { }
-
-fonts.helpers = fonts.helpers or { }
-local helpers = fonts.helpers
-
-fonts.handlers = fonts.handlers or { }
-local handlers = fonts.handlers
-
-handlers.otf = handlers.otf or { }
-local otf = handlers.otf
-
-otf.version = otf.version or 2.743
-otf.cache = otf.cache or containers.define("fonts", "otf", otf.version, true)
-
-function otf.loadcached(filename,format,sub)
- -- no recache when version mismatch
- local name = file.basename(file.removesuffix(filename))
- if sub == "" then sub = false end
- local hash = name
- if sub then
- hash = hash .. "-" .. sub
- end
- hash = containers.cleanname(hash)
- local data = containers.read(otf.cache, hash)
- if data and not data.verbose then
- otf.enhancers.unpack(data)
- return data
- else
- return nil
- end
-end
-
-local featuregroups = { "gsub", "gpos" }
-
-function fonts.helpers.getfeatures(name,t,script,language) -- maybe per font type
- local t = lower(t or (name and file.suffix(name)) or "")
- if t == "otf" or t == "ttf" or t == "ttc" or t == "dfont" then
- local filename = resolvers.findfile(name,t) or ""
- if filename ~= "" then
- local data = otf.loadcached(filename)
- if data and data.resources and data.resources.features then
- return data.resources.features
- else
- local ff = fontloader.open(filename)
- if ff then
- local data = fontloader.to_table(ff)
- fontloader.close(ff)
- local features = { }
- for k=1,#featuregroups do
- local what = featuregroups[k]
- local dw = data[what]
- if dw then
- local f = { }
- features[what] = f
- for i=1,#dw do
- local d = dw[i]
- local dfeatures = d.features
- if dfeatures then
- for i=1,#dfeatures do
- local df = dfeatures[i]
- local tag = strip(lower(df.tag))
- local ft = f[tag] if not ft then ft = {} f[tag] = ft end
- local dfscripts = df.scripts
- for i=1,#dfscripts do
- local ds = dfscripts[i]
- local scri = strip(lower(ds.script))
- local fts = ft[scri] if not fts then fts = {} ft[scri] = fts end
- local dslangs = ds.langs
- for i=1,#dslangs do
- local lang = dslangs[i]
- lang = strip(lower(lang))
- if scri == script then
- if lang == language then
- fts[lang] = 'sl'
- else
- fts[lang] = 's'
- end
- else
- if lang == language then
- fts[lang] = 'l'
- else
- fts[lang] = true
- end
- end
- end
- end
- end
- end
- end
- end
- end
- return features
- end
- end
- end
- end
- return nil, nil
-end
+if not modules then modules = { } end modules ['font-mis'] = {
+ version = 1.001,
+ comment = "companion to mtx-fonts",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local next = next
+local lower, strip = string.lower, string.strip
+
+-- also used in other scripts so we need to check some tables:
+
+fonts = fonts or { }
+
+fonts.helpers = fonts.helpers or { }
+local helpers = fonts.helpers
+
+fonts.handlers = fonts.handlers or { }
+local handlers = fonts.handlers
+
+handlers.otf = handlers.otf or { }
+local otf = handlers.otf
+
+otf.version = otf.version or 2.743
+otf.cache = otf.cache or containers.define("fonts", "otf", otf.version, true)
+
+function otf.loadcached(filename,format,sub)
+ -- no recache when version mismatch
+ local name = file.basename(file.removesuffix(filename))
+ if sub == "" then sub = false end
+ local hash = name
+ if sub then
+ hash = hash .. "-" .. sub
+ end
+ hash = containers.cleanname(hash)
+ local data = containers.read(otf.cache, hash)
+ if data and not data.verbose then
+ otf.enhancers.unpack(data)
+ return data
+ else
+ return nil
+ end
+end
+
+local featuregroups = { "gsub", "gpos" }
+
+function fonts.helpers.getfeatures(name,t,script,language) -- maybe per font type
+ local t = lower(t or (name and file.suffix(name)) or "")
+ if t == "otf" or t == "ttf" or t == "ttc" or t == "dfont" then
+ local filename = resolvers.findfile(name,t) or ""
+ if filename ~= "" then
+ local data = otf.loadcached(filename)
+ if data and data.resources and data.resources.features then
+ return data.resources.features
+ else
+ local ff = fontloader.open(filename)
+ if ff then
+ local data = fontloader.to_table(ff)
+ fontloader.close(ff)
+ local features = { }
+ for k=1,#featuregroups do
+ local what = featuregroups[k]
+ local dw = data[what]
+ if dw then
+ local f = { }
+ features[what] = f
+ for i=1,#dw do
+ local d = dw[i]
+ local dfeatures = d.features
+ if dfeatures then
+ for i=1,#dfeatures do
+ local df = dfeatures[i]
+ local tag = strip(lower(df.tag))
+ local ft = f[tag] if not ft then ft = {} f[tag] = ft end
+ local dfscripts = df.scripts
+ for i=1,#dfscripts do
+ local ds = dfscripts[i]
+ local scri = strip(lower(ds.script))
+ local fts = ft[scri] if not fts then fts = {} ft[scri] = fts end
+ local dslangs = ds.langs
+ for i=1,#dslangs do
+ local lang = dslangs[i]
+ lang = strip(lower(lang))
+ if scri == script then
+ if lang == language then
+ fts[lang] = 'sl'
+ else
+ fts[lang] = 's'
+ end
+ else
+ if lang == language then
+ fts[lang] = 'l'
+ else
+ fts[lang] = true
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ return features
+ end
+ end
+ end
+ end
+ return nil, nil
+end
diff --git a/tex/context/base/font-nod.lua b/tex/context/base/font-nod.lua
index 7c93e294c..f99130279 100644
--- a/tex/context/base/font-nod.lua
+++ b/tex/context/base/font-nod.lua
@@ -1,434 +1,434 @@
-if not modules then modules = { } end modules ['font-nod'] = {
- 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>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 tonumber, tostring = tonumber, tostring
-local utfchar = utf.char
-local concat = table.concat
-local match, gmatch, concat, rep = string.match, string.gmatch, table.concat, string.rep
-
-local report_nodes = logs.reporter("fonts","tracing")
-
-fonts = fonts or { }
-nodes = nodes or { }
-
-local fonts, nodes, node, context = fonts, nodes, node, context
-
-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 char_tracers = tracers.characters or { }
-tracers.characters = char_tracers
-
-local step_tracers = tracers.steppers or { }
-tracers.steppers = step_tracers
-
-local copy_node_list = node.copy_list
-local hpack_node_list = node.hpack
-local free_node_list = node.flush_list
-local traverse_nodes = node.traverse
-
-local nodecodes = nodes.nodecodes
-local whatcodes = nodes.whatcodes
-
-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 whatsit_code = nodecodes.whatsit
-local spec_code = nodecodes.glue_spec
-
-local localpar_code = whatcodes.localpar
-local dir_code = whatcodes.dir
-
-local nodepool = nodes.pool
-local new_glyph = nodepool.glyph
-
-local formatters = string.formatters
-local formatter = string.formatter
-
-local hashes = fonts.hashes
-
-local fontidentifiers = hashes.identifiers
-local fontdescriptions = hashes.descriptions
-local fontcharacters = hashes.characters
-local fontproperties = hashes.properties
-local fontparameters = hashes.parameters
-
-function char_tracers.collect(head,list,tag,n)
- n = n or 0
- local ok, fn = false, nil
- while head do
- local id = head.id
- if id == glyph_code then
- local f = head.font
- if f ~= fn then
- ok, fn = false, f
- end
- local c = head.char
- local i = fontidentifiers[f].indices[c] or 0
- if not ok then
- ok = true
- n = n + 1
- list[n] = list[n] or { }
- list[n][tag] = { }
- end
- local l = list[n][tag]
- l[#l+1] = { c, f, i }
- elseif id == disc_code then
- -- skip
- else
- ok = false
- end
- head = head.next
- end
-end
-
-function char_tracers.equal(ta, tb)
- if #ta ~= #tb then
- return false
- else
- for i=1,#ta do
- local a, b = ta[i], tb[i]
- if a[1] ~= b[1] or a[2] ~= b[2] or a[3] ~= b[3] then
- return false
- end
- end
- end
- return true
-end
-
-function char_tracers.string(t)
- local tt = { }
- for i=1,#t do
- tt[i] = utfchar(t[i][1])
- end
- return concat(tt,"")
-end
-
-local f_unicode = formatters["%U"]
-
-function char_tracers.unicodes(t,decimal)
- local tt = { }
- for i=1,#t do
- local n = t[i][1]
- if n == 0 then
- tt[i] = "-"
- elseif decimal then
- tt[i] = n
- else
- tt[i] = f_unicode(n)
- end
- end
- return concat(tt," ")
-end
-
-function char_tracers.indices(t,decimal)
- local tt = { }
- for i=1,#t do
- local n = t[i][3]
- if n == 0 then
- tt[i] = "-"
- elseif decimal then
- tt[i] = n
- else
- tt[i] = f_unicode(n)
- end
- end
- return concat(tt," ")
-end
-
-function char_tracers.start()
- local npc = handlers.characters
- local list = { }
- function handlers.characters(head)
- local n = #list
- char_tracers.collect(head,list,'before',n)
- local h, d = npc(head)
- char_tracers.collect(head,list,'after',n)
- if #list > n then
- list[#list+1] = { }
- end
- return h, d
- end
- function char_tracers.stop()
- tracers.list['characters'] = list
- local variables = {
- ['title'] = 'ConTeXt Character Processing Information',
- ['color-background-one'] = lmx.get('color-background-yellow'),
- ['color-background-two'] = lmx.get('color-background-purple'),
- }
- lmx.show('context-characters.lmx',variables)
- handlers.characters = npc
- tasks.restart("processors", "characters")
- end
- tasks.restart("processors", "characters")
-end
-
-local stack = { }
-
-function tracers.start(tag)
- stack[#stack+1] = tag
- local tracer = tracers[tag]
- if tracer and tracer.start then
- tracer.start()
- end
-end
-function tracers.stop()
- local tracer = stack[#stack]
- if tracer and tracer.stop then
- tracer.stop()
- end
- stack[#stack] = nil
-end
-
--- experimental
-
-local collection, collecting, messages = { }, false, { }
-
-function step_tracers.start()
- collecting = true
-end
-
-function step_tracers.stop()
- collecting = false
-end
-
-function step_tracers.reset()
- for i=1,#collection do
- local c = collection[i]
- if c then
- free_node_list(c)
- end
- end
- collection, messages = { }, { }
-end
-
-function step_tracers.nofsteps()
- return context(#collection)
-end
-
-function step_tracers.glyphs(n,i)
- local c = collection[i]
- if c then
- tex.box[n] = hpack_node_list(copy_node_list(c))
- end
-end
-
-function step_tracers.features()
- -- we cannot use first_glyph here as it only finds characters with subtype < 256
- local f = collection[1]
- while f do
- if f.id == glyph_code then
- local tfmdata, t = fontidentifiers[f.font], { }
- for feature, value in table.sortedhash(tfmdata.shared.features) do
- if feature == "number" or feature == "features" then
- -- private
- elseif type(value) == "boolean" then
- if value then
- t[#t+1] = formatters["%s=yes"](feature)
- else
- -- skip
- end
- else
- t[#t+1] = formatters["%s=%s"](feature,value)
- end
- end
- if #t > 0 then
- context(concat(t,", "))
- else
- context("no features")
- end
- return
- end
- f = f.next
- end
-end
-
-function tracers.fontchar(font,char)
- local n = new_glyph()
- n.font, n.char, n.subtype = font, char, 256
- context(n)
-end
-
-function step_tracers.font(command)
- local c = collection[1]
- while c do
- local id = c.id
- if id == glyph_code then
- local font = c.font
- local name = file.basename(fontproperties[font].filename or "unknown")
- local size = fontparameters[font].size or 0
- if command then
- context[command](font,name,size) -- size in sp
- else
- context("[%s: %s @ %p]",font,name,size)
- end
- return
- else
- c = c.next
- end
- end
-end
-
-function step_tracers.codes(i,command)
- local c = collection[i]
- while c do
- local id = c.id
- if id == glyph_code then
- if command then
- local f, c = c.font,c.char
- local d = fontdescriptions[f]
- local d = d and d[c]
- context[command](f,c,d and d.class or "")
- else
- context("[%s:U+%04X]",c.font,c.char)
- end
- elseif id == whatsit_code and (c.subtype == localpar_code or c.subtype == dir_code) then
- context("[%s]",c.dir)
- else
- context("[%s]",nodecodes[id])
- end
- c = c.next
- end
-end
-
-function step_tracers.messages(i,command,split)
- local list = messages[i] -- or { "no messages" }
- if list then
- for i=1,#list do
- local l = list[i]
- if not command then
- context("(%s)",l)
- elseif split then
- local a, b = match(l,"^(.-)%s*:%s*(.*)$")
- context[command](a or l or "",b or "")
- else
- context[command](l)
- end
- end
- end
-end
-
--- hooks into the node list processor (see otf)
-
-function step_tracers.check(head)
- if collecting then
- step_tracers.reset()
- local n = copy_node_list(head)
- injections.handler(n,nil,"trace",true)
- handlers.protectglyphs(n) -- can be option
- collection[1] = n
- end
-end
-
-function step_tracers.register(head)
- if collecting then
- local nc = #collection+1
- if messages[nc] then
- local n = copy_node_list(head)
- injections.handler(n,nil,"trace",true)
- handlers.protectglyphs(n) -- can be option
- collection[nc] = n
- end
- end
-end
-
-function step_tracers.message(str,...)
- str = formatter(str,...)
- if collecting then
- local n = #collection + 1
- local m = messages[n]
- if not m then m = { } messages[n] = m end
- m[#m+1] = str
- end
- return str -- saves an intermediate var in the caller
-end
-
---
-
-local threshold = 65536
-
-local function toutf(list,result,nofresult,stopcriterium)
- if list then
- for n in traverse_nodes(list) do
- local id = n.id
- if id == glyph_code then
- local components = n.components
- if components then
- result, nofresult = toutf(components,result,nofresult)
- else
- local c = n.char
- local fc = fontcharacters[n.font]
- if fc then
- local u = fc[c].tounicode
- if u then
- for s in gmatch(u,"....") do
- nofresult = nofresult + 1
- result[nofresult] = utfchar(tonumber(s,16))
- end
- else
- nofresult = nofresult + 1
- result[nofresult] = utfchar(c)
- end
- else
- nofresult = nofresult + 1
- result[nofresult] = utfchar(c)
- end
- end
- elseif id == disc_code then
- result, nofresult = toutf(n.replace,result,nofresult) -- needed?
- elseif id == hlist_code or id == vlist_code then
- -- if nofresult > 0 and result[nofresult] ~= " " then
- -- nofresult = nofresult + 1
- -- result[nofresult] = " "
- -- end
- result, nofresult = toutf(n.list,result,nofresult)
- elseif id == glue_code then
- if nofresult > 0 and result[nofresult] ~= " " then
- nofresult = nofresult + 1
- result[nofresult] = " "
- end
- elseif id == kern_code and n.kern > threshold then
- if nofresult > 0 and result[nofresult] ~= " " then
- nofresult = nofresult + 1
- result[nofresult] = " "
- end
- end
- if n == stopcriterium then
- break
- end
- end
- end
- if nofresult > 0 and result[nofresult] == " " then
- result[nofresult] = nil
- nofresult = nofresult - 1
- end
- return result, nofresult
-end
-
-function nodes.toutf(list,stopcriterium)
- local result, nofresult = toutf(list,{},0,stopcriterium)
- return concat(result)
-end
+if not modules then modules = { } end modules ['font-nod'] = {
+ 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>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 tonumber, tostring = tonumber, tostring
+local utfchar = utf.char
+local concat = table.concat
+local match, gmatch, concat, rep = string.match, string.gmatch, table.concat, string.rep
+
+local report_nodes = logs.reporter("fonts","tracing")
+
+fonts = fonts or { }
+nodes = nodes or { }
+
+local fonts, nodes, node, context = fonts, nodes, node, context
+
+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 char_tracers = tracers.characters or { }
+tracers.characters = char_tracers
+
+local step_tracers = tracers.steppers or { }
+tracers.steppers = step_tracers
+
+local copy_node_list = node.copy_list
+local hpack_node_list = node.hpack
+local free_node_list = node.flush_list
+local traverse_nodes = node.traverse
+
+local nodecodes = nodes.nodecodes
+local whatcodes = nodes.whatcodes
+
+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 whatsit_code = nodecodes.whatsit
+local spec_code = nodecodes.glue_spec
+
+local localpar_code = whatcodes.localpar
+local dir_code = whatcodes.dir
+
+local nodepool = nodes.pool
+local new_glyph = nodepool.glyph
+
+local formatters = string.formatters
+local formatter = string.formatter
+
+local hashes = fonts.hashes
+
+local fontidentifiers = hashes.identifiers
+local fontdescriptions = hashes.descriptions
+local fontcharacters = hashes.characters
+local fontproperties = hashes.properties
+local fontparameters = hashes.parameters
+
+function char_tracers.collect(head,list,tag,n)
+ n = n or 0
+ local ok, fn = false, nil
+ while head do
+ local id = head.id
+ if id == glyph_code then
+ local f = head.font
+ if f ~= fn then
+ ok, fn = false, f
+ end
+ local c = head.char
+ local i = fontidentifiers[f].indices[c] or 0
+ if not ok then
+ ok = true
+ n = n + 1
+ list[n] = list[n] or { }
+ list[n][tag] = { }
+ end
+ local l = list[n][tag]
+ l[#l+1] = { c, f, i }
+ elseif id == disc_code then
+ -- skip
+ else
+ ok = false
+ end
+ head = head.next
+ end
+end
+
+function char_tracers.equal(ta, tb)
+ if #ta ~= #tb then
+ return false
+ else
+ for i=1,#ta do
+ local a, b = ta[i], tb[i]
+ if a[1] ~= b[1] or a[2] ~= b[2] or a[3] ~= b[3] then
+ return false
+ end
+ end
+ end
+ return true
+end
+
+function char_tracers.string(t)
+ local tt = { }
+ for i=1,#t do
+ tt[i] = utfchar(t[i][1])
+ end
+ return concat(tt,"")
+end
+
+local f_unicode = formatters["%U"]
+
+function char_tracers.unicodes(t,decimal)
+ local tt = { }
+ for i=1,#t do
+ local n = t[i][1]
+ if n == 0 then
+ tt[i] = "-"
+ elseif decimal then
+ tt[i] = n
+ else
+ tt[i] = f_unicode(n)
+ end
+ end
+ return concat(tt," ")
+end
+
+function char_tracers.indices(t,decimal)
+ local tt = { }
+ for i=1,#t do
+ local n = t[i][3]
+ if n == 0 then
+ tt[i] = "-"
+ elseif decimal then
+ tt[i] = n
+ else
+ tt[i] = f_unicode(n)
+ end
+ end
+ return concat(tt," ")
+end
+
+function char_tracers.start()
+ local npc = handlers.characters
+ local list = { }
+ function handlers.characters(head)
+ local n = #list
+ char_tracers.collect(head,list,'before',n)
+ local h, d = npc(head)
+ char_tracers.collect(head,list,'after',n)
+ if #list > n then
+ list[#list+1] = { }
+ end
+ return h, d
+ end
+ function char_tracers.stop()
+ tracers.list['characters'] = list
+ local variables = {
+ ['title'] = 'ConTeXt Character Processing Information',
+ ['color-background-one'] = lmx.get('color-background-yellow'),
+ ['color-background-two'] = lmx.get('color-background-purple'),
+ }
+ lmx.show('context-characters.lmx',variables)
+ handlers.characters = npc
+ tasks.restart("processors", "characters")
+ end
+ tasks.restart("processors", "characters")
+end
+
+local stack = { }
+
+function tracers.start(tag)
+ stack[#stack+1] = tag
+ local tracer = tracers[tag]
+ if tracer and tracer.start then
+ tracer.start()
+ end
+end
+function tracers.stop()
+ local tracer = stack[#stack]
+ if tracer and tracer.stop then
+ tracer.stop()
+ end
+ stack[#stack] = nil
+end
+
+-- experimental
+
+local collection, collecting, messages = { }, false, { }
+
+function step_tracers.start()
+ collecting = true
+end
+
+function step_tracers.stop()
+ collecting = false
+end
+
+function step_tracers.reset()
+ for i=1,#collection do
+ local c = collection[i]
+ if c then
+ free_node_list(c)
+ end
+ end
+ collection, messages = { }, { }
+end
+
+function step_tracers.nofsteps()
+ return context(#collection)
+end
+
+function step_tracers.glyphs(n,i)
+ local c = collection[i]
+ if c then
+ tex.box[n] = hpack_node_list(copy_node_list(c))
+ end
+end
+
+function step_tracers.features()
+ -- we cannot use first_glyph here as it only finds characters with subtype < 256
+ local f = collection[1]
+ while f do
+ if f.id == glyph_code then
+ local tfmdata, t = fontidentifiers[f.font], { }
+ for feature, value in table.sortedhash(tfmdata.shared.features) do
+ if feature == "number" or feature == "features" then
+ -- private
+ elseif type(value) == "boolean" then
+ if value then
+ t[#t+1] = formatters["%s=yes"](feature)
+ else
+ -- skip
+ end
+ else
+ t[#t+1] = formatters["%s=%s"](feature,value)
+ end
+ end
+ if #t > 0 then
+ context(concat(t,", "))
+ else
+ context("no features")
+ end
+ return
+ end
+ f = f.next
+ end
+end
+
+function tracers.fontchar(font,char)
+ local n = new_glyph()
+ n.font, n.char, n.subtype = font, char, 256
+ context(n)
+end
+
+function step_tracers.font(command)
+ local c = collection[1]
+ while c do
+ local id = c.id
+ if id == glyph_code then
+ local font = c.font
+ local name = file.basename(fontproperties[font].filename or "unknown")
+ local size = fontparameters[font].size or 0
+ if command then
+ context[command](font,name,size) -- size in sp
+ else
+ context("[%s: %s @ %p]",font,name,size)
+ end
+ return
+ else
+ c = c.next
+ end
+ end
+end
+
+function step_tracers.codes(i,command)
+ local c = collection[i]
+ while c do
+ local id = c.id
+ if id == glyph_code then
+ if command then
+ local f, c = c.font,c.char
+ local d = fontdescriptions[f]
+ local d = d and d[c]
+ context[command](f,c,d and d.class or "")
+ else
+ context("[%s:U+%04X]",c.font,c.char)
+ end
+ elseif id == whatsit_code and (c.subtype == localpar_code or c.subtype == dir_code) then
+ context("[%s]",c.dir)
+ else
+ context("[%s]",nodecodes[id])
+ end
+ c = c.next
+ end
+end
+
+function step_tracers.messages(i,command,split)
+ local list = messages[i] -- or { "no messages" }
+ if list then
+ for i=1,#list do
+ local l = list[i]
+ if not command then
+ context("(%s)",l)
+ elseif split then
+ local a, b = match(l,"^(.-)%s*:%s*(.*)$")
+ context[command](a or l or "",b or "")
+ else
+ context[command](l)
+ end
+ end
+ end
+end
+
+-- hooks into the node list processor (see otf)
+
+function step_tracers.check(head)
+ if collecting then
+ step_tracers.reset()
+ local n = copy_node_list(head)
+ injections.handler(n,nil,"trace",true)
+ handlers.protectglyphs(n) -- can be option
+ collection[1] = n
+ end
+end
+
+function step_tracers.register(head)
+ if collecting then
+ local nc = #collection+1
+ if messages[nc] then
+ local n = copy_node_list(head)
+ injections.handler(n,nil,"trace",true)
+ handlers.protectglyphs(n) -- can be option
+ collection[nc] = n
+ end
+ end
+end
+
+function step_tracers.message(str,...)
+ str = formatter(str,...)
+ if collecting then
+ local n = #collection + 1
+ local m = messages[n]
+ if not m then m = { } messages[n] = m end
+ m[#m+1] = str
+ end
+ return str -- saves an intermediate var in the caller
+end
+
+--
+
+local threshold = 65536
+
+local function toutf(list,result,nofresult,stopcriterium)
+ if list then
+ for n in traverse_nodes(list) do
+ local id = n.id
+ if id == glyph_code then
+ local components = n.components
+ if components then
+ result, nofresult = toutf(components,result,nofresult)
+ else
+ local c = n.char
+ local fc = fontcharacters[n.font]
+ if fc then
+ local u = fc[c].tounicode
+ if u then
+ for s in gmatch(u,"....") do
+ nofresult = nofresult + 1
+ result[nofresult] = utfchar(tonumber(s,16))
+ end
+ else
+ nofresult = nofresult + 1
+ result[nofresult] = utfchar(c)
+ end
+ else
+ nofresult = nofresult + 1
+ result[nofresult] = utfchar(c)
+ end
+ end
+ elseif id == disc_code then
+ result, nofresult = toutf(n.replace,result,nofresult) -- needed?
+ elseif id == hlist_code or id == vlist_code then
+ -- if nofresult > 0 and result[nofresult] ~= " " then
+ -- nofresult = nofresult + 1
+ -- result[nofresult] = " "
+ -- end
+ result, nofresult = toutf(n.list,result,nofresult)
+ elseif id == glue_code then
+ if nofresult > 0 and result[nofresult] ~= " " then
+ nofresult = nofresult + 1
+ result[nofresult] = " "
+ end
+ elseif id == kern_code and n.kern > threshold then
+ if nofresult > 0 and result[nofresult] ~= " " then
+ nofresult = nofresult + 1
+ result[nofresult] = " "
+ end
+ end
+ if n == stopcriterium then
+ break
+ end
+ end
+ end
+ if nofresult > 0 and result[nofresult] == " " then
+ result[nofresult] = nil
+ nofresult = nofresult - 1
+ end
+ return result, nofresult
+end
+
+function nodes.toutf(list,stopcriterium)
+ local result, nofresult = toutf(list,{},0,stopcriterium)
+ return concat(result)
+end
diff --git a/tex/context/base/font-odk.lua b/tex/context/base/font-odk.lua
index 3ed562348..c34efc120 100644
--- a/tex/context/base/font-odk.lua
+++ b/tex/context/base/font-odk.lua
@@ -1,904 +1,904 @@
--- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
--- We keep the original around for a while so that we can check it --
--- when the above code does it wrong (data tables are not included). --
--- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-
--- author : Kai Eigner, TAT Zetwerk
--- copyright : TAT Zetwerk
--- comment : see font-odv.lua for current implementation
-
--- local state = attributes.private('state')
--- local sylnr = attributes.private('syllabe')
---
--- local function install_dev(tfmdata)
--- local features = tfmdata.resources.features
--- local sequences = tfmdata.resources.sequences
---
--- local insertpos = 1
--- for s=1,#sequences do -- classify chars
--- for k in pairs(basic_shaping_forms) do
--- if sequences[s].features and ( sequences[s].features[k] or sequences[s].features.locl ) then insertpos = s + 1 end
--- end
--- end
---
--- features.gsub["dev2_reorder_matras"] = { ["dev2"] = { ["dflt"] = true } }
--- features.gsub["dev2_reorder_reph"] = { ["dev2"] = { ["dflt"] = true } }
--- features.gsub["dev2_reorder_pre_base_reordering_consonants"] = { ["dev2"] = { ["dflt"] = true } }
--- features.gsub["remove_joiners"] = { ["deva"] = { ["dflt"] = true }, ["dev2"] = { ["dflt"] = true } }
---
--- local sequence_dev2_reorder_matras = {
--- chain = 0,
--- features = { dev2_reorder_matras = { dev2 = { dflt = true } } },
--- flags = { false, false, false, false },
--- name = "dev2_reorder_matras",
--- subtables = { "dev2_reorder_matras" },
--- type = "dev2_reorder_matras",
--- }
--- local sequence_dev2_reorder_reph = {
--- chain = 0,
--- features = { dev2_reorder_reph = { dev2 = { dflt = true } } },
--- flags = { false, false, false, false },
--- name = "dev2_reorder_reph",
--- subtables = { "dev2_reorder_reph" },
--- type = "dev2_reorder_reph",
--- }
--- local sequence_dev2_reorder_pre_base_reordering_consonants = {
--- chain = 0,
--- features = { dev2_reorder_pre_base_reordering_consonants = { dev2 = { dflt = true } } },
--- flags = { false, false, false, false },
--- name = "dev2_reorder_pre_base_reordering_consonants",
--- subtables = { "dev2_reorder_pre_base_reordering_consonants" },
--- type = "dev2_reorder_pre_base_reordering_consonants",
--- }
--- local sequence_remove_joiners = {
--- chain = 0,
--- features = { remove_joiners = { deva = { dflt = true }, dev2 = { dflt = true } } },
--- flags = { false, false, false, false },
--- name = "remove_joiners",
--- subtables = { "remove_joiners" },
--- type = "remove_joiners",
--- }
--- table.insert(sequences, insertpos, sequence_dev2_reorder_pre_base_reordering_consonants)
--- table.insert(sequences, insertpos, sequence_dev2_reorder_reph)
--- table.insert(sequences, insertpos, sequence_dev2_reorder_matras)
--- table.insert(sequences, insertpos, sequence_remove_joiners)
--- end
---
--- local function deva_reorder(head,start,stop,font,attr)
--- local tfmdata = fontdata[font]
--- local lookuphash = tfmdata.resources.lookuphash
--- local sequences = tfmdata.resources.sequences
---
--- if not lookuphash["remove_joiners"] then install_dev(tfmdata) end --install Devanagari-features
---
--- local sharedfeatures = tfmdata.shared.features
--- sharedfeatures["remove_joiners"] = true
--- local datasets = otf.dataset(tfmdata,font,attr)
---
--- lookuphash["remove_joiners"] = { [0x200C] = true, [0x200D] = true }
---
--- local current, n, base, firstcons, lastcons, basefound = start, start.next, nil, nil, nil, false
--- local reph, vattu = false, false
--- for s=1,#sequences do
--- local dataset = datasets[s]
--- featurevalue = dataset and dataset[1]
--- if featurevalue and dataset[4] == "rphf" then reph = true end
--- if featurevalue and dataset[4] == "blwf" then vattu = true end
--- end
--- if ra[start.char] and halant[n.char] and reph then -- if syllable starts with Ra + H and script has 'Reph' then exclude Reph from candidates for base consonants
--- if n == stop then return head, stop end
--- if zwj[n.next.char] then
--- current = start
--- else
--- current = n.next
--- set_attribute(start,state,5) -- rphf
--- end
--- end
---
--- if nbsp[current.char] then --Stand Alone cluster
--- if current == stop then
--- stop = stop.prev
--- head = node.remove(head, current)
--- node.free(current)
--- return head, stop
--- else
--- base, firstcons, lastcons = current, current, current
--- current = current.next
--- if current ~= stop then
--- if nukta[current.char] then current = current.next end
--- if zwj[current.char] then
--- if current ~= stop and current.next ~= stop and halant[current.next.char] then
--- current = current.next
--- local tmp = current.next.next
--- local changestop = current.next == stop
--- local tempcurrent = node.copy(current.next)
--- tempcurrent.next = node.copy(current)
--- tempcurrent.next.prev = tempcurrent
--- set_attribute(tempcurrent,state,8) --blwf
--- tempcurrent = nodes.handlers.characters(tempcurrent)
--- unset_attribute(tempcurrent,state)
--- if current.next.char == tempcurrent.char then
--- node.flush_list(tempcurrent)
--- local n = node.copy(current)
--- current.char = dotted_circle
--- head = node.insert_after(head, current, n)
--- else
--- current.char = tempcurrent.char -- (assumes that result of blwf consists of one node)
--- local freenode = current.next
--- current.next = tmp
--- tmp.prev = current
--- node.free(freenode)
--- node.flush_list(tempcurrent)
--- if changestop then stop = current end
--- end
--- end
--- end
--- end
--- end
--- end
---
--- while not basefound do -- find base consonant
--- if consonant[current.char] then
--- set_attribute(current, state, 6) -- half
--- if not firstcons then firstcons = current end
--- lastcons = current
--- if not base then
--- base = current
--- else --check whether consonant has below-base (or post-base) form
--- local baseform = true
--- for s=1,#sequences do
--- local sequence = sequences[s]
--- local dataset = datasets[s]
--- featurevalue = dataset and dataset[1]
--- if featurevalue and dataset[4] == "blwf" then
--- local subtables = sequence.subtables
--- for i=1,#subtables do
--- local lookupname = subtables[i]
--- local lookupcache = lookuphash[lookupname]
--- if lookupcache then
--- local lookupmatch = lookupcache[current.char]
--- if lookupmatch then
--- set_attribute(current, state, 8) -- blwf
--- baseform = false
--- end
--- end
--- end
--- end
--- end
--- if baseform then base = current end
--- end
--- end
--- basefound = current == stop
--- current = current.next
--- end
--- if base ~= lastcons then -- if base consonant is not last one then move halant from base consonant to last one
--- n = base.next
--- if nukta[n.char] then n = n.next end
--- if halant[n.char] then
--- if lastcons ~= stop then
--- local ln = lastcons.next
--- if nukta[ln.char] then lastcons = ln end
--- end
--- local np, nn, ln = n.prev, n.next, lastcons.next
--- np.next = n.next
--- nn.prev = n.prev
--- lastcons.next = n
--- if ln then ln.prev = n end
--- n.next = ln
--- n.prev = lastcons
--- if lastcons == stop then stop = n end
--- end
--- end
---
--- n = start.next
--- if ra[start.char] and halant[n.char] and not ( n ~= stop and ( zwj[n.next.char] or zwnj[n.next.char] ) ) then -- if syllable starts with Ra + H then move this combination so that it follows either: the post-base 'matra' (if any) or the base consonant
--- local matra = base
--- if base ~= stop and dependent_vowel[base.next.char] then matra = base.next end
--- local sp, nn, mn = start.prev, n.next, matra.next
--- if sp then sp.next = nn end
--- nn.prev = sp
--- matra.next = start
--- start.prev = matra
--- n.next = mn
--- if mn then mn.prev = n end
--- if head == start then head = nn end
--- start = nn
--- if matra == stop then stop = n end
--- end
---
--- local current = start
--- while current ~= stop do
--- if halant[current.next.char] and current.next ~= stop and zwnj[current.next.next.char] then unset_attribute(current, state) end
--- current = current.next
--- end
---
--- if has_attribute(base, state) and base ~= stop and halant[base.next.char] and not ( base.next ~= stop and zwj[base.next.next.char] ) then unset_attribute(base, state) end
---
--- local current, allreordered, moved = start, false, { [base] = true }
--- local a, b, p, bn = base, base, base, base.next
--- if base ~= stop and nukta[bn.char] then a, b, p = bn, bn, bn end
--- while not allreordered do
--- local c, n, l = current, current.next, nil --current is always consonant
--- if c ~= stop and nukta[n.char] then c = n n = n.next end
--- if c ~= stop and halant[n.char] then c = n n = n.next end
--- while c ~= stop and dependent_vowel[n.char] do c = n n = n.next end
--- if c ~= stop and vowel_modifier[n.char] then c = n n = n.next end
--- if c ~= stop and stress_tone_mark[n.char] then c = n n = n.next end
--- local bp, cn = firstcons.prev, current.next
--- while cn ~= c.next do -- move pre-base matras...
--- if pre_mark[cn.char] then
--- if bp then bp.next = cn end
--- cn.prev.next = cn.next
--- if cn.next then cn.next.prev = cn.prev end
--- if cn == stop then stop = cn.prev end
--- cn.prev = bp
--- cn.next = firstcons
--- firstcons.prev = cn
--- if firstcons == start then
--- if head == start then head = cn end
--- start = cn
--- end
--- break
--- end
--- cn = cn.next
--- end
--- allreordered = c == stop
--- current = c.next
--- end
---
--- if reph or vattu then
--- local current, cns = start, nil
--- while current ~= stop do
--- local c, n = current, current.next
--- if ra[current.char] and halant[n.char] then
--- c, n = n, n.next
--- local b, bn = base, base
--- while bn ~= stop do
--- if dependent_vowel[bn.next.char] then b = bn.next end
--- bn = bn.next
--- end
--- if has_attribute(current,state,attribute) == 5 then -- position Reph (Ra + H) after post-base 'matra' (if any) since these become marks on the 'matra', not on the base glyph
--- if b ~= current then
--- if current == start then
--- if head == start then head = n end
--- start = n
--- end
--- if b == stop then stop = c end
--- if current.prev then current.prev.next = n end
--- if n then n.prev = current.prev end
--- c.next = b.next
--- if b.next then b.next.prev = c end
--- b.next = current
--- current.prev = b
--- end
--- elseif cns and cns.next ~= current then -- position below-base Ra (vattu) following the consonants on which it is placed (either the base consonant or one of the pre-base consonants)
--- local cp, cnsn = current.prev, cns.next
--- if cp then cp.next = n end
--- if n then n.prev = cp end
--- cns.next = current
--- current.prev = cns
--- c.next = cnsn
--- if cnsn then cnsn.prev = c end
--- if c == stop then stop = cp break end
--- current = n.prev
--- end
--- elseif consonant[current.char] or nbsp[current.char] then
--- cns = current
--- if halant[cns.next.char] then cns = cns.next end
--- end
--- current = current.next
--- end
--- end
---
--- if nbsp[base.char] then
--- head = node.remove(head, base)
--- node.free(base)
--- end
---
--- return head, stop
--- end
---
--- function dev2_reorder_matras(start,kind,lookupname,replacement)
--- local current = start
--- while current and current.id == glyph and current.subtype<256 and current.font == start.font and has_attribute(current, sylnr) == has_attribute(start, sylnr) do
--- if halant[current.char] and not has_attribute(current, state) then
--- if current.next and current.next.id == glyph and current.next.subtype<256 and current.next.font == start.font and has_attribute(current.next, sylnr) == has_attribute(start, sylnr) and ( zwj[current.next.char] or zwnj[current.next.char] ) then current = current.next end
--- local sn = start.next
--- start.next.prev = start.prev
--- if start.prev then start.prev.next = start.next end
--- if current.next then current.next.prev = start end
--- start.next = current.next
--- current.next = start
--- start.prev = current
--- start = sn
--- break
--- end
--- current = current.next
--- end
--- return start, true
--- end
---
--- function dev2_reorder_reph(start,kind,lookupname,replacement)
--- local current, sn = start.next, nil
--- while current and current.id == glyph and current.subtype<256 and current.font == start.font and has_attribute(current, sylnr) == has_attribute(start, sylnr) do --step 2
--- if halant[current.char] and not has_attribute(current, state) then
--- if current.next and current.next.id == glyph and current.next.subtype<256 and current.next.font == start.font and has_attribute(current.next, sylnr) == has_attribute(start, sylnr) and ( zwj[current.next.char] or zwnj[current.next.char] ) then current = current.next end
--- sn = start.next
--- start.next.prev = start.prev
--- if start.prev then start.prev.next = start.next end
--- if current.next then current.next.prev = start end
--- start.next = current.next
--- current.next = start
--- start.prev = current
--- start = sn
--- break
--- end
--- current = current.next
--- end
--- if not sn then
--- current = start.next
--- while current and current.id == glyph and current.subtype<256 and current.font == start.font and has_attribute(current, sylnr) == has_attribute(start, sylnr) do --step 4
--- if has_attribute(current, state) == 9 then --post-base
--- sn = start.next
--- start.next.prev = start.prev
--- if start.prev then start.prev.next = start.next end
--- start.prev = current.prev
--- current.prev.next = start
--- start.next = current
--- current.prev = start
--- start = sn
--- break
--- end
--- current = current.next
--- end
--- end
--- if not sn then
--- current = start.next
--- local c = nil
--- while current and current.id == glyph and current.subtype<256 and current.font == start.font and has_attribute(current, sylnr) == has_attribute(start, sylnr) do --step 5
--- if not c and ( above_mark[current.char] or below_mark[current.char] or post_mark[current.char] ) and ReorderClass[current.char] ~= "after subscript" then c = current end
--- current = current.next
--- end
--- if c then
--- sn = start.next
--- start.next.prev = start.prev
--- if start.prev then start.prev.next = start.next end
--- start.prev = c.prev
--- c.prev.next = start
--- start.next = c
--- c.prev = start
--- start = sn
--- end
--- end
--- if not sn then
--- current = start
--- while current.next and current.next.id == glyph and current.next.subtype<256 and current.next.font == start.font and has_attribute(current.next, sylnr) == has_attribute(start, sylnr) do --step 6
--- current = current.next
--- end
--- if start ~= current then
--- sn = start.next
--- start.next.prev = start.prev
--- if start.prev then start.prev.next = start.next end
--- if current.next then current.next.prev = start end
--- start.next = current.next
--- current.next = start
--- start.prev = current
--- start = sn
--- end
--- end
--- return start, true
--- end
---
--- function dev2_reorder_pre_base_reordering_consonants(start,kind,lookupname,replacement)
--- local current, sn = start, nil
--- while current and current.id == glyph and current.subtype<256 and current.font == start.font and has_attribute(current, sylnr) == has_attribute(start, sylnr) do
--- if halant[current.char] and not has_attribute(current, state) then
--- if current.next and current.next.id == glyph and current.next.subtype<256 and current.next.font == start.font and has_attribute(current.next, sylnr) == has_attribute(start, sylnr) and ( zwj[current.next.char] or zwnj[current.next.char] ) then current = current.next end
--- sn = start.next
--- start.next.prev = start.prev
--- if start.prev then start.prev.next = start.next end
--- if current.next then current.next.prev = start end
--- start.next = current.next
--- current.next = start
--- start.prev = current
--- start = sn
--- break
--- end
--- current = current.next
--- end
--- if not sn then
--- current = start.next
--- while current and current.id == glyph and current.subtype<256 and current.font == start.font and has_attribute(current, sylnr) == has_attribute(start, sylnr) do
--- if not consonant[current.char] and has_attribute(current, state) then --main
--- sn = start.next
--- start.next.prev = start.prev
--- if start.prev then start.prev.next = start.next end
--- start.prev = current.prev
--- current.prev.next = start
--- start.next = current
--- current.prev = start
--- start = sn
--- break
--- end
--- current = current.next
--- end
--- end
--- return start, true
--- end
---
--- function remove_joiners(start,kind,lookupname,replacement)
--- local stop = start.next
--- while stop and stop.id == glyph and stop.subtype<256 and stop.font == start.font and (zwj[stop.char] or zwnj[stop.char]) do stop = stop.next end
--- if stop then stop.prev.next = nil stop.prev = start.prev end
--- if start.prev then start.prev.next = stop end
--- node.flush_list(start)
--- return stop, true
--- end
---
--- local function dev2_reorder(head,start,stop,font,attr)
--- local tfmdata = fontdata[font]
--- local lookuphash = tfmdata.resources.lookuphash
--- local sequences = tfmdata.resources.sequences
---
--- if not lookuphash["remove_joiners"] then install_dev(tfmdata) end --install Devanagari-features
---
--- local sharedfeatures = tfmdata.shared.features
--- sharedfeatures["dev2_reorder_matras"] = true
--- sharedfeatures["dev2_reorder_reph"] = true
--- sharedfeatures["dev2_reorder_pre_base_reordering_consonants"] = true
--- sharedfeatures["remove_joiners"] = true
--- local datasets = otf.dataset(tfmdata,font,attr)
---
--- local reph, pre_base_reordering_consonants = false, nil
--- local halfpos, basepos, subpos, postpos = nil, nil, nil, nil
--- local locl = { }
---
--- for s=1,#sequences do -- classify chars
--- local sequence = sequences[s]
--- local dataset = datasets[s]
--- featurevalue = dataset and dataset[1]
--- if featurevalue and dataset[4] then
--- local subtables = sequence.subtables
--- for i=1,#subtables do
--- local lookupname = subtables[i]
--- local lookupcache = lookuphash[lookupname]
--- if lookupcache then
--- if dataset[4] == "rphf" then
--- if dataset[3] ~= 0 then --rphf is result of of chain
--- else
--- reph = lookupcache[0x0930] and lookupcache[0x0930][0x094D] and lookupcache[0x0930][0x094D]["ligature"]
--- end
--- end
--- if dataset[4] == "pref" and not pre_base_reordering_consonants then
--- for k, v in pairs(lookupcache[0x094D]) do
--- pre_base_reordering_consonants[k] = v and v["ligature"] --ToDo: reph might also be result of chain
--- end
--- end
--- local current = start
--- while current ~= stop.next do
--- if dataset[4] == "locl" then locl[current] = lookupcache[current.char] end --ToDo: locl might also be result of chain
--- if current ~= stop then
--- local c, n = locl[current] or current.char, locl[current.next] or current.next.char
--- if dataset[4] == "rphf" and lookupcache[c] and lookupcache[c][n] then --above-base: rphf Consonant + Halant
--- if current.next ~= stop and ( zwj[current.next.next.char] or zwnj[current.next.next.char] ) then --ZWJ and ZWNJ prevent creation of reph
--- current = current.next
--- elseif current == start then
--- set_attribute(current,state,5)
--- end
--- current = current.next
--- end
--- if dataset[4] == "half" and lookupcache[c] and lookupcache[c][n] then --half forms: half Consonant + Halant
--- if current.next ~= stop and zwnj[current.next.next.char] then --ZWNJ prevent creation of half
--- current = current.next
--- else
--- set_attribute(current,state,6)
--- if not halfpos then halfpos = current end
--- end
--- current = current.next
--- end
--- if dataset[4] == "pref" and lookupcache[c] and lookupcache[c][n] then --pre-base: pref Halant + Consonant
--- set_attribute(current,state,7)
--- set_attribute(current.next,state,7)
--- current = current.next
--- end
--- if dataset[4] == "blwf" and lookupcache[c] and lookupcache[c][n] then --below-base: blwf Halant + Consonant
--- set_attribute(current,state,8)
--- set_attribute(current.next,state,8)
--- current = current.next
--- subpos = current
--- end
--- if dataset[4] == "pstf" and lookupcache[c] and lookupcache[c][n] then --post-base: pstf Halant + Consonant
--- set_attribute(current,state,9)
--- set_attribute(current.next,state,9)
--- current = current.next
--- postpos = current
--- end
--- end
--- current = current.next
--- end
--- end
--- end
--- end
--- end
---
--- lookuphash["dev2_reorder_matras"] = pre_mark
--- lookuphash["dev2_reorder_reph"] = { [reph] = true }
--- lookuphash["dev2_reorder_pre_base_reordering_consonants"] = pre_base_reordering_consonants or { }
--- lookuphash["remove_joiners"] = { [0x200C] = true, [0x200D] = true }
---
--- local current, base, firstcons = start, nil, nil
--- if has_attribute(start,state) == 5 then current = start.next.next end -- if syllable starts with Ra + H and script has 'Reph' then exclude Reph from candidates for base consonants
---
--- if current ~= stop.next and nbsp[current.char] then --Stand Alone cluster
--- if current == stop then
--- stop = stop.prev
--- head = node.remove(head, current)
--- node.free(current)
--- return head, stop
--- else
--- base = current
--- current = current.next
--- if current ~= stop then
--- if nukta[current.char] then current = current.next end
--- if zwj[current.char] then
--- if current ~= stop and current.next ~= stop and halant[current.next.char] then
--- current = current.next
--- local tmp = current.next.next
--- local changestop = current.next == stop
--- current.next.next = nil
--- set_attribute(current,state,7) --pref
--- current = nodes.handlers.characters(current)
--- set_attribute(current,state,8) --blwf
--- current = nodes.handlers.characters(current)
--- set_attribute(current,state,9) --pstf
--- current = nodes.handlers.characters(current)
--- unset_attribute(current,state)
--- if halant[current.char] then
--- current.next.next = tmp
--- local nc = node.copy(current)
--- current.char = dotted_circle
--- head = node.insert_after(head, current, nc)
--- else
--- current.next = tmp -- (assumes that result of pref, blwf, or pstf consists of one node)
--- if changestop then stop = current end
--- end
--- end
--- end
--- end
--- end
--- else --not Stand Alone cluster
--- while current ~= stop.next do -- find base consonant
--- if consonant[current.char] and not ( current ~= stop and halant[current.next.char] and current.next ~= stop and zwj[current.next.next.char] ) then
--- if not firstcons then firstcons = current end
--- if not ( has_attribute(current, state) == 7 or has_attribute(current, state) == 8 or has_attribute(current, state) == 9 ) then base = current end --check whether consonant has below-base or post-base form or is pre-base reordering Ra
--- end
--- current = current.next
--- end
--- if not base then
--- base = firstcons
--- end
--- end
---
--- if not base then
--- if has_attribute(start, state) == 5 then unset_attribute(start, state) end
--- return head, stop
--- else
--- if has_attribute(base, state) then unset_attribute(base, state) end
--- basepos = base
--- end
--- if not halfpos then halfpos = base end
--- if not subpos then subpos = base end
--- if not postpos then postpos = subpos or base end
---
--- --Matra characters are classified and reordered by which consonant in a conjunct they have affinity for
--- local moved = { }
--- current = start
--- while current ~= stop.next do
--- local char, target, cn = locl[current] or current.char, nil, current.next
--- if not moved[current] and dependent_vowel[char] then
--- if pre_mark[char] then -- Before first half form in the syllable
--- moved[current] = true
--- if current.prev then current.prev.next = current.next end
--- if current.next then current.next.prev = current.prev end
--- if current == stop then stop = current.prev end
--- if halfpos == start then
--- if head == start then head = current end
--- start = current
--- end
--- if halfpos.prev then halfpos.prev.next = current end
--- current.prev = halfpos.prev
--- halfpos.prev = current
--- current.next = halfpos
--- halfpos = current
--- elseif above_mark[char] then -- After main consonant
--- target = basepos
--- if subpos == basepos then subpos = current end
--- if postpos == basepos then postpos = current end
--- basepos = current
--- elseif below_mark[char] then -- After subjoined consonants
--- target = subpos
--- if postpos == subpos then postpos = current end
--- subpos = current
--- elseif post_mark[char] then -- After post-form consonant
--- target = postpos
--- postpos = current
--- end
--- if ( above_mark[char] or below_mark[char] or post_mark[char] ) and current.prev ~= target then
--- if current.prev then current.prev.next = current.next end
--- if current.next then current.next.prev = current.prev end
--- if current == stop then stop = current.prev end
--- if target.next then target.next.prev = current end
--- current.next = target.next
--- target.next = current
--- current.prev = target
--- end
--- end
--- current = cn
--- end
---
--- --Reorder marks to canonical order: Adjacent nukta and halant or nukta and vedic sign are always repositioned if necessary, so that the nukta is first.
--- local current, c = start, nil
--- while current ~= stop do
--- if halant[current.char] or stress_tone_mark[current.char] then
--- if not c then c = current end
--- else
--- c = nil
--- end
--- if c and nukta[current.next.char] then
--- if head == c then head = current.next end
--- if stop == current.next then stop = current end
--- if c.prev then c.prev.next = current.next end
--- current.next.prev = c.prev
--- current.next = current.next.next
--- if current.next.next then current.next.next.prev = current end
--- c.prev = current.next
--- current.next.next = c
--- end
--- if stop == current then break end
--- current = current.next
--- end
---
--- if nbsp[base.char] then
--- head = node.remove(head, base)
--- node.free(base)
--- end
---
--- return head, stop
--- end
---
--- function fonts.analyzers.methods.deva(head,font,attr)
--- local orighead = head
--- local current, start, done = head, true, false
--- while current do
--- if current.id == glyph and current.subtype<256 and current.font == font then
--- done = true
--- local syllablestart, syllableend = current, nil
---
--- local c = current --Checking Stand Alone cluster (this behavior is copied from dev2)
--- if ra[c.char] and c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and halant[c.next.char] and c.next.next and c.next.next.id == glyph and c.next.next.subtype<256 and c.next.next.font == font then c = c.next.next end
--- if nbsp[c.char] and ( not current.prev or current.prev.id ~= glyph or current.prev.subtype>=256 or current.prev.font ~= font or
--- ( not consonant[current.prev.char] and not independent_vowel[current.prev.char] and not dependent_vowel[current.prev.char] and
--- not vowel_modifier[current.prev.char] and not stress_tone_mark[current.prev.char] and not nukta[current.prev.char] and not halant[current.prev.char] )
--- ) then --Stand Alone cluster (at the start of the word only): #[Ra+H]+NBSP+[N]+[<[<ZWJ|ZWNJ>]+H+C>]+[{M}+[N]+[H]]+[SM]+[(VD)]
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and nukta[c.next.char] then c = c.next end
--- local n = c.next
--- if n and n.id == glyph and n.subtype<256 and n.font == font then
--- local ni = n.next
--- if ( zwj[n.char] or zwnj[n.char] ) and ni and ni.id == glyph and ni.subtype<256 and ni.font == font then n = ni ni = ni.next end
--- if halant[n.char] and ni and ni.id == glyph and ni.subtype<256 and ni.font == font and consonant[ni.char] then c = ni end
--- end
--- while c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and dependent_vowel[c.next.char] do c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and nukta[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and halant[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and vowel_modifier[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and stress_tone_mark[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and stress_tone_mark[c.next.char] then c = c.next end
--- current = c.next
--- syllableend = c
--- if syllablestart ~= syllableend then
--- head, current = deva_reorder(head, syllablestart,syllableend,font,attr)
--- current = current.next
--- end
--- elseif consonant[current.char] then -- syllable containing consonant
--- prevc = true
--- while prevc do
--- prevc = false
--- local n = current.next
--- if n and n.id == glyph and n.subtype<256 and n.font == font and nukta[n.char] then n = n.next end
--- if n and n.id == glyph and n.subtype<256 and n.font == font and halant[n.char] then
--- local n = n.next
--- if n and n.id == glyph and n.subtype<256 and n.font == font and ( zwj[n.char] or zwnj[n.char] ) then n = n.next end
--- if n and n.id == glyph and n.subtype<256 and n.font == font and consonant[n.char] then
--- prevc = true
--- current = n
--- end
--- end
--- end
--- if current.next and current.next.id == glyph and current.next.subtype<256 and current.next.font == font and nukta[current.next.char] then current = current.next end -- nukta (not specified in Microsft Devanagari OpenType specification)
--- syllableend = current
--- current = current.next
--- if current and current.id == glyph and current.subtype<256 and current.font == font and halant[current.char] then -- syllable containing consonant without vowels: {C + [Nukta] + H} + C + H
--- if current.next and current.next.id == glyph and current.next.subtype<256 and current.next.font == font and ( zwj[current.next.char] or zwnj[current.next.char] ) then current = current.next end
--- syllableend = current
--- current = current.next
--- else -- syllable containing consonant with vowels: {C + [Nukta] + H} + C + [M] + [VM] + [SM]
--- if current and current.id == glyph and current.subtype<256 and current.font == font and dependent_vowel[current.char] then
--- syllableend = current
--- current = current.next
--- end
--- if current and current.id == glyph and current.subtype<256 and current.font == font and vowel_modifier[current.char] then
--- syllableend = current
--- current = current.next
--- end
--- if current and current.id == glyph and current.subtype<256 and current.font == font and stress_tone_mark[current.char] then
--- syllableend = current
--- current = current.next
--- end
--- end
--- if syllablestart ~= syllableend then
--- head, current = deva_reorder(head,syllablestart,syllableend,font,attr)
--- current = current.next
--- end
--- elseif current.id == glyph and current.subtype<256 and current.font == font and independent_vowel[current.char] then -- syllable without consonants: VO + [VM] + [SM]
--- syllableend = current
--- current = current.next
--- if current and current.id == glyph and current.subtype<256 and current.font == font and vowel_modifier[current.char] then
--- syllableend = current
--- current = current.next
--- end
--- if current and current.id == glyph and current.subtype<256 and current.font == font and stress_tone_mark[current.char] then
--- syllableend = current
--- current = current.next
--- end
--- else -- Syntax error
--- if pre_mark[current.char] or above_mark[current.char] or below_mark[current.char] or post_mark[current.char] then
--- local n = node.copy(current)
--- if pre_mark[current.char] then
--- n.char = dotted_circle
--- else
--- current.char = dotted_circle
--- end
--- head, current = node.insert_after(head, current, n)
--- end
--- current = current.next
--- end
--- else
--- current = current.next
--- end
--- start = false
--- end
---
--- return head, done
--- end
---
--- function fonts.analyzers.methods.dev2(head,font,attr)
--- local current, start, done, syl_nr = head, true, false, 0
--- while current do
--- local syllablestart, syllableend = nil, nil
--- if current.id == glyph and current.subtype<256 and current.font == font then
--- syllablestart = current
--- done = true
--- local c, n = current, current.next
--- if ra[current.char] and n and n.id == glyph and n.subtype<256 and n.font == font and halant[n.char] and n.next and n.next.id == glyph and n.next.subtype<256 and n.next.font == font then c = n.next end
--- if independent_vowel[c.char] then --Vowel-based syllable: [Ra+H]+V+[N]+[<[<ZWJ|ZWNJ>]+H+C|ZWJ+C>]+[{M}+[N]+[H]]+[SM]+[(VD)]
--- n = c.next
--- local ni, nii = nil, nil
--- if n and n.id == glyph and n.subtype<256 and n.font == font and nukta[n.char] then n = n.next end
--- if n and n.id == glyph and n.subtype<256 and n.font == font then local ni = n.next end
--- if ni and ni.id == glyph and ni.subtype<256 and ni.font == font and ni.next and ni.next.id == glyph and ni.next.subtype<256 and ni.next.font == font then
--- nii = ni.next
--- if zwj[ni.char] and consonant[nii.char] then
--- c = nii
--- elseif (zwj[ni.char] or zwnj[ni.char]) and halant[nii.char] and nii.next and nii.next.id == glyph and nii.next.subtype<256 and nii.next.font == font and consonant[nii.next.char] then
--- c = nii.next
--- end
--- end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and dependent_vowel[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and nukta[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and halant[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and vowel_modifier[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and stress_tone_mark[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and stress_tone_mark[c.next.char] then c = c.next end
--- current = c
--- syllableend = c
--- elseif nbsp[c.char] and ( not current.prev or current.prev.id ~= glyph or current.prev.subtype>=256 or current.prev.font ~= font or
--- ( not consonant[current.prev.char] and not independent_vowel[current.prev.char] and not dependent_vowel[current.prev.char] and
--- not vowel_modifier[current.prev.char] and not stress_tone_mark[current.prev.char] and not nukta[current.prev.char] and not halant[current.prev.char] )
--- ) then --Stand Alone cluster (at the start of the word only): #[Ra+H]+NBSP+[N]+[<[<ZWJ|ZWNJ>]+H+C>]+[{M}+[N]+[H]]+[SM]+[(VD)]
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and nukta[c.next.char] then c = c.next end
--- n = c.next
--- if n and n.id == glyph and n.subtype<256 and n.font == font then
--- local ni = n.next
--- if ( zwj[n.char] or zwnj[n.char] ) and ni and ni.id == glyph and ni.subtype<256 and ni.font == font then n = ni ni = ni.next end
--- if halant[n.char] and ni and ni.id == glyph and ni.subtype<256 and ni.font == font and consonant[ni.char] then c = ni end
--- end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and dependent_vowel[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and nukta[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and halant[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and vowel_modifier[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and stress_tone_mark[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and stress_tone_mark[c.next.char] then c = c.next end
--- current = c
--- syllableend = c
--- elseif consonant[current.char] then --Consonant syllable: {C+[N]+<H+[<ZWNJ|ZWJ>]|<ZWNJ|ZWJ>+H>} + C+[N]+[A] + [< H+[<ZWNJ|ZWJ>] | {M}+[N]+[H]>]+[SM]+[(VD)]
--- c = current
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and nukta[c.next.char] then c = c.next end
--- n = c
--- while n.next and n.next.id == glyph and n.next.subtype<256 and n.next.font == font and ( halant[n.next.char] or zwnj[n.next.char] or zwj[n.next.char] ) do
--- if halant[n.next.char] then
--- n = n.next
--- if n.next and n.next.id == glyph and n.next.subtype<256 and n.next.font == font and ( zwnj[n.next.char] or zwj[n.next.char] ) then n = n.next end
--- else
--- if n.next.next and n.next.next.id == glyph and n.next.next.subtype<256 and n.next.next.font == font and halant[n.next.next.char] then n = n.next.next end
--- end
--- if n.next and n.next.id == glyph and n.next.subtype<256 and n.next.font == font and consonant[n.next.char] then
--- n = n.next
--- if n.next and n.next.id == glyph and n.next.subtype<256 and n.next.font == font and nukta[n.next.char] then n = n.next end
--- c = n
--- else
--- break
--- end
--- end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and anudatta[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and halant[c.next.char] then
--- c = c.next
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and ( zwnj[c.next.char] or zwj[c.next.char] ) then c = c.next end
--- else
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and dependent_vowel[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and nukta[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and halant[c.next.char] then c = c.next end
--- end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and vowel_modifier[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and stress_tone_mark[c.next.char] then c = c.next end
--- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and stress_tone_mark[c.next.char] then c = c.next end
--- current = c
--- syllableend = c
--- end
--- end
---
--- if syllableend then
--- syl_nr = syl_nr + 1
--- c = syllablestart
--- while c ~= syllableend.next do
--- set_attribute(c,sylnr,syl_nr)
--- c = c.next
--- end
--- end
--- if syllableend and syllablestart ~= syllableend then
--- head, current = dev2_reorder(head,syllablestart,syllableend,font,attr)
--- end
---
--- if not syllableend and not has_attribute(current, state) and current.id == glyph and current.subtype<256 and current.font == font then -- Syntax error
--- if pre_mark[current.char] or above_mark[current.char] or below_mark[current.char] or post_mark[current.char] then
--- local n = node.copy(current)
--- if pre_mark[current.char] then
--- n.char = dotted_circle
--- else
--- current.char = dotted_circle
--- end
--- head, current = node.insert_after(head, current, n)
--- end
--- end
---
--- start = false
--- current = current.next
--- end
---
--- return head, done
--- end
---
--- function otf.handlers.dev2_reorder_matras(start,kind,lookupname,replacement)
--- return dev2_reorder_matras(start,kind,lookupname,replacement)
--- end
---
--- function otf.handlers.dev2_reorder_reph(start,kind,lookupname,replacement)
--- return dev2_reorder_reph(start,kind,lookupname,replacement)
--- end
---
--- function otf.handlers.dev2_reorder_pre_base_reordering_consonants(start,kind,lookupname,replacement)
--- return dev2_reorder_pre_base_reordering_consonants(start,kind,lookupname,replacement)
--- end
---
--- function otf.handlers.remove_joiners(start,kind,lookupname,replacement)
--- return remove_joiners(start,kind,lookupname,replacement)
--- end
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+-- We keep the original around for a while so that we can check it --
+-- when the above code does it wrong (data tables are not included). --
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+
+-- author : Kai Eigner, TAT Zetwerk
+-- copyright : TAT Zetwerk
+-- comment : see font-odv.lua for current implementation
+
+-- local state = attributes.private('state')
+-- local sylnr = attributes.private('syllabe')
+--
+-- local function install_dev(tfmdata)
+-- local features = tfmdata.resources.features
+-- local sequences = tfmdata.resources.sequences
+--
+-- local insertpos = 1
+-- for s=1,#sequences do -- classify chars
+-- for k in pairs(basic_shaping_forms) do
+-- if sequences[s].features and ( sequences[s].features[k] or sequences[s].features.locl ) then insertpos = s + 1 end
+-- end
+-- end
+--
+-- features.gsub["dev2_reorder_matras"] = { ["dev2"] = { ["dflt"] = true } }
+-- features.gsub["dev2_reorder_reph"] = { ["dev2"] = { ["dflt"] = true } }
+-- features.gsub["dev2_reorder_pre_base_reordering_consonants"] = { ["dev2"] = { ["dflt"] = true } }
+-- features.gsub["remove_joiners"] = { ["deva"] = { ["dflt"] = true }, ["dev2"] = { ["dflt"] = true } }
+--
+-- local sequence_dev2_reorder_matras = {
+-- chain = 0,
+-- features = { dev2_reorder_matras = { dev2 = { dflt = true } } },
+-- flags = { false, false, false, false },
+-- name = "dev2_reorder_matras",
+-- subtables = { "dev2_reorder_matras" },
+-- type = "dev2_reorder_matras",
+-- }
+-- local sequence_dev2_reorder_reph = {
+-- chain = 0,
+-- features = { dev2_reorder_reph = { dev2 = { dflt = true } } },
+-- flags = { false, false, false, false },
+-- name = "dev2_reorder_reph",
+-- subtables = { "dev2_reorder_reph" },
+-- type = "dev2_reorder_reph",
+-- }
+-- local sequence_dev2_reorder_pre_base_reordering_consonants = {
+-- chain = 0,
+-- features = { dev2_reorder_pre_base_reordering_consonants = { dev2 = { dflt = true } } },
+-- flags = { false, false, false, false },
+-- name = "dev2_reorder_pre_base_reordering_consonants",
+-- subtables = { "dev2_reorder_pre_base_reordering_consonants" },
+-- type = "dev2_reorder_pre_base_reordering_consonants",
+-- }
+-- local sequence_remove_joiners = {
+-- chain = 0,
+-- features = { remove_joiners = { deva = { dflt = true }, dev2 = { dflt = true } } },
+-- flags = { false, false, false, false },
+-- name = "remove_joiners",
+-- subtables = { "remove_joiners" },
+-- type = "remove_joiners",
+-- }
+-- table.insert(sequences, insertpos, sequence_dev2_reorder_pre_base_reordering_consonants)
+-- table.insert(sequences, insertpos, sequence_dev2_reorder_reph)
+-- table.insert(sequences, insertpos, sequence_dev2_reorder_matras)
+-- table.insert(sequences, insertpos, sequence_remove_joiners)
+-- end
+--
+-- local function deva_reorder(head,start,stop,font,attr)
+-- local tfmdata = fontdata[font]
+-- local lookuphash = tfmdata.resources.lookuphash
+-- local sequences = tfmdata.resources.sequences
+--
+-- if not lookuphash["remove_joiners"] then install_dev(tfmdata) end --install Devanagari-features
+--
+-- local sharedfeatures = tfmdata.shared.features
+-- sharedfeatures["remove_joiners"] = true
+-- local datasets = otf.dataset(tfmdata,font,attr)
+--
+-- lookuphash["remove_joiners"] = { [0x200C] = true, [0x200D] = true }
+--
+-- local current, n, base, firstcons, lastcons, basefound = start, start.next, nil, nil, nil, false
+-- local reph, vattu = false, false
+-- for s=1,#sequences do
+-- local dataset = datasets[s]
+-- featurevalue = dataset and dataset[1]
+-- if featurevalue and dataset[4] == "rphf" then reph = true end
+-- if featurevalue and dataset[4] == "blwf" then vattu = true end
+-- end
+-- if ra[start.char] and halant[n.char] and reph then -- if syllable starts with Ra + H and script has 'Reph' then exclude Reph from candidates for base consonants
+-- if n == stop then return head, stop end
+-- if zwj[n.next.char] then
+-- current = start
+-- else
+-- current = n.next
+-- set_attribute(start,state,5) -- rphf
+-- end
+-- end
+--
+-- if nbsp[current.char] then --Stand Alone cluster
+-- if current == stop then
+-- stop = stop.prev
+-- head = node.remove(head, current)
+-- node.free(current)
+-- return head, stop
+-- else
+-- base, firstcons, lastcons = current, current, current
+-- current = current.next
+-- if current ~= stop then
+-- if nukta[current.char] then current = current.next end
+-- if zwj[current.char] then
+-- if current ~= stop and current.next ~= stop and halant[current.next.char] then
+-- current = current.next
+-- local tmp = current.next.next
+-- local changestop = current.next == stop
+-- local tempcurrent = node.copy(current.next)
+-- tempcurrent.next = node.copy(current)
+-- tempcurrent.next.prev = tempcurrent
+-- set_attribute(tempcurrent,state,8) --blwf
+-- tempcurrent = nodes.handlers.characters(tempcurrent)
+-- unset_attribute(tempcurrent,state)
+-- if current.next.char == tempcurrent.char then
+-- node.flush_list(tempcurrent)
+-- local n = node.copy(current)
+-- current.char = dotted_circle
+-- head = node.insert_after(head, current, n)
+-- else
+-- current.char = tempcurrent.char -- (assumes that result of blwf consists of one node)
+-- local freenode = current.next
+-- current.next = tmp
+-- tmp.prev = current
+-- node.free(freenode)
+-- node.flush_list(tempcurrent)
+-- if changestop then stop = current end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+--
+-- while not basefound do -- find base consonant
+-- if consonant[current.char] then
+-- set_attribute(current, state, 6) -- half
+-- if not firstcons then firstcons = current end
+-- lastcons = current
+-- if not base then
+-- base = current
+-- else --check whether consonant has below-base (or post-base) form
+-- local baseform = true
+-- for s=1,#sequences do
+-- local sequence = sequences[s]
+-- local dataset = datasets[s]
+-- featurevalue = dataset and dataset[1]
+-- if featurevalue and dataset[4] == "blwf" then
+-- local subtables = sequence.subtables
+-- for i=1,#subtables do
+-- local lookupname = subtables[i]
+-- local lookupcache = lookuphash[lookupname]
+-- if lookupcache then
+-- local lookupmatch = lookupcache[current.char]
+-- if lookupmatch then
+-- set_attribute(current, state, 8) -- blwf
+-- baseform = false
+-- end
+-- end
+-- end
+-- end
+-- end
+-- if baseform then base = current end
+-- end
+-- end
+-- basefound = current == stop
+-- current = current.next
+-- end
+-- if base ~= lastcons then -- if base consonant is not last one then move halant from base consonant to last one
+-- n = base.next
+-- if nukta[n.char] then n = n.next end
+-- if halant[n.char] then
+-- if lastcons ~= stop then
+-- local ln = lastcons.next
+-- if nukta[ln.char] then lastcons = ln end
+-- end
+-- local np, nn, ln = n.prev, n.next, lastcons.next
+-- np.next = n.next
+-- nn.prev = n.prev
+-- lastcons.next = n
+-- if ln then ln.prev = n end
+-- n.next = ln
+-- n.prev = lastcons
+-- if lastcons == stop then stop = n end
+-- end
+-- end
+--
+-- n = start.next
+-- if ra[start.char] and halant[n.char] and not ( n ~= stop and ( zwj[n.next.char] or zwnj[n.next.char] ) ) then -- if syllable starts with Ra + H then move this combination so that it follows either: the post-base 'matra' (if any) or the base consonant
+-- local matra = base
+-- if base ~= stop and dependent_vowel[base.next.char] then matra = base.next end
+-- local sp, nn, mn = start.prev, n.next, matra.next
+-- if sp then sp.next = nn end
+-- nn.prev = sp
+-- matra.next = start
+-- start.prev = matra
+-- n.next = mn
+-- if mn then mn.prev = n end
+-- if head == start then head = nn end
+-- start = nn
+-- if matra == stop then stop = n end
+-- end
+--
+-- local current = start
+-- while current ~= stop do
+-- if halant[current.next.char] and current.next ~= stop and zwnj[current.next.next.char] then unset_attribute(current, state) end
+-- current = current.next
+-- end
+--
+-- if has_attribute(base, state) and base ~= stop and halant[base.next.char] and not ( base.next ~= stop and zwj[base.next.next.char] ) then unset_attribute(base, state) end
+--
+-- local current, allreordered, moved = start, false, { [base] = true }
+-- local a, b, p, bn = base, base, base, base.next
+-- if base ~= stop and nukta[bn.char] then a, b, p = bn, bn, bn end
+-- while not allreordered do
+-- local c, n, l = current, current.next, nil --current is always consonant
+-- if c ~= stop and nukta[n.char] then c = n n = n.next end
+-- if c ~= stop and halant[n.char] then c = n n = n.next end
+-- while c ~= stop and dependent_vowel[n.char] do c = n n = n.next end
+-- if c ~= stop and vowel_modifier[n.char] then c = n n = n.next end
+-- if c ~= stop and stress_tone_mark[n.char] then c = n n = n.next end
+-- local bp, cn = firstcons.prev, current.next
+-- while cn ~= c.next do -- move pre-base matras...
+-- if pre_mark[cn.char] then
+-- if bp then bp.next = cn end
+-- cn.prev.next = cn.next
+-- if cn.next then cn.next.prev = cn.prev end
+-- if cn == stop then stop = cn.prev end
+-- cn.prev = bp
+-- cn.next = firstcons
+-- firstcons.prev = cn
+-- if firstcons == start then
+-- if head == start then head = cn end
+-- start = cn
+-- end
+-- break
+-- end
+-- cn = cn.next
+-- end
+-- allreordered = c == stop
+-- current = c.next
+-- end
+--
+-- if reph or vattu then
+-- local current, cns = start, nil
+-- while current ~= stop do
+-- local c, n = current, current.next
+-- if ra[current.char] and halant[n.char] then
+-- c, n = n, n.next
+-- local b, bn = base, base
+-- while bn ~= stop do
+-- if dependent_vowel[bn.next.char] then b = bn.next end
+-- bn = bn.next
+-- end
+-- if has_attribute(current,state,attribute) == 5 then -- position Reph (Ra + H) after post-base 'matra' (if any) since these become marks on the 'matra', not on the base glyph
+-- if b ~= current then
+-- if current == start then
+-- if head == start then head = n end
+-- start = n
+-- end
+-- if b == stop then stop = c end
+-- if current.prev then current.prev.next = n end
+-- if n then n.prev = current.prev end
+-- c.next = b.next
+-- if b.next then b.next.prev = c end
+-- b.next = current
+-- current.prev = b
+-- end
+-- elseif cns and cns.next ~= current then -- position below-base Ra (vattu) following the consonants on which it is placed (either the base consonant or one of the pre-base consonants)
+-- local cp, cnsn = current.prev, cns.next
+-- if cp then cp.next = n end
+-- if n then n.prev = cp end
+-- cns.next = current
+-- current.prev = cns
+-- c.next = cnsn
+-- if cnsn then cnsn.prev = c end
+-- if c == stop then stop = cp break end
+-- current = n.prev
+-- end
+-- elseif consonant[current.char] or nbsp[current.char] then
+-- cns = current
+-- if halant[cns.next.char] then cns = cns.next end
+-- end
+-- current = current.next
+-- end
+-- end
+--
+-- if nbsp[base.char] then
+-- head = node.remove(head, base)
+-- node.free(base)
+-- end
+--
+-- return head, stop
+-- end
+--
+-- function dev2_reorder_matras(start,kind,lookupname,replacement)
+-- local current = start
+-- while current and current.id == glyph and current.subtype<256 and current.font == start.font and has_attribute(current, sylnr) == has_attribute(start, sylnr) do
+-- if halant[current.char] and not has_attribute(current, state) then
+-- if current.next and current.next.id == glyph and current.next.subtype<256 and current.next.font == start.font and has_attribute(current.next, sylnr) == has_attribute(start, sylnr) and ( zwj[current.next.char] or zwnj[current.next.char] ) then current = current.next end
+-- local sn = start.next
+-- start.next.prev = start.prev
+-- if start.prev then start.prev.next = start.next end
+-- if current.next then current.next.prev = start end
+-- start.next = current.next
+-- current.next = start
+-- start.prev = current
+-- start = sn
+-- break
+-- end
+-- current = current.next
+-- end
+-- return start, true
+-- end
+--
+-- function dev2_reorder_reph(start,kind,lookupname,replacement)
+-- local current, sn = start.next, nil
+-- while current and current.id == glyph and current.subtype<256 and current.font == start.font and has_attribute(current, sylnr) == has_attribute(start, sylnr) do --step 2
+-- if halant[current.char] and not has_attribute(current, state) then
+-- if current.next and current.next.id == glyph and current.next.subtype<256 and current.next.font == start.font and has_attribute(current.next, sylnr) == has_attribute(start, sylnr) and ( zwj[current.next.char] or zwnj[current.next.char] ) then current = current.next end
+-- sn = start.next
+-- start.next.prev = start.prev
+-- if start.prev then start.prev.next = start.next end
+-- if current.next then current.next.prev = start end
+-- start.next = current.next
+-- current.next = start
+-- start.prev = current
+-- start = sn
+-- break
+-- end
+-- current = current.next
+-- end
+-- if not sn then
+-- current = start.next
+-- while current and current.id == glyph and current.subtype<256 and current.font == start.font and has_attribute(current, sylnr) == has_attribute(start, sylnr) do --step 4
+-- if has_attribute(current, state) == 9 then --post-base
+-- sn = start.next
+-- start.next.prev = start.prev
+-- if start.prev then start.prev.next = start.next end
+-- start.prev = current.prev
+-- current.prev.next = start
+-- start.next = current
+-- current.prev = start
+-- start = sn
+-- break
+-- end
+-- current = current.next
+-- end
+-- end
+-- if not sn then
+-- current = start.next
+-- local c = nil
+-- while current and current.id == glyph and current.subtype<256 and current.font == start.font and has_attribute(current, sylnr) == has_attribute(start, sylnr) do --step 5
+-- if not c and ( above_mark[current.char] or below_mark[current.char] or post_mark[current.char] ) and ReorderClass[current.char] ~= "after subscript" then c = current end
+-- current = current.next
+-- end
+-- if c then
+-- sn = start.next
+-- start.next.prev = start.prev
+-- if start.prev then start.prev.next = start.next end
+-- start.prev = c.prev
+-- c.prev.next = start
+-- start.next = c
+-- c.prev = start
+-- start = sn
+-- end
+-- end
+-- if not sn then
+-- current = start
+-- while current.next and current.next.id == glyph and current.next.subtype<256 and current.next.font == start.font and has_attribute(current.next, sylnr) == has_attribute(start, sylnr) do --step 6
+-- current = current.next
+-- end
+-- if start ~= current then
+-- sn = start.next
+-- start.next.prev = start.prev
+-- if start.prev then start.prev.next = start.next end
+-- if current.next then current.next.prev = start end
+-- start.next = current.next
+-- current.next = start
+-- start.prev = current
+-- start = sn
+-- end
+-- end
+-- return start, true
+-- end
+--
+-- function dev2_reorder_pre_base_reordering_consonants(start,kind,lookupname,replacement)
+-- local current, sn = start, nil
+-- while current and current.id == glyph and current.subtype<256 and current.font == start.font and has_attribute(current, sylnr) == has_attribute(start, sylnr) do
+-- if halant[current.char] and not has_attribute(current, state) then
+-- if current.next and current.next.id == glyph and current.next.subtype<256 and current.next.font == start.font and has_attribute(current.next, sylnr) == has_attribute(start, sylnr) and ( zwj[current.next.char] or zwnj[current.next.char] ) then current = current.next end
+-- sn = start.next
+-- start.next.prev = start.prev
+-- if start.prev then start.prev.next = start.next end
+-- if current.next then current.next.prev = start end
+-- start.next = current.next
+-- current.next = start
+-- start.prev = current
+-- start = sn
+-- break
+-- end
+-- current = current.next
+-- end
+-- if not sn then
+-- current = start.next
+-- while current and current.id == glyph and current.subtype<256 and current.font == start.font and has_attribute(current, sylnr) == has_attribute(start, sylnr) do
+-- if not consonant[current.char] and has_attribute(current, state) then --main
+-- sn = start.next
+-- start.next.prev = start.prev
+-- if start.prev then start.prev.next = start.next end
+-- start.prev = current.prev
+-- current.prev.next = start
+-- start.next = current
+-- current.prev = start
+-- start = sn
+-- break
+-- end
+-- current = current.next
+-- end
+-- end
+-- return start, true
+-- end
+--
+-- function remove_joiners(start,kind,lookupname,replacement)
+-- local stop = start.next
+-- while stop and stop.id == glyph and stop.subtype<256 and stop.font == start.font and (zwj[stop.char] or zwnj[stop.char]) do stop = stop.next end
+-- if stop then stop.prev.next = nil stop.prev = start.prev end
+-- if start.prev then start.prev.next = stop end
+-- node.flush_list(start)
+-- return stop, true
+-- end
+--
+-- local function dev2_reorder(head,start,stop,font,attr)
+-- local tfmdata = fontdata[font]
+-- local lookuphash = tfmdata.resources.lookuphash
+-- local sequences = tfmdata.resources.sequences
+--
+-- if not lookuphash["remove_joiners"] then install_dev(tfmdata) end --install Devanagari-features
+--
+-- local sharedfeatures = tfmdata.shared.features
+-- sharedfeatures["dev2_reorder_matras"] = true
+-- sharedfeatures["dev2_reorder_reph"] = true
+-- sharedfeatures["dev2_reorder_pre_base_reordering_consonants"] = true
+-- sharedfeatures["remove_joiners"] = true
+-- local datasets = otf.dataset(tfmdata,font,attr)
+--
+-- local reph, pre_base_reordering_consonants = false, nil
+-- local halfpos, basepos, subpos, postpos = nil, nil, nil, nil
+-- local locl = { }
+--
+-- for s=1,#sequences do -- classify chars
+-- local sequence = sequences[s]
+-- local dataset = datasets[s]
+-- featurevalue = dataset and dataset[1]
+-- if featurevalue and dataset[4] then
+-- local subtables = sequence.subtables
+-- for i=1,#subtables do
+-- local lookupname = subtables[i]
+-- local lookupcache = lookuphash[lookupname]
+-- if lookupcache then
+-- if dataset[4] == "rphf" then
+-- if dataset[3] ~= 0 then --rphf is result of of chain
+-- else
+-- reph = lookupcache[0x0930] and lookupcache[0x0930][0x094D] and lookupcache[0x0930][0x094D]["ligature"]
+-- end
+-- end
+-- if dataset[4] == "pref" and not pre_base_reordering_consonants then
+-- for k, v in pairs(lookupcache[0x094D]) do
+-- pre_base_reordering_consonants[k] = v and v["ligature"] --ToDo: reph might also be result of chain
+-- end
+-- end
+-- local current = start
+-- while current ~= stop.next do
+-- if dataset[4] == "locl" then locl[current] = lookupcache[current.char] end --ToDo: locl might also be result of chain
+-- if current ~= stop then
+-- local c, n = locl[current] or current.char, locl[current.next] or current.next.char
+-- if dataset[4] == "rphf" and lookupcache[c] and lookupcache[c][n] then --above-base: rphf Consonant + Halant
+-- if current.next ~= stop and ( zwj[current.next.next.char] or zwnj[current.next.next.char] ) then --ZWJ and ZWNJ prevent creation of reph
+-- current = current.next
+-- elseif current == start then
+-- set_attribute(current,state,5)
+-- end
+-- current = current.next
+-- end
+-- if dataset[4] == "half" and lookupcache[c] and lookupcache[c][n] then --half forms: half Consonant + Halant
+-- if current.next ~= stop and zwnj[current.next.next.char] then --ZWNJ prevent creation of half
+-- current = current.next
+-- else
+-- set_attribute(current,state,6)
+-- if not halfpos then halfpos = current end
+-- end
+-- current = current.next
+-- end
+-- if dataset[4] == "pref" and lookupcache[c] and lookupcache[c][n] then --pre-base: pref Halant + Consonant
+-- set_attribute(current,state,7)
+-- set_attribute(current.next,state,7)
+-- current = current.next
+-- end
+-- if dataset[4] == "blwf" and lookupcache[c] and lookupcache[c][n] then --below-base: blwf Halant + Consonant
+-- set_attribute(current,state,8)
+-- set_attribute(current.next,state,8)
+-- current = current.next
+-- subpos = current
+-- end
+-- if dataset[4] == "pstf" and lookupcache[c] and lookupcache[c][n] then --post-base: pstf Halant + Consonant
+-- set_attribute(current,state,9)
+-- set_attribute(current.next,state,9)
+-- current = current.next
+-- postpos = current
+-- end
+-- end
+-- current = current.next
+-- end
+-- end
+-- end
+-- end
+-- end
+--
+-- lookuphash["dev2_reorder_matras"] = pre_mark
+-- lookuphash["dev2_reorder_reph"] = { [reph] = true }
+-- lookuphash["dev2_reorder_pre_base_reordering_consonants"] = pre_base_reordering_consonants or { }
+-- lookuphash["remove_joiners"] = { [0x200C] = true, [0x200D] = true }
+--
+-- local current, base, firstcons = start, nil, nil
+-- if has_attribute(start,state) == 5 then current = start.next.next end -- if syllable starts with Ra + H and script has 'Reph' then exclude Reph from candidates for base consonants
+--
+-- if current ~= stop.next and nbsp[current.char] then --Stand Alone cluster
+-- if current == stop then
+-- stop = stop.prev
+-- head = node.remove(head, current)
+-- node.free(current)
+-- return head, stop
+-- else
+-- base = current
+-- current = current.next
+-- if current ~= stop then
+-- if nukta[current.char] then current = current.next end
+-- if zwj[current.char] then
+-- if current ~= stop and current.next ~= stop and halant[current.next.char] then
+-- current = current.next
+-- local tmp = current.next.next
+-- local changestop = current.next == stop
+-- current.next.next = nil
+-- set_attribute(current,state,7) --pref
+-- current = nodes.handlers.characters(current)
+-- set_attribute(current,state,8) --blwf
+-- current = nodes.handlers.characters(current)
+-- set_attribute(current,state,9) --pstf
+-- current = nodes.handlers.characters(current)
+-- unset_attribute(current,state)
+-- if halant[current.char] then
+-- current.next.next = tmp
+-- local nc = node.copy(current)
+-- current.char = dotted_circle
+-- head = node.insert_after(head, current, nc)
+-- else
+-- current.next = tmp -- (assumes that result of pref, blwf, or pstf consists of one node)
+-- if changestop then stop = current end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- else --not Stand Alone cluster
+-- while current ~= stop.next do -- find base consonant
+-- if consonant[current.char] and not ( current ~= stop and halant[current.next.char] and current.next ~= stop and zwj[current.next.next.char] ) then
+-- if not firstcons then firstcons = current end
+-- if not ( has_attribute(current, state) == 7 or has_attribute(current, state) == 8 or has_attribute(current, state) == 9 ) then base = current end --check whether consonant has below-base or post-base form or is pre-base reordering Ra
+-- end
+-- current = current.next
+-- end
+-- if not base then
+-- base = firstcons
+-- end
+-- end
+--
+-- if not base then
+-- if has_attribute(start, state) == 5 then unset_attribute(start, state) end
+-- return head, stop
+-- else
+-- if has_attribute(base, state) then unset_attribute(base, state) end
+-- basepos = base
+-- end
+-- if not halfpos then halfpos = base end
+-- if not subpos then subpos = base end
+-- if not postpos then postpos = subpos or base end
+--
+-- --Matra characters are classified and reordered by which consonant in a conjunct they have affinity for
+-- local moved = { }
+-- current = start
+-- while current ~= stop.next do
+-- local char, target, cn = locl[current] or current.char, nil, current.next
+-- if not moved[current] and dependent_vowel[char] then
+-- if pre_mark[char] then -- Before first half form in the syllable
+-- moved[current] = true
+-- if current.prev then current.prev.next = current.next end
+-- if current.next then current.next.prev = current.prev end
+-- if current == stop then stop = current.prev end
+-- if halfpos == start then
+-- if head == start then head = current end
+-- start = current
+-- end
+-- if halfpos.prev then halfpos.prev.next = current end
+-- current.prev = halfpos.prev
+-- halfpos.prev = current
+-- current.next = halfpos
+-- halfpos = current
+-- elseif above_mark[char] then -- After main consonant
+-- target = basepos
+-- if subpos == basepos then subpos = current end
+-- if postpos == basepos then postpos = current end
+-- basepos = current
+-- elseif below_mark[char] then -- After subjoined consonants
+-- target = subpos
+-- if postpos == subpos then postpos = current end
+-- subpos = current
+-- elseif post_mark[char] then -- After post-form consonant
+-- target = postpos
+-- postpos = current
+-- end
+-- if ( above_mark[char] or below_mark[char] or post_mark[char] ) and current.prev ~= target then
+-- if current.prev then current.prev.next = current.next end
+-- if current.next then current.next.prev = current.prev end
+-- if current == stop then stop = current.prev end
+-- if target.next then target.next.prev = current end
+-- current.next = target.next
+-- target.next = current
+-- current.prev = target
+-- end
+-- end
+-- current = cn
+-- end
+--
+-- --Reorder marks to canonical order: Adjacent nukta and halant or nukta and vedic sign are always repositioned if necessary, so that the nukta is first.
+-- local current, c = start, nil
+-- while current ~= stop do
+-- if halant[current.char] or stress_tone_mark[current.char] then
+-- if not c then c = current end
+-- else
+-- c = nil
+-- end
+-- if c and nukta[current.next.char] then
+-- if head == c then head = current.next end
+-- if stop == current.next then stop = current end
+-- if c.prev then c.prev.next = current.next end
+-- current.next.prev = c.prev
+-- current.next = current.next.next
+-- if current.next.next then current.next.next.prev = current end
+-- c.prev = current.next
+-- current.next.next = c
+-- end
+-- if stop == current then break end
+-- current = current.next
+-- end
+--
+-- if nbsp[base.char] then
+-- head = node.remove(head, base)
+-- node.free(base)
+-- end
+--
+-- return head, stop
+-- end
+--
+-- function fonts.analyzers.methods.deva(head,font,attr)
+-- local orighead = head
+-- local current, start, done = head, true, false
+-- while current do
+-- if current.id == glyph and current.subtype<256 and current.font == font then
+-- done = true
+-- local syllablestart, syllableend = current, nil
+--
+-- local c = current --Checking Stand Alone cluster (this behavior is copied from dev2)
+-- if ra[c.char] and c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and halant[c.next.char] and c.next.next and c.next.next.id == glyph and c.next.next.subtype<256 and c.next.next.font == font then c = c.next.next end
+-- if nbsp[c.char] and ( not current.prev or current.prev.id ~= glyph or current.prev.subtype>=256 or current.prev.font ~= font or
+-- ( not consonant[current.prev.char] and not independent_vowel[current.prev.char] and not dependent_vowel[current.prev.char] and
+-- not vowel_modifier[current.prev.char] and not stress_tone_mark[current.prev.char] and not nukta[current.prev.char] and not halant[current.prev.char] )
+-- ) then --Stand Alone cluster (at the start of the word only): #[Ra+H]+NBSP+[N]+[<[<ZWJ|ZWNJ>]+H+C>]+[{M}+[N]+[H]]+[SM]+[(VD)]
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and nukta[c.next.char] then c = c.next end
+-- local n = c.next
+-- if n and n.id == glyph and n.subtype<256 and n.font == font then
+-- local ni = n.next
+-- if ( zwj[n.char] or zwnj[n.char] ) and ni and ni.id == glyph and ni.subtype<256 and ni.font == font then n = ni ni = ni.next end
+-- if halant[n.char] and ni and ni.id == glyph and ni.subtype<256 and ni.font == font and consonant[ni.char] then c = ni end
+-- end
+-- while c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and dependent_vowel[c.next.char] do c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and nukta[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and halant[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and vowel_modifier[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and stress_tone_mark[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and stress_tone_mark[c.next.char] then c = c.next end
+-- current = c.next
+-- syllableend = c
+-- if syllablestart ~= syllableend then
+-- head, current = deva_reorder(head, syllablestart,syllableend,font,attr)
+-- current = current.next
+-- end
+-- elseif consonant[current.char] then -- syllable containing consonant
+-- prevc = true
+-- while prevc do
+-- prevc = false
+-- local n = current.next
+-- if n and n.id == glyph and n.subtype<256 and n.font == font and nukta[n.char] then n = n.next end
+-- if n and n.id == glyph and n.subtype<256 and n.font == font and halant[n.char] then
+-- local n = n.next
+-- if n and n.id == glyph and n.subtype<256 and n.font == font and ( zwj[n.char] or zwnj[n.char] ) then n = n.next end
+-- if n and n.id == glyph and n.subtype<256 and n.font == font and consonant[n.char] then
+-- prevc = true
+-- current = n
+-- end
+-- end
+-- end
+-- if current.next and current.next.id == glyph and current.next.subtype<256 and current.next.font == font and nukta[current.next.char] then current = current.next end -- nukta (not specified in Microsft Devanagari OpenType specification)
+-- syllableend = current
+-- current = current.next
+-- if current and current.id == glyph and current.subtype<256 and current.font == font and halant[current.char] then -- syllable containing consonant without vowels: {C + [Nukta] + H} + C + H
+-- if current.next and current.next.id == glyph and current.next.subtype<256 and current.next.font == font and ( zwj[current.next.char] or zwnj[current.next.char] ) then current = current.next end
+-- syllableend = current
+-- current = current.next
+-- else -- syllable containing consonant with vowels: {C + [Nukta] + H} + C + [M] + [VM] + [SM]
+-- if current and current.id == glyph and current.subtype<256 and current.font == font and dependent_vowel[current.char] then
+-- syllableend = current
+-- current = current.next
+-- end
+-- if current and current.id == glyph and current.subtype<256 and current.font == font and vowel_modifier[current.char] then
+-- syllableend = current
+-- current = current.next
+-- end
+-- if current and current.id == glyph and current.subtype<256 and current.font == font and stress_tone_mark[current.char] then
+-- syllableend = current
+-- current = current.next
+-- end
+-- end
+-- if syllablestart ~= syllableend then
+-- head, current = deva_reorder(head,syllablestart,syllableend,font,attr)
+-- current = current.next
+-- end
+-- elseif current.id == glyph and current.subtype<256 and current.font == font and independent_vowel[current.char] then -- syllable without consonants: VO + [VM] + [SM]
+-- syllableend = current
+-- current = current.next
+-- if current and current.id == glyph and current.subtype<256 and current.font == font and vowel_modifier[current.char] then
+-- syllableend = current
+-- current = current.next
+-- end
+-- if current and current.id == glyph and current.subtype<256 and current.font == font and stress_tone_mark[current.char] then
+-- syllableend = current
+-- current = current.next
+-- end
+-- else -- Syntax error
+-- if pre_mark[current.char] or above_mark[current.char] or below_mark[current.char] or post_mark[current.char] then
+-- local n = node.copy(current)
+-- if pre_mark[current.char] then
+-- n.char = dotted_circle
+-- else
+-- current.char = dotted_circle
+-- end
+-- head, current = node.insert_after(head, current, n)
+-- end
+-- current = current.next
+-- end
+-- else
+-- current = current.next
+-- end
+-- start = false
+-- end
+--
+-- return head, done
+-- end
+--
+-- function fonts.analyzers.methods.dev2(head,font,attr)
+-- local current, start, done, syl_nr = head, true, false, 0
+-- while current do
+-- local syllablestart, syllableend = nil, nil
+-- if current.id == glyph and current.subtype<256 and current.font == font then
+-- syllablestart = current
+-- done = true
+-- local c, n = current, current.next
+-- if ra[current.char] and n and n.id == glyph and n.subtype<256 and n.font == font and halant[n.char] and n.next and n.next.id == glyph and n.next.subtype<256 and n.next.font == font then c = n.next end
+-- if independent_vowel[c.char] then --Vowel-based syllable: [Ra+H]+V+[N]+[<[<ZWJ|ZWNJ>]+H+C|ZWJ+C>]+[{M}+[N]+[H]]+[SM]+[(VD)]
+-- n = c.next
+-- local ni, nii = nil, nil
+-- if n and n.id == glyph and n.subtype<256 and n.font == font and nukta[n.char] then n = n.next end
+-- if n and n.id == glyph and n.subtype<256 and n.font == font then local ni = n.next end
+-- if ni and ni.id == glyph and ni.subtype<256 and ni.font == font and ni.next and ni.next.id == glyph and ni.next.subtype<256 and ni.next.font == font then
+-- nii = ni.next
+-- if zwj[ni.char] and consonant[nii.char] then
+-- c = nii
+-- elseif (zwj[ni.char] or zwnj[ni.char]) and halant[nii.char] and nii.next and nii.next.id == glyph and nii.next.subtype<256 and nii.next.font == font and consonant[nii.next.char] then
+-- c = nii.next
+-- end
+-- end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and dependent_vowel[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and nukta[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and halant[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and vowel_modifier[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and stress_tone_mark[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and stress_tone_mark[c.next.char] then c = c.next end
+-- current = c
+-- syllableend = c
+-- elseif nbsp[c.char] and ( not current.prev or current.prev.id ~= glyph or current.prev.subtype>=256 or current.prev.font ~= font or
+-- ( not consonant[current.prev.char] and not independent_vowel[current.prev.char] and not dependent_vowel[current.prev.char] and
+-- not vowel_modifier[current.prev.char] and not stress_tone_mark[current.prev.char] and not nukta[current.prev.char] and not halant[current.prev.char] )
+-- ) then --Stand Alone cluster (at the start of the word only): #[Ra+H]+NBSP+[N]+[<[<ZWJ|ZWNJ>]+H+C>]+[{M}+[N]+[H]]+[SM]+[(VD)]
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and nukta[c.next.char] then c = c.next end
+-- n = c.next
+-- if n and n.id == glyph and n.subtype<256 and n.font == font then
+-- local ni = n.next
+-- if ( zwj[n.char] or zwnj[n.char] ) and ni and ni.id == glyph and ni.subtype<256 and ni.font == font then n = ni ni = ni.next end
+-- if halant[n.char] and ni and ni.id == glyph and ni.subtype<256 and ni.font == font and consonant[ni.char] then c = ni end
+-- end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and dependent_vowel[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and nukta[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and halant[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and vowel_modifier[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and stress_tone_mark[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and stress_tone_mark[c.next.char] then c = c.next end
+-- current = c
+-- syllableend = c
+-- elseif consonant[current.char] then --Consonant syllable: {C+[N]+<H+[<ZWNJ|ZWJ>]|<ZWNJ|ZWJ>+H>} + C+[N]+[A] + [< H+[<ZWNJ|ZWJ>] | {M}+[N]+[H]>]+[SM]+[(VD)]
+-- c = current
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and nukta[c.next.char] then c = c.next end
+-- n = c
+-- while n.next and n.next.id == glyph and n.next.subtype<256 and n.next.font == font and ( halant[n.next.char] or zwnj[n.next.char] or zwj[n.next.char] ) do
+-- if halant[n.next.char] then
+-- n = n.next
+-- if n.next and n.next.id == glyph and n.next.subtype<256 and n.next.font == font and ( zwnj[n.next.char] or zwj[n.next.char] ) then n = n.next end
+-- else
+-- if n.next.next and n.next.next.id == glyph and n.next.next.subtype<256 and n.next.next.font == font and halant[n.next.next.char] then n = n.next.next end
+-- end
+-- if n.next and n.next.id == glyph and n.next.subtype<256 and n.next.font == font and consonant[n.next.char] then
+-- n = n.next
+-- if n.next and n.next.id == glyph and n.next.subtype<256 and n.next.font == font and nukta[n.next.char] then n = n.next end
+-- c = n
+-- else
+-- break
+-- end
+-- end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and anudatta[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and halant[c.next.char] then
+-- c = c.next
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and ( zwnj[c.next.char] or zwj[c.next.char] ) then c = c.next end
+-- else
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and dependent_vowel[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and nukta[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and halant[c.next.char] then c = c.next end
+-- end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and vowel_modifier[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and stress_tone_mark[c.next.char] then c = c.next end
+-- if c.next and c.next.id == glyph and c.next.subtype<256 and c.next.font == font and stress_tone_mark[c.next.char] then c = c.next end
+-- current = c
+-- syllableend = c
+-- end
+-- end
+--
+-- if syllableend then
+-- syl_nr = syl_nr + 1
+-- c = syllablestart
+-- while c ~= syllableend.next do
+-- set_attribute(c,sylnr,syl_nr)
+-- c = c.next
+-- end
+-- end
+-- if syllableend and syllablestart ~= syllableend then
+-- head, current = dev2_reorder(head,syllablestart,syllableend,font,attr)
+-- end
+--
+-- if not syllableend and not has_attribute(current, state) and current.id == glyph and current.subtype<256 and current.font == font then -- Syntax error
+-- if pre_mark[current.char] or above_mark[current.char] or below_mark[current.char] or post_mark[current.char] then
+-- local n = node.copy(current)
+-- if pre_mark[current.char] then
+-- n.char = dotted_circle
+-- else
+-- current.char = dotted_circle
+-- end
+-- head, current = node.insert_after(head, current, n)
+-- end
+-- end
+--
+-- start = false
+-- current = current.next
+-- end
+--
+-- return head, done
+-- end
+--
+-- function otf.handlers.dev2_reorder_matras(start,kind,lookupname,replacement)
+-- return dev2_reorder_matras(start,kind,lookupname,replacement)
+-- end
+--
+-- function otf.handlers.dev2_reorder_reph(start,kind,lookupname,replacement)
+-- return dev2_reorder_reph(start,kind,lookupname,replacement)
+-- end
+--
+-- function otf.handlers.dev2_reorder_pre_base_reordering_consonants(start,kind,lookupname,replacement)
+-- return dev2_reorder_pre_base_reordering_consonants(start,kind,lookupname,replacement)
+-- end
+--
+-- function otf.handlers.remove_joiners(start,kind,lookupname,replacement)
+-- return remove_joiners(start,kind,lookupname,replacement)
+-- end
diff --git a/tex/context/base/font-otb.lua b/tex/context/base/font-otb.lua
index 75bda383e..2a7b821ea 100644
--- a/tex/context/base/font-otb.lua
+++ b/tex/context/base/font-otb.lua
@@ -1,657 +1,657 @@
-if not modules then modules = { } end modules ['font-otb'] = {
- 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 concat = table.concat
-local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip
-local type, next, tonumber, tostring = type, next, tonumber, tostring
-local lpegmatch = lpeg.match
-local utfchar = utf.char
-
-local trace_baseinit = false trackers.register("otf.baseinit", function(v) trace_baseinit = v end)
-local trace_singles = false trackers.register("otf.singles", function(v) trace_singles = v end)
-local trace_multiples = false trackers.register("otf.multiples", function(v) trace_multiples = v end)
-local trace_alternatives = false trackers.register("otf.alternatives", function(v) trace_alternatives = v end)
-local trace_ligatures = false trackers.register("otf.ligatures", function(v) trace_ligatures = v end)
-local trace_ligatures_detail = false trackers.register("otf.ligatures.detail", function(v) trace_ligatures_detail = v end)
-local trace_kerns = false trackers.register("otf.kerns", function(v) trace_kerns = v end)
-local trace_preparing = false trackers.register("otf.preparing", function(v) trace_preparing = v end)
-
-local report_prepare = logs.reporter("fonts","otf prepare")
-
-local fonts = fonts
-local otf = fonts.handlers.otf
-
-local otffeatures = otf.features
-local registerotffeature = otffeatures.register
-
-otf.defaultbasealternate = "none" -- first last
-
-local wildcard = "*"
-local default = "dflt"
-
-local formatters = string.formatters
-local f_unicode = formatters["%U"]
-local f_uniname = formatters["%U (%s)"]
-local f_unilist = formatters["% t (% t)"]
-
-local function gref(descriptions,n)
- if type(n) == "number" then
- local name = descriptions[n].name
- if name then
- return f_uniname(n,name)
- else
- return f_unicode(n)
- end
- elseif n then
- local num, nam = { }, { }
- for i=2,#n do
- local ni = n[i]
- if tonumber(ni) then -- first is likely a key
- local di = descriptions[ni]
- num[i] = f_unicode(ni)
- nam[i] = di and di.name or "-"
- end
- end
- return f_unilist(num,nam)
- else
- return "<error in base mode tracing>"
- end
-end
-
-local function cref(feature,lookupname)
- if lookupname then
- return formatters["feature %a, lookup %a"](feature,lookupname)
- else
- return formatters["feature %a"](feature)
- end
-end
-
-local function report_alternate(feature,lookupname,descriptions,unicode,replacement,value,comment)
- report_prepare("%s: base alternate %s => %s (%S => %S)",
- cref(feature,lookupname),
- gref(descriptions,unicode),
- replacement and gref(descriptions,replacement),
- value,
- comment)
-end
-
-local function report_substitution(feature,lookupname,descriptions,unicode,substitution)
- report_prepare("%s: base substitution %s => %S",
- cref(feature,lookupname),
- gref(descriptions,unicode),
- gref(descriptions,substitution))
-end
-
-local function report_ligature(feature,lookupname,descriptions,unicode,ligature)
- report_prepare("%s: base ligature %s => %S",
- cref(feature,lookupname),
- gref(descriptions,ligature),
- gref(descriptions,unicode))
-end
-
-local function report_kern(feature,lookupname,descriptions,unicode,otherunicode,value)
- report_prepare("%s: base kern %s + %s => %S",
- cref(feature,lookupname),
- gref(descriptions,unicode),
- gref(descriptions,otherunicode),
- value)
-end
-
-local basemethods = { }
-local basemethod = "<unset>"
-
-local function applybasemethod(what,...)
- local m = basemethods[basemethod][what]
- if m then
- return m(...)
- end
-end
-
--- We need to make sure that luatex sees the difference between
--- base fonts that have different glyphs in the same slots in fonts
--- that have the same fullname (or filename). LuaTeX will merge fonts
--- eventually (and subset later on). If needed we can use a more
--- verbose name as long as we don't use <()<>[]{}/%> and the length
--- is < 128.
-
-local basehash, basehashes, applied = { }, 1, { }
-
-local function registerbasehash(tfmdata)
- local properties = tfmdata.properties
- local hash = concat(applied," ")
- local base = basehash[hash]
- if not base then
- basehashes = basehashes + 1
- base = basehashes
- basehash[hash] = base
- end
- properties.basehash = base
- properties.fullname = properties.fullname .. "-" .. base
- -- report_prepare("fullname base hash '%a, featureset %a",tfmdata.properties.fullname,hash)
- applied = { }
-end
-
-local function registerbasefeature(feature,value)
- applied[#applied+1] = feature .. "=" .. tostring(value)
-end
-
--- The original basemode ligature builder used the names of components
--- and did some expression juggling to get the chain right. The current
--- variant starts with unicodes but still uses names to make the chain.
--- This is needed because we have to create intermediates when needed
--- but use predefined snippets when available. To some extend the
--- current builder is more stupid but I don't worry that much about it
--- as ligatures are rather predicatable.
---
--- Personally I think that an ff + i == ffi rule as used in for instance
--- latin modern is pretty weird as no sane person will key that in and
--- expect a glyph for that ligature plus the following character. Anyhow,
--- as we need to deal with this, we do, but no guarantes are given.
---
--- latin modern dejavu
---
--- f+f 102 102 102 102
--- f+i 102 105 102 105
--- f+l 102 108 102 108
--- f+f+i 102 102 105
--- f+f+l 102 102 108 102 102 108
--- ff+i 64256 105 64256 105
--- ff+l 64256 108
---
--- As you can see here, latin modern is less complete than dejavu but
--- in practice one will not notice it.
---
--- The while loop is needed because we need to resolve for instance
--- pseudo names like hyphen_hyphen to endash so in practice we end
--- up with a bit too many definitions but the overhead is neglectable.
---
--- Todo: if changed[first] or changed[second] then ... end
-
-local trace = false
-
-local function finalize_ligatures(tfmdata,ligatures)
- local nofligatures = #ligatures
- if nofligatures > 0 then
- local characters = tfmdata.characters
- local descriptions = tfmdata.descriptions
- local resources = tfmdata.resources
- local unicodes = resources.unicodes
- local private = resources.private
- local alldone = false
- while not alldone do
- local done = 0
- for i=1,nofligatures do
- local ligature = ligatures[i]
- if ligature then
- local unicode, lookupdata = ligature[1], ligature[2]
- if trace then
- trace_ligatures_detail("building % a into %a",lookupdata,unicode)
- end
- local size = #lookupdata
- local firstcode = lookupdata[1] -- [2]
- local firstdata = characters[firstcode]
- local okay = false
- if firstdata then
- local firstname = "ctx_" .. firstcode
- for i=1,size-1 do -- for i=2,size-1 do
- local firstdata = characters[firstcode]
- if not firstdata then
- firstcode = private
- if trace then
- trace_ligatures_detail("defining %a as %a",firstname,firstcode)
- end
- unicodes[firstname] = firstcode
- firstdata = { intermediate = true, ligatures = { } }
- characters[firstcode] = firstdata
- descriptions[firstcode] = { name = firstname }
- private = private + 1
- end
- local target
- local secondcode = lookupdata[i+1]
- local secondname = firstname .. "_" .. secondcode
- if i == size - 1 then
- target = unicode
- if not unicodes[secondname] then
- unicodes[secondname] = unicode -- map final ligature onto intermediates
- end
- okay = true
- else
- target = unicodes[secondname]
- if not target then
- break
- end
- end
- if trace then
- trace_ligatures_detail("codes (%a,%a) + (%a,%a) -> %a",firstname,firstcode,secondname,secondcode,target)
- end
- local firstligs = firstdata.ligatures
- if firstligs then
- firstligs[secondcode] = { char = target }
- else
- firstdata.ligatures = { [secondcode] = { char = target } }
- end
- firstcode = target
- firstname = secondname
- end
- end
- if okay then
- ligatures[i] = false
- done = done + 1
- end
- end
- end
- alldone = done == 0
- end
- if trace then
- for k, v in next, characters do
- if v.ligatures then table.print(v,k) end
- end
- end
- tfmdata.resources.private = private
- end
-end
-
-local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist)
- local characters = tfmdata.characters
- local descriptions = tfmdata.descriptions
- local resources = tfmdata.resources
- local changed = tfmdata.changed
- local unicodes = resources.unicodes
- local lookuphash = resources.lookuphash
- local lookuptypes = resources.lookuptypes
-
- local ligatures = { }
- local alternate = tonumber(value)
- local defaultalt = otf.defaultbasealternate
-
- local trace_singles = trace_baseinit and trace_singles
- local trace_alternatives = trace_baseinit and trace_alternatives
- local trace_ligatures = trace_baseinit and trace_ligatures
-
- local actions = {
- substitution = function(lookupdata,lookupname,description,unicode)
- if trace_singles then
- report_substitution(feature,lookupname,descriptions,unicode,lookupdata)
- end
- changed[unicode] = lookupdata
- end,
- alternate = function(lookupdata,lookupname,description,unicode)
- local replacement = lookupdata[alternate]
- if replacement then
- changed[unicode] = replacement
- if trace_alternatives then
- report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"normal")
- end
- elseif defaultalt == "first" then
- replacement = lookupdata[1]
- changed[unicode] = replacement
- if trace_alternatives then
- report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt)
- end
- elseif defaultalt == "last" then
- replacement = lookupdata[#data]
- if trace_alternatives then
- report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt)
- end
- else
- if trace_alternatives then
- report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"unknown")
- end
- end
- end,
- ligature = function(lookupdata,lookupname,description,unicode)
- if trace_ligatures then
- report_ligature(feature,lookupname,descriptions,unicode,lookupdata)
- end
- ligatures[#ligatures+1] = { unicode, lookupdata }
- end,
- }
-
- for unicode, character in next, characters do
- local description = descriptions[unicode]
- local lookups = description.slookups
- if lookups then
- for l=1,#lookuplist do
- local lookupname = lookuplist[l]
- local lookupdata = lookups[lookupname]
- if lookupdata then
- local lookuptype = lookuptypes[lookupname]
- local action = actions[lookuptype]
- if action then
- action(lookupdata,lookupname,description,unicode)
- end
- end
- end
- end
- local lookups = description.mlookups
- if lookups then
- for l=1,#lookuplist do
- local lookupname = lookuplist[l]
- local lookuplist = lookups[lookupname]
- if lookuplist then
- local lookuptype = lookuptypes[lookupname]
- local action = actions[lookuptype]
- if action then
- for i=1,#lookuplist do
- action(lookuplist[i],lookupname,description,unicode)
- end
- end
- end
- end
- end
- end
-
- finalize_ligatures(tfmdata,ligatures)
-end
-
-local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist) -- todo what kind of kerns, currently all
- local characters = tfmdata.characters
- local descriptions = tfmdata.descriptions
- local resources = tfmdata.resources
- local unicodes = resources.unicodes
- local sharedkerns = { }
- local traceindeed = trace_baseinit and trace_kerns
- for unicode, character in next, characters do
- local description = descriptions[unicode]
- local rawkerns = description.kerns -- shared
- if rawkerns then
- local s = sharedkerns[rawkerns]
- if s == false then
- -- skip
- elseif s then
- character.kerns = s
- else
- local newkerns = character.kerns
- local done = false
- for l=1,#lookuplist do
- local lookup = lookuplist[l]
- local kerns = rawkerns[lookup]
- if kerns then
- for otherunicode, value in next, kerns do
- if value == 0 then
- -- maybe no 0 test here
- elseif not newkerns then
- newkerns = { [otherunicode] = value }
- done = true
- if traceindeed then
- report_kern(feature,lookup,descriptions,unicode,otherunicode,value)
- end
- elseif not newkerns[otherunicode] then -- first wins
- newkerns[otherunicode] = value
- done = true
- if traceindeed then
- report_kern(feature,lookup,descriptions,unicode,otherunicode,value)
- end
- end
- end
- end
- end
- if done then
- sharedkerns[rawkerns] = newkerns
- character.kerns = newkerns -- no empty assignments
- else
- sharedkerns[rawkerns] = false
- end
- end
- end
- end
-end
-
-basemethods.independent = {
- preparesubstitutions = preparesubstitutions,
- preparepositionings = preparepositionings,
-}
-
-local function makefake(tfmdata,name,present)
- local resources = tfmdata.resources
- local private = resources.private
- local character = { intermediate = true, ligatures = { } }
- resources.unicodes[name] = private
- tfmdata.characters[private] = character
- tfmdata.descriptions[private] = { name = name }
- resources.private = private + 1
- present[name] = private
- return character
-end
-
-local function make_1(present,tree,name)
- for k, v in next, tree do
- if k == "ligature" then
- present[name] = v
- else
- make_1(present,v,name .. "_" .. k)
- end
- end
-end
-
-local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,done,lookupname)
- for k, v in next, tree do
- if k == "ligature" then
- local character = characters[preceding]
- if not character then
- if trace_baseinit then
- report_prepare("weird ligature in lookup %a, current %C, preceding %C",lookupname,v,preceding)
- end
- character = makefake(tfmdata,name,present)
- end
- local ligatures = character.ligatures
- if ligatures then
- ligatures[unicode] = { char = v }
- else
- character.ligatures = { [unicode] = { char = v } }
- end
- if done then
- local d = done[lookupname]
- if not d then
- done[lookupname] = { "dummy", v }
- else
- d[#d+1] = v
- end
- end
- else
- local code = present[name] or unicode
- local name = name .. "_" .. k
- make_2(present,tfmdata,characters,v,name,code,k,done,lookupname)
- end
- end
-end
-
-local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist)
- local characters = tfmdata.characters
- local descriptions = tfmdata.descriptions
- local resources = tfmdata.resources
- local changed = tfmdata.changed
- local lookuphash = resources.lookuphash
- local lookuptypes = resources.lookuptypes
-
- local ligatures = { }
- local alternate = tonumber(value)
- local defaultalt = otf.defaultbasealternate
-
- local trace_singles = trace_baseinit and trace_singles
- local trace_alternatives = trace_baseinit and trace_alternatives
- local trace_ligatures = trace_baseinit and trace_ligatures
-
- for l=1,#lookuplist do
- local lookupname = lookuplist[l]
- local lookupdata = lookuphash[lookupname]
- local lookuptype = lookuptypes[lookupname]
- for unicode, data in next, lookupdata do
- if lookuptype == "substitution" then
- if trace_singles then
- report_substitution(feature,lookupname,descriptions,unicode,data)
- end
- changed[unicode] = data
- elseif lookuptype == "alternate" then
- local replacement = data[alternate]
- if replacement then
- changed[unicode] = replacement
- if trace_alternatives then
- report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"normal")
- end
- elseif defaultalt == "first" then
- replacement = data[1]
- changed[unicode] = replacement
- if trace_alternatives then
- report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt)
- end
- elseif defaultalt == "last" then
- replacement = data[#data]
- if trace_alternatives then
- report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt)
- end
- else
- if trace_alternatives then
- report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"unknown")
- end
- end
- elseif lookuptype == "ligature" then
- ligatures[#ligatures+1] = { unicode, data, lookupname }
- if trace_ligatures then
- report_ligature(feature,lookupname,descriptions,unicode,data)
- end
- end
- end
- end
-
- local nofligatures = #ligatures
-
- if nofligatures > 0 then
-
- local characters = tfmdata.characters
- local present = { }
- local done = trace_baseinit and trace_ligatures and { }
-
- for i=1,nofligatures do
- local ligature = ligatures[i]
- local unicode, tree = ligature[1], ligature[2]
- make_1(present,tree,"ctx_"..unicode)
- end
-
- for i=1,nofligatures do
- local ligature = ligatures[i]
- local unicode, tree, lookupname = ligature[1], ligature[2], ligature[3]
- make_2(present,tfmdata,characters,tree,"ctx_"..unicode,unicode,unicode,done,lookupname)
- end
-
- end
-
-end
-
-local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist)
- local characters = tfmdata.characters
- local descriptions = tfmdata.descriptions
- local resources = tfmdata.resources
- local lookuphash = resources.lookuphash
- local traceindeed = trace_baseinit and trace_kerns
-
- -- check out this sharedkerns trickery
-
- for l=1,#lookuplist do
- local lookupname = lookuplist[l]
- local lookupdata = lookuphash[lookupname]
- for unicode, data in next, lookupdata do
- local character = characters[unicode]
- local kerns = character.kerns
- if not kerns then
- kerns = { }
- character.kerns = kerns
- end
- if traceindeed then
- for otherunicode, kern in next, data do
- if not kerns[otherunicode] and kern ~= 0 then
- kerns[otherunicode] = kern
- report_kern(feature,lookup,descriptions,unicode,otherunicode,kern)
- end
- end
- else
- for otherunicode, kern in next, data do
- if not kerns[otherunicode] and kern ~= 0 then
- kerns[otherunicode] = kern
- end
- end
- end
- end
- end
-
-end
-
-local function initializehashes(tfmdata)
- nodeinitializers.features(tfmdata)
-end
-
-basemethods.shared = {
- initializehashes = initializehashes,
- preparesubstitutions = preparesubstitutions,
- preparepositionings = preparepositionings,
-}
-
-basemethod = "independent"
-
-local function featuresinitializer(tfmdata,value)
- if true then -- value then
- local t = trace_preparing and os.clock()
- local features = tfmdata.shared.features
- if features then
- applybasemethod("initializehashes",tfmdata)
- local collectlookups = otf.collectlookups
- local rawdata = tfmdata.shared.rawdata
- local properties = tfmdata.properties
- local script = properties.script
- local language = properties.language
- local basesubstitutions = rawdata.resources.features.gsub
- local basepositionings = rawdata.resources.features.gpos
- if basesubstitutions then
- for feature, data in next, basesubstitutions do
- local value = features[feature]
- if value then
- local validlookups, lookuplist = collectlookups(rawdata,feature,script,language)
- if validlookups then
- applybasemethod("preparesubstitutions",tfmdata,feature,value,validlookups,lookuplist)
- registerbasefeature(feature,value)
- end
- end
- end
- end
- if basepositionings then
- for feature, data in next, basepositionings do
- local value = features[feature]
- if value then
- local validlookups, lookuplist = collectlookups(rawdata,feature,script,language)
- if validlookups then
- applybasemethod("preparepositionings",tfmdata,feature,features[feature],validlookups,lookuplist)
- registerbasefeature(feature,value)
- end
- end
- end
- end
- registerbasehash(tfmdata)
- end
- if trace_preparing then
- report_prepare("preparation time is %0.3f seconds for %a",os.clock()-t,tfmdata.properties.fullname)
- end
- end
-end
-
-registerotffeature {
- name = "features",
- description = "features",
- default = true,
- initializers = {
- -- position = 1, -- after setscript (temp hack ... we need to force script / language to 1
- base = featuresinitializer,
- }
-}
-
--- independent : collect lookups independently (takes more runtime ... neglectable)
--- shared : shares lookups with node mode (takes more memory unless also a node mode variant is used ... noticeable)
-
-directives.register("fonts.otf.loader.basemethod", function(v)
- if basemethods[v] then
- basemethod = v
- end
-end)
+if not modules then modules = { } end modules ['font-otb'] = {
+ 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 concat = table.concat
+local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip
+local type, next, tonumber, tostring = type, next, tonumber, tostring
+local lpegmatch = lpeg.match
+local utfchar = utf.char
+
+local trace_baseinit = false trackers.register("otf.baseinit", function(v) trace_baseinit = v end)
+local trace_singles = false trackers.register("otf.singles", function(v) trace_singles = v end)
+local trace_multiples = false trackers.register("otf.multiples", function(v) trace_multiples = v end)
+local trace_alternatives = false trackers.register("otf.alternatives", function(v) trace_alternatives = v end)
+local trace_ligatures = false trackers.register("otf.ligatures", function(v) trace_ligatures = v end)
+local trace_ligatures_detail = false trackers.register("otf.ligatures.detail", function(v) trace_ligatures_detail = v end)
+local trace_kerns = false trackers.register("otf.kerns", function(v) trace_kerns = v end)
+local trace_preparing = false trackers.register("otf.preparing", function(v) trace_preparing = v end)
+
+local report_prepare = logs.reporter("fonts","otf prepare")
+
+local fonts = fonts
+local otf = fonts.handlers.otf
+
+local otffeatures = otf.features
+local registerotffeature = otffeatures.register
+
+otf.defaultbasealternate = "none" -- first last
+
+local wildcard = "*"
+local default = "dflt"
+
+local formatters = string.formatters
+local f_unicode = formatters["%U"]
+local f_uniname = formatters["%U (%s)"]
+local f_unilist = formatters["% t (% t)"]
+
+local function gref(descriptions,n)
+ if type(n) == "number" then
+ local name = descriptions[n].name
+ if name then
+ return f_uniname(n,name)
+ else
+ return f_unicode(n)
+ end
+ elseif n then
+ local num, nam = { }, { }
+ for i=2,#n do
+ local ni = n[i]
+ if tonumber(ni) then -- first is likely a key
+ local di = descriptions[ni]
+ num[i] = f_unicode(ni)
+ nam[i] = di and di.name or "-"
+ end
+ end
+ return f_unilist(num,nam)
+ else
+ return "<error in base mode tracing>"
+ end
+end
+
+local function cref(feature,lookupname)
+ if lookupname then
+ return formatters["feature %a, lookup %a"](feature,lookupname)
+ else
+ return formatters["feature %a"](feature)
+ end
+end
+
+local function report_alternate(feature,lookupname,descriptions,unicode,replacement,value,comment)
+ report_prepare("%s: base alternate %s => %s (%S => %S)",
+ cref(feature,lookupname),
+ gref(descriptions,unicode),
+ replacement and gref(descriptions,replacement),
+ value,
+ comment)
+end
+
+local function report_substitution(feature,lookupname,descriptions,unicode,substitution)
+ report_prepare("%s: base substitution %s => %S",
+ cref(feature,lookupname),
+ gref(descriptions,unicode),
+ gref(descriptions,substitution))
+end
+
+local function report_ligature(feature,lookupname,descriptions,unicode,ligature)
+ report_prepare("%s: base ligature %s => %S",
+ cref(feature,lookupname),
+ gref(descriptions,ligature),
+ gref(descriptions,unicode))
+end
+
+local function report_kern(feature,lookupname,descriptions,unicode,otherunicode,value)
+ report_prepare("%s: base kern %s + %s => %S",
+ cref(feature,lookupname),
+ gref(descriptions,unicode),
+ gref(descriptions,otherunicode),
+ value)
+end
+
+local basemethods = { }
+local basemethod = "<unset>"
+
+local function applybasemethod(what,...)
+ local m = basemethods[basemethod][what]
+ if m then
+ return m(...)
+ end
+end
+
+-- We need to make sure that luatex sees the difference between
+-- base fonts that have different glyphs in the same slots in fonts
+-- that have the same fullname (or filename). LuaTeX will merge fonts
+-- eventually (and subset later on). If needed we can use a more
+-- verbose name as long as we don't use <()<>[]{}/%> and the length
+-- is < 128.
+
+local basehash, basehashes, applied = { }, 1, { }
+
+local function registerbasehash(tfmdata)
+ local properties = tfmdata.properties
+ local hash = concat(applied," ")
+ local base = basehash[hash]
+ if not base then
+ basehashes = basehashes + 1
+ base = basehashes
+ basehash[hash] = base
+ end
+ properties.basehash = base
+ properties.fullname = properties.fullname .. "-" .. base
+ -- report_prepare("fullname base hash '%a, featureset %a",tfmdata.properties.fullname,hash)
+ applied = { }
+end
+
+local function registerbasefeature(feature,value)
+ applied[#applied+1] = feature .. "=" .. tostring(value)
+end
+
+-- The original basemode ligature builder used the names of components
+-- and did some expression juggling to get the chain right. The current
+-- variant starts with unicodes but still uses names to make the chain.
+-- This is needed because we have to create intermediates when needed
+-- but use predefined snippets when available. To some extend the
+-- current builder is more stupid but I don't worry that much about it
+-- as ligatures are rather predicatable.
+--
+-- Personally I think that an ff + i == ffi rule as used in for instance
+-- latin modern is pretty weird as no sane person will key that in and
+-- expect a glyph for that ligature plus the following character. Anyhow,
+-- as we need to deal with this, we do, but no guarantes are given.
+--
+-- latin modern dejavu
+--
+-- f+f 102 102 102 102
+-- f+i 102 105 102 105
+-- f+l 102 108 102 108
+-- f+f+i 102 102 105
+-- f+f+l 102 102 108 102 102 108
+-- ff+i 64256 105 64256 105
+-- ff+l 64256 108
+--
+-- As you can see here, latin modern is less complete than dejavu but
+-- in practice one will not notice it.
+--
+-- The while loop is needed because we need to resolve for instance
+-- pseudo names like hyphen_hyphen to endash so in practice we end
+-- up with a bit too many definitions but the overhead is neglectable.
+--
+-- Todo: if changed[first] or changed[second] then ... end
+
+local trace = false
+
+local function finalize_ligatures(tfmdata,ligatures)
+ local nofligatures = #ligatures
+ if nofligatures > 0 then
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local resources = tfmdata.resources
+ local unicodes = resources.unicodes
+ local private = resources.private
+ local alldone = false
+ while not alldone do
+ local done = 0
+ for i=1,nofligatures do
+ local ligature = ligatures[i]
+ if ligature then
+ local unicode, lookupdata = ligature[1], ligature[2]
+ if trace then
+ trace_ligatures_detail("building % a into %a",lookupdata,unicode)
+ end
+ local size = #lookupdata
+ local firstcode = lookupdata[1] -- [2]
+ local firstdata = characters[firstcode]
+ local okay = false
+ if firstdata then
+ local firstname = "ctx_" .. firstcode
+ for i=1,size-1 do -- for i=2,size-1 do
+ local firstdata = characters[firstcode]
+ if not firstdata then
+ firstcode = private
+ if trace then
+ trace_ligatures_detail("defining %a as %a",firstname,firstcode)
+ end
+ unicodes[firstname] = firstcode
+ firstdata = { intermediate = true, ligatures = { } }
+ characters[firstcode] = firstdata
+ descriptions[firstcode] = { name = firstname }
+ private = private + 1
+ end
+ local target
+ local secondcode = lookupdata[i+1]
+ local secondname = firstname .. "_" .. secondcode
+ if i == size - 1 then
+ target = unicode
+ if not unicodes[secondname] then
+ unicodes[secondname] = unicode -- map final ligature onto intermediates
+ end
+ okay = true
+ else
+ target = unicodes[secondname]
+ if not target then
+ break
+ end
+ end
+ if trace then
+ trace_ligatures_detail("codes (%a,%a) + (%a,%a) -> %a",firstname,firstcode,secondname,secondcode,target)
+ end
+ local firstligs = firstdata.ligatures
+ if firstligs then
+ firstligs[secondcode] = { char = target }
+ else
+ firstdata.ligatures = { [secondcode] = { char = target } }
+ end
+ firstcode = target
+ firstname = secondname
+ end
+ end
+ if okay then
+ ligatures[i] = false
+ done = done + 1
+ end
+ end
+ end
+ alldone = done == 0
+ end
+ if trace then
+ for k, v in next, characters do
+ if v.ligatures then table.print(v,k) end
+ end
+ end
+ tfmdata.resources.private = private
+ end
+end
+
+local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist)
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local resources = tfmdata.resources
+ local changed = tfmdata.changed
+ local unicodes = resources.unicodes
+ local lookuphash = resources.lookuphash
+ local lookuptypes = resources.lookuptypes
+
+ local ligatures = { }
+ local alternate = tonumber(value)
+ local defaultalt = otf.defaultbasealternate
+
+ local trace_singles = trace_baseinit and trace_singles
+ local trace_alternatives = trace_baseinit and trace_alternatives
+ local trace_ligatures = trace_baseinit and trace_ligatures
+
+ local actions = {
+ substitution = function(lookupdata,lookupname,description,unicode)
+ if trace_singles then
+ report_substitution(feature,lookupname,descriptions,unicode,lookupdata)
+ end
+ changed[unicode] = lookupdata
+ end,
+ alternate = function(lookupdata,lookupname,description,unicode)
+ local replacement = lookupdata[alternate]
+ if replacement then
+ changed[unicode] = replacement
+ if trace_alternatives then
+ report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"normal")
+ end
+ elseif defaultalt == "first" then
+ replacement = lookupdata[1]
+ changed[unicode] = replacement
+ if trace_alternatives then
+ report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt)
+ end
+ elseif defaultalt == "last" then
+ replacement = lookupdata[#data]
+ if trace_alternatives then
+ report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt)
+ end
+ else
+ if trace_alternatives then
+ report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"unknown")
+ end
+ end
+ end,
+ ligature = function(lookupdata,lookupname,description,unicode)
+ if trace_ligatures then
+ report_ligature(feature,lookupname,descriptions,unicode,lookupdata)
+ end
+ ligatures[#ligatures+1] = { unicode, lookupdata }
+ end,
+ }
+
+ for unicode, character in next, characters do
+ local description = descriptions[unicode]
+ local lookups = description.slookups
+ if lookups then
+ for l=1,#lookuplist do
+ local lookupname = lookuplist[l]
+ local lookupdata = lookups[lookupname]
+ if lookupdata then
+ local lookuptype = lookuptypes[lookupname]
+ local action = actions[lookuptype]
+ if action then
+ action(lookupdata,lookupname,description,unicode)
+ end
+ end
+ end
+ end
+ local lookups = description.mlookups
+ if lookups then
+ for l=1,#lookuplist do
+ local lookupname = lookuplist[l]
+ local lookuplist = lookups[lookupname]
+ if lookuplist then
+ local lookuptype = lookuptypes[lookupname]
+ local action = actions[lookuptype]
+ if action then
+ for i=1,#lookuplist do
+ action(lookuplist[i],lookupname,description,unicode)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ finalize_ligatures(tfmdata,ligatures)
+end
+
+local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist) -- todo what kind of kerns, currently all
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local resources = tfmdata.resources
+ local unicodes = resources.unicodes
+ local sharedkerns = { }
+ local traceindeed = trace_baseinit and trace_kerns
+ for unicode, character in next, characters do
+ local description = descriptions[unicode]
+ local rawkerns = description.kerns -- shared
+ if rawkerns then
+ local s = sharedkerns[rawkerns]
+ if s == false then
+ -- skip
+ elseif s then
+ character.kerns = s
+ else
+ local newkerns = character.kerns
+ local done = false
+ for l=1,#lookuplist do
+ local lookup = lookuplist[l]
+ local kerns = rawkerns[lookup]
+ if kerns then
+ for otherunicode, value in next, kerns do
+ if value == 0 then
+ -- maybe no 0 test here
+ elseif not newkerns then
+ newkerns = { [otherunicode] = value }
+ done = true
+ if traceindeed then
+ report_kern(feature,lookup,descriptions,unicode,otherunicode,value)
+ end
+ elseif not newkerns[otherunicode] then -- first wins
+ newkerns[otherunicode] = value
+ done = true
+ if traceindeed then
+ report_kern(feature,lookup,descriptions,unicode,otherunicode,value)
+ end
+ end
+ end
+ end
+ end
+ if done then
+ sharedkerns[rawkerns] = newkerns
+ character.kerns = newkerns -- no empty assignments
+ else
+ sharedkerns[rawkerns] = false
+ end
+ end
+ end
+ end
+end
+
+basemethods.independent = {
+ preparesubstitutions = preparesubstitutions,
+ preparepositionings = preparepositionings,
+}
+
+local function makefake(tfmdata,name,present)
+ local resources = tfmdata.resources
+ local private = resources.private
+ local character = { intermediate = true, ligatures = { } }
+ resources.unicodes[name] = private
+ tfmdata.characters[private] = character
+ tfmdata.descriptions[private] = { name = name }
+ resources.private = private + 1
+ present[name] = private
+ return character
+end
+
+local function make_1(present,tree,name)
+ for k, v in next, tree do
+ if k == "ligature" then
+ present[name] = v
+ else
+ make_1(present,v,name .. "_" .. k)
+ end
+ end
+end
+
+local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,done,lookupname)
+ for k, v in next, tree do
+ if k == "ligature" then
+ local character = characters[preceding]
+ if not character then
+ if trace_baseinit then
+ report_prepare("weird ligature in lookup %a, current %C, preceding %C",lookupname,v,preceding)
+ end
+ character = makefake(tfmdata,name,present)
+ end
+ local ligatures = character.ligatures
+ if ligatures then
+ ligatures[unicode] = { char = v }
+ else
+ character.ligatures = { [unicode] = { char = v } }
+ end
+ if done then
+ local d = done[lookupname]
+ if not d then
+ done[lookupname] = { "dummy", v }
+ else
+ d[#d+1] = v
+ end
+ end
+ else
+ local code = present[name] or unicode
+ local name = name .. "_" .. k
+ make_2(present,tfmdata,characters,v,name,code,k,done,lookupname)
+ end
+ end
+end
+
+local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist)
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local resources = tfmdata.resources
+ local changed = tfmdata.changed
+ local lookuphash = resources.lookuphash
+ local lookuptypes = resources.lookuptypes
+
+ local ligatures = { }
+ local alternate = tonumber(value)
+ local defaultalt = otf.defaultbasealternate
+
+ local trace_singles = trace_baseinit and trace_singles
+ local trace_alternatives = trace_baseinit and trace_alternatives
+ local trace_ligatures = trace_baseinit and trace_ligatures
+
+ for l=1,#lookuplist do
+ local lookupname = lookuplist[l]
+ local lookupdata = lookuphash[lookupname]
+ local lookuptype = lookuptypes[lookupname]
+ for unicode, data in next, lookupdata do
+ if lookuptype == "substitution" then
+ if trace_singles then
+ report_substitution(feature,lookupname,descriptions,unicode,data)
+ end
+ changed[unicode] = data
+ elseif lookuptype == "alternate" then
+ local replacement = data[alternate]
+ if replacement then
+ changed[unicode] = replacement
+ if trace_alternatives then
+ report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"normal")
+ end
+ elseif defaultalt == "first" then
+ replacement = data[1]
+ changed[unicode] = replacement
+ if trace_alternatives then
+ report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt)
+ end
+ elseif defaultalt == "last" then
+ replacement = data[#data]
+ if trace_alternatives then
+ report_alternate(feature,lookupname,descriptions,unicode,replacement,value,defaultalt)
+ end
+ else
+ if trace_alternatives then
+ report_alternate(feature,lookupname,descriptions,unicode,replacement,value,"unknown")
+ end
+ end
+ elseif lookuptype == "ligature" then
+ ligatures[#ligatures+1] = { unicode, data, lookupname }
+ if trace_ligatures then
+ report_ligature(feature,lookupname,descriptions,unicode,data)
+ end
+ end
+ end
+ end
+
+ local nofligatures = #ligatures
+
+ if nofligatures > 0 then
+
+ local characters = tfmdata.characters
+ local present = { }
+ local done = trace_baseinit and trace_ligatures and { }
+
+ for i=1,nofligatures do
+ local ligature = ligatures[i]
+ local unicode, tree = ligature[1], ligature[2]
+ make_1(present,tree,"ctx_"..unicode)
+ end
+
+ for i=1,nofligatures do
+ local ligature = ligatures[i]
+ local unicode, tree, lookupname = ligature[1], ligature[2], ligature[3]
+ make_2(present,tfmdata,characters,tree,"ctx_"..unicode,unicode,unicode,done,lookupname)
+ end
+
+ end
+
+end
+
+local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist)
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local resources = tfmdata.resources
+ local lookuphash = resources.lookuphash
+ local traceindeed = trace_baseinit and trace_kerns
+
+ -- check out this sharedkerns trickery
+
+ for l=1,#lookuplist do
+ local lookupname = lookuplist[l]
+ local lookupdata = lookuphash[lookupname]
+ for unicode, data in next, lookupdata do
+ local character = characters[unicode]
+ local kerns = character.kerns
+ if not kerns then
+ kerns = { }
+ character.kerns = kerns
+ end
+ if traceindeed then
+ for otherunicode, kern in next, data do
+ if not kerns[otherunicode] and kern ~= 0 then
+ kerns[otherunicode] = kern
+ report_kern(feature,lookup,descriptions,unicode,otherunicode,kern)
+ end
+ end
+ else
+ for otherunicode, kern in next, data do
+ if not kerns[otherunicode] and kern ~= 0 then
+ kerns[otherunicode] = kern
+ end
+ end
+ end
+ end
+ end
+
+end
+
+local function initializehashes(tfmdata)
+ nodeinitializers.features(tfmdata)
+end
+
+basemethods.shared = {
+ initializehashes = initializehashes,
+ preparesubstitutions = preparesubstitutions,
+ preparepositionings = preparepositionings,
+}
+
+basemethod = "independent"
+
+local function featuresinitializer(tfmdata,value)
+ if true then -- value then
+ local t = trace_preparing and os.clock()
+ local features = tfmdata.shared.features
+ if features then
+ applybasemethod("initializehashes",tfmdata)
+ local collectlookups = otf.collectlookups
+ local rawdata = tfmdata.shared.rawdata
+ local properties = tfmdata.properties
+ local script = properties.script
+ local language = properties.language
+ local basesubstitutions = rawdata.resources.features.gsub
+ local basepositionings = rawdata.resources.features.gpos
+ if basesubstitutions then
+ for feature, data in next, basesubstitutions do
+ local value = features[feature]
+ if value then
+ local validlookups, lookuplist = collectlookups(rawdata,feature,script,language)
+ if validlookups then
+ applybasemethod("preparesubstitutions",tfmdata,feature,value,validlookups,lookuplist)
+ registerbasefeature(feature,value)
+ end
+ end
+ end
+ end
+ if basepositionings then
+ for feature, data in next, basepositionings do
+ local value = features[feature]
+ if value then
+ local validlookups, lookuplist = collectlookups(rawdata,feature,script,language)
+ if validlookups then
+ applybasemethod("preparepositionings",tfmdata,feature,features[feature],validlookups,lookuplist)
+ registerbasefeature(feature,value)
+ end
+ end
+ end
+ end
+ registerbasehash(tfmdata)
+ end
+ if trace_preparing then
+ report_prepare("preparation time is %0.3f seconds for %a",os.clock()-t,tfmdata.properties.fullname)
+ end
+ end
+end
+
+registerotffeature {
+ name = "features",
+ description = "features",
+ default = true,
+ initializers = {
+ -- position = 1, -- after setscript (temp hack ... we need to force script / language to 1
+ base = featuresinitializer,
+ }
+}
+
+-- independent : collect lookups independently (takes more runtime ... neglectable)
+-- shared : shares lookups with node mode (takes more memory unless also a node mode variant is used ... noticeable)
+
+directives.register("fonts.otf.loader.basemethod", function(v)
+ if basemethods[v] then
+ basemethod = v
+ end
+end)
diff --git a/tex/context/base/font-otc.lua b/tex/context/base/font-otc.lua
index 0ea900008..a87dcadf8 100644
--- a/tex/context/base/font-otc.lua
+++ b/tex/context/base/font-otc.lua
@@ -1,333 +1,333 @@
-if not modules then modules = { } end modules ['font-otc'] = {
- version = 1.001,
- comment = "companion to font-otf.lua (context)",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local format, insert = string.format, table.insert
-local type, next = type, next
-local lpegmatch = lpeg.match
-
--- we assume that the other otf stuff is loaded already
-
-local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end)
-local report_otf = logs.reporter("fonts","otf loading")
-
-local fonts = fonts
-local otf = fonts.handlers.otf
-local registerotffeature = otf.features.register
-local setmetatableindex = table.setmetatableindex
-
--- In the userdata interface we can not longer tweak the loaded font as
--- conveniently as before. For instance, instead of pushing extra data in
--- in the table using the original structure, we now have to operate on
--- the mkiv representation. And as the fontloader interface is modelled
--- after fontforge we cannot change that one too much either.
-
-local types = {
- substitution = "gsub_single",
- ligature = "gsub_ligature",
- alternate = "gsub_alternate",
-}
-
-setmetatableindex(types, function(t,k) t[k] = k return k end) -- "key"
-
-local everywhere = { ["*"] = { ["*"] = true } } -- or: { ["*"] = { "*" } }
-local noflags = { }
-
-local function addfeature(data,feature,specifications)
- local descriptions = data.descriptions
- local resources = data.resources
- local lookups = resources.lookups
- local gsubfeatures = resources.features.gsub
- if gsubfeatures and gsubfeatures[feature] then
- -- already present
- else
- local sequences = resources.sequences
- local fontfeatures = resources.features
- local unicodes = resources.unicodes
- local lookuptypes = resources.lookuptypes
- local splitter = lpeg.splitter(" ",unicodes)
- local done = 0
- local skip = 0
- if not specifications[1] then
- -- so we accept a one entry specification
- specifications = { specifications }
- end
- -- subtables are tables themselves but we also accept flattened singular subtables
- for s=1,#specifications do
- local specification = specifications[s]
- local valid = specification.valid
- if not valid or valid(data,specification,feature) then
- local initialize = specification.initialize
- if initialize then
- -- when false is returned we initialize only once
- specification.initialize = initialize(specification) and initialize or nil
- end
- local askedfeatures = specification.features or everywhere
- local subtables = specification.subtables or { specification.data } or { }
- local featuretype = types[specification.type or "substitution"]
- local featureflags = specification.flags or noflags
- local added = false
- local featurename = format("ctx_%s_%s",feature,s)
- local st = { }
- for t=1,#subtables do
- local list = subtables[t]
- local full = format("%s_%s",featurename,t)
- st[t] = full
- if featuretype == "gsub_ligature" then
- lookuptypes[full] = "ligature"
- for code, ligature in next, list do
- local unicode = tonumber(code) or unicodes[code]
- local description = descriptions[unicode]
- if description then
- local slookups = description.slookups
- if type(ligature) == "string" then
- ligature = { lpegmatch(splitter,ligature) }
- end
- local present = true
- for i=1,#ligature do
- if not descriptions[ligature[i]] then
- present = false
- break
- end
- end
- if present then
- if slookups then
- slookups[full] = ligature
- else
- description.slookups = { [full] = ligature }
- end
- done, added = done + 1, true
- else
- skip = skip + 1
- end
- end
- end
- elseif featuretype == "gsub_single" then
- lookuptypes[full] = "substitution"
- for code, replacement in next, list do
- local unicode = tonumber(code) or unicodes[code]
- local description = descriptions[unicode]
- if description then
- local slookups = description.slookups
- replacement = tonumber(replacement) or unicodes[replacement]
- if descriptions[replacement] then
- if slookups then
- slookups[full] = replacement
- else
- description.slookups = { [full] = replacement }
- end
- done, added = done + 1, true
- end
- end
- end
- end
- end
- if added then
- -- script = { lang1, lang2, lang3 } or script = { lang1 = true, ... }
- for k, v in next, askedfeatures do
- if v[1] then
- askedfeatures[k] = table.tohash(v)
- end
- end
- sequences[#sequences+1] = {
- chain = 0,
- features = { [feature] = askedfeatures },
- flags = featureflags,
- name = featurename,
- subtables = st,
- type = featuretype,
- }
- -- register in metadata (merge as there can be a few)
- if not gsubfeatures then
- gsubfeatures = { }
- fontfeatures.gsub = gsubfeatures
- end
- local k = gsubfeatures[feature]
- if not k then
- k = { }
- gsubfeatures[feature] = k
- end
- for script, languages in next, askedfeatures do
- local kk = k[script]
- if not kk then
- kk = { }
- k[script] = kk
- end
- for language, value in next, languages do
- kk[language] = value
- end
- end
- end
- end
- end
- if trace_loading then
- report_otf("registering feature %a, affected glyphs %a, skipped glyphs %a",feature,done,skip)
- end
- end
-end
-
-otf.enhancers.addfeature = addfeature
-
-local extrafeatures = { }
-
-function otf.addfeature(name,specification)
- extrafeatures[name] = specification
-end
-
-local function enhance(data,filename,raw)
- for feature, specification in next, extrafeatures do
- addfeature(data,feature,specification)
- end
-end
-
-otf.enhancers.register("check extra features",enhance)
-
--- tlig --
-
-local tlig = {
- endash = "hyphen hyphen",
- emdash = "hyphen hyphen hyphen",
- -- quotedblleft = "quoteleft quoteleft",
- -- quotedblright = "quoteright quoteright",
- -- quotedblleft = "grave grave",
- -- quotedblright = "quotesingle quotesingle",
- -- quotedblbase = "comma comma",
-}
-
-local tlig_specification = {
- type = "ligature",
- features = everywhere,
- data = tlig,
- flags = noflags,
-}
-
-otf.addfeature("tlig",tlig_specification)
-
-registerotffeature {
- name = 'tlig',
- description = 'tex ligatures',
-}
-
--- trep
-
-local trep = {
- -- [0x0022] = 0x201D,
- [0x0027] = 0x2019,
- -- [0x0060] = 0x2018,
-}
-
-local trep_specification = {
- type = "substitution",
- features = everywhere,
- data = trep,
- flags = noflags,
-}
-
-otf.addfeature("trep",trep_specification)
-
-registerotffeature {
- name = 'trep',
- description = 'tex replacements',
-}
-
--- tcom
-
-if characters.combined then
-
- local tcom = { }
-
- local function initialize()
- characters.initialize()
- for first, seconds in next, characters.combined do
- for second, combination in next, seconds do
- tcom[combination] = { first, second }
- end
- end
- -- return false
- end
-
- local tcom_specification = {
- type = "ligature",
- features = everywhere,
- data = tcom,
- flags = noflags,
- initialize = initialize,
- }
-
- otf.addfeature("tcom",tcom_specification)
-
- registerotffeature {
- name = 'tcom',
- description = 'tex combinations',
- }
-
-end
-
--- anum
-
-local anum_arabic = {
- [0x0030] = 0x0660,
- [0x0031] = 0x0661,
- [0x0032] = 0x0662,
- [0x0033] = 0x0663,
- [0x0034] = 0x0664,
- [0x0035] = 0x0665,
- [0x0036] = 0x0666,
- [0x0037] = 0x0667,
- [0x0038] = 0x0668,
- [0x0039] = 0x0669,
-}
-
-local anum_persian = {
- [0x0030] = 0x06F0,
- [0x0031] = 0x06F1,
- [0x0032] = 0x06F2,
- [0x0033] = 0x06F3,
- [0x0034] = 0x06F4,
- [0x0035] = 0x06F5,
- [0x0036] = 0x06F6,
- [0x0037] = 0x06F7,
- [0x0038] = 0x06F8,
- [0x0039] = 0x06F9,
-}
-
-local function valid(data)
- local features = data.resources.features
- if features then
- for k, v in next, features do
- for k, v in next, v do
- if v.arab then
- return true
- end
- end
- end
- end
-end
-
-local anum_specification = {
- {
- type = "substitution",
- features = { arab = { urd = true, dflt = true } },
- data = anum_arabic,
- flags = noflags, -- { },
- valid = valid,
- },
- {
- type = "substitution",
- features = { arab = { urd = true } },
- data = anum_persian,
- flags = noflags, -- { },
- valid = valid,
- },
-}
-
-otf.addfeature("anum",anum_specification) -- todo: only when there is already an arab script feature
-
-registerotffeature {
- name = 'anum',
- description = 'arabic digits',
-}
+if not modules then modules = { } end modules ['font-otc'] = {
+ version = 1.001,
+ comment = "companion to font-otf.lua (context)",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local format, insert = string.format, table.insert
+local type, next = type, next
+local lpegmatch = lpeg.match
+
+-- we assume that the other otf stuff is loaded already
+
+local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end)
+local report_otf = logs.reporter("fonts","otf loading")
+
+local fonts = fonts
+local otf = fonts.handlers.otf
+local registerotffeature = otf.features.register
+local setmetatableindex = table.setmetatableindex
+
+-- In the userdata interface we can not longer tweak the loaded font as
+-- conveniently as before. For instance, instead of pushing extra data in
+-- in the table using the original structure, we now have to operate on
+-- the mkiv representation. And as the fontloader interface is modelled
+-- after fontforge we cannot change that one too much either.
+
+local types = {
+ substitution = "gsub_single",
+ ligature = "gsub_ligature",
+ alternate = "gsub_alternate",
+}
+
+setmetatableindex(types, function(t,k) t[k] = k return k end) -- "key"
+
+local everywhere = { ["*"] = { ["*"] = true } } -- or: { ["*"] = { "*" } }
+local noflags = { }
+
+local function addfeature(data,feature,specifications)
+ local descriptions = data.descriptions
+ local resources = data.resources
+ local lookups = resources.lookups
+ local gsubfeatures = resources.features.gsub
+ if gsubfeatures and gsubfeatures[feature] then
+ -- already present
+ else
+ local sequences = resources.sequences
+ local fontfeatures = resources.features
+ local unicodes = resources.unicodes
+ local lookuptypes = resources.lookuptypes
+ local splitter = lpeg.splitter(" ",unicodes)
+ local done = 0
+ local skip = 0
+ if not specifications[1] then
+ -- so we accept a one entry specification
+ specifications = { specifications }
+ end
+ -- subtables are tables themselves but we also accept flattened singular subtables
+ for s=1,#specifications do
+ local specification = specifications[s]
+ local valid = specification.valid
+ if not valid or valid(data,specification,feature) then
+ local initialize = specification.initialize
+ if initialize then
+ -- when false is returned we initialize only once
+ specification.initialize = initialize(specification) and initialize or nil
+ end
+ local askedfeatures = specification.features or everywhere
+ local subtables = specification.subtables or { specification.data } or { }
+ local featuretype = types[specification.type or "substitution"]
+ local featureflags = specification.flags or noflags
+ local added = false
+ local featurename = format("ctx_%s_%s",feature,s)
+ local st = { }
+ for t=1,#subtables do
+ local list = subtables[t]
+ local full = format("%s_%s",featurename,t)
+ st[t] = full
+ if featuretype == "gsub_ligature" then
+ lookuptypes[full] = "ligature"
+ for code, ligature in next, list do
+ local unicode = tonumber(code) or unicodes[code]
+ local description = descriptions[unicode]
+ if description then
+ local slookups = description.slookups
+ if type(ligature) == "string" then
+ ligature = { lpegmatch(splitter,ligature) }
+ end
+ local present = true
+ for i=1,#ligature do
+ if not descriptions[ligature[i]] then
+ present = false
+ break
+ end
+ end
+ if present then
+ if slookups then
+ slookups[full] = ligature
+ else
+ description.slookups = { [full] = ligature }
+ end
+ done, added = done + 1, true
+ else
+ skip = skip + 1
+ end
+ end
+ end
+ elseif featuretype == "gsub_single" then
+ lookuptypes[full] = "substitution"
+ for code, replacement in next, list do
+ local unicode = tonumber(code) or unicodes[code]
+ local description = descriptions[unicode]
+ if description then
+ local slookups = description.slookups
+ replacement = tonumber(replacement) or unicodes[replacement]
+ if descriptions[replacement] then
+ if slookups then
+ slookups[full] = replacement
+ else
+ description.slookups = { [full] = replacement }
+ end
+ done, added = done + 1, true
+ end
+ end
+ end
+ end
+ end
+ if added then
+ -- script = { lang1, lang2, lang3 } or script = { lang1 = true, ... }
+ for k, v in next, askedfeatures do
+ if v[1] then
+ askedfeatures[k] = table.tohash(v)
+ end
+ end
+ sequences[#sequences+1] = {
+ chain = 0,
+ features = { [feature] = askedfeatures },
+ flags = featureflags,
+ name = featurename,
+ subtables = st,
+ type = featuretype,
+ }
+ -- register in metadata (merge as there can be a few)
+ if not gsubfeatures then
+ gsubfeatures = { }
+ fontfeatures.gsub = gsubfeatures
+ end
+ local k = gsubfeatures[feature]
+ if not k then
+ k = { }
+ gsubfeatures[feature] = k
+ end
+ for script, languages in next, askedfeatures do
+ local kk = k[script]
+ if not kk then
+ kk = { }
+ k[script] = kk
+ end
+ for language, value in next, languages do
+ kk[language] = value
+ end
+ end
+ end
+ end
+ end
+ if trace_loading then
+ report_otf("registering feature %a, affected glyphs %a, skipped glyphs %a",feature,done,skip)
+ end
+ end
+end
+
+otf.enhancers.addfeature = addfeature
+
+local extrafeatures = { }
+
+function otf.addfeature(name,specification)
+ extrafeatures[name] = specification
+end
+
+local function enhance(data,filename,raw)
+ for feature, specification in next, extrafeatures do
+ addfeature(data,feature,specification)
+ end
+end
+
+otf.enhancers.register("check extra features",enhance)
+
+-- tlig --
+
+local tlig = {
+ endash = "hyphen hyphen",
+ emdash = "hyphen hyphen hyphen",
+ -- quotedblleft = "quoteleft quoteleft",
+ -- quotedblright = "quoteright quoteright",
+ -- quotedblleft = "grave grave",
+ -- quotedblright = "quotesingle quotesingle",
+ -- quotedblbase = "comma comma",
+}
+
+local tlig_specification = {
+ type = "ligature",
+ features = everywhere,
+ data = tlig,
+ flags = noflags,
+}
+
+otf.addfeature("tlig",tlig_specification)
+
+registerotffeature {
+ name = 'tlig',
+ description = 'tex ligatures',
+}
+
+-- trep
+
+local trep = {
+ -- [0x0022] = 0x201D,
+ [0x0027] = 0x2019,
+ -- [0x0060] = 0x2018,
+}
+
+local trep_specification = {
+ type = "substitution",
+ features = everywhere,
+ data = trep,
+ flags = noflags,
+}
+
+otf.addfeature("trep",trep_specification)
+
+registerotffeature {
+ name = 'trep',
+ description = 'tex replacements',
+}
+
+-- tcom
+
+if characters.combined then
+
+ local tcom = { }
+
+ local function initialize()
+ characters.initialize()
+ for first, seconds in next, characters.combined do
+ for second, combination in next, seconds do
+ tcom[combination] = { first, second }
+ end
+ end
+ -- return false
+ end
+
+ local tcom_specification = {
+ type = "ligature",
+ features = everywhere,
+ data = tcom,
+ flags = noflags,
+ initialize = initialize,
+ }
+
+ otf.addfeature("tcom",tcom_specification)
+
+ registerotffeature {
+ name = 'tcom',
+ description = 'tex combinations',
+ }
+
+end
+
+-- anum
+
+local anum_arabic = {
+ [0x0030] = 0x0660,
+ [0x0031] = 0x0661,
+ [0x0032] = 0x0662,
+ [0x0033] = 0x0663,
+ [0x0034] = 0x0664,
+ [0x0035] = 0x0665,
+ [0x0036] = 0x0666,
+ [0x0037] = 0x0667,
+ [0x0038] = 0x0668,
+ [0x0039] = 0x0669,
+}
+
+local anum_persian = {
+ [0x0030] = 0x06F0,
+ [0x0031] = 0x06F1,
+ [0x0032] = 0x06F2,
+ [0x0033] = 0x06F3,
+ [0x0034] = 0x06F4,
+ [0x0035] = 0x06F5,
+ [0x0036] = 0x06F6,
+ [0x0037] = 0x06F7,
+ [0x0038] = 0x06F8,
+ [0x0039] = 0x06F9,
+}
+
+local function valid(data)
+ local features = data.resources.features
+ if features then
+ for k, v in next, features do
+ for k, v in next, v do
+ if v.arab then
+ return true
+ end
+ end
+ end
+ end
+end
+
+local anum_specification = {
+ {
+ type = "substitution",
+ features = { arab = { urd = true, dflt = true } },
+ data = anum_arabic,
+ flags = noflags, -- { },
+ valid = valid,
+ },
+ {
+ type = "substitution",
+ features = { arab = { urd = true } },
+ data = anum_persian,
+ flags = noflags, -- { },
+ valid = valid,
+ },
+}
+
+otf.addfeature("anum",anum_specification) -- todo: only when there is already an arab script feature
+
+registerotffeature {
+ name = 'anum',
+ description = 'arabic digits',
+}
diff --git a/tex/context/base/font-otd.lua b/tex/context/base/font-otd.lua
index 12e2da55f..a9d093d6d 100644
--- a/tex/context/base/font-otd.lua
+++ b/tex/context/base/font-otd.lua
@@ -1,261 +1,261 @@
-if not modules then modules = { } end modules ['font-otd'] = {
- 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 type = type
-local match = string.match
-local sequenced = table.sequenced
-
-local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end)
-local trace_applied = false trackers.register("otf.applied", function(v) trace_applied = v end)
-
-local report_otf = logs.reporter("fonts","otf loading")
-local report_process = logs.reporter("fonts","otf process")
-
-local allocate = utilities.storage.allocate
-
-local fonts = fonts
-local otf = fonts.handlers.otf
-local hashes = fonts.hashes
-local definers = fonts.definers
-local constructors = fonts.constructors
-local specifiers = fonts.specifiers
-
-local fontidentifiers = hashes.identifiers
-local fontresources = hashes.resources
-local fontproperties = hashes.properties
-local fontdynamics = hashes.dynamics
-
-local contextsetups = specifiers.contextsetups
-local contextnumbers = specifiers.contextnumbers
-local contextmerged = specifiers.contextmerged
-
-local setmetatableindex = table.setmetatableindex
-
-local otffeatures = fonts.constructors.newfeatures("otf")
-local registerotffeature = otffeatures.register
-
-local a_to_script = { }
-local a_to_language = { }
-
--- we can have a scripts hash in fonts.hashes
-
-function otf.setdynamics(font,attribute)
- -- local features = contextsetups[contextnumbers[attribute]] -- can be moved to caller
- local features = contextsetups[attribute]
- if features then
- local dynamics = fontdynamics[font]
- dynamic = contextmerged[attribute] or 0
- local script, language
- if dynamic == 2 then -- merge
- language = features.language or fontproperties[font].language or "dflt"
- script = features.script or fontproperties[font].script or "dflt"
- else -- if dynamic == 1 then -- replace
- language = features.language or "dflt"
- script = features.script or "dflt"
- end
- if script == "auto" then
- -- checkedscript and resources are defined later so we cannot shortcut them -- todo: make installer
- script = definers.checkedscript(fontidentifiers[font],fontresources[font],features)
- end
- local ds = dynamics[script] -- can be metatable magic (less testing)
- if not ds then
- ds = { }
- dynamics[script] = ds
- end
- local dsl = ds[language]
- if not dsl then
- dsl = { }
- ds[language] = dsl
- end
- local dsla = dsl[attribute]
- if not dsla then
- local tfmdata = fontidentifiers[font]
- a_to_script [attribute] = script
- a_to_language[attribute] = language
- -- we need to save some values .. quite messy
- local properties = tfmdata.properties
- local shared = tfmdata.shared
- local s_script = properties.script
- local s_language = properties.language
- local s_mode = properties.mode
- local s_features = shared.features
- properties.mode = "node"
- properties.language = language
- properties.script = script
- properties.dynamics = true -- handy for tracing
- shared.features = { }
- -- end of save
- local set = constructors.checkedfeatures("otf",features)
- set.mode = "node" -- really needed
- dsla = otf.setfeatures(tfmdata,set)
- if trace_dynamics then
- report_otf("setting dynamics %s: attribute %a, script %a, language %a, set %a",contextnumbers[attribute],attribute,script,language,set)
- end
- -- we need to restore some values
- properties.script = s_script
- properties.language = s_language
- properties.mode = s_mode
- shared.features = s_features
- -- end of restore
- dynamics[script][language][attribute] = dsla -- cache
- elseif trace_dynamics then
- -- report_otf("using dynamics %s: attribute %a, script %a, language %a",contextnumbers[attribute],attribute,script,language)
- end
- return dsla
- end
-end
-
-function otf.scriptandlanguage(tfmdata,attr)
- local properties = tfmdata.properties
- if attr and attr > 0 then
- return a_to_script[attr] or properties.script or "dflt", a_to_language[attr] or properties.language or "dflt"
- else
- return properties.script or "dflt", properties.language or "dflt"
- end
-end
-
--- we reimplement the dataset resolver
-
-local autofeatures = fonts.analyzers.features -- was: constants
-
-local resolved = { } -- we only resolve a font,script,language,attribute pair once
-local wildcard = "*"
-local default = "dflt"
-
--- what about analyze in local and not in font
-
-local function initialize(sequence,script,language,s_enabled,a_enabled,font,attr,dynamic)
- local features = sequence.features
- if features then
- for kind, scripts in next, features do
- local e_e
- local a_e = a_enabled and a_enabled[kind] -- the value (location)
- if a_e ~= nil then
- e_e = a_e
- else
- e_e = s_enabled and s_enabled[kind] -- the value (font)
- end
- if e_e then
- local languages = scripts[script] or scripts[wildcard]
- if languages then
- -- local valid, what = false
- local valid = false
- -- not languages[language] or languages[default] or languages[wildcard] because we want tracing
- -- only first attribute match check, so we assume simple fina's
- -- default can become a font feature itself
- if languages[language] then
- valid = e_e -- was true
- -- what = language
- -- elseif languages[default] then
- -- valid = true
- -- what = default
- elseif languages[wildcard] then
- valid = e_e -- was true
- -- what = wildcard
- end
- if valid then
- local attribute = autofeatures[kind] or false
- -- if a_e and dynamic < 0 then
- -- valid = false
- -- end
- -- if trace_applied then
- -- local typ, action = match(sequence.type,"(.*)_(.*)") -- brrr
- -- report_process(
- -- "%s font: %03i, dynamic: %03i, kind: %s, script: %-4s, language: %-4s (%-4s), type: %s, action: %s, name: %s",
- -- (valid and "+") or "-",font,attr or 0,kind,script,language,what,typ,action,sequence.name)
- -- end
- if trace_applied then
- report_process(
- "font %s, dynamic %a (%a), feature %a, script %a, language %a, lookup %a, value %a",
- font,attr or 0,dynamic,kind,script,language,sequence.name,valid)
- end
- return { valid, attribute, sequence.chain or 0, kind, sequence }
- end
- end
- end
- end
- return false -- { valid, attribute, chain, "generic", sequence } -- false anyway, could be flag instead of table
- else
- return false -- { false, false, chain, false, sequence } -- indirect lookup, part of chain (todo: make this a separate table)
- end
-end
-
--- there is some fuzzy language/script state stuff in properties (temporary)
-
-function otf.dataset(tfmdata,font,attr) -- attr only when explicit (as in special parbuilder)
-
- local script, language, s_enabled, a_enabled, dynamic
-
- if attr and attr ~= 0 then
- dynamic = contextmerged[attr] or 0
- -- local features = contextsetups[contextnumbers[attr]] -- could be a direct list
- local features = contextsetups[attr]
- a_enabled = features -- location based
- if dynamic == 1 then -- or dynamic == -1 then
- -- replace
- language = features.language or "dflt"
- script = features.script or "dflt"
- elseif dynamic == 2 then -- or dynamic == -2 then
- -- merge
- local properties = tfmdata.properties
- s_enabled = tfmdata.shared.features -- font based
- language = features.language or properties.language or "dflt"
- script = features.script or properties.script or "dflt"
- else
- -- error
- local properties = tfmdata.properties
- language = properties.language or "dflt"
- script = properties.script or "dflt"
- end
- else
- local properties = tfmdata.properties
- language = properties.language or "dflt"
- script = properties.script or "dflt"
- s_enabled = tfmdata.shared.features -- can be made local to the resolver
- dynamic = 0
- end
-
- 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 = { }
- rs[language] = rl
- end
- local ra = rl[attr]
- if ra == nil then -- attr can be false
- ra = {
- -- indexed but we can also add specific data by key in:
- }
- rl[attr] = ra
- local sequences = tfmdata.resources.sequences
--- setmetatableindex(ra, function(t,k)
--- if type(k) == "number" then
--- local v = initialize(sequences[k],script,language,s_enabled,a_enabled,font,attr,dynamic)
--- t[k] = v or false
--- return v
--- end
--- end)
-for s=1,#sequences do
- local v = initialize(sequences[s],script,language,s_enabled,a_enabled,font,attr,dynamic)
- if v then
- ra[#ra+1] = v
- end
-end
- end
- return ra
-
-end
+if not modules then modules = { } end modules ['font-otd'] = {
+ 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 type = type
+local match = string.match
+local sequenced = table.sequenced
+
+local trace_dynamics = false trackers.register("otf.dynamics", function(v) trace_dynamics = v end)
+local trace_applied = false trackers.register("otf.applied", function(v) trace_applied = v end)
+
+local report_otf = logs.reporter("fonts","otf loading")
+local report_process = logs.reporter("fonts","otf process")
+
+local allocate = utilities.storage.allocate
+
+local fonts = fonts
+local otf = fonts.handlers.otf
+local hashes = fonts.hashes
+local definers = fonts.definers
+local constructors = fonts.constructors
+local specifiers = fonts.specifiers
+
+local fontidentifiers = hashes.identifiers
+local fontresources = hashes.resources
+local fontproperties = hashes.properties
+local fontdynamics = hashes.dynamics
+
+local contextsetups = specifiers.contextsetups
+local contextnumbers = specifiers.contextnumbers
+local contextmerged = specifiers.contextmerged
+
+local setmetatableindex = table.setmetatableindex
+
+local otffeatures = fonts.constructors.newfeatures("otf")
+local registerotffeature = otffeatures.register
+
+local a_to_script = { }
+local a_to_language = { }
+
+-- we can have a scripts hash in fonts.hashes
+
+function otf.setdynamics(font,attribute)
+ -- local features = contextsetups[contextnumbers[attribute]] -- can be moved to caller
+ local features = contextsetups[attribute]
+ if features then
+ local dynamics = fontdynamics[font]
+ dynamic = contextmerged[attribute] or 0
+ local script, language
+ if dynamic == 2 then -- merge
+ language = features.language or fontproperties[font].language or "dflt"
+ script = features.script or fontproperties[font].script or "dflt"
+ else -- if dynamic == 1 then -- replace
+ language = features.language or "dflt"
+ script = features.script or "dflt"
+ end
+ if script == "auto" then
+ -- checkedscript and resources are defined later so we cannot shortcut them -- todo: make installer
+ script = definers.checkedscript(fontidentifiers[font],fontresources[font],features)
+ end
+ local ds = dynamics[script] -- can be metatable magic (less testing)
+ if not ds then
+ ds = { }
+ dynamics[script] = ds
+ end
+ local dsl = ds[language]
+ if not dsl then
+ dsl = { }
+ ds[language] = dsl
+ end
+ local dsla = dsl[attribute]
+ if not dsla then
+ local tfmdata = fontidentifiers[font]
+ a_to_script [attribute] = script
+ a_to_language[attribute] = language
+ -- we need to save some values .. quite messy
+ local properties = tfmdata.properties
+ local shared = tfmdata.shared
+ local s_script = properties.script
+ local s_language = properties.language
+ local s_mode = properties.mode
+ local s_features = shared.features
+ properties.mode = "node"
+ properties.language = language
+ properties.script = script
+ properties.dynamics = true -- handy for tracing
+ shared.features = { }
+ -- end of save
+ local set = constructors.checkedfeatures("otf",features)
+ set.mode = "node" -- really needed
+ dsla = otf.setfeatures(tfmdata,set)
+ if trace_dynamics then
+ report_otf("setting dynamics %s: attribute %a, script %a, language %a, set %a",contextnumbers[attribute],attribute,script,language,set)
+ end
+ -- we need to restore some values
+ properties.script = s_script
+ properties.language = s_language
+ properties.mode = s_mode
+ shared.features = s_features
+ -- end of restore
+ dynamics[script][language][attribute] = dsla -- cache
+ elseif trace_dynamics then
+ -- report_otf("using dynamics %s: attribute %a, script %a, language %a",contextnumbers[attribute],attribute,script,language)
+ end
+ return dsla
+ end
+end
+
+function otf.scriptandlanguage(tfmdata,attr)
+ local properties = tfmdata.properties
+ if attr and attr > 0 then
+ return a_to_script[attr] or properties.script or "dflt", a_to_language[attr] or properties.language or "dflt"
+ else
+ return properties.script or "dflt", properties.language or "dflt"
+ end
+end
+
+-- we reimplement the dataset resolver
+
+local autofeatures = fonts.analyzers.features -- was: constants
+
+local resolved = { } -- we only resolve a font,script,language,attribute pair once
+local wildcard = "*"
+local default = "dflt"
+
+-- what about analyze in local and not in font
+
+local function initialize(sequence,script,language,s_enabled,a_enabled,font,attr,dynamic)
+ local features = sequence.features
+ if features then
+ for kind, scripts in next, features do
+ local e_e
+ local a_e = a_enabled and a_enabled[kind] -- the value (location)
+ if a_e ~= nil then
+ e_e = a_e
+ else
+ e_e = s_enabled and s_enabled[kind] -- the value (font)
+ end
+ if e_e then
+ local languages = scripts[script] or scripts[wildcard]
+ if languages then
+ -- local valid, what = false
+ local valid = false
+ -- not languages[language] or languages[default] or languages[wildcard] because we want tracing
+ -- only first attribute match check, so we assume simple fina's
+ -- default can become a font feature itself
+ if languages[language] then
+ valid = e_e -- was true
+ -- what = language
+ -- elseif languages[default] then
+ -- valid = true
+ -- what = default
+ elseif languages[wildcard] then
+ valid = e_e -- was true
+ -- what = wildcard
+ end
+ if valid then
+ local attribute = autofeatures[kind] or false
+ -- if a_e and dynamic < 0 then
+ -- valid = false
+ -- end
+ -- if trace_applied then
+ -- local typ, action = match(sequence.type,"(.*)_(.*)") -- brrr
+ -- report_process(
+ -- "%s font: %03i, dynamic: %03i, kind: %s, script: %-4s, language: %-4s (%-4s), type: %s, action: %s, name: %s",
+ -- (valid and "+") or "-",font,attr or 0,kind,script,language,what,typ,action,sequence.name)
+ -- end
+ if trace_applied then
+ report_process(
+ "font %s, dynamic %a (%a), feature %a, script %a, language %a, lookup %a, value %a",
+ font,attr or 0,dynamic,kind,script,language,sequence.name,valid)
+ end
+ return { valid, attribute, sequence.chain or 0, kind, sequence }
+ end
+ end
+ end
+ end
+ return false -- { valid, attribute, chain, "generic", sequence } -- false anyway, could be flag instead of table
+ else
+ return false -- { false, false, chain, false, sequence } -- indirect lookup, part of chain (todo: make this a separate table)
+ end
+end
+
+-- there is some fuzzy language/script state stuff in properties (temporary)
+
+function otf.dataset(tfmdata,font,attr) -- attr only when explicit (as in special parbuilder)
+
+ local script, language, s_enabled, a_enabled, dynamic
+
+ if attr and attr ~= 0 then
+ dynamic = contextmerged[attr] or 0
+ -- local features = contextsetups[contextnumbers[attr]] -- could be a direct list
+ local features = contextsetups[attr]
+ a_enabled = features -- location based
+ if dynamic == 1 then -- or dynamic == -1 then
+ -- replace
+ language = features.language or "dflt"
+ script = features.script or "dflt"
+ elseif dynamic == 2 then -- or dynamic == -2 then
+ -- merge
+ local properties = tfmdata.properties
+ s_enabled = tfmdata.shared.features -- font based
+ language = features.language or properties.language or "dflt"
+ script = features.script or properties.script or "dflt"
+ else
+ -- error
+ local properties = tfmdata.properties
+ language = properties.language or "dflt"
+ script = properties.script or "dflt"
+ end
+ else
+ local properties = tfmdata.properties
+ language = properties.language or "dflt"
+ script = properties.script or "dflt"
+ s_enabled = tfmdata.shared.features -- can be made local to the resolver
+ dynamic = 0
+ end
+
+ 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 = { }
+ rs[language] = rl
+ end
+ local ra = rl[attr]
+ if ra == nil then -- attr can be false
+ ra = {
+ -- indexed but we can also add specific data by key in:
+ }
+ rl[attr] = ra
+ local sequences = tfmdata.resources.sequences
+-- setmetatableindex(ra, function(t,k)
+-- if type(k) == "number" then
+-- local v = initialize(sequences[k],script,language,s_enabled,a_enabled,font,attr,dynamic)
+-- t[k] = v or false
+-- return v
+-- end
+-- end)
+for s=1,#sequences do
+ local v = initialize(sequences[s],script,language,s_enabled,a_enabled,font,attr,dynamic)
+ if v then
+ ra[#ra+1] = v
+ end
+end
+ end
+ return ra
+
+end
diff --git a/tex/context/base/font-otf.lua b/tex/context/base/font-otf.lua
index 737dc9927..c1f2f14fc 100644
--- a/tex/context/base/font-otf.lua
+++ b/tex/context/base/font-otf.lua
@@ -1,2155 +1,2155 @@
-if not modules then modules = { } end modules ['font-otf'] = {
- 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"
-}
-
--- langs -> languages enz
--- anchor_classes vs kernclasses
--- modification/creationtime in subfont is runtime dus zinloos
--- to_table -> totable
--- ascent descent
-
--- more checking against low level calls of functions
-
-local utfbyte = utf.byte
-local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip
-local type, next, tonumber, tostring = type, next, tonumber, tostring
-local abs = math.abs
-local getn = table.getn
-local lpegmatch = lpeg.match
-local reversed, concat, remove = table.reversed, table.concat, table.remove
-local ioflush = io.flush
-local fastcopy, tohash, derivetable = table.fastcopy, table.tohash, table.derive
-local formatters = string.formatters
-
-local allocate = utilities.storage.allocate
-local registertracker = trackers.register
-local registerdirective = directives.register
-local starttiming = statistics.starttiming
-local stoptiming = statistics.stoptiming
-local elapsedtime = statistics.elapsedtime
-local findbinfile = resolvers.findbinfile
-
-local trace_private = false registertracker("otf.private", function(v) trace_private = v end)
-local trace_loading = false registertracker("otf.loading", function(v) trace_loading = v end)
-local trace_features = false registertracker("otf.features", function(v) trace_features = v end)
-local trace_dynamics = false registertracker("otf.dynamics", function(v) trace_dynamics = v end)
-local trace_sequences = false registertracker("otf.sequences", function(v) trace_sequences = v end)
-local trace_markwidth = false registertracker("otf.markwidth", function(v) trace_markwidth = v end)
-local trace_defining = false registertracker("fonts.defining", function(v) trace_defining = v end)
-
-local report_otf = logs.reporter("fonts","otf loading")
-
-local fonts = fonts
-local otf = fonts.handlers.otf
-
-otf.glists = { "gsub", "gpos" }
-
-otf.version = 2.743 -- beware: also sync font-mis.lua
-otf.cache = containers.define("fonts", "otf", otf.version, true)
-
-local fontdata = fonts.hashes.identifiers
-local chardata = characters and characters.data -- not used
-
-local otffeatures = fonts.constructors.newfeatures("otf")
-local registerotffeature = otffeatures.register
-
-local enhancers = allocate()
-otf.enhancers = enhancers
-local patches = { }
-enhancers.patches = patches
-
-local definers = fonts.definers
-local readers = fonts.readers
-local constructors = fonts.constructors
-
-local forceload = false
-local cleanup = 0 -- mk: 0=885M 1=765M 2=735M (regular run 730M)
-local usemetatables = false -- .4 slower on mk but 30 M less mem so we might change the default -- will be directive
-local packdata = true
-local syncspace = true
-local forcenotdef = false
-local includesubfonts = false
-
-local wildcard = "*"
-local default = "dflt"
-
-local fontloaderfields = fontloader.fields
-local mainfields = nil
-local glyphfields = nil -- not used yet
-
-registerdirective("fonts.otf.loader.cleanup", function(v) cleanup = tonumber(v) or (v and 1) or 0 end)
-registerdirective("fonts.otf.loader.force", function(v) forceload = v end)
-registerdirective("fonts.otf.loader.usemetatables", function(v) usemetatables = v end)
-registerdirective("fonts.otf.loader.pack", function(v) packdata = v end)
-registerdirective("fonts.otf.loader.syncspace", function(v) syncspace = v end)
-registerdirective("fonts.otf.loader.forcenotdef", function(v) forcenotdef = v end)
-
-local function load_featurefile(raw,featurefile)
- if featurefile and featurefile ~= "" then
- if trace_loading then
- report_otf("using featurefile %a", featurefile)
- end
- fontloader.apply_featurefile(raw, featurefile)
- end
-end
-
-local function showfeatureorder(rawdata,filename)
- local sequences = rawdata.resources.sequences
- if sequences and #sequences > 0 then
- if trace_loading then
- report_otf("font %a has %s sequences",filename,#sequences)
- report_otf(" ")
- end
- for nos=1,#sequences do
- local sequence = sequences[nos]
- local typ = sequence.type or "no-type"
- local name = sequence.name or "no-name"
- local subtables = sequence.subtables or { "no-subtables" }
- local features = sequence.features
- if trace_loading then
- report_otf("%3i %-15s %-20s [% t]",nos,name,typ,subtables)
- end
- if features then
- for feature, scripts in next, features do
- local tt = { }
- if type(scripts) == "table" then
- for script, languages in next, scripts do
- local ttt = { }
- for language, _ in next, languages do
- ttt[#ttt+1] = language
- end
- tt[#tt+1] = formatters["[%s: % t]"](script,ttt)
- end
- if trace_loading then
- report_otf(" %s: % t",feature,tt)
- end
- else
- if trace_loading then
- report_otf(" %s: %S",feature,scripts)
- end
- end
- end
- end
- end
- if trace_loading then
- report_otf("\n")
- end
- elseif trace_loading then
- report_otf("font %a has no sequences",filename)
- end
-end
-
---[[ldx--
-<p>We start with a lot of tables and related functions.</p>
---ldx]]--
-
-local valid_fields = table.tohash {
- -- "anchor_classes",
- "ascent",
- -- "cache_version",
- "cidinfo",
- "copyright",
- -- "creationtime",
- "descent",
- "design_range_bottom",
- "design_range_top",
- "design_size",
- "encodingchanged",
- "extrema_bound",
- "familyname",
- "fontname",
- "fontname",
- "fontstyle_id",
- "fontstyle_name",
- "fullname",
- -- "glyphs",
- "hasvmetrics",
- -- "head_optimized_for_cleartype",
- "horiz_base",
- "issans",
- "isserif",
- "italicangle",
- -- "kerns",
- -- "lookups",
- "macstyle",
- -- "modificationtime",
- "onlybitmaps",
- "origname",
- "os2_version",
- "pfminfo",
- -- "private",
- "serifcheck",
- "sfd_version",
- -- "size",
- "strokedfont",
- "strokewidth",
- -- "subfonts",
- "table_version",
- -- "tables",
- -- "ttf_tab_saved",
- "ttf_tables",
- "uni_interp",
- "uniqueid",
- "units_per_em",
- "upos",
- "use_typo_metrics",
- "uwidth",
- -- "validation_state",
- "version",
- "vert_base",
- "weight",
- "weight_width_slope_only",
- -- "xuid",
-}
-
-local ordered_enhancers = {
- "prepare tables",
-
- "prepare glyphs",
- "prepare lookups",
-
- "analyze glyphs",
- "analyze math",
-
- "prepare tounicode", -- maybe merge with prepare
-
- "reorganize lookups",
- "reorganize mark classes",
- "reorganize anchor classes",
-
- "reorganize glyph kerns",
- "reorganize glyph lookups",
- "reorganize glyph anchors",
-
- "merge kern classes",
-
- "reorganize features",
- "reorganize subtables",
-
- "check glyphs",
- "check metadata",
- "check extra features", -- after metadata
-
- "check encoding", -- moved
- "add duplicates",
-
- "cleanup tables",
-}
-
---[[ldx--
-<p>Here we go.</p>
---ldx]]--
-
-local actions = allocate()
-local before = allocate()
-local after = allocate()
-
-patches.before = before
-patches.after = after
-
-local function enhance(name,data,filename,raw)
- local enhancer = actions[name]
- if enhancer then
- if trace_loading then
- report_otf("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
-
-function enhancers.apply(data,filename,raw)
- local basename = file.basename(lower(filename))
- if trace_loading then
- report_otf("%s enhancing file %a","start",filename)
- end
- ioflush() -- we want instant messages
- for e=1,#ordered_enhancers do
- local enhancer = ordered_enhancers[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)
- 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_loading then
- report_otf("%s enhancing file %a","stop",filename)
- end
- ioflush() -- we want instant messages
-end
-
--- patches.register("before","migrate metadata","cambria",function() end)
-
-function patches.register(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}
- end
- end
-end
-
-function patches.report(fmt,...)
- if trace_loading then
- report_otf("patching: %s",formatters[fmt](...))
- end
-end
-
-function enhancers.register(what,action) -- only already registered can be overloaded
- actions[what] = action
-end
-
-function otf.load(filename,format,sub,featurefile)
- local base = file.basename(file.removesuffix(filename))
- local name = file.removesuffix(base)
- local attr = lfs.attributes(filename)
- local size = attr and attr.size or 0
- local time = attr and attr.modification or 0
- if featurefile then
- name = name .. "@" .. file.removesuffix(file.basename(featurefile))
- end
- if sub == "" then
- sub = false
- end
- local hash = name
- if sub then
- hash = hash .. "-" .. sub
- end
- hash = containers.cleanname(hash)
- local featurefiles
- if featurefile then
- featurefiles = { }
- for s in gmatch(featurefile,"[^,]+") do
- local name = resolvers.findfile(file.addsuffix(s,'fea'),'fea') or ""
- if name == "" then
- report_otf("loading error, no featurefile %a",s)
- else
- local attr = lfs.attributes(name)
- featurefiles[#featurefiles+1] = {
- name = name,
- size = attr and attr.size or 0,
- time = attr and attr.modification or 0,
- }
- end
- end
- if #featurefiles == 0 then
- featurefiles = nil
- end
- end
- local data = containers.read(otf.cache,hash)
- local reload = not data or data.size ~= size or data.time ~= time
- if forceload then
- report_otf("forced reload of %a due to hard coded flag",filename)
- reload = true
- end
- if not reload then
- local featuredata = data.featuredata
- if featurefiles then
- if not featuredata or #featuredata ~= #featurefiles then
- reload = true
- else
- for i=1,#featurefiles do
- local fi, fd = featurefiles[i], featuredata[i]
- if fi.name ~= fd.name or fi.size ~= fd.size or fi.time ~= fd.time then
- reload = true
- break
- end
- end
- end
- elseif featuredata then
- reload = true
- end
- if reload then
- report_otf("loading: forced reload due to changed featurefile specification %a",featurefile)
- end
- end
- if reload then
- report_otf("loading %a, hash %a",filename,hash)
- local fontdata, messages
- if sub then
- fontdata, messages = fontloader.open(filename,sub)
- else
- fontdata, messages = fontloader.open(filename)
- end
- if fontdata then
- mainfields = mainfields or (fontloaderfields and fontloaderfields(fontdata))
- end
- if trace_loading and messages and #messages > 0 then
- if type(messages) == "string" then
- report_otf("warning: %s",messages)
- else
- for m=1,#messages do
- report_otf("warning: %S",messages[m])
- end
- end
- else
- report_otf("loading done")
- end
- if fontdata then
- if featurefiles then
- for i=1,#featurefiles do
- load_featurefile(fontdata,featurefiles[i].name)
- end
- end
- local unicodes = {
- -- names to unicodes
- }
- local splitter = lpeg.splitter(" ",unicodes)
- data = {
- size = size,
- time = time,
- format = format,
- featuredata = featurefiles,
- resources = {
- filename = resolvers.unresolve(filename), -- no shortcut
- version = otf.version,
- creator = "context mkiv",
- unicodes = unicodes,
- indices = {
- -- index to unicodes
- },
- duplicates = {
- -- alternative unicodes
- },
- variants = {
- -- alternative unicodes (variants)
- },
- lookuptypes = {
- },
- },
- metadata = {
- -- raw metadata, not to be used
- },
- properties = {
- -- normalized metadata
- },
- descriptions = {
- },
- goodies = {
- },
- helpers = {
- tounicodelist = splitter,
- tounicodetable = lpeg.Ct(splitter),
- },
- }
- starttiming(data)
- report_otf("file size: %s", size)
- enhancers.apply(data,filename,fontdata)
- local packtime = { }
- if packdata then
- if cleanup > 0 then
- collectgarbage("collect")
- end
- starttiming(packtime)
- enhance("pack",data,filename,nil)
- stoptiming(packtime)
- end
- report_otf("saving %a in cache",filename)
- data = containers.write(otf.cache, hash, data)
- if cleanup > 1 then
- collectgarbage("collect")
- end
- stoptiming(data)
- if elapsedtime then -- not in generic
- report_otf("preprocessing and caching time %s, packtime %s",
- elapsedtime(data),packdata and elapsedtime(packtime) or 0)
- end
- fontloader.close(fontdata) -- free memory
- if cleanup > 3 then
- collectgarbage("collect")
- end
- data = containers.read(otf.cache, hash) -- this frees the old table and load the sparse one
- if cleanup > 2 then
- collectgarbage("collect")
- end
- else
- data = nil
- report_otf("loading failed due to read error")
- end
- end
- if data then
- if trace_defining then
- report_otf("loading from cache using hash %a",hash)
- end
- enhance("unpack",data,filename,nil,false)
- enhance("add dimensions",data,filename,nil,false)
- if trace_sequences then
- showfeatureorder(data,filename)
- end
- end
- return data
-end
-
-local mt = {
- __index = function(t,k) -- maybe set it
- if k == "height" then
- local ht = t.boundingbox[4]
- return ht < 0 and 0 or ht
- elseif k == "depth" then
- local dp = -t.boundingbox[2]
- return dp < 0 and 0 or dp
- elseif k == "width" then
- return 0
- elseif k == "name" then -- or maybe uni*
- return forcenotdef and ".notdef"
- end
- end
-}
-
-actions["prepare tables"] = function(data,filename,raw)
- data.properties.hasitalics = false
-end
-
-actions["add dimensions"] = function(data,filename)
- -- todo: forget about the width if it's the defaultwidth (saves mem)
- -- we could also build the marks hash here (instead of storing it)
- if data then
- local descriptions = data.descriptions
- local resources = data.resources
- local defaultwidth = resources.defaultwidth or 0
- local defaultheight = resources.defaultheight or 0
- local defaultdepth = resources.defaultdepth or 0
- local basename = trace_markwidth and file.basename(filename)
- if usemetatables then
- for _, d in next, descriptions do
- local wd = d.width
- if not wd then
- d.width = defaultwidth
- elseif trace_markwidth and wd ~= 0 and d.class == "mark" then
- report_otf("mark %a with width %b found in %a",d.name or "<noname>",wd,basename)
- -- d.width = -wd
- end
- setmetatable(d,mt)
- end
- else
- for _, d in next, descriptions do
- local bb, wd = d.boundingbox, d.width
- if not wd then
- d.width = defaultwidth
- elseif trace_markwidth and wd ~= 0 and d.class == "mark" then
- report_otf("mark %a with width %b found in %a",d.name or "<noname>",wd,basename)
- -- d.width = -wd
- end
- -- if forcenotdef and not d.name then
- -- d.name = ".notdef"
- -- end
- if bb then
- local ht, dp = bb[4], -bb[2]
- if ht == 0 or ht < 0 then
- -- not set
- else
- d.height = ht
- end
- if dp == 0 or dp < 0 then
- -- not set
- else
- d.depth = dp
- end
- end
- end
- end
- end
-end
-
-local function somecopy(old) -- fast one
- if old then
- local new = { }
- if type(old) == "table" then
- for k, v in next, old do
- if k == "glyphs" then
- -- skip
- elseif type(v) == "table" then
- new[k] = somecopy(v)
- else
- new[k] = v
- end
- end
- else
- for i=1,#mainfields do
- local k = mainfields[i]
- local v = old[k]
- if k == "glyphs" then
- -- skip
- elseif type(v) == "table" then
- new[k] = somecopy(v)
- else
- new[k] = v
- end
- end
- end
- return new
- else
- return { }
- end
-end
-
--- not setting hasitalics and class (when nil) during table cronstruction can save some mem
-
-actions["prepare glyphs"] = function(data,filename,raw)
- local rawglyphs = raw.glyphs
- local rawsubfonts = raw.subfonts
- local rawcidinfo = raw.cidinfo
- local criterium = constructors.privateoffset
- local private = criterium
- local resources = data.resources
- local metadata = data.metadata
- local properties = data.properties
- local descriptions = data.descriptions
- local unicodes = resources.unicodes -- name to unicode
- local indices = resources.indices -- index to unicode
- local duplicates = resources.duplicates
- local variants = resources.variants
-
- if rawsubfonts then
-
- metadata.subfonts = includesubfonts and { }
- properties.cidinfo = rawcidinfo
-
- if rawcidinfo.registry then
- local cidmap = fonts.cid.getmap(rawcidinfo)
- if cidmap then
- rawcidinfo.usedname = cidmap.usedname
- local nofnames, nofunicodes = 0, 0
- local cidunicodes, cidnames = cidmap.unicodes, cidmap.names
- for cidindex=1,#rawsubfonts do
- local subfont = rawsubfonts[cidindex]
- local cidglyphs = subfont.glyphs
- if includesubfonts then
- metadata.subfonts[cidindex] = somecopy(subfont)
- end
- for index=0,subfont.glyphcnt-1 do -- we could take the previous glyphcnt instead of 0
- local glyph = cidglyphs[index]
- if glyph then
- local unicode = glyph.unicode
- local name = glyph.name or cidnames[index]
- if not unicode or unicode == -1 or unicode >= criterium then
- unicode = cidunicodes[index]
- end
- if unicode and descriptions[unicode] then
- report_otf("preventing glyph %a at index %H to overload unicode %U",name or "noname",index,unicode)
- unicode = -1
- end
- if not unicode or unicode == -1 or unicode >= criterium then
- if not name then
- name = format("u%06X",private)
- end
- unicode = private
- unicodes[name] = private
- if trace_private then
- report_otf("glyph %a at index %H is moved to private unicode slot %U",name,index,private)
- end
- private = private + 1
- nofnames = nofnames + 1
- else
- if not name then
- name = format("u%06X",unicode)
- end
- unicodes[name] = unicode
- nofunicodes = nofunicodes + 1
- end
- indices[index] = unicode -- each index is unique (at least now)
-
- local description = {
- -- width = glyph.width,
- boundingbox = glyph.boundingbox,
- name = glyph.name or name or "unknown", -- uniXXXX
- cidindex = cidindex,
- index = index,
- glyph = glyph,
- }
-
- descriptions[unicode] = description
- else
- -- report_otf("potential problem: glyph %U is used but empty",index)
- end
- end
- end
- if trace_loading then
- report_otf("cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes, nofnames, nofunicodes+nofnames)
- end
- elseif trace_loading then
- report_otf("unable to remap cid font, missing cid file for %a",filename)
- end
- elseif trace_loading then
- report_otf("font %a has no glyphs",filename)
- end
-
- else
-
- for index=0,raw.glyphcnt-1 do -- not raw.glyphmax-1 (as that will crash)
- local glyph = rawglyphs[index]
- if glyph then
- local unicode = glyph.unicode
- local name = glyph.name
- if not unicode or unicode == -1 or unicode >= criterium then
- unicode = private
- unicodes[name] = private
- if trace_private then
- report_otf("glyph %a at index %H is moved to private unicode slot %U",name,index,private)
- end
- private = private + 1
- else
- unicodes[name] = unicode
- end
- indices[index] = unicode
- if not name then
- name = format("u%06X",unicode)
- end
- descriptions[unicode] = {
- -- width = glyph.width,
- boundingbox = glyph.boundingbox,
- name = name,
- index = index,
- glyph = glyph,
- }
- local altuni = glyph.altuni
- if altuni then
- local d
- for i=1,#altuni do
- local a = altuni[i]
- local u = a.unicode
- local v = a.variant
- if v then
- -- tricky: no addition to d? needs checking but in practice such dups are either very simple
- -- shapes or e.g cjk with not that many features
- local vv = variants[v]
- if vv then
- vv[u] = unicode
- else -- xits-math has some:
- vv = { [u] = unicode }
- variants[v] = vv
- end
- elseif d then
- d[#d+1] = u
- else
- d = { u }
- end
- end
- if d then
- duplicates[unicode] = d
- end
- end
- else
- report_otf("potential problem: glyph %U is used but empty",index)
- end
- end
-
- end
-
- resources.private = private
-
-end
-
--- the next one is still messy but will get better when we have
--- flattened map/enc tables in the font loader
-
-actions["check encoding"] = function(data,filename,raw)
- local descriptions = data.descriptions
- local resources = data.resources
- local properties = data.properties
- local unicodes = resources.unicodes -- name to unicode
- local indices = resources.indices -- index to unicodes
- local duplicates = resources.duplicates
-
- -- begin of messy (not needed when cidmap)
-
- local mapdata = raw.map or { }
- local unicodetoindex = mapdata and mapdata.map or { }
- -- local encname = lower(data.enc_name or raw.enc_name or mapdata.enc_name or "")
- local encname = lower(data.enc_name or mapdata.enc_name or "")
- local criterium = 0xFFFF -- for instance cambria has a lot of mess up there
-
- -- end of messy
-
- if find(encname,"unicode") then -- unicodebmp, unicodefull, ...
- if trace_loading then
- report_otf("checking embedded unicode map %a",encname)
- end
- for unicode, index in next, unicodetoindex do -- altuni already covers this
- if unicode <= criterium and not descriptions[unicode] then
- local parent = indices[index] -- why nil?
- if not parent then
- report_otf("weird, unicode %U points to nowhere with index %H",unicode,index)
- else
- local parentdescription = descriptions[parent]
- if parentdescription then
- local altuni = parentdescription.altuni
- if not altuni then
- altuni = { { unicode = parent } }
- parentdescription.altuni = altuni
- duplicates[parent] = { unicode }
- else
- local done = false
- for i=1,#altuni do
- if altuni[i].unicode == parent then
- done = true
- break
- end
- end
- if not done then
- -- let's assume simple cjk reuse
- altuni[#altuni+1] = { unicode = parent }
- table.insert(duplicates[parent],unicode)
- end
- end
- if trace_loading then
- report_otf("weird, unicode %U points to nowhere with index %H",unicode,index)
- end
- else
- report_otf("weird, unicode %U points to %U with index %H",unicode,index)
- end
- end
- end
- end
- elseif properties.cidinfo then
- report_otf("warning: no unicode map, used cidmap %a",properties.cidinfo.usedname)
- else
- report_otf("warning: non unicode map %a, only using glyph unicode data",encname or "whatever")
- end
-
- if mapdata then
- mapdata.map = { } -- clear some memory
- end
-end
-
--- for the moment we assume that a font with lookups will not use
--- altuni so we stick to kerns only
-
-actions["add duplicates"] = function(data,filename,raw)
- local descriptions = data.descriptions
- local resources = data.resources
- local properties = data.properties
- local unicodes = resources.unicodes -- name to unicode
- local indices = resources.indices -- index to unicodes
- local duplicates = resources.duplicates
-
- for unicode, d in next, duplicates do
- for i=1,#d do
- local u = d[i]
- if not descriptions[u] then
- local description = descriptions[unicode]
- local duplicate = table.copy(description) -- else packing problem
- duplicate.comment = format("copy of U+%05X", unicode)
- descriptions[u] = duplicate
- local n = 0
- for _, description in next, descriptions do
- if kerns then
- local kerns = description.kerns
- for _, k in next, kerns do
- local ku = k[unicode]
- if ku then
- k[u] = ku
- n = n + 1
- end
- end
- end
- -- todo: lookups etc
- end
- if trace_loading then
- report_otf("duplicating %U to %U with index %H (%s kerns)",unicode,u,description.index,n)
- end
- end
- end
- end
-end
-
--- class : nil base mark ligature component (maybe we don't need it in description)
--- boundingbox: split into ht/dp takes more memory (larger tables and less sharing)
-
-actions["analyze glyphs"] = function(data,filename,raw) -- maybe integrate this in the previous
- local descriptions = data.descriptions
- local resources = data.resources
- local metadata = data.metadata
- local properties = data.properties
- local hasitalics = false
- local widths = { }
- local marks = { } -- always present (saves checking)
- for unicode, description in next, descriptions do
- local glyph = description.glyph
- local italic = glyph.italic_correction
- if not italic then
- -- skip
- elseif italic == 0 then
- -- skip
- else
- description.italic = italic
- hasitalics = true
- end
- local width = glyph.width
- widths[width] = (widths[width] or 0) + 1
- local class = glyph.class
- if class then
- if class == "mark" then
- marks[unicode] = true
- end
- description.class = class
- end
- end
- -- flag italic
- properties.hasitalics = hasitalics
- -- flag marks
- resources.marks = marks
- -- share most common width for cjk fonts
- local wd, most = 0, 1
- for k,v in next, widths do
- if v > most then
- wd, most = k, v
- end
- end
- if most > 1000 then -- maybe 500
- if trace_loading then
- report_otf("most common width: %s (%s times), sharing (cjk font)",wd,most)
- end
- for unicode, description in next, descriptions do
- if description.width == wd then
- -- description.width = nil
- else
- description.width = description.glyph.width
- end
- end
- resources.defaultwidth = wd
- else
- for unicode, description in next, descriptions do
- description.width = description.glyph.width
- end
- end
-end
-
-actions["reorganize mark classes"] = function(data,filename,raw)
- local mark_classes = raw.mark_classes
- if mark_classes then
- local resources = data.resources
- local unicodes = resources.unicodes
- local markclasses = { }
- resources.markclasses = markclasses -- reversed
- for name, class in next, mark_classes do
- local t = { }
- for s in gmatch(class,"[^ ]+") do
- t[unicodes[s]] = true
- end
- markclasses[name] = t
- end
- end
-end
-
-actions["reorganize features"] = function(data,filename,raw) -- combine with other
- local features = { }
- data.resources.features = features
- for k, what in next, otf.glists do
- local dw = raw[what]
- if dw then
- local f = { }
- features[what] = f
- for i=1,#dw do
- local d= dw[i]
- local dfeatures = d.features
- if dfeatures then
- for i=1,#dfeatures do
- local df = dfeatures[i]
- local tag = strip(lower(df.tag))
- local ft = f[tag]
- if not ft then
- ft = { }
- f[tag] = ft
- end
- local dscripts = df.scripts
- for i=1,#dscripts do
- local d = dscripts[i]
- local languages = d.langs
- local script = strip(lower(d.script))
- local fts = ft[script] if not fts then fts = {} ft[script] = fts end
- for i=1,#languages do
- fts[strip(lower(languages[i]))] = true
- end
- end
- end
- end
- end
- end
- end
-end
-
-actions["reorganize anchor classes"] = function(data,filename,raw)
- local resources = data.resources
- local anchor_to_lookup = { }
- local lookup_to_anchor = { }
- resources.anchor_to_lookup = anchor_to_lookup
- resources.lookup_to_anchor = lookup_to_anchor
- local classes = raw.anchor_classes -- anchor classes not in final table
- if classes then
- for c=1,#classes do
- local class = classes[c]
- local anchor = class.name
- local lookups = class.lookup
- if type(lookups) ~= "table" then
- lookups = { lookups }
- end
- local a = anchor_to_lookup[anchor]
- if not a then
- a = { }
- anchor_to_lookup[anchor] = a
- end
- for l=1,#lookups do
- local lookup = lookups[l]
- local l = lookup_to_anchor[lookup]
- if l then
- l[anchor] = true
- else
- l = { [anchor] = true }
- lookup_to_anchor[lookup] = l
- end
- a[lookup] = true
- end
- end
- end
-end
-
-actions["prepare tounicode"] = function(data,filename,raw)
- fonts.mappings.addtounicode(data,filename)
-end
-
-local g_directions = {
- gsub_contextchain = 1,
- gpos_contextchain = 1,
- -- gsub_context = 1,
- -- gpos_context = 1,
- gsub_reversecontextchain = -1,
- gpos_reversecontextchain = -1,
-}
-
--- Research by Khaled Hosny has demonstrated that the font loader merges
--- regular and AAT features and that these can interfere (especially because
--- we dropped checking for valid features elsewhere. So, we just check for
--- the special flag and drop the feature if such a tag is found.
-
-local function supported(features)
- for i=1,#features do
- if features[i].ismac then
- return false
- end
- end
- return true
-end
-
-actions["reorganize subtables"] = function(data,filename,raw)
- local resources = data.resources
- local sequences = { }
- local lookups = { }
- local chainedfeatures = { }
- resources.sequences = sequences
- resources.lookups = lookups
- for _, what in next, otf.glists do
- local dw = raw[what]
- if dw then
- for k=1,#dw do
- local gk = dw[k]
- local features = gk.features
--- if features and supported(features) then
- if not features or supported(features) then -- not always features !
- local typ = gk.type
- local chain = g_directions[typ] or 0
- local subtables = gk.subtables
- if subtables then
- local t = { }
- for s=1,#subtables do
- t[s] = subtables[s].name
- end
- subtables = t
- end
- local flags, markclass = gk.flags, nil
- if flags then
- local t = { -- forcing false packs nicer
- (flags.ignorecombiningmarks and "mark") or false,
- (flags.ignoreligatures and "ligature") or false,
- (flags.ignorebaseglyphs and "base") or false,
- flags.r2l or false,
- }
- markclass = flags.mark_class
- if markclass then
- markclass = resources.markclasses[markclass]
- end
- flags = t
- end
- --
- local name = gk.name
- --
- if not name then
- -- in fact an error
- report_otf("skipping weird lookup number %s",k)
- elseif features then
- -- scripts, tag, ismac
- local f = { }
- for i=1,#features do
- local df = features[i]
- local tag = strip(lower(df.tag))
- local ft = f[tag] if not ft then ft = {} f[tag] = ft end
- local dscripts = df.scripts
- for i=1,#dscripts do
- local d = dscripts[i]
- local languages = d.langs
- local script = strip(lower(d.script))
- local fts = ft[script] if not fts then fts = {} ft[script] = fts end
- for i=1,#languages do
- fts[strip(lower(languages[i]))] = true
- end
- end
- end
- sequences[#sequences+1] = {
- type = typ,
- chain = chain,
- flags = flags,
- name = name,
- subtables = subtables,
- markclass = markclass,
- features = f,
- }
- else
- lookups[name] = {
- type = typ,
- chain = chain,
- flags = flags,
- subtables = subtables,
- markclass = markclass,
- }
- end
- end
- end
- end
- end
-end
-
--- test this:
---
--- for _, what in next, otf.glists do
--- raw[what] = nil
--- end
-
-actions["prepare lookups"] = function(data,filename,raw)
- local lookups = raw.lookups
- if lookups then
- data.lookups = lookups
- end
-end
-
--- The reverse handler does a bit redundant splitting but it's seldom
--- seen so we don't bother too much. We could store the replacement
--- in the current list (value instead of true) but it makes other code
--- uglier. Maybe some day.
-
-local function t_uncover(splitter,cache,covers)
- local result = { }
- for n=1,#covers do
- local cover = covers[n]
- local uncovered = cache[cover]
- if not uncovered then
- uncovered = lpegmatch(splitter,cover)
- cache[cover] = uncovered
- end
- result[n] = uncovered
- end
- return result
-end
-
-local function s_uncover(splitter,cache,cover)
- if cover == "" then
- return nil
- else
- local uncovered = cache[cover]
- if not uncovered then
- uncovered = lpegmatch(splitter,cover)
--- for i=1,#uncovered do
--- uncovered[i] = { [uncovered[i]] = true }
--- end
- cache[cover] = uncovered
- end
- return { uncovered }
- end
-end
-
-local function t_hashed(t,cache)
- if t then
- local ht = { }
- for i=1,#t do
- local ti = t[i]
- local tih = cache[ti]
- if not tih then
- tih = { }
- for i=1,#ti do
- tih[ti[i]] = true
- end
- cache[ti] = tih
- end
- ht[i] = tih
- end
- return ht
- else
- return nil
- end
-end
-
--- local s_hashed = t_hashed
-
-local function s_hashed(t,cache)
- if t then
- local ht = { }
- local tf = t[1]
- for i=1,#tf do
- ht[i] = { [tf[i]] = true }
- end
- return ht
- else
- return nil
- end
-end
-
-local function r_uncover(splitter,cache,cover,replacements)
- if cover == "" then
- return nil
- else
- -- we always have current as { } even in the case of one
- local uncovered = cover[1]
- local replaced = cache[replacements]
- if not replaced then
- replaced = lpegmatch(splitter,replacements)
- cache[replacements] = replaced
- end
- local nu, nr = #uncovered, #replaced
- local r = { }
- if nu == nr then
- for i=1,nu do
- r[uncovered[i]] = replaced[i]
- end
- end
- return r
- end
-end
-
-actions["reorganize lookups"] = function(data,filename,raw) -- we could check for "" and n == 0
- -- we prefer the before lookups in a normal order
- if data.lookups then
- local splitter = data.helpers.tounicodetable
- local t_u_cache = { }
- local s_u_cache = t_u_cache -- string keys
- local t_h_cache = { }
- local s_h_cache = t_h_cache -- table keys (so we could use one cache)
- local r_u_cache = { } -- maybe shared
- for _, lookup in next, data.lookups do
- local rules = lookup.rules
- if rules then
- local format = lookup.format
- if format == "class" then
- local before_class = lookup.before_class
- if before_class then
- before_class = t_uncover(splitter,t_u_cache,reversed(before_class))
- end
- local current_class = lookup.current_class
- if current_class then
- current_class = t_uncover(splitter,t_u_cache,current_class)
- end
- local after_class = lookup.after_class
- if after_class then
- after_class = t_uncover(splitter,t_u_cache,after_class)
- end
- for i=1,#rules do
- local rule = rules[i]
- local class = rule.class
- local before = class.before
- if before then
- for i=1,#before do
- before[i] = before_class[before[i]] or { }
- end
- rule.before = t_hashed(before,t_h_cache)
- end
- local current = class.current
- local lookups = rule.lookups
- if current then
- for i=1,#current do
- current[i] = current_class[current[i]] or { }
- -- let's not be sparse
- if lookups and not lookups[i] then
- lookups[i] = "" -- (was: false) e.g. we can have two lookups and one replacement
- end
- -- end of fix
- end
- rule.current = t_hashed(current,t_h_cache)
- end
- local after = class.after
- if after then
- for i=1,#after do
- after[i] = after_class[after[i]] or { }
- end
- rule.after = t_hashed(after,t_h_cache)
- end
- rule.class = nil
- end
- lookup.before_class = nil
- lookup.current_class = nil
- lookup.after_class = nil
- lookup.format = "coverage"
- elseif format == "coverage" then
- for i=1,#rules do
- local rule = rules[i]
- local coverage = rule.coverage
- if coverage then
- local before = coverage.before
- if before then
- before = t_uncover(splitter,t_u_cache,reversed(before))
- rule.before = t_hashed(before,t_h_cache)
- end
- local current = coverage.current
- if current then
- current = t_uncover(splitter,t_u_cache,current)
- -- let's not be sparse
- local lookups = rule.lookups
- if lookups then
- for i=1,#current do
- if not lookups[i] then
- lookups[i] = "" -- fix sparse array
- end
- end
- end
- --
- rule.current = t_hashed(current,t_h_cache)
- end
- local after = coverage.after
- if after then
- after = t_uncover(splitter,t_u_cache,after)
- rule.after = t_hashed(after,t_h_cache)
- end
- rule.coverage = nil
- end
- end
- elseif format == "reversecoverage" then -- special case, single substitution only
- for i=1,#rules do
- local rule = rules[i]
- local reversecoverage = rule.reversecoverage
- if reversecoverage then
- local before = reversecoverage.before
- if before then
- before = t_uncover(splitter,t_u_cache,reversed(before))
- rule.before = t_hashed(before,t_h_cache)
- end
- local current = reversecoverage.current
- if current then
- current = t_uncover(splitter,t_u_cache,current)
- rule.current = t_hashed(current,t_h_cache)
- end
- local after = reversecoverage.after
- if after then
- after = t_uncover(splitter,t_u_cache,after)
- rule.after = t_hashed(after,t_h_cache)
- end
- local replacements = reversecoverage.replacements
- if replacements then
- rule.replacements = r_uncover(splitter,r_u_cache,current,replacements)
- end
- rule.reversecoverage = nil
- end
- end
- elseif format == "glyphs" then
- -- I could store these more efficient (as not we use a nested tables for before,
- -- after and current but this features happens so seldom that I don't bother
- -- about it right now.
- for i=1,#rules do
- local rule = rules[i]
- local glyphs = rule.glyphs
- if glyphs then
- local fore = glyphs.fore
- if fore and fore ~= "" then
- fore = s_uncover(splitter,s_u_cache,fore)
- rule.before = s_hashed(fore,s_h_cache)
- end
- local back = glyphs.back
- if back then
- back = s_uncover(splitter,s_u_cache,back)
- rule.after = s_hashed(back,s_h_cache)
- end
- local names = glyphs.names
- if names then
- names = s_uncover(splitter,s_u_cache,names)
- rule.current = s_hashed(names,s_h_cache)
- end
- rule.glyphs = nil
- end
- end
- end
- end
- end
- end
-end
-
-local function check_variants(unicode,the_variants,splitter,unicodes)
- local variants = the_variants.variants
- if variants then -- use splitter
- local glyphs = lpegmatch(splitter,variants)
- local done = { [unicode] = true }
- local n = 0
- for i=1,#glyphs do
- local g = glyphs[i]
- if done[g] then
- report_otf("skipping cyclic reference %U in math variant %U",g,unicode)
- else
- if n == 0 then
- n = 1
- variants = { g }
- else
- n = n + 1
- variants[n] = g
- end
- done[g] = true
- end
- end
- if n == 0 then
- variants = nil
- end
- end
- local parts = the_variants.parts
- if parts then
- local p = #parts
- if p > 0 then
- for i=1,p do
- local pi = parts[i]
- pi.glyph = unicodes[pi.component] or 0
- pi.component = nil
- end
- else
- parts = nil
- end
- end
- local italic_correction = the_variants.italic_correction
- if italic_correction and italic_correction == 0 then
- italic_correction = nil
- end
- return variants, parts, italic_correction
-end
-
-actions["analyze math"] = function(data,filename,raw)
- if raw.math then
- data.metadata.math = raw.math
- local unicodes = data.resources.unicodes
- local splitter = data.helpers.tounicodetable
- for unicode, description in next, data.descriptions do
- local glyph = description.glyph
- local mathkerns = glyph.mathkern -- singular
- local horiz_variants = glyph.horiz_variants
- local vert_variants = glyph.vert_variants
- local top_accent = glyph.top_accent
- if mathkerns or horiz_variants or vert_variants or top_accent then
- local math = { }
- if top_accent then
- math.top_accent = top_accent
- end
- if mathkerns then
- for k, v in next, mathkerns do
- if not next(v) then
- mathkerns[k] = nil
- else
- for k, v in next, v do
- if v == 0 then
- k[v] = nil -- height / kern can be zero
- end
- end
- end
- end
- math.kerns = mathkerns
- end
- if horiz_variants then
- math.horiz_variants, math.horiz_parts, math.horiz_italic_correction = check_variants(unicode,horiz_variants,splitter,unicodes)
- end
- if vert_variants then
- math.vert_variants, math.vert_parts, math.vert_italic_correction = check_variants(unicode,vert_variants,splitter,unicodes)
- end
- local italic_correction = description.italic
- if italic_correction and italic_correction ~= 0 then
- math.italic_correction = italic_correction
- end
- description.math = math
- end
- end
- end
-end
-
-actions["reorganize glyph kerns"] = function(data,filename,raw)
- local descriptions = data.descriptions
- local resources = data.resources
- local unicodes = resources.unicodes
- for unicode, description in next, descriptions do
- local kerns = description.glyph.kerns
- if kerns then
- local newkerns = { }
- for k, kern in next, kerns do
- local name = kern.char
- local offset = kern.off
- local lookup = kern.lookup
- if name and offset and lookup then
- local unicode = unicodes[name]
- if unicode then
- if type(lookup) == "table" then
- for l=1,#lookup do
- local lookup = lookup[l]
- local lookupkerns = newkerns[lookup]
- if lookupkerns then
- lookupkerns[unicode] = offset
- else
- newkerns[lookup] = { [unicode] = offset }
- end
- end
- else
- local lookupkerns = newkerns[lookup]
- if lookupkerns then
- lookupkerns[unicode] = offset
- else
- newkerns[lookup] = { [unicode] = offset }
- end
- end
- elseif trace_loading then
- report_otf("problems with unicode %a of kern %a of glyph %U",name,k,unicode)
- end
- end
- end
- description.kerns = newkerns
- end
- end
-end
-
-actions["merge kern classes"] = function(data,filename,raw)
- local gposlist = raw.gpos
- if gposlist then
- local descriptions = data.descriptions
- local resources = data.resources
- local unicodes = resources.unicodes
- local splitter = data.helpers.tounicodetable
- for gp=1,#gposlist do
- local gpos = gposlist[gp]
- local subtables = gpos.subtables
- if subtables then
- for s=1,#subtables do
- local subtable = subtables[s]
- local kernclass = subtable.kernclass -- name is inconsistent with anchor_classes
- if kernclass then -- the next one is quite slow
- local split = { } -- saves time
- for k=1,#kernclass do
- local kcl = kernclass[k]
- local firsts = kcl.firsts
- local seconds = kcl.seconds
- local offsets = kcl.offsets
- local lookups = kcl.lookup -- singular
- if type(lookups) ~= "table" then
- lookups = { lookups }
- end
- -- if offsets[1] == nil then
- -- offsets[1] = ""
- -- end
- -- we can check the max in the loop
- -- local maxseconds = getn(seconds)
- for n, s in next, firsts do
- split[s] = split[s] or lpegmatch(splitter,s)
- end
- local maxseconds = 0
- for n, s in next, seconds do
- if n > maxseconds then
- maxseconds = n
- end
- split[s] = split[s] or lpegmatch(splitter,s)
- end
- for l=1,#lookups do
- local lookup = lookups[l]
- for fk=1,#firsts do -- maxfirsts ?
- local fv = firsts[fk]
- local splt = split[fv]
- if splt then
- local extrakerns = { }
- local baseoffset = (fk-1) * maxseconds
- for sk=2,maxseconds do -- will become 1 based in future luatex
- local sv = seconds[sk]
- -- for sk, sv in next, seconds do
- local splt = split[sv]
- if splt then -- redundant test
- local offset = offsets[baseoffset + sk]
- if offset then
- for i=1,#splt do
- extrakerns[splt[i]] = offset
- end
- end
- end
- end
- for i=1,#splt do
- local first_unicode = splt[i]
- local description = descriptions[first_unicode]
- if description then
- local kerns = description.kerns
- if not kerns then
- kerns = { } -- unicode indexed !
- description.kerns = kerns
- end
- local lookupkerns = kerns[lookup]
- if not lookupkerns then
- lookupkerns = { }
- kerns[lookup] = lookupkerns
- end
- for second_unicode, kern in next, extrakerns do
- lookupkerns[second_unicode] = kern
- end
- elseif trace_loading then
- report_otf("no glyph data for %U", first_unicode)
- end
- end
- end
- end
- end
- end
- subtable.kernclass = { }
- end
- end
- end
- end
- end
-end
-
-actions["check glyphs"] = function(data,filename,raw)
- for unicode, description in next, data.descriptions do
- description.glyph = nil
- end
-end
-
--- future versions will remove _
-
-actions["check metadata"] = function(data,filename,raw)
- local metadata = data.metadata
- for _, k in next, mainfields do
- if valid_fields[k] then
- local v = raw[k]
- if not metadata[k] then
- metadata[k] = v
- end
- end
- end
- -- metadata.pfminfo = raw.pfminfo -- not already done?
- local ttftables = metadata.ttf_tables
- if ttftables then
- for i=1,#ttftables do
- ttftables[i].data = "deleted"
- end
- end
-end
-
-actions["cleanup tables"] = function(data,filename,raw)
- data.resources.indices = nil -- not needed
- data.helpers = nil
-end
-
--- kern: ttf has a table with kerns
---
--- Weird, as maxfirst and maxseconds can have holes, first seems to be indexed, but
--- seconds can start at 2 .. this need to be fixed as getn as well as # are sort of
--- unpredictable alternatively we could force an [1] if not set (maybe I will do that
--- anyway).
-
--- we can share { } as it is never set
-
---- ligatures have an extra specification.char entry that we don't use
-
-actions["reorganize glyph lookups"] = function(data,filename,raw)
- local resources = data.resources
- local unicodes = resources.unicodes
- local descriptions = data.descriptions
- local splitter = data.helpers.tounicodelist
-
- local lookuptypes = resources.lookuptypes
-
- for unicode, description in next, descriptions do
- local lookups = description.glyph.lookups
- if lookups then
- for tag, lookuplist in next, lookups do
- for l=1,#lookuplist do
- local lookup = lookuplist[l]
- local specification = lookup.specification
- local lookuptype = lookup.type
- local lt = lookuptypes[tag]
- if not lt then
- lookuptypes[tag] = lookuptype
- elseif lt ~= lookuptype then
- report_otf("conflicting lookuptypes, %a points to %a and %a",tag,lt,lookuptype)
- end
- if lookuptype == "ligature" then
- lookuplist[l] = { lpegmatch(splitter,specification.components) }
- elseif lookuptype == "alternate" then
- lookuplist[l] = { lpegmatch(splitter,specification.components) }
- elseif lookuptype == "substitution" then
- lookuplist[l] = unicodes[specification.variant]
- elseif lookuptype == "multiple" then
- lookuplist[l] = { lpegmatch(splitter,specification.components) }
- elseif lookuptype == "position" then
- lookuplist[l] = {
- specification.x or 0,
- specification.y or 0,
- specification.h or 0,
- specification.v or 0
- }
- elseif lookuptype == "pair" then
- local one = specification.offsets[1]
- local two = specification.offsets[2]
- local paired = unicodes[specification.paired]
- if one then
- if two then
- lookuplist[l] = { paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0 } }
- else
- lookuplist[l] = { paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 } }
- end
- else
- if two then
- lookuplist[l] = { paired, { }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0} } -- maybe nil instead of { }
- else
- lookuplist[l] = { paired }
- end
- end
- end
- end
- end
- local slookups, mlookups
- for tag, lookuplist in next, lookups do
- if #lookuplist == 1 then
- if slookups then
- slookups[tag] = lookuplist[1]
- else
- slookups = { [tag] = lookuplist[1] }
- end
- else
- if mlookups then
- mlookups[tag] = lookuplist
- else
- mlookups = { [tag] = lookuplist }
- end
- end
- end
- if slookups then
- description.slookups = slookups
- end
- if mlookups then
- description.mlookups = mlookups
- end
- end
- end
-
-end
-
-actions["reorganize glyph anchors"] = function(data,filename,raw) -- when we replace inplace we safe entries
- local descriptions = data.descriptions
- for unicode, description in next, descriptions do
- local anchors = description.glyph.anchors
- if anchors then
- for class, data in next, anchors do
- if class == "baselig" then
- for tag, specification in next, data do
- for i=1,#specification do
- local si = specification[i]
- specification[i] = { si.x or 0, si.y or 0 }
- end
- end
- else
- for tag, specification in next, data do
- data[tag] = { specification.x or 0, specification.y or 0 }
- end
- end
- end
- description.anchors = anchors
- end
- end
-end
-
--- modes: node, base, none
-
-function otf.setfeatures(tfmdata,features)
- local okay = constructors.initializefeatures("otf",tfmdata,features,trace_features,report_otf)
- if okay then
- return constructors.collectprocessors("otf",tfmdata,features,trace_features,report_otf)
- else
- return { } -- will become false
- end
-end
-
--- the first version made a top/mid/not extensible table, now we just
--- pass on the variants data and deal with it in the tfm scaler (there
--- is no longer an extensible table anyway)
---
--- we cannot share descriptions as virtual fonts might extend them (ok,
--- we could use a cache with a hash
---
--- we already assing an empty tabel to characters as we can add for
--- instance protruding info and loop over characters; one is not supposed
--- to change descriptions and if one does so one should make a copy!
-
-local function copytotfm(data,cache_id)
- if data then
- local metadata = data.metadata
- local resources = data.resources
- local properties = derivetable(data.properties)
- local descriptions = derivetable(data.descriptions)
- local goodies = derivetable(data.goodies)
- local characters = { }
- local parameters = { }
- local mathparameters = { }
- --
- local pfminfo = metadata.pfminfo or { }
- local resources = data.resources
- local unicodes = resources.unicodes
- -- local mode = data.mode or "base"
- local spaceunits = 500
- local spacer = "space"
- local designsize = metadata.designsize or metadata.design_size or 100
- local mathspecs = metadata.math
- --
- if designsize == 0 then
- designsize = 100
- end
- if mathspecs then
- for name, value in next, mathspecs do
- mathparameters[name] = value
- end
- end
- for unicode, _ in next, data.descriptions do -- use parent table
- characters[unicode] = { }
- end
- if mathspecs then
- -- we could move this to the scaler but not that much is saved
- -- and this is cleaner
- for unicode, character in next, characters do
- local d = descriptions[unicode]
- local m = d.math
- if m then
- -- watch out: luatex uses horiz_variants for the parts
- local variants = m.horiz_variants
- local parts = m.horiz_parts
- -- local done = { [unicode] = true }
- if variants then
- local c = character
- for i=1,#variants do
- local un = variants[i]
- -- if done[un] then
- -- -- report_otf("skipping cyclic reference %U in math variant %U",un,unicode)
- -- else
- c.next = un
- c = characters[un]
- -- done[un] = true
- -- end
- end -- c is now last in chain
- c.horiz_variants = parts
- elseif parts then
- character.horiz_variants = parts
- end
- local variants = m.vert_variants
- local parts = m.vert_parts
- -- local done = { [unicode] = true }
- if variants then
- local c = character
- for i=1,#variants do
- local un = variants[i]
- -- if done[un] then
- -- -- report_otf("skipping cyclic reference %U in math variant %U",un,unicode)
- -- else
- c.next = un
- c = characters[un]
- -- done[un] = true
- -- end
- end -- c is now last in chain
- c.vert_variants = parts
- elseif parts then
- character.vert_variants = parts
- end
- local italic_correction = m.vert_italic_correction
- if italic_correction then
- character.vert_italic_correction = italic_correction -- was c.
- end
- local top_accent = m.top_accent
- if top_accent then
- character.top_accent = top_accent
- end
- local kerns = m.kerns
- if kerns then
- character.mathkerns = kerns
- end
- end
- end
- end
- -- end math
- local monospaced = metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion == "Monospaced")
- local charwidth = pfminfo.avgwidth -- or unset
- local italicangle = metadata.italicangle
- local charxheight = pfminfo.os2_xheight and pfminfo.os2_xheight > 0 and pfminfo.os2_xheight
- properties.monospaced = monospaced
- parameters.italicangle = italicangle
- parameters.charwidth = charwidth
- parameters.charxheight = charxheight
- --
- local space = 0x0020 -- unicodes['space'], unicodes['emdash']
- local emdash = 0x2014 -- unicodes['space'], unicodes['emdash']
- if monospaced then
- if descriptions[space] then
- spaceunits, spacer = descriptions[space].width, "space"
- end
- if not spaceunits and descriptions[emdash] then
- spaceunits, spacer = descriptions[emdash].width, "emdash"
- end
- if not spaceunits and charwidth then
- spaceunits, spacer = charwidth, "charwidth"
- end
- else
- if descriptions[space] then
- spaceunits, spacer = descriptions[space].width, "space"
- end
- if not spaceunits and descriptions[emdash] then
- spaceunits, spacer = descriptions[emdash].width/2, "emdash/2"
- end
- if not spaceunits and charwidth then
- spaceunits, spacer = charwidth, "charwidth"
- end
- end
- spaceunits = tonumber(spaceunits) or 500 -- brrr
- -- we need a runtime lookup because of running from cdrom or zip, brrr (shouldn't we use the basename then?)
- local filename = constructors.checkedfilename(resources)
- local fontname = metadata.fontname
- local fullname = metadata.fullname or fontname
- local units = metadata.units_per_em or 1000
- --
- if units == 0 then -- catch bugs in fonts
- units = 1000
- metadata.units_per_em = 1000
- end
- --
- parameters.slant = 0
- parameters.space = spaceunits -- 3.333 (cmr10)
- parameters.space_stretch = units/2 -- 500 -- 1.666 (cmr10)
- parameters.space_shrink = 1*units/3 -- 333 -- 1.111 (cmr10)
- parameters.x_height = 2*units/5 -- 400
- parameters.quad = units -- 1000
- if spaceunits < 2*units/5 then
- -- todo: warning
- end
- if italicangle then
- parameters.italicangle = italicangle
- parameters.italicfactor = math.cos(math.rad(90+italicangle))
- parameters.slant = - math.round(math.tan(italicangle*math.pi/180))
- end
- if monospaced then
- parameters.space_stretch = 0
- parameters.space_shrink = 0
- elseif syncspace then --
- parameters.space_stretch = spaceunits/2
- parameters.space_shrink = spaceunits/3
- end
- parameters.extra_space = parameters.space_shrink -- 1.111 (cmr10)
- if charxheight then
- parameters.x_height = charxheight
- else
- local x = 0x78 -- unicodes['x']
- if x then
- local x = descriptions[x]
- if x then
- parameters.x_height = x.height
- end
- end
- end
- --
- parameters.designsize = (designsize/10)*65536
- parameters.ascender = abs(metadata.ascent or 0)
- parameters.descender = abs(metadata.descent or 0)
- parameters.units = units
- --
- properties.space = spacer
- properties.encodingbytes = 2
- properties.format = data.format or fonts.formats[filename] or "opentype"
- properties.noglyphnames = true
- properties.filename = filename
- properties.fontname = fontname
- properties.fullname = fullname
- properties.psname = fontname or fullname
- properties.name = filename or fullname
- --
- -- properties.name = specification.name
- -- properties.sub = specification.sub
- return {
- characters = characters,
- descriptions = descriptions,
- parameters = parameters,
- mathparameters = mathparameters,
- resources = resources,
- properties = properties,
- goodies = goodies,
- }
- end
-end
-
-local function otftotfm(specification)
- local cache_id = specification.hash
- local tfmdata = containers.read(constructors.cache,cache_id)
- if not tfmdata then
- local name = specification.name
- local sub = specification.sub
- local filename = specification.filename
- local format = specification.format
- local features = specification.features.normal
- local rawdata = otf.load(filename,format,sub,features and features.featurefile)
- if rawdata and next(rawdata) then
- rawdata.lookuphash = { }
- tfmdata = copytotfm(rawdata,cache_id)
- if tfmdata and next(tfmdata) then
- -- at this moment no characters are assigned yet, only empty slots
- local features = constructors.checkedfeatures("otf",features)
- local shared = tfmdata.shared
- if not shared then
- shared = { }
- tfmdata.shared = shared
- end
- shared.rawdata = rawdata
- -- shared.features = features -- default
- shared.dynamics = { }
- -- shared.processes = { }
- tfmdata.changed = { }
- shared.features = features
- shared.processes = otf.setfeatures(tfmdata,features)
- end
- end
- containers.write(constructors.cache,cache_id,tfmdata)
- end
- return tfmdata
-end
-
-local function read_from_otf(specification)
- local tfmdata = otftotfm(specification)
- if tfmdata then
- -- this late ? .. needs checking
- tfmdata.properties.name = specification.name
- tfmdata.properties.sub = specification.sub
- --
- tfmdata = constructors.scale(tfmdata,specification)
- local allfeatures = tfmdata.shared.features or specification.features.normal
- constructors.applymanipulators("otf",tfmdata,allfeatures,trace_features,report_otf)
- constructors.setname(tfmdata,specification) -- only otf?
- fonts.loggers.register(tfmdata,file.suffix(specification.filename),specification)
- end
- return tfmdata
-end
-
-local function checkmathsize(tfmdata,mathsize)
- local mathdata = tfmdata.shared.rawdata.metadata.math
- local mathsize = tonumber(mathsize)
- if mathdata then -- we cannot use mathparameters as luatex will complain
- local parameters = tfmdata.parameters
- parameters.scriptpercentage = mathdata.ScriptPercentScaleDown
- parameters.scriptscriptpercentage = mathdata.ScriptScriptPercentScaleDown
- parameters.mathsize = mathsize
- end
-end
-
-registerotffeature {
- name = "mathsize",
- description = "apply mathsize specified in the font",
- initializers = {
- base = checkmathsize,
- node = checkmathsize,
- }
-}
-
--- helpers
-
-function otf.collectlookups(rawdata,kind,script,language)
- local sequences = rawdata.resources.sequences
- if sequences then
- local featuremap, featurelist = { }, { }
- for s=1,#sequences do
- local sequence = sequences[s]
- local features = sequence.features
- features = features and features[kind]
- features = features and (features[script] or features[default] or features[wildcard])
- features = features and (features[language] or features[default] or features[wildcard])
- if features then
- local subtables = sequence.subtables
- if subtables then
- for s=1,#subtables do
- local ss = subtables[s]
- if not featuremap[s] then
- featuremap[ss] = true
- featurelist[#featurelist+1] = ss
- end
- end
- end
- end
- end
- if #featurelist > 0 then
- return featuremap, featurelist
- end
- end
- return nil, nil
-end
-
--- readers
-
-local function check_otf(forced,specification,suffix,what)
- local name = specification.name
- if forced then
- name = file.addsuffix(name,suffix,true)
- end
- local fullname = findbinfile(name,suffix) or ""
- if fullname == "" then
- fullname = fonts.names.getfilename(name,suffix) or ""
- end
- if fullname ~= "" then
- specification.filename = fullname
- specification.format = what
- return read_from_otf(specification)
- end
-end
-
-local function opentypereader(specification,suffix,what)
- local forced = specification.forced or ""
- if forced == "otf" then
- return check_otf(true,specification,forced,"opentype")
- elseif forced == "ttf" or forced == "ttc" or forced == "dfont" then
- return check_otf(true,specification,forced,"truetype")
- else
- return check_otf(false,specification,suffix,what)
- end
-end
-
-readers.opentype = opentypereader
-
-local formats = fonts.formats
-
-formats.otf = "opentype"
-formats.ttf = "truetype"
-formats.ttc = "truetype"
-formats.dfont = "truetype"
-
-function readers.otf (specification) return opentypereader(specification,"otf",formats.otf ) end
-function readers.ttf (specification) return opentypereader(specification,"ttf",formats.ttf ) end
-function readers.ttc (specification) return opentypereader(specification,"ttf",formats.ttc ) end
-function readers.dfont(specification) return opentypereader(specification,"ttf",formats.dfont) end
-
--- this will be overloaded
-
-function otf.scriptandlanguage(tfmdata,attr)
- local properties = tfmdata.properties
- return properties.script or "dflt", properties.language or "dflt"
-end
+if not modules then modules = { } end modules ['font-otf'] = {
+ 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"
+}
+
+-- langs -> languages enz
+-- anchor_classes vs kernclasses
+-- modification/creationtime in subfont is runtime dus zinloos
+-- to_table -> totable
+-- ascent descent
+
+-- more checking against low level calls of functions
+
+local utfbyte = utf.byte
+local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip
+local type, next, tonumber, tostring = type, next, tonumber, tostring
+local abs = math.abs
+local getn = table.getn
+local lpegmatch = lpeg.match
+local reversed, concat, remove = table.reversed, table.concat, table.remove
+local ioflush = io.flush
+local fastcopy, tohash, derivetable = table.fastcopy, table.tohash, table.derive
+local formatters = string.formatters
+
+local allocate = utilities.storage.allocate
+local registertracker = trackers.register
+local registerdirective = directives.register
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+local elapsedtime = statistics.elapsedtime
+local findbinfile = resolvers.findbinfile
+
+local trace_private = false registertracker("otf.private", function(v) trace_private = v end)
+local trace_loading = false registertracker("otf.loading", function(v) trace_loading = v end)
+local trace_features = false registertracker("otf.features", function(v) trace_features = v end)
+local trace_dynamics = false registertracker("otf.dynamics", function(v) trace_dynamics = v end)
+local trace_sequences = false registertracker("otf.sequences", function(v) trace_sequences = v end)
+local trace_markwidth = false registertracker("otf.markwidth", function(v) trace_markwidth = v end)
+local trace_defining = false registertracker("fonts.defining", function(v) trace_defining = v end)
+
+local report_otf = logs.reporter("fonts","otf loading")
+
+local fonts = fonts
+local otf = fonts.handlers.otf
+
+otf.glists = { "gsub", "gpos" }
+
+otf.version = 2.743 -- beware: also sync font-mis.lua
+otf.cache = containers.define("fonts", "otf", otf.version, true)
+
+local fontdata = fonts.hashes.identifiers
+local chardata = characters and characters.data -- not used
+
+local otffeatures = fonts.constructors.newfeatures("otf")
+local registerotffeature = otffeatures.register
+
+local enhancers = allocate()
+otf.enhancers = enhancers
+local patches = { }
+enhancers.patches = patches
+
+local definers = fonts.definers
+local readers = fonts.readers
+local constructors = fonts.constructors
+
+local forceload = false
+local cleanup = 0 -- mk: 0=885M 1=765M 2=735M (regular run 730M)
+local usemetatables = false -- .4 slower on mk but 30 M less mem so we might change the default -- will be directive
+local packdata = true
+local syncspace = true
+local forcenotdef = false
+local includesubfonts = false
+
+local wildcard = "*"
+local default = "dflt"
+
+local fontloaderfields = fontloader.fields
+local mainfields = nil
+local glyphfields = nil -- not used yet
+
+registerdirective("fonts.otf.loader.cleanup", function(v) cleanup = tonumber(v) or (v and 1) or 0 end)
+registerdirective("fonts.otf.loader.force", function(v) forceload = v end)
+registerdirective("fonts.otf.loader.usemetatables", function(v) usemetatables = v end)
+registerdirective("fonts.otf.loader.pack", function(v) packdata = v end)
+registerdirective("fonts.otf.loader.syncspace", function(v) syncspace = v end)
+registerdirective("fonts.otf.loader.forcenotdef", function(v) forcenotdef = v end)
+
+local function load_featurefile(raw,featurefile)
+ if featurefile and featurefile ~= "" then
+ if trace_loading then
+ report_otf("using featurefile %a", featurefile)
+ end
+ fontloader.apply_featurefile(raw, featurefile)
+ end
+end
+
+local function showfeatureorder(rawdata,filename)
+ local sequences = rawdata.resources.sequences
+ if sequences and #sequences > 0 then
+ if trace_loading then
+ report_otf("font %a has %s sequences",filename,#sequences)
+ report_otf(" ")
+ end
+ for nos=1,#sequences do
+ local sequence = sequences[nos]
+ local typ = sequence.type or "no-type"
+ local name = sequence.name or "no-name"
+ local subtables = sequence.subtables or { "no-subtables" }
+ local features = sequence.features
+ if trace_loading then
+ report_otf("%3i %-15s %-20s [% t]",nos,name,typ,subtables)
+ end
+ if features then
+ for feature, scripts in next, features do
+ local tt = { }
+ if type(scripts) == "table" then
+ for script, languages in next, scripts do
+ local ttt = { }
+ for language, _ in next, languages do
+ ttt[#ttt+1] = language
+ end
+ tt[#tt+1] = formatters["[%s: % t]"](script,ttt)
+ end
+ if trace_loading then
+ report_otf(" %s: % t",feature,tt)
+ end
+ else
+ if trace_loading then
+ report_otf(" %s: %S",feature,scripts)
+ end
+ end
+ end
+ end
+ end
+ if trace_loading then
+ report_otf("\n")
+ end
+ elseif trace_loading then
+ report_otf("font %a has no sequences",filename)
+ end
+end
+
+--[[ldx--
+<p>We start with a lot of tables and related functions.</p>
+--ldx]]--
+
+local valid_fields = table.tohash {
+ -- "anchor_classes",
+ "ascent",
+ -- "cache_version",
+ "cidinfo",
+ "copyright",
+ -- "creationtime",
+ "descent",
+ "design_range_bottom",
+ "design_range_top",
+ "design_size",
+ "encodingchanged",
+ "extrema_bound",
+ "familyname",
+ "fontname",
+ "fontname",
+ "fontstyle_id",
+ "fontstyle_name",
+ "fullname",
+ -- "glyphs",
+ "hasvmetrics",
+ -- "head_optimized_for_cleartype",
+ "horiz_base",
+ "issans",
+ "isserif",
+ "italicangle",
+ -- "kerns",
+ -- "lookups",
+ "macstyle",
+ -- "modificationtime",
+ "onlybitmaps",
+ "origname",
+ "os2_version",
+ "pfminfo",
+ -- "private",
+ "serifcheck",
+ "sfd_version",
+ -- "size",
+ "strokedfont",
+ "strokewidth",
+ -- "subfonts",
+ "table_version",
+ -- "tables",
+ -- "ttf_tab_saved",
+ "ttf_tables",
+ "uni_interp",
+ "uniqueid",
+ "units_per_em",
+ "upos",
+ "use_typo_metrics",
+ "uwidth",
+ -- "validation_state",
+ "version",
+ "vert_base",
+ "weight",
+ "weight_width_slope_only",
+ -- "xuid",
+}
+
+local ordered_enhancers = {
+ "prepare tables",
+
+ "prepare glyphs",
+ "prepare lookups",
+
+ "analyze glyphs",
+ "analyze math",
+
+ "prepare tounicode", -- maybe merge with prepare
+
+ "reorganize lookups",
+ "reorganize mark classes",
+ "reorganize anchor classes",
+
+ "reorganize glyph kerns",
+ "reorganize glyph lookups",
+ "reorganize glyph anchors",
+
+ "merge kern classes",
+
+ "reorganize features",
+ "reorganize subtables",
+
+ "check glyphs",
+ "check metadata",
+ "check extra features", -- after metadata
+
+ "check encoding", -- moved
+ "add duplicates",
+
+ "cleanup tables",
+}
+
+--[[ldx--
+<p>Here we go.</p>
+--ldx]]--
+
+local actions = allocate()
+local before = allocate()
+local after = allocate()
+
+patches.before = before
+patches.after = after
+
+local function enhance(name,data,filename,raw)
+ local enhancer = actions[name]
+ if enhancer then
+ if trace_loading then
+ report_otf("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
+
+function enhancers.apply(data,filename,raw)
+ local basename = file.basename(lower(filename))
+ if trace_loading then
+ report_otf("%s enhancing file %a","start",filename)
+ end
+ ioflush() -- we want instant messages
+ for e=1,#ordered_enhancers do
+ local enhancer = ordered_enhancers[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)
+ 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_loading then
+ report_otf("%s enhancing file %a","stop",filename)
+ end
+ ioflush() -- we want instant messages
+end
+
+-- patches.register("before","migrate metadata","cambria",function() end)
+
+function patches.register(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}
+ end
+ end
+end
+
+function patches.report(fmt,...)
+ if trace_loading then
+ report_otf("patching: %s",formatters[fmt](...))
+ end
+end
+
+function enhancers.register(what,action) -- only already registered can be overloaded
+ actions[what] = action
+end
+
+function otf.load(filename,format,sub,featurefile)
+ local base = file.basename(file.removesuffix(filename))
+ local name = file.removesuffix(base)
+ local attr = lfs.attributes(filename)
+ local size = attr and attr.size or 0
+ local time = attr and attr.modification or 0
+ if featurefile then
+ name = name .. "@" .. file.removesuffix(file.basename(featurefile))
+ end
+ if sub == "" then
+ sub = false
+ end
+ local hash = name
+ if sub then
+ hash = hash .. "-" .. sub
+ end
+ hash = containers.cleanname(hash)
+ local featurefiles
+ if featurefile then
+ featurefiles = { }
+ for s in gmatch(featurefile,"[^,]+") do
+ local name = resolvers.findfile(file.addsuffix(s,'fea'),'fea') or ""
+ if name == "" then
+ report_otf("loading error, no featurefile %a",s)
+ else
+ local attr = lfs.attributes(name)
+ featurefiles[#featurefiles+1] = {
+ name = name,
+ size = attr and attr.size or 0,
+ time = attr and attr.modification or 0,
+ }
+ end
+ end
+ if #featurefiles == 0 then
+ featurefiles = nil
+ end
+ end
+ local data = containers.read(otf.cache,hash)
+ local reload = not data or data.size ~= size or data.time ~= time
+ if forceload then
+ report_otf("forced reload of %a due to hard coded flag",filename)
+ reload = true
+ end
+ if not reload then
+ local featuredata = data.featuredata
+ if featurefiles then
+ if not featuredata or #featuredata ~= #featurefiles then
+ reload = true
+ else
+ for i=1,#featurefiles do
+ local fi, fd = featurefiles[i], featuredata[i]
+ if fi.name ~= fd.name or fi.size ~= fd.size or fi.time ~= fd.time then
+ reload = true
+ break
+ end
+ end
+ end
+ elseif featuredata then
+ reload = true
+ end
+ if reload then
+ report_otf("loading: forced reload due to changed featurefile specification %a",featurefile)
+ end
+ end
+ if reload then
+ report_otf("loading %a, hash %a",filename,hash)
+ local fontdata, messages
+ if sub then
+ fontdata, messages = fontloader.open(filename,sub)
+ else
+ fontdata, messages = fontloader.open(filename)
+ end
+ if fontdata then
+ mainfields = mainfields or (fontloaderfields and fontloaderfields(fontdata))
+ end
+ if trace_loading and messages and #messages > 0 then
+ if type(messages) == "string" then
+ report_otf("warning: %s",messages)
+ else
+ for m=1,#messages do
+ report_otf("warning: %S",messages[m])
+ end
+ end
+ else
+ report_otf("loading done")
+ end
+ if fontdata then
+ if featurefiles then
+ for i=1,#featurefiles do
+ load_featurefile(fontdata,featurefiles[i].name)
+ end
+ end
+ local unicodes = {
+ -- names to unicodes
+ }
+ local splitter = lpeg.splitter(" ",unicodes)
+ data = {
+ size = size,
+ time = time,
+ format = format,
+ featuredata = featurefiles,
+ resources = {
+ filename = resolvers.unresolve(filename), -- no shortcut
+ version = otf.version,
+ creator = "context mkiv",
+ unicodes = unicodes,
+ indices = {
+ -- index to unicodes
+ },
+ duplicates = {
+ -- alternative unicodes
+ },
+ variants = {
+ -- alternative unicodes (variants)
+ },
+ lookuptypes = {
+ },
+ },
+ metadata = {
+ -- raw metadata, not to be used
+ },
+ properties = {
+ -- normalized metadata
+ },
+ descriptions = {
+ },
+ goodies = {
+ },
+ helpers = {
+ tounicodelist = splitter,
+ tounicodetable = lpeg.Ct(splitter),
+ },
+ }
+ starttiming(data)
+ report_otf("file size: %s", size)
+ enhancers.apply(data,filename,fontdata)
+ local packtime = { }
+ if packdata then
+ if cleanup > 0 then
+ collectgarbage("collect")
+ end
+ starttiming(packtime)
+ enhance("pack",data,filename,nil)
+ stoptiming(packtime)
+ end
+ report_otf("saving %a in cache",filename)
+ data = containers.write(otf.cache, hash, data)
+ if cleanup > 1 then
+ collectgarbage("collect")
+ end
+ stoptiming(data)
+ if elapsedtime then -- not in generic
+ report_otf("preprocessing and caching time %s, packtime %s",
+ elapsedtime(data),packdata and elapsedtime(packtime) or 0)
+ end
+ fontloader.close(fontdata) -- free memory
+ if cleanup > 3 then
+ collectgarbage("collect")
+ end
+ data = containers.read(otf.cache, hash) -- this frees the old table and load the sparse one
+ if cleanup > 2 then
+ collectgarbage("collect")
+ end
+ else
+ data = nil
+ report_otf("loading failed due to read error")
+ end
+ end
+ if data then
+ if trace_defining then
+ report_otf("loading from cache using hash %a",hash)
+ end
+ enhance("unpack",data,filename,nil,false)
+ enhance("add dimensions",data,filename,nil,false)
+ if trace_sequences then
+ showfeatureorder(data,filename)
+ end
+ end
+ return data
+end
+
+local mt = {
+ __index = function(t,k) -- maybe set it
+ if k == "height" then
+ local ht = t.boundingbox[4]
+ return ht < 0 and 0 or ht
+ elseif k == "depth" then
+ local dp = -t.boundingbox[2]
+ return dp < 0 and 0 or dp
+ elseif k == "width" then
+ return 0
+ elseif k == "name" then -- or maybe uni*
+ return forcenotdef and ".notdef"
+ end
+ end
+}
+
+actions["prepare tables"] = function(data,filename,raw)
+ data.properties.hasitalics = false
+end
+
+actions["add dimensions"] = function(data,filename)
+ -- todo: forget about the width if it's the defaultwidth (saves mem)
+ -- we could also build the marks hash here (instead of storing it)
+ if data then
+ local descriptions = data.descriptions
+ local resources = data.resources
+ local defaultwidth = resources.defaultwidth or 0
+ local defaultheight = resources.defaultheight or 0
+ local defaultdepth = resources.defaultdepth or 0
+ local basename = trace_markwidth and file.basename(filename)
+ if usemetatables then
+ for _, d in next, descriptions do
+ local wd = d.width
+ if not wd then
+ d.width = defaultwidth
+ elseif trace_markwidth and wd ~= 0 and d.class == "mark" then
+ report_otf("mark %a with width %b found in %a",d.name or "<noname>",wd,basename)
+ -- d.width = -wd
+ end
+ setmetatable(d,mt)
+ end
+ else
+ for _, d in next, descriptions do
+ local bb, wd = d.boundingbox, d.width
+ if not wd then
+ d.width = defaultwidth
+ elseif trace_markwidth and wd ~= 0 and d.class == "mark" then
+ report_otf("mark %a with width %b found in %a",d.name or "<noname>",wd,basename)
+ -- d.width = -wd
+ end
+ -- if forcenotdef and not d.name then
+ -- d.name = ".notdef"
+ -- end
+ if bb then
+ local ht, dp = bb[4], -bb[2]
+ if ht == 0 or ht < 0 then
+ -- not set
+ else
+ d.height = ht
+ end
+ if dp == 0 or dp < 0 then
+ -- not set
+ else
+ d.depth = dp
+ end
+ end
+ end
+ end
+ end
+end
+
+local function somecopy(old) -- fast one
+ if old then
+ local new = { }
+ if type(old) == "table" then
+ for k, v in next, old do
+ if k == "glyphs" then
+ -- skip
+ elseif type(v) == "table" then
+ new[k] = somecopy(v)
+ else
+ new[k] = v
+ end
+ end
+ else
+ for i=1,#mainfields do
+ local k = mainfields[i]
+ local v = old[k]
+ if k == "glyphs" then
+ -- skip
+ elseif type(v) == "table" then
+ new[k] = somecopy(v)
+ else
+ new[k] = v
+ end
+ end
+ end
+ return new
+ else
+ return { }
+ end
+end
+
+-- not setting hasitalics and class (when nil) during table cronstruction can save some mem
+
+actions["prepare glyphs"] = function(data,filename,raw)
+ local rawglyphs = raw.glyphs
+ local rawsubfonts = raw.subfonts
+ local rawcidinfo = raw.cidinfo
+ local criterium = constructors.privateoffset
+ local private = criterium
+ local resources = data.resources
+ local metadata = data.metadata
+ local properties = data.properties
+ local descriptions = data.descriptions
+ local unicodes = resources.unicodes -- name to unicode
+ local indices = resources.indices -- index to unicode
+ local duplicates = resources.duplicates
+ local variants = resources.variants
+
+ if rawsubfonts then
+
+ metadata.subfonts = includesubfonts and { }
+ properties.cidinfo = rawcidinfo
+
+ if rawcidinfo.registry then
+ local cidmap = fonts.cid.getmap(rawcidinfo)
+ if cidmap then
+ rawcidinfo.usedname = cidmap.usedname
+ local nofnames, nofunicodes = 0, 0
+ local cidunicodes, cidnames = cidmap.unicodes, cidmap.names
+ for cidindex=1,#rawsubfonts do
+ local subfont = rawsubfonts[cidindex]
+ local cidglyphs = subfont.glyphs
+ if includesubfonts then
+ metadata.subfonts[cidindex] = somecopy(subfont)
+ end
+ for index=0,subfont.glyphcnt-1 do -- we could take the previous glyphcnt instead of 0
+ local glyph = cidglyphs[index]
+ if glyph then
+ local unicode = glyph.unicode
+ local name = glyph.name or cidnames[index]
+ if not unicode or unicode == -1 or unicode >= criterium then
+ unicode = cidunicodes[index]
+ end
+ if unicode and descriptions[unicode] then
+ report_otf("preventing glyph %a at index %H to overload unicode %U",name or "noname",index,unicode)
+ unicode = -1
+ end
+ if not unicode or unicode == -1 or unicode >= criterium then
+ if not name then
+ name = format("u%06X",private)
+ end
+ unicode = private
+ unicodes[name] = private
+ if trace_private then
+ report_otf("glyph %a at index %H is moved to private unicode slot %U",name,index,private)
+ end
+ private = private + 1
+ nofnames = nofnames + 1
+ else
+ if not name then
+ name = format("u%06X",unicode)
+ end
+ unicodes[name] = unicode
+ nofunicodes = nofunicodes + 1
+ end
+ indices[index] = unicode -- each index is unique (at least now)
+
+ local description = {
+ -- width = glyph.width,
+ boundingbox = glyph.boundingbox,
+ name = glyph.name or name or "unknown", -- uniXXXX
+ cidindex = cidindex,
+ index = index,
+ glyph = glyph,
+ }
+
+ descriptions[unicode] = description
+ else
+ -- report_otf("potential problem: glyph %U is used but empty",index)
+ end
+ end
+ end
+ if trace_loading then
+ report_otf("cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes, nofnames, nofunicodes+nofnames)
+ end
+ elseif trace_loading then
+ report_otf("unable to remap cid font, missing cid file for %a",filename)
+ end
+ elseif trace_loading then
+ report_otf("font %a has no glyphs",filename)
+ end
+
+ else
+
+ for index=0,raw.glyphcnt-1 do -- not raw.glyphmax-1 (as that will crash)
+ local glyph = rawglyphs[index]
+ if glyph then
+ local unicode = glyph.unicode
+ local name = glyph.name
+ if not unicode or unicode == -1 or unicode >= criterium then
+ unicode = private
+ unicodes[name] = private
+ if trace_private then
+ report_otf("glyph %a at index %H is moved to private unicode slot %U",name,index,private)
+ end
+ private = private + 1
+ else
+ unicodes[name] = unicode
+ end
+ indices[index] = unicode
+ if not name then
+ name = format("u%06X",unicode)
+ end
+ descriptions[unicode] = {
+ -- width = glyph.width,
+ boundingbox = glyph.boundingbox,
+ name = name,
+ index = index,
+ glyph = glyph,
+ }
+ local altuni = glyph.altuni
+ if altuni then
+ local d
+ for i=1,#altuni do
+ local a = altuni[i]
+ local u = a.unicode
+ local v = a.variant
+ if v then
+ -- tricky: no addition to d? needs checking but in practice such dups are either very simple
+ -- shapes or e.g cjk with not that many features
+ local vv = variants[v]
+ if vv then
+ vv[u] = unicode
+ else -- xits-math has some:
+ vv = { [u] = unicode }
+ variants[v] = vv
+ end
+ elseif d then
+ d[#d+1] = u
+ else
+ d = { u }
+ end
+ end
+ if d then
+ duplicates[unicode] = d
+ end
+ end
+ else
+ report_otf("potential problem: glyph %U is used but empty",index)
+ end
+ end
+
+ end
+
+ resources.private = private
+
+end
+
+-- the next one is still messy but will get better when we have
+-- flattened map/enc tables in the font loader
+
+actions["check encoding"] = function(data,filename,raw)
+ local descriptions = data.descriptions
+ local resources = data.resources
+ local properties = data.properties
+ local unicodes = resources.unicodes -- name to unicode
+ local indices = resources.indices -- index to unicodes
+ local duplicates = resources.duplicates
+
+ -- begin of messy (not needed when cidmap)
+
+ local mapdata = raw.map or { }
+ local unicodetoindex = mapdata and mapdata.map or { }
+ -- local encname = lower(data.enc_name or raw.enc_name or mapdata.enc_name or "")
+ local encname = lower(data.enc_name or mapdata.enc_name or "")
+ local criterium = 0xFFFF -- for instance cambria has a lot of mess up there
+
+ -- end of messy
+
+ if find(encname,"unicode") then -- unicodebmp, unicodefull, ...
+ if trace_loading then
+ report_otf("checking embedded unicode map %a",encname)
+ end
+ for unicode, index in next, unicodetoindex do -- altuni already covers this
+ if unicode <= criterium and not descriptions[unicode] then
+ local parent = indices[index] -- why nil?
+ if not parent then
+ report_otf("weird, unicode %U points to nowhere with index %H",unicode,index)
+ else
+ local parentdescription = descriptions[parent]
+ if parentdescription then
+ local altuni = parentdescription.altuni
+ if not altuni then
+ altuni = { { unicode = parent } }
+ parentdescription.altuni = altuni
+ duplicates[parent] = { unicode }
+ else
+ local done = false
+ for i=1,#altuni do
+ if altuni[i].unicode == parent then
+ done = true
+ break
+ end
+ end
+ if not done then
+ -- let's assume simple cjk reuse
+ altuni[#altuni+1] = { unicode = parent }
+ table.insert(duplicates[parent],unicode)
+ end
+ end
+ if trace_loading then
+ report_otf("weird, unicode %U points to nowhere with index %H",unicode,index)
+ end
+ else
+ report_otf("weird, unicode %U points to %U with index %H",unicode,index)
+ end
+ end
+ end
+ end
+ elseif properties.cidinfo then
+ report_otf("warning: no unicode map, used cidmap %a",properties.cidinfo.usedname)
+ else
+ report_otf("warning: non unicode map %a, only using glyph unicode data",encname or "whatever")
+ end
+
+ if mapdata then
+ mapdata.map = { } -- clear some memory
+ end
+end
+
+-- for the moment we assume that a font with lookups will not use
+-- altuni so we stick to kerns only
+
+actions["add duplicates"] = function(data,filename,raw)
+ local descriptions = data.descriptions
+ local resources = data.resources
+ local properties = data.properties
+ local unicodes = resources.unicodes -- name to unicode
+ local indices = resources.indices -- index to unicodes
+ local duplicates = resources.duplicates
+
+ for unicode, d in next, duplicates do
+ for i=1,#d do
+ local u = d[i]
+ if not descriptions[u] then
+ local description = descriptions[unicode]
+ local duplicate = table.copy(description) -- else packing problem
+ duplicate.comment = format("copy of U+%05X", unicode)
+ descriptions[u] = duplicate
+ local n = 0
+ for _, description in next, descriptions do
+ if kerns then
+ local kerns = description.kerns
+ for _, k in next, kerns do
+ local ku = k[unicode]
+ if ku then
+ k[u] = ku
+ n = n + 1
+ end
+ end
+ end
+ -- todo: lookups etc
+ end
+ if trace_loading then
+ report_otf("duplicating %U to %U with index %H (%s kerns)",unicode,u,description.index,n)
+ end
+ end
+ end
+ end
+end
+
+-- class : nil base mark ligature component (maybe we don't need it in description)
+-- boundingbox: split into ht/dp takes more memory (larger tables and less sharing)
+
+actions["analyze glyphs"] = function(data,filename,raw) -- maybe integrate this in the previous
+ local descriptions = data.descriptions
+ local resources = data.resources
+ local metadata = data.metadata
+ local properties = data.properties
+ local hasitalics = false
+ local widths = { }
+ local marks = { } -- always present (saves checking)
+ for unicode, description in next, descriptions do
+ local glyph = description.glyph
+ local italic = glyph.italic_correction
+ if not italic then
+ -- skip
+ elseif italic == 0 then
+ -- skip
+ else
+ description.italic = italic
+ hasitalics = true
+ end
+ local width = glyph.width
+ widths[width] = (widths[width] or 0) + 1
+ local class = glyph.class
+ if class then
+ if class == "mark" then
+ marks[unicode] = true
+ end
+ description.class = class
+ end
+ end
+ -- flag italic
+ properties.hasitalics = hasitalics
+ -- flag marks
+ resources.marks = marks
+ -- share most common width for cjk fonts
+ local wd, most = 0, 1
+ for k,v in next, widths do
+ if v > most then
+ wd, most = k, v
+ end
+ end
+ if most > 1000 then -- maybe 500
+ if trace_loading then
+ report_otf("most common width: %s (%s times), sharing (cjk font)",wd,most)
+ end
+ for unicode, description in next, descriptions do
+ if description.width == wd then
+ -- description.width = nil
+ else
+ description.width = description.glyph.width
+ end
+ end
+ resources.defaultwidth = wd
+ else
+ for unicode, description in next, descriptions do
+ description.width = description.glyph.width
+ end
+ end
+end
+
+actions["reorganize mark classes"] = function(data,filename,raw)
+ local mark_classes = raw.mark_classes
+ if mark_classes then
+ local resources = data.resources
+ local unicodes = resources.unicodes
+ local markclasses = { }
+ resources.markclasses = markclasses -- reversed
+ for name, class in next, mark_classes do
+ local t = { }
+ for s in gmatch(class,"[^ ]+") do
+ t[unicodes[s]] = true
+ end
+ markclasses[name] = t
+ end
+ end
+end
+
+actions["reorganize features"] = function(data,filename,raw) -- combine with other
+ local features = { }
+ data.resources.features = features
+ for k, what in next, otf.glists do
+ local dw = raw[what]
+ if dw then
+ local f = { }
+ features[what] = f
+ for i=1,#dw do
+ local d= dw[i]
+ local dfeatures = d.features
+ if dfeatures then
+ for i=1,#dfeatures do
+ local df = dfeatures[i]
+ local tag = strip(lower(df.tag))
+ local ft = f[tag]
+ if not ft then
+ ft = { }
+ f[tag] = ft
+ end
+ local dscripts = df.scripts
+ for i=1,#dscripts do
+ local d = dscripts[i]
+ local languages = d.langs
+ local script = strip(lower(d.script))
+ local fts = ft[script] if not fts then fts = {} ft[script] = fts end
+ for i=1,#languages do
+ fts[strip(lower(languages[i]))] = true
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+actions["reorganize anchor classes"] = function(data,filename,raw)
+ local resources = data.resources
+ local anchor_to_lookup = { }
+ local lookup_to_anchor = { }
+ resources.anchor_to_lookup = anchor_to_lookup
+ resources.lookup_to_anchor = lookup_to_anchor
+ local classes = raw.anchor_classes -- anchor classes not in final table
+ if classes then
+ for c=1,#classes do
+ local class = classes[c]
+ local anchor = class.name
+ local lookups = class.lookup
+ if type(lookups) ~= "table" then
+ lookups = { lookups }
+ end
+ local a = anchor_to_lookup[anchor]
+ if not a then
+ a = { }
+ anchor_to_lookup[anchor] = a
+ end
+ for l=1,#lookups do
+ local lookup = lookups[l]
+ local l = lookup_to_anchor[lookup]
+ if l then
+ l[anchor] = true
+ else
+ l = { [anchor] = true }
+ lookup_to_anchor[lookup] = l
+ end
+ a[lookup] = true
+ end
+ end
+ end
+end
+
+actions["prepare tounicode"] = function(data,filename,raw)
+ fonts.mappings.addtounicode(data,filename)
+end
+
+local g_directions = {
+ gsub_contextchain = 1,
+ gpos_contextchain = 1,
+ -- gsub_context = 1,
+ -- gpos_context = 1,
+ gsub_reversecontextchain = -1,
+ gpos_reversecontextchain = -1,
+}
+
+-- Research by Khaled Hosny has demonstrated that the font loader merges
+-- regular and AAT features and that these can interfere (especially because
+-- we dropped checking for valid features elsewhere. So, we just check for
+-- the special flag and drop the feature if such a tag is found.
+
+local function supported(features)
+ for i=1,#features do
+ if features[i].ismac then
+ return false
+ end
+ end
+ return true
+end
+
+actions["reorganize subtables"] = function(data,filename,raw)
+ local resources = data.resources
+ local sequences = { }
+ local lookups = { }
+ local chainedfeatures = { }
+ resources.sequences = sequences
+ resources.lookups = lookups
+ for _, what in next, otf.glists do
+ local dw = raw[what]
+ if dw then
+ for k=1,#dw do
+ local gk = dw[k]
+ local features = gk.features
+-- if features and supported(features) then
+ if not features or supported(features) then -- not always features !
+ local typ = gk.type
+ local chain = g_directions[typ] or 0
+ local subtables = gk.subtables
+ if subtables then
+ local t = { }
+ for s=1,#subtables do
+ t[s] = subtables[s].name
+ end
+ subtables = t
+ end
+ local flags, markclass = gk.flags, nil
+ if flags then
+ local t = { -- forcing false packs nicer
+ (flags.ignorecombiningmarks and "mark") or false,
+ (flags.ignoreligatures and "ligature") or false,
+ (flags.ignorebaseglyphs and "base") or false,
+ flags.r2l or false,
+ }
+ markclass = flags.mark_class
+ if markclass then
+ markclass = resources.markclasses[markclass]
+ end
+ flags = t
+ end
+ --
+ local name = gk.name
+ --
+ if not name then
+ -- in fact an error
+ report_otf("skipping weird lookup number %s",k)
+ elseif features then
+ -- scripts, tag, ismac
+ local f = { }
+ for i=1,#features do
+ local df = features[i]
+ local tag = strip(lower(df.tag))
+ local ft = f[tag] if not ft then ft = {} f[tag] = ft end
+ local dscripts = df.scripts
+ for i=1,#dscripts do
+ local d = dscripts[i]
+ local languages = d.langs
+ local script = strip(lower(d.script))
+ local fts = ft[script] if not fts then fts = {} ft[script] = fts end
+ for i=1,#languages do
+ fts[strip(lower(languages[i]))] = true
+ end
+ end
+ end
+ sequences[#sequences+1] = {
+ type = typ,
+ chain = chain,
+ flags = flags,
+ name = name,
+ subtables = subtables,
+ markclass = markclass,
+ features = f,
+ }
+ else
+ lookups[name] = {
+ type = typ,
+ chain = chain,
+ flags = flags,
+ subtables = subtables,
+ markclass = markclass,
+ }
+ end
+ end
+ end
+ end
+ end
+end
+
+-- test this:
+--
+-- for _, what in next, otf.glists do
+-- raw[what] = nil
+-- end
+
+actions["prepare lookups"] = function(data,filename,raw)
+ local lookups = raw.lookups
+ if lookups then
+ data.lookups = lookups
+ end
+end
+
+-- The reverse handler does a bit redundant splitting but it's seldom
+-- seen so we don't bother too much. We could store the replacement
+-- in the current list (value instead of true) but it makes other code
+-- uglier. Maybe some day.
+
+local function t_uncover(splitter,cache,covers)
+ local result = { }
+ for n=1,#covers do
+ local cover = covers[n]
+ local uncovered = cache[cover]
+ if not uncovered then
+ uncovered = lpegmatch(splitter,cover)
+ cache[cover] = uncovered
+ end
+ result[n] = uncovered
+ end
+ return result
+end
+
+local function s_uncover(splitter,cache,cover)
+ if cover == "" then
+ return nil
+ else
+ local uncovered = cache[cover]
+ if not uncovered then
+ uncovered = lpegmatch(splitter,cover)
+-- for i=1,#uncovered do
+-- uncovered[i] = { [uncovered[i]] = true }
+-- end
+ cache[cover] = uncovered
+ end
+ return { uncovered }
+ end
+end
+
+local function t_hashed(t,cache)
+ if t then
+ local ht = { }
+ for i=1,#t do
+ local ti = t[i]
+ local tih = cache[ti]
+ if not tih then
+ tih = { }
+ for i=1,#ti do
+ tih[ti[i]] = true
+ end
+ cache[ti] = tih
+ end
+ ht[i] = tih
+ end
+ return ht
+ else
+ return nil
+ end
+end
+
+-- local s_hashed = t_hashed
+
+local function s_hashed(t,cache)
+ if t then
+ local ht = { }
+ local tf = t[1]
+ for i=1,#tf do
+ ht[i] = { [tf[i]] = true }
+ end
+ return ht
+ else
+ return nil
+ end
+end
+
+local function r_uncover(splitter,cache,cover,replacements)
+ if cover == "" then
+ return nil
+ else
+ -- we always have current as { } even in the case of one
+ local uncovered = cover[1]
+ local replaced = cache[replacements]
+ if not replaced then
+ replaced = lpegmatch(splitter,replacements)
+ cache[replacements] = replaced
+ end
+ local nu, nr = #uncovered, #replaced
+ local r = { }
+ if nu == nr then
+ for i=1,nu do
+ r[uncovered[i]] = replaced[i]
+ end
+ end
+ return r
+ end
+end
+
+actions["reorganize lookups"] = function(data,filename,raw) -- we could check for "" and n == 0
+ -- we prefer the before lookups in a normal order
+ if data.lookups then
+ local splitter = data.helpers.tounicodetable
+ local t_u_cache = { }
+ local s_u_cache = t_u_cache -- string keys
+ local t_h_cache = { }
+ local s_h_cache = t_h_cache -- table keys (so we could use one cache)
+ local r_u_cache = { } -- maybe shared
+ for _, lookup in next, data.lookups do
+ local rules = lookup.rules
+ if rules then
+ local format = lookup.format
+ if format == "class" then
+ local before_class = lookup.before_class
+ if before_class then
+ before_class = t_uncover(splitter,t_u_cache,reversed(before_class))
+ end
+ local current_class = lookup.current_class
+ if current_class then
+ current_class = t_uncover(splitter,t_u_cache,current_class)
+ end
+ local after_class = lookup.after_class
+ if after_class then
+ after_class = t_uncover(splitter,t_u_cache,after_class)
+ end
+ for i=1,#rules do
+ local rule = rules[i]
+ local class = rule.class
+ local before = class.before
+ if before then
+ for i=1,#before do
+ before[i] = before_class[before[i]] or { }
+ end
+ rule.before = t_hashed(before,t_h_cache)
+ end
+ local current = class.current
+ local lookups = rule.lookups
+ if current then
+ for i=1,#current do
+ current[i] = current_class[current[i]] or { }
+ -- let's not be sparse
+ if lookups and not lookups[i] then
+ lookups[i] = "" -- (was: false) e.g. we can have two lookups and one replacement
+ end
+ -- end of fix
+ end
+ rule.current = t_hashed(current,t_h_cache)
+ end
+ local after = class.after
+ if after then
+ for i=1,#after do
+ after[i] = after_class[after[i]] or { }
+ end
+ rule.after = t_hashed(after,t_h_cache)
+ end
+ rule.class = nil
+ end
+ lookup.before_class = nil
+ lookup.current_class = nil
+ lookup.after_class = nil
+ lookup.format = "coverage"
+ elseif format == "coverage" then
+ for i=1,#rules do
+ local rule = rules[i]
+ local coverage = rule.coverage
+ if coverage then
+ local before = coverage.before
+ if before then
+ before = t_uncover(splitter,t_u_cache,reversed(before))
+ rule.before = t_hashed(before,t_h_cache)
+ end
+ local current = coverage.current
+ if current then
+ current = t_uncover(splitter,t_u_cache,current)
+ -- let's not be sparse
+ local lookups = rule.lookups
+ if lookups then
+ for i=1,#current do
+ if not lookups[i] then
+ lookups[i] = "" -- fix sparse array
+ end
+ end
+ end
+ --
+ rule.current = t_hashed(current,t_h_cache)
+ end
+ local after = coverage.after
+ if after then
+ after = t_uncover(splitter,t_u_cache,after)
+ rule.after = t_hashed(after,t_h_cache)
+ end
+ rule.coverage = nil
+ end
+ end
+ elseif format == "reversecoverage" then -- special case, single substitution only
+ for i=1,#rules do
+ local rule = rules[i]
+ local reversecoverage = rule.reversecoverage
+ if reversecoverage then
+ local before = reversecoverage.before
+ if before then
+ before = t_uncover(splitter,t_u_cache,reversed(before))
+ rule.before = t_hashed(before,t_h_cache)
+ end
+ local current = reversecoverage.current
+ if current then
+ current = t_uncover(splitter,t_u_cache,current)
+ rule.current = t_hashed(current,t_h_cache)
+ end
+ local after = reversecoverage.after
+ if after then
+ after = t_uncover(splitter,t_u_cache,after)
+ rule.after = t_hashed(after,t_h_cache)
+ end
+ local replacements = reversecoverage.replacements
+ if replacements then
+ rule.replacements = r_uncover(splitter,r_u_cache,current,replacements)
+ end
+ rule.reversecoverage = nil
+ end
+ end
+ elseif format == "glyphs" then
+ -- I could store these more efficient (as not we use a nested tables for before,
+ -- after and current but this features happens so seldom that I don't bother
+ -- about it right now.
+ for i=1,#rules do
+ local rule = rules[i]
+ local glyphs = rule.glyphs
+ if glyphs then
+ local fore = glyphs.fore
+ if fore and fore ~= "" then
+ fore = s_uncover(splitter,s_u_cache,fore)
+ rule.before = s_hashed(fore,s_h_cache)
+ end
+ local back = glyphs.back
+ if back then
+ back = s_uncover(splitter,s_u_cache,back)
+ rule.after = s_hashed(back,s_h_cache)
+ end
+ local names = glyphs.names
+ if names then
+ names = s_uncover(splitter,s_u_cache,names)
+ rule.current = s_hashed(names,s_h_cache)
+ end
+ rule.glyphs = nil
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+local function check_variants(unicode,the_variants,splitter,unicodes)
+ local variants = the_variants.variants
+ if variants then -- use splitter
+ local glyphs = lpegmatch(splitter,variants)
+ local done = { [unicode] = true }
+ local n = 0
+ for i=1,#glyphs do
+ local g = glyphs[i]
+ if done[g] then
+ report_otf("skipping cyclic reference %U in math variant %U",g,unicode)
+ else
+ if n == 0 then
+ n = 1
+ variants = { g }
+ else
+ n = n + 1
+ variants[n] = g
+ end
+ done[g] = true
+ end
+ end
+ if n == 0 then
+ variants = nil
+ end
+ end
+ local parts = the_variants.parts
+ if parts then
+ local p = #parts
+ if p > 0 then
+ for i=1,p do
+ local pi = parts[i]
+ pi.glyph = unicodes[pi.component] or 0
+ pi.component = nil
+ end
+ else
+ parts = nil
+ end
+ end
+ local italic_correction = the_variants.italic_correction
+ if italic_correction and italic_correction == 0 then
+ italic_correction = nil
+ end
+ return variants, parts, italic_correction
+end
+
+actions["analyze math"] = function(data,filename,raw)
+ if raw.math then
+ data.metadata.math = raw.math
+ local unicodes = data.resources.unicodes
+ local splitter = data.helpers.tounicodetable
+ for unicode, description in next, data.descriptions do
+ local glyph = description.glyph
+ local mathkerns = glyph.mathkern -- singular
+ local horiz_variants = glyph.horiz_variants
+ local vert_variants = glyph.vert_variants
+ local top_accent = glyph.top_accent
+ if mathkerns or horiz_variants or vert_variants or top_accent then
+ local math = { }
+ if top_accent then
+ math.top_accent = top_accent
+ end
+ if mathkerns then
+ for k, v in next, mathkerns do
+ if not next(v) then
+ mathkerns[k] = nil
+ else
+ for k, v in next, v do
+ if v == 0 then
+ k[v] = nil -- height / kern can be zero
+ end
+ end
+ end
+ end
+ math.kerns = mathkerns
+ end
+ if horiz_variants then
+ math.horiz_variants, math.horiz_parts, math.horiz_italic_correction = check_variants(unicode,horiz_variants,splitter,unicodes)
+ end
+ if vert_variants then
+ math.vert_variants, math.vert_parts, math.vert_italic_correction = check_variants(unicode,vert_variants,splitter,unicodes)
+ end
+ local italic_correction = description.italic
+ if italic_correction and italic_correction ~= 0 then
+ math.italic_correction = italic_correction
+ end
+ description.math = math
+ end
+ end
+ end
+end
+
+actions["reorganize glyph kerns"] = function(data,filename,raw)
+ local descriptions = data.descriptions
+ local resources = data.resources
+ local unicodes = resources.unicodes
+ for unicode, description in next, descriptions do
+ local kerns = description.glyph.kerns
+ if kerns then
+ local newkerns = { }
+ for k, kern in next, kerns do
+ local name = kern.char
+ local offset = kern.off
+ local lookup = kern.lookup
+ if name and offset and lookup then
+ local unicode = unicodes[name]
+ if unicode then
+ if type(lookup) == "table" then
+ for l=1,#lookup do
+ local lookup = lookup[l]
+ local lookupkerns = newkerns[lookup]
+ if lookupkerns then
+ lookupkerns[unicode] = offset
+ else
+ newkerns[lookup] = { [unicode] = offset }
+ end
+ end
+ else
+ local lookupkerns = newkerns[lookup]
+ if lookupkerns then
+ lookupkerns[unicode] = offset
+ else
+ newkerns[lookup] = { [unicode] = offset }
+ end
+ end
+ elseif trace_loading then
+ report_otf("problems with unicode %a of kern %a of glyph %U",name,k,unicode)
+ end
+ end
+ end
+ description.kerns = newkerns
+ end
+ end
+end
+
+actions["merge kern classes"] = function(data,filename,raw)
+ local gposlist = raw.gpos
+ if gposlist then
+ local descriptions = data.descriptions
+ local resources = data.resources
+ local unicodes = resources.unicodes
+ local splitter = data.helpers.tounicodetable
+ for gp=1,#gposlist do
+ local gpos = gposlist[gp]
+ local subtables = gpos.subtables
+ if subtables then
+ for s=1,#subtables do
+ local subtable = subtables[s]
+ local kernclass = subtable.kernclass -- name is inconsistent with anchor_classes
+ if kernclass then -- the next one is quite slow
+ local split = { } -- saves time
+ for k=1,#kernclass do
+ local kcl = kernclass[k]
+ local firsts = kcl.firsts
+ local seconds = kcl.seconds
+ local offsets = kcl.offsets
+ local lookups = kcl.lookup -- singular
+ if type(lookups) ~= "table" then
+ lookups = { lookups }
+ end
+ -- if offsets[1] == nil then
+ -- offsets[1] = ""
+ -- end
+ -- we can check the max in the loop
+ -- local maxseconds = getn(seconds)
+ for n, s in next, firsts do
+ split[s] = split[s] or lpegmatch(splitter,s)
+ end
+ local maxseconds = 0
+ for n, s in next, seconds do
+ if n > maxseconds then
+ maxseconds = n
+ end
+ split[s] = split[s] or lpegmatch(splitter,s)
+ end
+ for l=1,#lookups do
+ local lookup = lookups[l]
+ for fk=1,#firsts do -- maxfirsts ?
+ local fv = firsts[fk]
+ local splt = split[fv]
+ if splt then
+ local extrakerns = { }
+ local baseoffset = (fk-1) * maxseconds
+ for sk=2,maxseconds do -- will become 1 based in future luatex
+ local sv = seconds[sk]
+ -- for sk, sv in next, seconds do
+ local splt = split[sv]
+ if splt then -- redundant test
+ local offset = offsets[baseoffset + sk]
+ if offset then
+ for i=1,#splt do
+ extrakerns[splt[i]] = offset
+ end
+ end
+ end
+ end
+ for i=1,#splt do
+ local first_unicode = splt[i]
+ local description = descriptions[first_unicode]
+ if description then
+ local kerns = description.kerns
+ if not kerns then
+ kerns = { } -- unicode indexed !
+ description.kerns = kerns
+ end
+ local lookupkerns = kerns[lookup]
+ if not lookupkerns then
+ lookupkerns = { }
+ kerns[lookup] = lookupkerns
+ end
+ for second_unicode, kern in next, extrakerns do
+ lookupkerns[second_unicode] = kern
+ end
+ elseif trace_loading then
+ report_otf("no glyph data for %U", first_unicode)
+ end
+ end
+ end
+ end
+ end
+ end
+ subtable.kernclass = { }
+ end
+ end
+ end
+ end
+ end
+end
+
+actions["check glyphs"] = function(data,filename,raw)
+ for unicode, description in next, data.descriptions do
+ description.glyph = nil
+ end
+end
+
+-- future versions will remove _
+
+actions["check metadata"] = function(data,filename,raw)
+ local metadata = data.metadata
+ for _, k in next, mainfields do
+ if valid_fields[k] then
+ local v = raw[k]
+ if not metadata[k] then
+ metadata[k] = v
+ end
+ end
+ end
+ -- metadata.pfminfo = raw.pfminfo -- not already done?
+ local ttftables = metadata.ttf_tables
+ if ttftables then
+ for i=1,#ttftables do
+ ttftables[i].data = "deleted"
+ end
+ end
+end
+
+actions["cleanup tables"] = function(data,filename,raw)
+ data.resources.indices = nil -- not needed
+ data.helpers = nil
+end
+
+-- kern: ttf has a table with kerns
+--
+-- Weird, as maxfirst and maxseconds can have holes, first seems to be indexed, but
+-- seconds can start at 2 .. this need to be fixed as getn as well as # are sort of
+-- unpredictable alternatively we could force an [1] if not set (maybe I will do that
+-- anyway).
+
+-- we can share { } as it is never set
+
+--- ligatures have an extra specification.char entry that we don't use
+
+actions["reorganize glyph lookups"] = function(data,filename,raw)
+ local resources = data.resources
+ local unicodes = resources.unicodes
+ local descriptions = data.descriptions
+ local splitter = data.helpers.tounicodelist
+
+ local lookuptypes = resources.lookuptypes
+
+ for unicode, description in next, descriptions do
+ local lookups = description.glyph.lookups
+ if lookups then
+ for tag, lookuplist in next, lookups do
+ for l=1,#lookuplist do
+ local lookup = lookuplist[l]
+ local specification = lookup.specification
+ local lookuptype = lookup.type
+ local lt = lookuptypes[tag]
+ if not lt then
+ lookuptypes[tag] = lookuptype
+ elseif lt ~= lookuptype then
+ report_otf("conflicting lookuptypes, %a points to %a and %a",tag,lt,lookuptype)
+ end
+ if lookuptype == "ligature" then
+ lookuplist[l] = { lpegmatch(splitter,specification.components) }
+ elseif lookuptype == "alternate" then
+ lookuplist[l] = { lpegmatch(splitter,specification.components) }
+ elseif lookuptype == "substitution" then
+ lookuplist[l] = unicodes[specification.variant]
+ elseif lookuptype == "multiple" then
+ lookuplist[l] = { lpegmatch(splitter,specification.components) }
+ elseif lookuptype == "position" then
+ lookuplist[l] = {
+ specification.x or 0,
+ specification.y or 0,
+ specification.h or 0,
+ specification.v or 0
+ }
+ elseif lookuptype == "pair" then
+ local one = specification.offsets[1]
+ local two = specification.offsets[2]
+ local paired = unicodes[specification.paired]
+ if one then
+ if two then
+ lookuplist[l] = { paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0 } }
+ else
+ lookuplist[l] = { paired, { one.x or 0, one.y or 0, one.h or 0, one.v or 0 } }
+ end
+ else
+ if two then
+ lookuplist[l] = { paired, { }, { two.x or 0, two.y or 0, two.h or 0, two.v or 0} } -- maybe nil instead of { }
+ else
+ lookuplist[l] = { paired }
+ end
+ end
+ end
+ end
+ end
+ local slookups, mlookups
+ for tag, lookuplist in next, lookups do
+ if #lookuplist == 1 then
+ if slookups then
+ slookups[tag] = lookuplist[1]
+ else
+ slookups = { [tag] = lookuplist[1] }
+ end
+ else
+ if mlookups then
+ mlookups[tag] = lookuplist
+ else
+ mlookups = { [tag] = lookuplist }
+ end
+ end
+ end
+ if slookups then
+ description.slookups = slookups
+ end
+ if mlookups then
+ description.mlookups = mlookups
+ end
+ end
+ end
+
+end
+
+actions["reorganize glyph anchors"] = function(data,filename,raw) -- when we replace inplace we safe entries
+ local descriptions = data.descriptions
+ for unicode, description in next, descriptions do
+ local anchors = description.glyph.anchors
+ if anchors then
+ for class, data in next, anchors do
+ if class == "baselig" then
+ for tag, specification in next, data do
+ for i=1,#specification do
+ local si = specification[i]
+ specification[i] = { si.x or 0, si.y or 0 }
+ end
+ end
+ else
+ for tag, specification in next, data do
+ data[tag] = { specification.x or 0, specification.y or 0 }
+ end
+ end
+ end
+ description.anchors = anchors
+ end
+ end
+end
+
+-- modes: node, base, none
+
+function otf.setfeatures(tfmdata,features)
+ local okay = constructors.initializefeatures("otf",tfmdata,features,trace_features,report_otf)
+ if okay then
+ return constructors.collectprocessors("otf",tfmdata,features,trace_features,report_otf)
+ else
+ return { } -- will become false
+ end
+end
+
+-- the first version made a top/mid/not extensible table, now we just
+-- pass on the variants data and deal with it in the tfm scaler (there
+-- is no longer an extensible table anyway)
+--
+-- we cannot share descriptions as virtual fonts might extend them (ok,
+-- we could use a cache with a hash
+--
+-- we already assing an empty tabel to characters as we can add for
+-- instance protruding info and loop over characters; one is not supposed
+-- to change descriptions and if one does so one should make a copy!
+
+local function copytotfm(data,cache_id)
+ if data then
+ local metadata = data.metadata
+ local resources = data.resources
+ local properties = derivetable(data.properties)
+ local descriptions = derivetable(data.descriptions)
+ local goodies = derivetable(data.goodies)
+ local characters = { }
+ local parameters = { }
+ local mathparameters = { }
+ --
+ local pfminfo = metadata.pfminfo or { }
+ local resources = data.resources
+ local unicodes = resources.unicodes
+ -- local mode = data.mode or "base"
+ local spaceunits = 500
+ local spacer = "space"
+ local designsize = metadata.designsize or metadata.design_size or 100
+ local mathspecs = metadata.math
+ --
+ if designsize == 0 then
+ designsize = 100
+ end
+ if mathspecs then
+ for name, value in next, mathspecs do
+ mathparameters[name] = value
+ end
+ end
+ for unicode, _ in next, data.descriptions do -- use parent table
+ characters[unicode] = { }
+ end
+ if mathspecs then
+ -- we could move this to the scaler but not that much is saved
+ -- and this is cleaner
+ for unicode, character in next, characters do
+ local d = descriptions[unicode]
+ local m = d.math
+ if m then
+ -- watch out: luatex uses horiz_variants for the parts
+ local variants = m.horiz_variants
+ local parts = m.horiz_parts
+ -- local done = { [unicode] = true }
+ if variants then
+ local c = character
+ for i=1,#variants do
+ local un = variants[i]
+ -- if done[un] then
+ -- -- report_otf("skipping cyclic reference %U in math variant %U",un,unicode)
+ -- else
+ c.next = un
+ c = characters[un]
+ -- done[un] = true
+ -- end
+ end -- c is now last in chain
+ c.horiz_variants = parts
+ elseif parts then
+ character.horiz_variants = parts
+ end
+ local variants = m.vert_variants
+ local parts = m.vert_parts
+ -- local done = { [unicode] = true }
+ if variants then
+ local c = character
+ for i=1,#variants do
+ local un = variants[i]
+ -- if done[un] then
+ -- -- report_otf("skipping cyclic reference %U in math variant %U",un,unicode)
+ -- else
+ c.next = un
+ c = characters[un]
+ -- done[un] = true
+ -- end
+ end -- c is now last in chain
+ c.vert_variants = parts
+ elseif parts then
+ character.vert_variants = parts
+ end
+ local italic_correction = m.vert_italic_correction
+ if italic_correction then
+ character.vert_italic_correction = italic_correction -- was c.
+ end
+ local top_accent = m.top_accent
+ if top_accent then
+ character.top_accent = top_accent
+ end
+ local kerns = m.kerns
+ if kerns then
+ character.mathkerns = kerns
+ end
+ end
+ end
+ end
+ -- end math
+ local monospaced = metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion == "Monospaced")
+ local charwidth = pfminfo.avgwidth -- or unset
+ local italicangle = metadata.italicangle
+ local charxheight = pfminfo.os2_xheight and pfminfo.os2_xheight > 0 and pfminfo.os2_xheight
+ properties.monospaced = monospaced
+ parameters.italicangle = italicangle
+ parameters.charwidth = charwidth
+ parameters.charxheight = charxheight
+ --
+ local space = 0x0020 -- unicodes['space'], unicodes['emdash']
+ local emdash = 0x2014 -- unicodes['space'], unicodes['emdash']
+ if monospaced then
+ if descriptions[space] then
+ spaceunits, spacer = descriptions[space].width, "space"
+ end
+ if not spaceunits and descriptions[emdash] then
+ spaceunits, spacer = descriptions[emdash].width, "emdash"
+ end
+ if not spaceunits and charwidth then
+ spaceunits, spacer = charwidth, "charwidth"
+ end
+ else
+ if descriptions[space] then
+ spaceunits, spacer = descriptions[space].width, "space"
+ end
+ if not spaceunits and descriptions[emdash] then
+ spaceunits, spacer = descriptions[emdash].width/2, "emdash/2"
+ end
+ if not spaceunits and charwidth then
+ spaceunits, spacer = charwidth, "charwidth"
+ end
+ end
+ spaceunits = tonumber(spaceunits) or 500 -- brrr
+ -- we need a runtime lookup because of running from cdrom or zip, brrr (shouldn't we use the basename then?)
+ local filename = constructors.checkedfilename(resources)
+ local fontname = metadata.fontname
+ local fullname = metadata.fullname or fontname
+ local units = metadata.units_per_em or 1000
+ --
+ if units == 0 then -- catch bugs in fonts
+ units = 1000
+ metadata.units_per_em = 1000
+ end
+ --
+ parameters.slant = 0
+ parameters.space = spaceunits -- 3.333 (cmr10)
+ parameters.space_stretch = units/2 -- 500 -- 1.666 (cmr10)
+ parameters.space_shrink = 1*units/3 -- 333 -- 1.111 (cmr10)
+ parameters.x_height = 2*units/5 -- 400
+ parameters.quad = units -- 1000
+ if spaceunits < 2*units/5 then
+ -- todo: warning
+ end
+ if italicangle then
+ parameters.italicangle = italicangle
+ parameters.italicfactor = math.cos(math.rad(90+italicangle))
+ parameters.slant = - math.round(math.tan(italicangle*math.pi/180))
+ end
+ if monospaced then
+ parameters.space_stretch = 0
+ parameters.space_shrink = 0
+ elseif syncspace then --
+ parameters.space_stretch = spaceunits/2
+ parameters.space_shrink = spaceunits/3
+ end
+ parameters.extra_space = parameters.space_shrink -- 1.111 (cmr10)
+ if charxheight then
+ parameters.x_height = charxheight
+ else
+ local x = 0x78 -- unicodes['x']
+ if x then
+ local x = descriptions[x]
+ if x then
+ parameters.x_height = x.height
+ end
+ end
+ end
+ --
+ parameters.designsize = (designsize/10)*65536
+ parameters.ascender = abs(metadata.ascent or 0)
+ parameters.descender = abs(metadata.descent or 0)
+ parameters.units = units
+ --
+ properties.space = spacer
+ properties.encodingbytes = 2
+ properties.format = data.format or fonts.formats[filename] or "opentype"
+ properties.noglyphnames = true
+ properties.filename = filename
+ properties.fontname = fontname
+ properties.fullname = fullname
+ properties.psname = fontname or fullname
+ properties.name = filename or fullname
+ --
+ -- properties.name = specification.name
+ -- properties.sub = specification.sub
+ return {
+ characters = characters,
+ descriptions = descriptions,
+ parameters = parameters,
+ mathparameters = mathparameters,
+ resources = resources,
+ properties = properties,
+ goodies = goodies,
+ }
+ end
+end
+
+local function otftotfm(specification)
+ local cache_id = specification.hash
+ local tfmdata = containers.read(constructors.cache,cache_id)
+ if not tfmdata then
+ local name = specification.name
+ local sub = specification.sub
+ local filename = specification.filename
+ local format = specification.format
+ local features = specification.features.normal
+ local rawdata = otf.load(filename,format,sub,features and features.featurefile)
+ if rawdata and next(rawdata) then
+ rawdata.lookuphash = { }
+ tfmdata = copytotfm(rawdata,cache_id)
+ if tfmdata and next(tfmdata) then
+ -- at this moment no characters are assigned yet, only empty slots
+ local features = constructors.checkedfeatures("otf",features)
+ local shared = tfmdata.shared
+ if not shared then
+ shared = { }
+ tfmdata.shared = shared
+ end
+ shared.rawdata = rawdata
+ -- shared.features = features -- default
+ shared.dynamics = { }
+ -- shared.processes = { }
+ tfmdata.changed = { }
+ shared.features = features
+ shared.processes = otf.setfeatures(tfmdata,features)
+ end
+ end
+ containers.write(constructors.cache,cache_id,tfmdata)
+ end
+ return tfmdata
+end
+
+local function read_from_otf(specification)
+ local tfmdata = otftotfm(specification)
+ if tfmdata then
+ -- this late ? .. needs checking
+ tfmdata.properties.name = specification.name
+ tfmdata.properties.sub = specification.sub
+ --
+ tfmdata = constructors.scale(tfmdata,specification)
+ local allfeatures = tfmdata.shared.features or specification.features.normal
+ constructors.applymanipulators("otf",tfmdata,allfeatures,trace_features,report_otf)
+ constructors.setname(tfmdata,specification) -- only otf?
+ fonts.loggers.register(tfmdata,file.suffix(specification.filename),specification)
+ end
+ return tfmdata
+end
+
+local function checkmathsize(tfmdata,mathsize)
+ local mathdata = tfmdata.shared.rawdata.metadata.math
+ local mathsize = tonumber(mathsize)
+ if mathdata then -- we cannot use mathparameters as luatex will complain
+ local parameters = tfmdata.parameters
+ parameters.scriptpercentage = mathdata.ScriptPercentScaleDown
+ parameters.scriptscriptpercentage = mathdata.ScriptScriptPercentScaleDown
+ parameters.mathsize = mathsize
+ end
+end
+
+registerotffeature {
+ name = "mathsize",
+ description = "apply mathsize specified in the font",
+ initializers = {
+ base = checkmathsize,
+ node = checkmathsize,
+ }
+}
+
+-- helpers
+
+function otf.collectlookups(rawdata,kind,script,language)
+ local sequences = rawdata.resources.sequences
+ if sequences then
+ local featuremap, featurelist = { }, { }
+ for s=1,#sequences do
+ local sequence = sequences[s]
+ local features = sequence.features
+ features = features and features[kind]
+ features = features and (features[script] or features[default] or features[wildcard])
+ features = features and (features[language] or features[default] or features[wildcard])
+ if features then
+ local subtables = sequence.subtables
+ if subtables then
+ for s=1,#subtables do
+ local ss = subtables[s]
+ if not featuremap[s] then
+ featuremap[ss] = true
+ featurelist[#featurelist+1] = ss
+ end
+ end
+ end
+ end
+ end
+ if #featurelist > 0 then
+ return featuremap, featurelist
+ end
+ end
+ return nil, nil
+end
+
+-- readers
+
+local function check_otf(forced,specification,suffix,what)
+ local name = specification.name
+ if forced then
+ name = file.addsuffix(name,suffix,true)
+ end
+ local fullname = findbinfile(name,suffix) or ""
+ if fullname == "" then
+ fullname = fonts.names.getfilename(name,suffix) or ""
+ end
+ if fullname ~= "" then
+ specification.filename = fullname
+ specification.format = what
+ return read_from_otf(specification)
+ end
+end
+
+local function opentypereader(specification,suffix,what)
+ local forced = specification.forced or ""
+ if forced == "otf" then
+ return check_otf(true,specification,forced,"opentype")
+ elseif forced == "ttf" or forced == "ttc" or forced == "dfont" then
+ return check_otf(true,specification,forced,"truetype")
+ else
+ return check_otf(false,specification,suffix,what)
+ end
+end
+
+readers.opentype = opentypereader
+
+local formats = fonts.formats
+
+formats.otf = "opentype"
+formats.ttf = "truetype"
+formats.ttc = "truetype"
+formats.dfont = "truetype"
+
+function readers.otf (specification) return opentypereader(specification,"otf",formats.otf ) end
+function readers.ttf (specification) return opentypereader(specification,"ttf",formats.ttf ) end
+function readers.ttc (specification) return opentypereader(specification,"ttf",formats.ttc ) end
+function readers.dfont(specification) return opentypereader(specification,"ttf",formats.dfont) end
+
+-- this will be overloaded
+
+function otf.scriptandlanguage(tfmdata,attr)
+ local properties = tfmdata.properties
+ return properties.script or "dflt", properties.language or "dflt"
+end
diff --git a/tex/context/base/font-oth.lua b/tex/context/base/font-oth.lua
index 5e2e567da..59dca31d9 100644
--- a/tex/context/base/font-oth.lua
+++ b/tex/context/base/font-oth.lua
@@ -1,51 +1,51 @@
-if not modules then modules = { } end modules ['font-oth'] = {
- version = 1.001,
- comment = "companion to font-oth.lua (helpers)",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local fonts = fonts
-local otf = fonts.handlers.otf
-
--- todo: use nodemode data is available
-
-function otf.getalternate(tfmdata,k,kind,value) -- just initialize nodemode and use that (larger mem print)
- if value then
- local description = tfmdata.descriptions[k]
- if description then
- local slookups = description.slookups -- we assume only slookups (we can always extend)
- if slookups then
- local shared = tfmdata.shared
- local rawdata = shared and shared.rawdata
- if rawdata then
- local lookuptypes = rawdata.resources.lookuptypes
- if lookuptypes then
- local properties = tfmdata.properties
- -- we could cache these
- local validlookups, lookuplist = otf.collectlookups(rawdata,kind,properties.script,properties.language)
- if validlookups then
- local choice = tonumber(value) or 1 -- no random here (yet)
- for l=1,#lookuplist do
- local lookup = lookuplist[l]
- local found = slookups[lookup]
- if found then
- local lookuptype = lookuptypes[lookup]
- if lookuptype == "substitution" then
- return found
- elseif lookuptype == "alternate" then
- return found[choice] or found[#found]
- else
- -- ignore
- end
- end
- end
- end
- end
- end
- end
- end
- end
- return k
-end
+if not modules then modules = { } end modules ['font-oth'] = {
+ version = 1.001,
+ comment = "companion to font-oth.lua (helpers)",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local fonts = fonts
+local otf = fonts.handlers.otf
+
+-- todo: use nodemode data is available
+
+function otf.getalternate(tfmdata,k,kind,value) -- just initialize nodemode and use that (larger mem print)
+ if value then
+ local description = tfmdata.descriptions[k]
+ if description then
+ local slookups = description.slookups -- we assume only slookups (we can always extend)
+ if slookups then
+ local shared = tfmdata.shared
+ local rawdata = shared and shared.rawdata
+ if rawdata then
+ local lookuptypes = rawdata.resources.lookuptypes
+ if lookuptypes then
+ local properties = tfmdata.properties
+ -- we could cache these
+ local validlookups, lookuplist = otf.collectlookups(rawdata,kind,properties.script,properties.language)
+ if validlookups then
+ local choice = tonumber(value) or 1 -- no random here (yet)
+ for l=1,#lookuplist do
+ local lookup = lookuplist[l]
+ local found = slookups[lookup]
+ if found then
+ local lookuptype = lookuptypes[lookup]
+ if lookuptype == "substitution" then
+ return found
+ elseif lookuptype == "alternate" then
+ return found[choice] or found[#found]
+ else
+ -- ignore
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ return k
+end
diff --git a/tex/context/base/font-oti.lua b/tex/context/base/font-oti.lua
index e33b57a6f..06c2a42fa 100644
--- a/tex/context/base/font-oti.lua
+++ b/tex/context/base/font-oti.lua
@@ -1,91 +1,91 @@
-if not modules then modules = { } end modules ['font-oti'] = {
- 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 lower = string.lower
-
-local fonts = fonts
-local constructors = fonts.constructors
-
-local otf = constructors.newhandler("otf")
-local otffeatures = constructors.newfeatures("otf")
-local otftables = otf.tables
-local registerotffeature = otffeatures.register
-
-local allocate = utilities.storage.allocate
-
-registerotffeature {
- name = "features",
- description = "initialization of feature handler",
- default = true,
-}
-
--- these are later hooked into node and base initializaters
-
-local function setmode(tfmdata,value)
- if value then
- tfmdata.properties.mode = lower(value)
- end
-end
-
-local function setlanguage(tfmdata,value)
- if value then
- local cleanvalue = lower(value)
- local languages = otftables and otftables.languages
- local properties = tfmdata.properties
- if not languages then
- properties.language = cleanvalue
- elseif languages[value] then
- properties.language = cleanvalue
- else
- properties.language = "dflt"
- end
- end
-end
-
-local function setscript(tfmdata,value)
- if value then
- local cleanvalue = lower(value)
- local scripts = otftables and otftables.scripts
- local properties = tfmdata.properties
- if not scripts then
- properties.script = cleanvalue
- elseif scripts[value] then
- properties.script = cleanvalue
- else
- properties.script = "dflt"
- end
- end
-end
-
-registerotffeature {
- name = "mode",
- description = "mode",
- initializers = {
- base = setmode,
- node = setmode,
- }
-}
-
-registerotffeature {
- name = "language",
- description = "language",
- initializers = {
- base = setlanguage,
- node = setlanguage,
- }
-}
-
-registerotffeature {
- name = "script",
- description = "script",
- initializers = {
- base = setscript,
- node = setscript,
- }
-}
-
+if not modules then modules = { } end modules ['font-oti'] = {
+ 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 lower = string.lower
+
+local fonts = fonts
+local constructors = fonts.constructors
+
+local otf = constructors.newhandler("otf")
+local otffeatures = constructors.newfeatures("otf")
+local otftables = otf.tables
+local registerotffeature = otffeatures.register
+
+local allocate = utilities.storage.allocate
+
+registerotffeature {
+ name = "features",
+ description = "initialization of feature handler",
+ default = true,
+}
+
+-- these are later hooked into node and base initializaters
+
+local function setmode(tfmdata,value)
+ if value then
+ tfmdata.properties.mode = lower(value)
+ end
+end
+
+local function setlanguage(tfmdata,value)
+ if value then
+ local cleanvalue = lower(value)
+ local languages = otftables and otftables.languages
+ local properties = tfmdata.properties
+ if not languages then
+ properties.language = cleanvalue
+ elseif languages[value] then
+ properties.language = cleanvalue
+ else
+ properties.language = "dflt"
+ end
+ end
+end
+
+local function setscript(tfmdata,value)
+ if value then
+ local cleanvalue = lower(value)
+ local scripts = otftables and otftables.scripts
+ local properties = tfmdata.properties
+ if not scripts then
+ properties.script = cleanvalue
+ elseif scripts[value] then
+ properties.script = cleanvalue
+ else
+ properties.script = "dflt"
+ end
+ end
+end
+
+registerotffeature {
+ name = "mode",
+ description = "mode",
+ initializers = {
+ base = setmode,
+ node = setmode,
+ }
+}
+
+registerotffeature {
+ name = "language",
+ description = "language",
+ initializers = {
+ base = setlanguage,
+ node = setlanguage,
+ }
+}
+
+registerotffeature {
+ name = "script",
+ description = "script",
+ initializers = {
+ base = setscript,
+ node = setscript,
+ }
+}
+
diff --git a/tex/context/base/font-otp.lua b/tex/context/base/font-otp.lua
index f0c2edd86..217bb7535 100644
--- a/tex/context/base/font-otp.lua
+++ b/tex/context/base/font-otp.lua
@@ -1,877 +1,877 @@
-if not modules then modules = { } end modules ['font-otp'] = {
- version = 1.001,
- comment = "companion to font-otf.lua (packing)",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- todo: pack math (but not that much to share)
---
--- pitfall 5.2: hashed tables can suddenly become indexed with nil slots
-
-local next, type = next, type
-local sort, concat = table.sort, table.concat
-local sortedhash = table.sortedhash
-
-local trace_packing = false trackers.register("otf.packing", function(v) trace_packing = v end)
-local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end)
-
-local report_otf = logs.reporter("fonts","otf loading")
-
--- also used in other scripts so we need to check some tables:
-
-fonts = fonts or { }
-
-local handlers = fonts.handlers or { }
-fonts.handlers = handlers
-
-local otf = handlers.otf or { }
-handlers.otf = otf
-
-local enhancers = otf.enhancers or { }
-otf.enhancers = enhancers
-
-local glists = otf.glists or { "gsub", "gpos" }
-otf.glists = glists
-
-local criterium = 1
-local threshold = 0
-
-local function tabstr_normal(t)
- local s = { }
- local n = 0
- for k, v in next, t do
- n = n + 1
- if type(v) == "table" then
- s[n] = k .. ">" .. tabstr_normal(v)
- elseif v == true then
- s[n] = k .. "+" -- "=true"
- elseif v then
- s[n] = k .. "=" .. v
- else
- s[n] = k .. "-" -- "=false"
- end
- end
- if n == 0 then
- return ""
- elseif n == 1 then
- return s[1]
- else
- sort(s) -- costly but needed (occasional wrong hit otherwise)
- return concat(s,",")
- end
-end
-
-local function tabstr_flat(t)
- local s = { }
- local n = 0
- for k, v in next, t do
- n = n + 1
- s[n] = k .. "=" .. v
- end
- if n == 0 then
- return ""
- elseif n == 1 then
- return s[1]
- else
- sort(s) -- costly but needed (occasional wrong hit otherwise)
- return concat(s,",")
- end
-end
-
-local function tabstr_mixed(t) -- indexed
- local s = { }
- local n = #t
- if n == 0 then
- return ""
- elseif n == 1 then
- local k = t[1]
- if k == true then
- return "++" -- we need to distinguish from "true"
- elseif k == false then
- return "--" -- we need to distinguish from "false"
- else
- return tostring(k) -- number or string
- end
- else
- for i=1,n do
- local k = t[i]
- if k == true then
- s[i] = "++" -- we need to distinguish from "true"
- elseif k == false then
- s[i] = "--" -- we need to distinguish from "false"
- else
- s[i] = k -- number or string
- end
- end
- return concat(s,",")
- end
-end
-
-local function tabstr_boolean(t)
- local s = { }
- local n = 0
- for k, v in next, t do
- n = n + 1
- if v then
- s[n] = k .. "+"
- else
- s[n] = k .. "-"
- end
- end
- if n == 0 then
- return ""
- elseif n == 1 then
- return s[1]
- else
- sort(s) -- costly but needed (occasional wrong hit otherwise)
- return concat(s,",")
- end
-end
-
--- tabstr_boolean_x = tabstr_boolean
-
--- tabstr_boolean = function(t)
--- local a = tabstr_normal(t)
--- local b = tabstr_boolean_x(t)
--- print(a)
--- print(b)
--- return b
--- end
-
-local function packdata(data)
- if data then
- -- stripdata(data)
- local h, t, c = { }, { }, { }
- local hh, tt, cc = { }, { }, { }
- local nt, ntt = 0, 0
- local function pack_normal(v)
- local tag = tabstr_normal(v)
- local ht = h[tag]
- if ht then
- c[ht] = c[ht] + 1
- return ht
- else
- nt = nt + 1
- t[nt] = v
- h[tag] = nt
- c[nt] = 1
- return nt
- end
- end
- local function pack_flat(v)
- local tag = tabstr_flat(v)
- local ht = h[tag]
- if ht then
- c[ht] = c[ht] + 1
- return ht
- else
- nt = nt + 1
- t[nt] = v
- h[tag] = nt
- c[nt] = 1
- return nt
- end
- end
- local function pack_boolean(v)
- local tag = tabstr_boolean(v)
- local ht = h[tag]
- if ht then
- c[ht] = c[ht] + 1
- return ht
- else
- nt = nt + 1
- t[nt] = v
- h[tag] = nt
- c[nt] = 1
- return nt
- end
- end
- local function pack_indexed(v)
- local tag = concat(v," ")
- local ht = h[tag]
- if ht then
- c[ht] = c[ht] + 1
- return ht
- else
- nt = nt + 1
- t[nt] = v
- h[tag] = nt
- c[nt] = 1
- return nt
- end
- end
- local function pack_mixed(v)
- local tag = tabstr_mixed(v)
- local ht = h[tag]
- if ht then
- c[ht] = c[ht] + 1
- return ht
- else
- nt = nt + 1
- t[nt] = v
- h[tag] = nt
- c[nt] = 1
- return nt
- end
- end
- local function pack_final(v)
- -- v == number
- if c[v] <= criterium then
- return t[v]
- else
- -- compact hash
- local hv = hh[v]
- if hv then
- return hv
- else
- ntt = ntt + 1
- tt[ntt] = t[v]
- hh[v] = ntt
- cc[ntt] = c[v]
- return ntt
- end
- end
- end
- local function success(stage,pass)
- if nt == 0 then
- if trace_loading or trace_packing then
- report_otf("pack quality: nothing to pack")
- end
- return false
- elseif nt >= threshold then
- local one, two, rest = 0, 0, 0
- if pass == 1 then
- for k,v in next, c do
- if v == 1 then
- one = one + 1
- elseif v == 2 then
- two = two + 1
- else
- rest = rest + 1
- end
- end
- else
- for k,v in next, cc do
- if v > 20 then
- rest = rest + 1
- elseif v > 10 then
- two = two + 1
- else
- one = one + 1
- end
- end
- data.tables = tt
- end
- if trace_loading or trace_packing then
- report_otf("pack quality: stage %s, pass %s, %s packed, 1-10:%s, 11-20:%s, rest:%s (criterium: %s)", stage, pass, one+two+rest, one, two, rest, criterium)
- end
- return true
- else
- if trace_loading or trace_packing then
- report_otf("pack quality: stage %s, pass %s, %s packed, aborting pack (threshold: %s)", stage, pass, nt, threshold)
- end
- return false
- end
- end
- local function packers(pass)
- if pass == 1 then
- return pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed
- else
- return pack_final, pack_final, pack_final, pack_final, pack_final
- end
- end
- local resources = data.resources
- local lookuptypes = resources.lookuptypes
- for pass=1,2 do
- if trace_packing then
- report_otf("start packing: stage 1, pass %s",pass)
- end
- local pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed = packers(pass)
- for unicode, description in next, data.descriptions do
- local boundingbox = description.boundingbox
- if boundingbox then
- description.boundingbox = pack_indexed(boundingbox)
- end
- local slookups = description.slookups
- if slookups then
- for tag, slookup in next, slookups do
- local what = lookuptypes[tag]
- if what == "pair" then
- local t = slookup[2] if t then slookup[2] = pack_indexed(t) end
- local t = slookup[3] if t then slookup[3] = pack_indexed(t) end
- elseif what ~= "substitution" then
- slookups[tag] = pack_indexed(slookup) -- true is new
- end
- end
- end
- local mlookups = description.mlookups
- if mlookups then
- for tag, mlookup in next, mlookups do
- local what = lookuptypes[tag]
- if what == "pair" then
- for i=1,#mlookup do
- local lookup = mlookup[i]
- local t = lookup[2] if t then lookup[2] = pack_indexed(t) end
- local t = lookup[3] if t then lookup[3] = pack_indexed(t) end
- end
- elseif what ~= "substitution" then
- for i=1,#mlookup do
- mlookup[i] = pack_indexed(mlookup[i]) -- true is new
- end
- end
- end
- end
- local kerns = description.kerns
- if kerns then
- for tag, kern in next, kerns do
- kerns[tag] = pack_flat(kern)
- end
- end
- local math = description.math
- if math then
- local kerns = math.kerns
- if kerns then
- for tag, kern in next, kerns do
- kerns[tag] = pack_normal(kern)
- end
- end
- end
- local anchors = description.anchors
- if anchors then
- for what, anchor in next, anchors do
- if what == "baselig" then
- for _, a in next, anchor do
- for k=1,#a do
- a[k] = pack_indexed(a[k])
- end
- end
- else
- for k, v in next, anchor do
- anchor[k] = pack_indexed(v)
- end
- end
- end
- end
- local altuni = description.altuni
- if altuni then
- for i=1,#altuni do
- altuni[i] = pack_flat(altuni[i])
- end
- end
- end
- local lookups = data.lookups
- if lookups then
- for _, lookup in next, lookups do
- local rules = lookup.rules
- if rules then
- for i=1,#rules do
- local rule = rules[i]
- local r = rule.before if r then for i=1,#r do r[i] = pack_boolean(r[i]) end end
- local r = rule.after if r then for i=1,#r do r[i] = pack_boolean(r[i]) end end
- local r = rule.current if r then for i=1,#r do r[i] = pack_boolean(r[i]) end end
- local r = rule.replacements if r then rule.replacements = pack_flat (r) end -- can have holes
- local r = rule.lookups if r then rule.lookups = pack_indexed(r) end -- can have ""
- -- local r = rule.lookups if r then rule.lookups = pack_flat(r) end -- can have holes (already taken care of some cases)
- end
- end
- end
- end
- local anchor_to_lookup = resources.anchor_to_lookup
- if anchor_to_lookup then
- for anchor, lookup in next, anchor_to_lookup do
- anchor_to_lookup[anchor] = pack_normal(lookup)
- end
- end
- local lookup_to_anchor = resources.lookup_to_anchor
- if lookup_to_anchor then
- for lookup, anchor in next, lookup_to_anchor do
- lookup_to_anchor[lookup] = pack_normal(anchor)
- end
- end
- local sequences = resources.sequences
- if sequences then
- for feature, sequence in next, sequences do
- local flags = sequence.flags
- if flags then
- sequence.flags = pack_normal(flags)
- end
- local subtables = sequence.subtables
- if subtables then
- sequence.subtables = pack_normal(subtables)
- end
- local features = sequence.features
- if features then
- for script, feature in next, features do
- features[script] = pack_normal(feature)
- end
- end
- end
- end
- local lookups = resources.lookups
- if lookups then
- for name, lookup in next, lookups do
- local flags = lookup.flags
- if flags then
- lookup.flags = pack_normal(flags)
- end
- local subtables = lookup.subtables
- if subtables then
- lookup.subtables = pack_normal(subtables)
- end
- end
- end
- local features = resources.features
- if features then
- for _, what in next, glists do
- local list = features[what]
- if list then
- for feature, spec in next, list do
- list[feature] = pack_normal(spec)
- end
- end
- end
- end
- if not success(1,pass) then
- return
- end
- end
- if nt > 0 then
- for pass=1,2 do
- if trace_packing then
- report_otf("start packing: stage 2, pass %s",pass)
- end
- local pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed = packers(pass)
- for unicode, description in next, data.descriptions do
- local kerns = description.kerns
- if kerns then
- description.kerns = pack_normal(kerns)
- end
- local math = description.math
- if math then
- local kerns = math.kerns
- if kerns then
- math.kerns = pack_normal(kerns)
- end
- end
- local anchors = description.anchors
- if anchors then
- description.anchors = pack_normal(anchors)
- end
- local mlookups = description.mlookups
- if mlookups then
- for tag, mlookup in next, mlookups do
- mlookups[tag] = pack_normal(mlookup)
- end
- end
- local altuni = description.altuni
- if altuni then
- description.altuni = pack_normal(altuni)
- end
- end
- local lookups = data.lookups
- if lookups then
- for _, lookup in next, lookups do
- local rules = lookup.rules
- if rules then
- for i=1,#rules do -- was next loop
- local rule = rules[i]
- local r = rule.before if r then rule.before = pack_normal(r) end
- local r = rule.after if r then rule.after = pack_normal(r) end
- local r = rule.current if r then rule.current = pack_normal(r) end
- end
- end
- end
- end
- local sequences = resources.sequences
- if sequences then
- for feature, sequence in next, sequences do
- sequence.features = pack_normal(sequence.features)
- end
- end
- if not success(2,pass) then
- -- return
- end
- end
-
- for pass=1,2 do
- local pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed = packers(pass)
- for unicode, description in next, data.descriptions do
- local slookups = description.slookups
- if slookups then
- description.slookups = pack_normal(slookups)
- end
- local mlookups = description.mlookups
- if mlookups then
- description.mlookups = pack_normal(mlookups)
- end
- end
- end
-
- end
- end
-end
-
-local unpacked_mt = {
- __index =
- function(t,k)
- t[k] = false
- return k -- next time true
- end
-}
-
-local function unpackdata(data)
- if data then
- local tables = data.tables
- if tables then
- local resources = data.resources
- local lookuptypes = resources.lookuptypes
- local unpacked = { }
- setmetatable(unpacked,unpacked_mt)
- for unicode, description in next, data.descriptions do
- local tv = tables[description.boundingbox]
- if tv then
- description.boundingbox = tv
- end
- local slookups = description.slookups
- if slookups then
- local tv = tables[slookups]
- if tv then
- description.slookups = tv
- slookups = unpacked[tv]
- end
- if slookups then
- for tag, lookup in next, slookups do
- local what = lookuptypes[tag]
- if what == "pair" then
- local tv = tables[lookup[2]]
- if tv then
- lookup[2] = tv
- end
- local tv = tables[lookup[3]]
- if tv then
- lookup[3] = tv
- end
- elseif what ~= "substitution" then
- local tv = tables[lookup]
- if tv then
- slookups[tag] = tv
- end
- end
- end
- end
- end
- local mlookups = description.mlookups
- if mlookups then
- local tv = tables[mlookups]
- if tv then
- description.mlookups = tv
- mlookups = unpacked[tv]
- end
- if mlookups then
- for tag, list in next, mlookups do
- local tv = tables[list]
- if tv then
- mlookups[tag] = tv
- list = unpacked[tv]
- end
- if list then
- local what = lookuptypes[tag]
- if what == "pair" then
- for i=1,#list do
- local lookup = list[i]
- local tv = tables[lookup[2]]
- if tv then
- lookup[2] = tv
- end
- local tv = tables[lookup[3]]
- if tv then
- lookup[3] = tv
- end
- end
- elseif what ~= "substitution" then
- for i=1,#list do
- local tv = tables[list[i]]
- if tv then
- list[i] = tv
- end
- end
- end
- end
- end
- end
- end
- local kerns = description.kerns
- if kerns then
- local tm = tables[kerns]
- if tm then
- description.kerns = tm
- kerns = unpacked[tm]
- end
- if kerns then
- for k, kern in next, kerns do
- local tv = tables[kern]
- if tv then
- kerns[k] = tv
- end
- end
- end
- end
- local math = description.math
- if math then
- local kerns = math.kerns
- if kerns then
- local tm = tables[kerns]
- if tm then
- math.kerns = tm
- kerns = unpacked[tm]
- end
- if kerns then
- for k, kern in next, kerns do
- local tv = tables[kern]
- if tv then
- kerns[k] = tv
- end
- end
- end
- end
- end
- local anchors = description.anchors
- if anchors then
- local ta = tables[anchors]
- if ta then
- description.anchors = ta
- anchors = unpacked[ta]
- end
- if anchors then
- for tag, anchor in next, anchors do
- if tag == "baselig" then
- for _, list in next, anchor do
- for i=1,#list do
- local tv = tables[list[i]]
- if tv then
- list[i] = tv
- end
- end
- end
- else
- for a, data in next, anchor do
- local tv = tables[data]
- if tv then
- anchor[a] = tv
- end
- end
- end
- end
- end
- end
- local altuni = description.altuni
- if altuni then
- local altuni = tables[altuni]
- if altuni then
- description.altuni = altuni
- for i=1,#altuni do
- local tv = tables[altuni[i]]
- if tv then
- altuni[i] = tv
- end
- end
- end
- end
- end
- local lookups = data.lookups
- if lookups then
- for _, lookup in next, lookups do
- local rules = lookup.rules
- if rules then
- for i=1,#rules do -- was next loop
- local rule = rules[i]
- local before = rule.before
- if before then
- local tv = tables[before]
- if tv then
- rule.before = tv
- before = unpacked[tv]
- end
- if before then
- for i=1,#before do
- local tv = tables[before[i]]
- if tv then
- before[i] = tv
- end
- end
- end
- end
- local after = rule.after
- if after then
- local tv = tables[after]
- if tv then
- rule.after = tv
- after = unpacked[tv]
- end
- if after then
- for i=1,#after do
- local tv = tables[after[i]]
- if tv then
- after[i] = tv
- end
- end
- end
- end
- local current = rule.current
- if current then
- local tv = tables[current]
- if tv then
- rule.current = tv
- current = unpacked[tv]
- end
- if current then
- for i=1,#current do
- local tv = tables[current[i]]
- if tv then
- current[i] = tv
- end
- end
- end
- end
- local replacements = rule.replacements
- if replacements then
- local tv = tables[replacements]
- if tv then
- rule.replacements = tv
- end
- end
- local fore = rule.fore
- if fore then
- local tv = tables[fore]
- if tv then
- rule.fore = tv
- end
- end
- local back = rule.back
- if back then
- local tv = tables[back]
- if tv then
- rule.back = tv
- end
- end
- local names = rule.names
- if names then
- local tv = tables[names]
- if tv then
- rule.names = tv
- end
- end
- local lookups = rule.lookups
- if lookups then
- local tv = tables[lookups]
- if tv then
- rule.lookups = tv
- end
- end
- end
- end
- end
- end
- local anchor_to_lookup = resources.anchor_to_lookup
- if anchor_to_lookup then
- for anchor, lookup in next, anchor_to_lookup do
- local tv = tables[lookup]
- if tv then
- anchor_to_lookup[anchor] = tv
- end
- end
- end
- local lookup_to_anchor = resources.lookup_to_anchor
- if lookup_to_anchor then
- for lookup, anchor in next, lookup_to_anchor do
- local tv = tables[anchor]
- if tv then
- lookup_to_anchor[lookup] = tv
- end
- end
- end
- local ls = resources.sequences
- if ls then
- for _, feature in next, ls do
- local flags = feature.flags
- if flags then
- local tv = tables[flags]
- if tv then
- feature.flags = tv
- end
- end
- local subtables = feature.subtables
- if subtables then
- local tv = tables[subtables]
- if tv then
- feature.subtables = tv
- end
- end
- local features = feature.features
- if features then
- local tv = tables[features]
- if tv then
- feature.features = tv
- features = unpacked[tv]
- end
- if features then
- for script, data in next, features do
- local tv = tables[data]
- if tv then
- features[script] = tv
- end
- end
- end
- end
- end
- end
- local lookups = resources.lookups
- if lookups then
- for _, lookup in next, lookups do
- local flags = lookup.flags
- if flags then
- local tv = tables[flags]
- if tv then
- lookup.flags = tv
- end
- end
- local subtables = lookup.subtables
- if subtables then
- local tv = tables[subtables]
- if tv then
- lookup.subtables = tv
- end
- end
- end
- end
- local features = resources.features
- if features then
- for _, what in next, glists do
- local feature = features[what]
- if feature then
- for tag, spec in next, feature do
- local tv = tables[spec]
- if tv then
- feature[tag] = tv
- end
- end
- end
- end
- end
- data.tables = nil
- end
- end
-end
-
-if otf.enhancers.register then
-
- otf.enhancers.register( "pack", packdata)
- otf.enhancers.register("unpack",unpackdata)
-
--- todo: directive
-
-end
-
-otf.enhancers.unpack = unpackdata -- used elsewhere
+if not modules then modules = { } end modules ['font-otp'] = {
+ version = 1.001,
+ comment = "companion to font-otf.lua (packing)",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo: pack math (but not that much to share)
+--
+-- pitfall 5.2: hashed tables can suddenly become indexed with nil slots
+
+local next, type = next, type
+local sort, concat = table.sort, table.concat
+local sortedhash = table.sortedhash
+
+local trace_packing = false trackers.register("otf.packing", function(v) trace_packing = v end)
+local trace_loading = false trackers.register("otf.loading", function(v) trace_loading = v end)
+
+local report_otf = logs.reporter("fonts","otf loading")
+
+-- also used in other scripts so we need to check some tables:
+
+fonts = fonts or { }
+
+local handlers = fonts.handlers or { }
+fonts.handlers = handlers
+
+local otf = handlers.otf or { }
+handlers.otf = otf
+
+local enhancers = otf.enhancers or { }
+otf.enhancers = enhancers
+
+local glists = otf.glists or { "gsub", "gpos" }
+otf.glists = glists
+
+local criterium = 1
+local threshold = 0
+
+local function tabstr_normal(t)
+ local s = { }
+ local n = 0
+ for k, v in next, t do
+ n = n + 1
+ if type(v) == "table" then
+ s[n] = k .. ">" .. tabstr_normal(v)
+ elseif v == true then
+ s[n] = k .. "+" -- "=true"
+ elseif v then
+ s[n] = k .. "=" .. v
+ else
+ s[n] = k .. "-" -- "=false"
+ end
+ end
+ if n == 0 then
+ return ""
+ elseif n == 1 then
+ return s[1]
+ else
+ sort(s) -- costly but needed (occasional wrong hit otherwise)
+ return concat(s,",")
+ end
+end
+
+local function tabstr_flat(t)
+ local s = { }
+ local n = 0
+ for k, v in next, t do
+ n = n + 1
+ s[n] = k .. "=" .. v
+ end
+ if n == 0 then
+ return ""
+ elseif n == 1 then
+ return s[1]
+ else
+ sort(s) -- costly but needed (occasional wrong hit otherwise)
+ return concat(s,",")
+ end
+end
+
+local function tabstr_mixed(t) -- indexed
+ local s = { }
+ local n = #t
+ if n == 0 then
+ return ""
+ elseif n == 1 then
+ local k = t[1]
+ if k == true then
+ return "++" -- we need to distinguish from "true"
+ elseif k == false then
+ return "--" -- we need to distinguish from "false"
+ else
+ return tostring(k) -- number or string
+ end
+ else
+ for i=1,n do
+ local k = t[i]
+ if k == true then
+ s[i] = "++" -- we need to distinguish from "true"
+ elseif k == false then
+ s[i] = "--" -- we need to distinguish from "false"
+ else
+ s[i] = k -- number or string
+ end
+ end
+ return concat(s,",")
+ end
+end
+
+local function tabstr_boolean(t)
+ local s = { }
+ local n = 0
+ for k, v in next, t do
+ n = n + 1
+ if v then
+ s[n] = k .. "+"
+ else
+ s[n] = k .. "-"
+ end
+ end
+ if n == 0 then
+ return ""
+ elseif n == 1 then
+ return s[1]
+ else
+ sort(s) -- costly but needed (occasional wrong hit otherwise)
+ return concat(s,",")
+ end
+end
+
+-- tabstr_boolean_x = tabstr_boolean
+
+-- tabstr_boolean = function(t)
+-- local a = tabstr_normal(t)
+-- local b = tabstr_boolean_x(t)
+-- print(a)
+-- print(b)
+-- return b
+-- end
+
+local function packdata(data)
+ if data then
+ -- stripdata(data)
+ local h, t, c = { }, { }, { }
+ local hh, tt, cc = { }, { }, { }
+ local nt, ntt = 0, 0
+ local function pack_normal(v)
+ local tag = tabstr_normal(v)
+ local ht = h[tag]
+ if ht then
+ c[ht] = c[ht] + 1
+ return ht
+ else
+ nt = nt + 1
+ t[nt] = v
+ h[tag] = nt
+ c[nt] = 1
+ return nt
+ end
+ end
+ local function pack_flat(v)
+ local tag = tabstr_flat(v)
+ local ht = h[tag]
+ if ht then
+ c[ht] = c[ht] + 1
+ return ht
+ else
+ nt = nt + 1
+ t[nt] = v
+ h[tag] = nt
+ c[nt] = 1
+ return nt
+ end
+ end
+ local function pack_boolean(v)
+ local tag = tabstr_boolean(v)
+ local ht = h[tag]
+ if ht then
+ c[ht] = c[ht] + 1
+ return ht
+ else
+ nt = nt + 1
+ t[nt] = v
+ h[tag] = nt
+ c[nt] = 1
+ return nt
+ end
+ end
+ local function pack_indexed(v)
+ local tag = concat(v," ")
+ local ht = h[tag]
+ if ht then
+ c[ht] = c[ht] + 1
+ return ht
+ else
+ nt = nt + 1
+ t[nt] = v
+ h[tag] = nt
+ c[nt] = 1
+ return nt
+ end
+ end
+ local function pack_mixed(v)
+ local tag = tabstr_mixed(v)
+ local ht = h[tag]
+ if ht then
+ c[ht] = c[ht] + 1
+ return ht
+ else
+ nt = nt + 1
+ t[nt] = v
+ h[tag] = nt
+ c[nt] = 1
+ return nt
+ end
+ end
+ local function pack_final(v)
+ -- v == number
+ if c[v] <= criterium then
+ return t[v]
+ else
+ -- compact hash
+ local hv = hh[v]
+ if hv then
+ return hv
+ else
+ ntt = ntt + 1
+ tt[ntt] = t[v]
+ hh[v] = ntt
+ cc[ntt] = c[v]
+ return ntt
+ end
+ end
+ end
+ local function success(stage,pass)
+ if nt == 0 then
+ if trace_loading or trace_packing then
+ report_otf("pack quality: nothing to pack")
+ end
+ return false
+ elseif nt >= threshold then
+ local one, two, rest = 0, 0, 0
+ if pass == 1 then
+ for k,v in next, c do
+ if v == 1 then
+ one = one + 1
+ elseif v == 2 then
+ two = two + 1
+ else
+ rest = rest + 1
+ end
+ end
+ else
+ for k,v in next, cc do
+ if v > 20 then
+ rest = rest + 1
+ elseif v > 10 then
+ two = two + 1
+ else
+ one = one + 1
+ end
+ end
+ data.tables = tt
+ end
+ if trace_loading or trace_packing then
+ report_otf("pack quality: stage %s, pass %s, %s packed, 1-10:%s, 11-20:%s, rest:%s (criterium: %s)", stage, pass, one+two+rest, one, two, rest, criterium)
+ end
+ return true
+ else
+ if trace_loading or trace_packing then
+ report_otf("pack quality: stage %s, pass %s, %s packed, aborting pack (threshold: %s)", stage, pass, nt, threshold)
+ end
+ return false
+ end
+ end
+ local function packers(pass)
+ if pass == 1 then
+ return pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed
+ else
+ return pack_final, pack_final, pack_final, pack_final, pack_final
+ end
+ end
+ local resources = data.resources
+ local lookuptypes = resources.lookuptypes
+ for pass=1,2 do
+ if trace_packing then
+ report_otf("start packing: stage 1, pass %s",pass)
+ end
+ local pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed = packers(pass)
+ for unicode, description in next, data.descriptions do
+ local boundingbox = description.boundingbox
+ if boundingbox then
+ description.boundingbox = pack_indexed(boundingbox)
+ end
+ local slookups = description.slookups
+ if slookups then
+ for tag, slookup in next, slookups do
+ local what = lookuptypes[tag]
+ if what == "pair" then
+ local t = slookup[2] if t then slookup[2] = pack_indexed(t) end
+ local t = slookup[3] if t then slookup[3] = pack_indexed(t) end
+ elseif what ~= "substitution" then
+ slookups[tag] = pack_indexed(slookup) -- true is new
+ end
+ end
+ end
+ local mlookups = description.mlookups
+ if mlookups then
+ for tag, mlookup in next, mlookups do
+ local what = lookuptypes[tag]
+ if what == "pair" then
+ for i=1,#mlookup do
+ local lookup = mlookup[i]
+ local t = lookup[2] if t then lookup[2] = pack_indexed(t) end
+ local t = lookup[3] if t then lookup[3] = pack_indexed(t) end
+ end
+ elseif what ~= "substitution" then
+ for i=1,#mlookup do
+ mlookup[i] = pack_indexed(mlookup[i]) -- true is new
+ end
+ end
+ end
+ end
+ local kerns = description.kerns
+ if kerns then
+ for tag, kern in next, kerns do
+ kerns[tag] = pack_flat(kern)
+ end
+ end
+ local math = description.math
+ if math then
+ local kerns = math.kerns
+ if kerns then
+ for tag, kern in next, kerns do
+ kerns[tag] = pack_normal(kern)
+ end
+ end
+ end
+ local anchors = description.anchors
+ if anchors then
+ for what, anchor in next, anchors do
+ if what == "baselig" then
+ for _, a in next, anchor do
+ for k=1,#a do
+ a[k] = pack_indexed(a[k])
+ end
+ end
+ else
+ for k, v in next, anchor do
+ anchor[k] = pack_indexed(v)
+ end
+ end
+ end
+ end
+ local altuni = description.altuni
+ if altuni then
+ for i=1,#altuni do
+ altuni[i] = pack_flat(altuni[i])
+ end
+ end
+ end
+ local lookups = data.lookups
+ if lookups then
+ for _, lookup in next, lookups do
+ local rules = lookup.rules
+ if rules then
+ for i=1,#rules do
+ local rule = rules[i]
+ local r = rule.before if r then for i=1,#r do r[i] = pack_boolean(r[i]) end end
+ local r = rule.after if r then for i=1,#r do r[i] = pack_boolean(r[i]) end end
+ local r = rule.current if r then for i=1,#r do r[i] = pack_boolean(r[i]) end end
+ local r = rule.replacements if r then rule.replacements = pack_flat (r) end -- can have holes
+ local r = rule.lookups if r then rule.lookups = pack_indexed(r) end -- can have ""
+ -- local r = rule.lookups if r then rule.lookups = pack_flat(r) end -- can have holes (already taken care of some cases)
+ end
+ end
+ end
+ end
+ local anchor_to_lookup = resources.anchor_to_lookup
+ if anchor_to_lookup then
+ for anchor, lookup in next, anchor_to_lookup do
+ anchor_to_lookup[anchor] = pack_normal(lookup)
+ end
+ end
+ local lookup_to_anchor = resources.lookup_to_anchor
+ if lookup_to_anchor then
+ for lookup, anchor in next, lookup_to_anchor do
+ lookup_to_anchor[lookup] = pack_normal(anchor)
+ end
+ end
+ local sequences = resources.sequences
+ if sequences then
+ for feature, sequence in next, sequences do
+ local flags = sequence.flags
+ if flags then
+ sequence.flags = pack_normal(flags)
+ end
+ local subtables = sequence.subtables
+ if subtables then
+ sequence.subtables = pack_normal(subtables)
+ end
+ local features = sequence.features
+ if features then
+ for script, feature in next, features do
+ features[script] = pack_normal(feature)
+ end
+ end
+ end
+ end
+ local lookups = resources.lookups
+ if lookups then
+ for name, lookup in next, lookups do
+ local flags = lookup.flags
+ if flags then
+ lookup.flags = pack_normal(flags)
+ end
+ local subtables = lookup.subtables
+ if subtables then
+ lookup.subtables = pack_normal(subtables)
+ end
+ end
+ end
+ local features = resources.features
+ if features then
+ for _, what in next, glists do
+ local list = features[what]
+ if list then
+ for feature, spec in next, list do
+ list[feature] = pack_normal(spec)
+ end
+ end
+ end
+ end
+ if not success(1,pass) then
+ return
+ end
+ end
+ if nt > 0 then
+ for pass=1,2 do
+ if trace_packing then
+ report_otf("start packing: stage 2, pass %s",pass)
+ end
+ local pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed = packers(pass)
+ for unicode, description in next, data.descriptions do
+ local kerns = description.kerns
+ if kerns then
+ description.kerns = pack_normal(kerns)
+ end
+ local math = description.math
+ if math then
+ local kerns = math.kerns
+ if kerns then
+ math.kerns = pack_normal(kerns)
+ end
+ end
+ local anchors = description.anchors
+ if anchors then
+ description.anchors = pack_normal(anchors)
+ end
+ local mlookups = description.mlookups
+ if mlookups then
+ for tag, mlookup in next, mlookups do
+ mlookups[tag] = pack_normal(mlookup)
+ end
+ end
+ local altuni = description.altuni
+ if altuni then
+ description.altuni = pack_normal(altuni)
+ end
+ end
+ local lookups = data.lookups
+ if lookups then
+ for _, lookup in next, lookups do
+ local rules = lookup.rules
+ if rules then
+ for i=1,#rules do -- was next loop
+ local rule = rules[i]
+ local r = rule.before if r then rule.before = pack_normal(r) end
+ local r = rule.after if r then rule.after = pack_normal(r) end
+ local r = rule.current if r then rule.current = pack_normal(r) end
+ end
+ end
+ end
+ end
+ local sequences = resources.sequences
+ if sequences then
+ for feature, sequence in next, sequences do
+ sequence.features = pack_normal(sequence.features)
+ end
+ end
+ if not success(2,pass) then
+ -- return
+ end
+ end
+
+ for pass=1,2 do
+ local pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed = packers(pass)
+ for unicode, description in next, data.descriptions do
+ local slookups = description.slookups
+ if slookups then
+ description.slookups = pack_normal(slookups)
+ end
+ local mlookups = description.mlookups
+ if mlookups then
+ description.mlookups = pack_normal(mlookups)
+ end
+ end
+ end
+
+ end
+ end
+end
+
+local unpacked_mt = {
+ __index =
+ function(t,k)
+ t[k] = false
+ return k -- next time true
+ end
+}
+
+local function unpackdata(data)
+ if data then
+ local tables = data.tables
+ if tables then
+ local resources = data.resources
+ local lookuptypes = resources.lookuptypes
+ local unpacked = { }
+ setmetatable(unpacked,unpacked_mt)
+ for unicode, description in next, data.descriptions do
+ local tv = tables[description.boundingbox]
+ if tv then
+ description.boundingbox = tv
+ end
+ local slookups = description.slookups
+ if slookups then
+ local tv = tables[slookups]
+ if tv then
+ description.slookups = tv
+ slookups = unpacked[tv]
+ end
+ if slookups then
+ for tag, lookup in next, slookups do
+ local what = lookuptypes[tag]
+ if what == "pair" then
+ local tv = tables[lookup[2]]
+ if tv then
+ lookup[2] = tv
+ end
+ local tv = tables[lookup[3]]
+ if tv then
+ lookup[3] = tv
+ end
+ elseif what ~= "substitution" then
+ local tv = tables[lookup]
+ if tv then
+ slookups[tag] = tv
+ end
+ end
+ end
+ end
+ end
+ local mlookups = description.mlookups
+ if mlookups then
+ local tv = tables[mlookups]
+ if tv then
+ description.mlookups = tv
+ mlookups = unpacked[tv]
+ end
+ if mlookups then
+ for tag, list in next, mlookups do
+ local tv = tables[list]
+ if tv then
+ mlookups[tag] = tv
+ list = unpacked[tv]
+ end
+ if list then
+ local what = lookuptypes[tag]
+ if what == "pair" then
+ for i=1,#list do
+ local lookup = list[i]
+ local tv = tables[lookup[2]]
+ if tv then
+ lookup[2] = tv
+ end
+ local tv = tables[lookup[3]]
+ if tv then
+ lookup[3] = tv
+ end
+ end
+ elseif what ~= "substitution" then
+ for i=1,#list do
+ local tv = tables[list[i]]
+ if tv then
+ list[i] = tv
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ local kerns = description.kerns
+ if kerns then
+ local tm = tables[kerns]
+ if tm then
+ description.kerns = tm
+ kerns = unpacked[tm]
+ end
+ if kerns then
+ for k, kern in next, kerns do
+ local tv = tables[kern]
+ if tv then
+ kerns[k] = tv
+ end
+ end
+ end
+ end
+ local math = description.math
+ if math then
+ local kerns = math.kerns
+ if kerns then
+ local tm = tables[kerns]
+ if tm then
+ math.kerns = tm
+ kerns = unpacked[tm]
+ end
+ if kerns then
+ for k, kern in next, kerns do
+ local tv = tables[kern]
+ if tv then
+ kerns[k] = tv
+ end
+ end
+ end
+ end
+ end
+ local anchors = description.anchors
+ if anchors then
+ local ta = tables[anchors]
+ if ta then
+ description.anchors = ta
+ anchors = unpacked[ta]
+ end
+ if anchors then
+ for tag, anchor in next, anchors do
+ if tag == "baselig" then
+ for _, list in next, anchor do
+ for i=1,#list do
+ local tv = tables[list[i]]
+ if tv then
+ list[i] = tv
+ end
+ end
+ end
+ else
+ for a, data in next, anchor do
+ local tv = tables[data]
+ if tv then
+ anchor[a] = tv
+ end
+ end
+ end
+ end
+ end
+ end
+ local altuni = description.altuni
+ if altuni then
+ local altuni = tables[altuni]
+ if altuni then
+ description.altuni = altuni
+ for i=1,#altuni do
+ local tv = tables[altuni[i]]
+ if tv then
+ altuni[i] = tv
+ end
+ end
+ end
+ end
+ end
+ local lookups = data.lookups
+ if lookups then
+ for _, lookup in next, lookups do
+ local rules = lookup.rules
+ if rules then
+ for i=1,#rules do -- was next loop
+ local rule = rules[i]
+ local before = rule.before
+ if before then
+ local tv = tables[before]
+ if tv then
+ rule.before = tv
+ before = unpacked[tv]
+ end
+ if before then
+ for i=1,#before do
+ local tv = tables[before[i]]
+ if tv then
+ before[i] = tv
+ end
+ end
+ end
+ end
+ local after = rule.after
+ if after then
+ local tv = tables[after]
+ if tv then
+ rule.after = tv
+ after = unpacked[tv]
+ end
+ if after then
+ for i=1,#after do
+ local tv = tables[after[i]]
+ if tv then
+ after[i] = tv
+ end
+ end
+ end
+ end
+ local current = rule.current
+ if current then
+ local tv = tables[current]
+ if tv then
+ rule.current = tv
+ current = unpacked[tv]
+ end
+ if current then
+ for i=1,#current do
+ local tv = tables[current[i]]
+ if tv then
+ current[i] = tv
+ end
+ end
+ end
+ end
+ local replacements = rule.replacements
+ if replacements then
+ local tv = tables[replacements]
+ if tv then
+ rule.replacements = tv
+ end
+ end
+ local fore = rule.fore
+ if fore then
+ local tv = tables[fore]
+ if tv then
+ rule.fore = tv
+ end
+ end
+ local back = rule.back
+ if back then
+ local tv = tables[back]
+ if tv then
+ rule.back = tv
+ end
+ end
+ local names = rule.names
+ if names then
+ local tv = tables[names]
+ if tv then
+ rule.names = tv
+ end
+ end
+ local lookups = rule.lookups
+ if lookups then
+ local tv = tables[lookups]
+ if tv then
+ rule.lookups = tv
+ end
+ end
+ end
+ end
+ end
+ end
+ local anchor_to_lookup = resources.anchor_to_lookup
+ if anchor_to_lookup then
+ for anchor, lookup in next, anchor_to_lookup do
+ local tv = tables[lookup]
+ if tv then
+ anchor_to_lookup[anchor] = tv
+ end
+ end
+ end
+ local lookup_to_anchor = resources.lookup_to_anchor
+ if lookup_to_anchor then
+ for lookup, anchor in next, lookup_to_anchor do
+ local tv = tables[anchor]
+ if tv then
+ lookup_to_anchor[lookup] = tv
+ end
+ end
+ end
+ local ls = resources.sequences
+ if ls then
+ for _, feature in next, ls do
+ local flags = feature.flags
+ if flags then
+ local tv = tables[flags]
+ if tv then
+ feature.flags = tv
+ end
+ end
+ local subtables = feature.subtables
+ if subtables then
+ local tv = tables[subtables]
+ if tv then
+ feature.subtables = tv
+ end
+ end
+ local features = feature.features
+ if features then
+ local tv = tables[features]
+ if tv then
+ feature.features = tv
+ features = unpacked[tv]
+ end
+ if features then
+ for script, data in next, features do
+ local tv = tables[data]
+ if tv then
+ features[script] = tv
+ end
+ end
+ end
+ end
+ end
+ end
+ local lookups = resources.lookups
+ if lookups then
+ for _, lookup in next, lookups do
+ local flags = lookup.flags
+ if flags then
+ local tv = tables[flags]
+ if tv then
+ lookup.flags = tv
+ end
+ end
+ local subtables = lookup.subtables
+ if subtables then
+ local tv = tables[subtables]
+ if tv then
+ lookup.subtables = tv
+ end
+ end
+ end
+ end
+ local features = resources.features
+ if features then
+ for _, what in next, glists do
+ local feature = features[what]
+ if feature then
+ for tag, spec in next, feature do
+ local tv = tables[spec]
+ if tv then
+ feature[tag] = tv
+ end
+ end
+ end
+ end
+ end
+ data.tables = nil
+ end
+ end
+end
+
+if otf.enhancers.register then
+
+ otf.enhancers.register( "pack", packdata)
+ otf.enhancers.register("unpack",unpackdata)
+
+-- todo: directive
+
+end
+
+otf.enhancers.unpack = unpackdata -- used elsewhere
diff --git a/tex/context/base/font-ott.lua b/tex/context/base/font-ott.lua
index 3b171c4a4..e3aacd0d1 100644
--- a/tex/context/base/font-ott.lua
+++ b/tex/context/base/font-ott.lua
@@ -1,1113 +1,1113 @@
-if not modules then modules = { } end modules ['font-ott'] = {
- version = 1.001,
- comment = "companion to font-otf.lua (tables)",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files",
- -- dataonly = true,
-}
-
-local type, next, tonumber, tostring, rawget, rawset = type, next, tonumber, tostring, rawget, rawset
-local gsub, lower, format, match = string.gsub, string.lower, string.format, string.match
-local is_boolean = string.is_boolean
-
-local setmetatableindex = table.setmetatableindex
-local setmetatablenewindex = table.setmetatablenewindex
-local allocate = utilities.storage.allocate
-
-local fonts = fonts
-local otf = fonts.handlers.otf
-local otffeatures = otf.features
-local registerotffeature = otffeatures.register
-
-local tables = otf.tables or { }
-otf.tables = tables
-
-local statistics = otf.statistics or { }
-otf.statistics = statistics
-
-local scripts = allocate {
- ['arab'] = 'arabic',
- ['armn'] = 'armenian',
- ['bali'] = 'balinese',
- ['beng'] = 'bengali',
- ['bopo'] = 'bopomofo',
- ['brai'] = 'braille',
- ['bugi'] = 'buginese',
- ['buhd'] = 'buhid',
- ['byzm'] = 'byzantine music',
- ['cans'] = 'canadian syllabics',
- ['cher'] = 'cherokee',
- ['copt'] = 'coptic',
- ['cprt'] = 'cypriot syllabary',
- ['cyrl'] = 'cyrillic',
- ['deva'] = 'devanagari',
- ['dsrt'] = 'deseret',
- ['ethi'] = 'ethiopic',
- ['geor'] = 'georgian',
- ['glag'] = 'glagolitic',
- ['goth'] = 'gothic',
- ['grek'] = 'greek',
- ['gujr'] = 'gujarati',
- ['guru'] = 'gurmukhi',
- ['hang'] = 'hangul',
- ['hani'] = 'cjk ideographic',
- ['hano'] = 'hanunoo',
- ['hebr'] = 'hebrew',
- ['ital'] = 'old italic',
- ['jamo'] = 'hangul jamo',
- ['java'] = 'javanese',
- ['kana'] = 'hiragana and katakana',
- ['khar'] = 'kharosthi',
- ['khmr'] = 'khmer',
- ['knda'] = 'kannada',
- ['lao' ] = 'lao',
- ['latn'] = 'latin',
- ['limb'] = 'limbu',
- ['linb'] = 'linear b',
- ['math'] = 'mathematical alphanumeric symbols',
- ['mlym'] = 'malayalam',
- ['mong'] = 'mongolian',
- ['musc'] = 'musical symbols',
- ['mymr'] = 'myanmar',
- ['nko' ] = "n'ko",
- ['ogam'] = 'ogham',
- ['orya'] = 'oriya',
- ['osma'] = 'osmanya',
- ['phag'] = 'phags-pa',
- ['phnx'] = 'phoenician',
- ['runr'] = 'runic',
- ['shaw'] = 'shavian',
- ['sinh'] = 'sinhala',
- ['sylo'] = 'syloti nagri',
- ['syrc'] = 'syriac',
- ['tagb'] = 'tagbanwa',
- ['tale'] = 'tai le',
- ['talu'] = 'tai lu',
- ['taml'] = 'tamil',
- ['telu'] = 'telugu',
- ['tfng'] = 'tifinagh',
- ['tglg'] = 'tagalog',
- ['thaa'] = 'thaana',
- ['thai'] = 'thai',
- ['tibt'] = 'tibetan',
- ['ugar'] = 'ugaritic cuneiform',
- ['xpeo'] = 'old persian cuneiform',
- ['xsux'] = 'sumero-akkadian cuneiform',
- ['yi' ] = 'yi',
-}
-
-local languages = allocate {
- ['aba'] = 'abaza',
- ['abk'] = 'abkhazian',
- ['ady'] = 'adyghe',
- ['afk'] = 'afrikaans',
- ['afr'] = 'afar',
- ['agw'] = 'agaw',
- ['als'] = 'alsatian',
- ['alt'] = 'altai',
- ['amh'] = 'amharic',
- ['ara'] = 'arabic',
- ['ari'] = 'aari',
- ['ark'] = 'arakanese',
- ['asm'] = 'assamese',
- ['ath'] = 'athapaskan',
- ['avr'] = 'avar',
- ['awa'] = 'awadhi',
- ['aym'] = 'aymara',
- ['aze'] = 'azeri',
- ['bad'] = 'badaga',
- ['bag'] = 'baghelkhandi',
- ['bal'] = 'balkar',
- ['bau'] = 'baule',
- ['bbr'] = 'berber',
- ['bch'] = 'bench',
- ['bcr'] = 'bible cree',
- ['bel'] = 'belarussian',
- ['bem'] = 'bemba',
- ['ben'] = 'bengali',
- ['bgr'] = 'bulgarian',
- ['bhi'] = 'bhili',
- ['bho'] = 'bhojpuri',
- ['bik'] = 'bikol',
- ['bil'] = 'bilen',
- ['bkf'] = 'blackfoot',
- ['bli'] = 'balochi',
- ['bln'] = 'balante',
- ['blt'] = 'balti',
- ['bmb'] = 'bambara',
- ['bml'] = 'bamileke',
- ['bos'] = 'bosnian',
- ['bre'] = 'breton',
- ['brh'] = 'brahui',
- ['bri'] = 'braj bhasha',
- ['brm'] = 'burmese',
- ['bsh'] = 'bashkir',
- ['bti'] = 'beti',
- ['cat'] = 'catalan',
- ['ceb'] = 'cebuano',
- ['che'] = 'chechen',
- ['chg'] = 'chaha gurage',
- ['chh'] = 'chattisgarhi',
- ['chi'] = 'chichewa',
- ['chk'] = 'chukchi',
- ['chp'] = 'chipewyan',
- ['chr'] = 'cherokee',
- ['chu'] = 'chuvash',
- ['cmr'] = 'comorian',
- ['cop'] = 'coptic',
- ['cos'] = 'corsican',
- ['cre'] = 'cree',
- ['crr'] = 'carrier',
- ['crt'] = 'crimean tatar',
- ['csl'] = 'church slavonic',
- ['csy'] = 'czech',
- ['dan'] = 'danish',
- ['dar'] = 'dargwa',
- ['dcr'] = 'woods cree',
- ['deu'] = 'german',
- ['dgr'] = 'dogri',
- ['div'] = 'divehi',
- ['djr'] = 'djerma',
- ['dng'] = 'dangme',
- ['dnk'] = 'dinka',
- ['dri'] = 'dari',
- ['dun'] = 'dungan',
- ['dzn'] = 'dzongkha',
- ['ebi'] = 'ebira',
- ['ecr'] = 'eastern cree',
- ['edo'] = 'edo',
- ['efi'] = 'efik',
- ['ell'] = 'greek',
- ['eng'] = 'english',
- ['erz'] = 'erzya',
- ['esp'] = 'spanish',
- ['eti'] = 'estonian',
- ['euq'] = 'basque',
- ['evk'] = 'evenki',
- ['evn'] = 'even',
- ['ewe'] = 'ewe',
- ['fan'] = 'french antillean',
- ['far'] = 'farsi',
- ['fin'] = 'finnish',
- ['fji'] = 'fijian',
- ['fle'] = 'flemish',
- ['fne'] = 'forest nenets',
- ['fon'] = 'fon',
- ['fos'] = 'faroese',
- ['fra'] = 'french',
- ['fri'] = 'frisian',
- ['frl'] = 'friulian',
- ['fta'] = 'futa',
- ['ful'] = 'fulani',
- ['gad'] = 'ga',
- ['gae'] = 'gaelic',
- ['gag'] = 'gagauz',
- ['gal'] = 'galician',
- ['gar'] = 'garshuni',
- ['gaw'] = 'garhwali',
- ['gez'] = "ge'ez",
- ['gil'] = 'gilyak',
- ['gmz'] = 'gumuz',
- ['gon'] = 'gondi',
- ['grn'] = 'greenlandic',
- ['gro'] = 'garo',
- ['gua'] = 'guarani',
- ['guj'] = 'gujarati',
- ['hai'] = 'haitian',
- ['hal'] = 'halam',
- ['har'] = 'harauti',
- ['hau'] = 'hausa',
- ['haw'] = 'hawaiin',
- ['hbn'] = 'hammer-banna',
- ['hil'] = 'hiligaynon',
- ['hin'] = 'hindi',
- ['hma'] = 'high mari',
- ['hnd'] = 'hindko',
- ['ho'] = 'ho',
- ['hri'] = 'harari',
- ['hrv'] = 'croatian',
- ['hun'] = 'hungarian',
- ['hye'] = 'armenian',
- ['ibo'] = 'igbo',
- ['ijo'] = 'ijo',
- ['ilo'] = 'ilokano',
- ['ind'] = 'indonesian',
- ['ing'] = 'ingush',
- ['inu'] = 'inuktitut',
- ['iri'] = 'irish',
- ['irt'] = 'irish traditional',
- ['isl'] = 'icelandic',
- ['ism'] = 'inari sami',
- ['ita'] = 'italian',
- ['iwr'] = 'hebrew',
- ['jan'] = 'japanese',
- ['jav'] = 'javanese',
- ['jii'] = 'yiddish',
- ['jud'] = 'judezmo',
- ['jul'] = 'jula',
- ['kab'] = 'kabardian',
- ['kac'] = 'kachchi',
- ['kal'] = 'kalenjin',
- ['kan'] = 'kannada',
- ['kar'] = 'karachay',
- ['kat'] = 'georgian',
- ['kaz'] = 'kazakh',
- ['keb'] = 'kebena',
- ['kge'] = 'khutsuri georgian',
- ['kha'] = 'khakass',
- ['khk'] = 'khanty-kazim',
- ['khm'] = 'khmer',
- ['khs'] = 'khanty-shurishkar',
- ['khv'] = 'khanty-vakhi',
- ['khw'] = 'khowar',
- ['kik'] = 'kikuyu',
- ['kir'] = 'kirghiz',
- ['kis'] = 'kisii',
- ['kkn'] = 'kokni',
- ['klm'] = 'kalmyk',
- ['kmb'] = 'kamba',
- ['kmn'] = 'kumaoni',
- ['kmo'] = 'komo',
- ['kms'] = 'komso',
- ['knr'] = 'kanuri',
- ['kod'] = 'kodagu',
- ['koh'] = 'korean old hangul',
- ['kok'] = 'konkani',
- ['kon'] = 'kikongo',
- ['kop'] = 'komi-permyak',
- ['kor'] = 'korean',
- ['koz'] = 'komi-zyrian',
- ['kpl'] = 'kpelle',
- ['kri'] = 'krio',
- ['krk'] = 'karakalpak',
- ['krl'] = 'karelian',
- ['krm'] = 'karaim',
- ['krn'] = 'karen',
- ['krt'] = 'koorete',
- ['ksh'] = 'kashmiri',
- ['ksi'] = 'khasi',
- ['ksm'] = 'kildin sami',
- ['kui'] = 'kui',
- ['kul'] = 'kulvi',
- ['kum'] = 'kumyk',
- ['kur'] = 'kurdish',
- ['kuu'] = 'kurukh',
- ['kuy'] = 'kuy',
- ['kyk'] = 'koryak',
- ['lad'] = 'ladin',
- ['lah'] = 'lahuli',
- ['lak'] = 'lak',
- ['lam'] = 'lambani',
- ['lao'] = 'lao',
- ['lat'] = 'latin',
- ['laz'] = 'laz',
- ['lcr'] = 'l-cree',
- ['ldk'] = 'ladakhi',
- ['lez'] = 'lezgi',
- ['lin'] = 'lingala',
- ['lma'] = 'low mari',
- ['lmb'] = 'limbu',
- ['lmw'] = 'lomwe',
- ['lsb'] = 'lower sorbian',
- ['lsm'] = 'lule sami',
- ['lth'] = 'lithuanian',
- ['ltz'] = 'luxembourgish',
- ['lub'] = 'luba',
- ['lug'] = 'luganda',
- ['luh'] = 'luhya',
- ['luo'] = 'luo',
- ['lvi'] = 'latvian',
- ['maj'] = 'majang',
- ['mak'] = 'makua',
- ['mal'] = 'malayalam traditional',
- ['man'] = 'mansi',
- ['map'] = 'mapudungun',
- ['mar'] = 'marathi',
- ['maw'] = 'marwari',
- ['mbn'] = 'mbundu',
- ['mch'] = 'manchu',
- ['mcr'] = 'moose cree',
- ['mde'] = 'mende',
- ['men'] = "me'en",
- ['miz'] = 'mizo',
- ['mkd'] = 'macedonian',
- ['mle'] = 'male',
- ['mlg'] = 'malagasy',
- ['mln'] = 'malinke',
- ['mlr'] = 'malayalam reformed',
- ['mly'] = 'malay',
- ['mnd'] = 'mandinka',
- ['mng'] = 'mongolian',
- ['mni'] = 'manipuri',
- ['mnk'] = 'maninka',
- ['mnx'] = 'manx gaelic',
- ['moh'] = 'mohawk',
- ['mok'] = 'moksha',
- ['mol'] = 'moldavian',
- ['mon'] = 'mon',
- ['mor'] = 'moroccan',
- ['mri'] = 'maori',
- ['mth'] = 'maithili',
- ['mts'] = 'maltese',
- ['mun'] = 'mundari',
- ['nag'] = 'naga-assamese',
- ['nan'] = 'nanai',
- ['nas'] = 'naskapi',
- ['ncr'] = 'n-cree',
- ['ndb'] = 'ndebele',
- ['ndg'] = 'ndonga',
- ['nep'] = 'nepali',
- ['new'] = 'newari',
- ['ngr'] = 'nagari',
- ['nhc'] = 'norway house cree',
- ['nis'] = 'nisi',
- ['niu'] = 'niuean',
- ['nkl'] = 'nkole',
- ['nko'] = "n'ko",
- ['nld'] = 'dutch',
- ['nog'] = 'nogai',
- ['nor'] = 'norwegian',
- ['nsm'] = 'northern sami',
- ['nta'] = 'northern tai',
- ['nto'] = 'esperanto',
- ['nyn'] = 'nynorsk',
- ['oci'] = 'occitan',
- ['ocr'] = 'oji-cree',
- ['ojb'] = 'ojibway',
- ['ori'] = 'oriya',
- ['oro'] = 'oromo',
- ['oss'] = 'ossetian',
- ['paa'] = 'palestinian aramaic',
- ['pal'] = 'pali',
- ['pan'] = 'punjabi',
- ['pap'] = 'palpa',
- ['pas'] = 'pashto',
- ['pgr'] = 'polytonic greek',
- ['pil'] = 'pilipino',
- ['plg'] = 'palaung',
- ['plk'] = 'polish',
- ['pro'] = 'provencal',
- ['ptg'] = 'portuguese',
- ['qin'] = 'chin',
- ['raj'] = 'rajasthani',
- ['rbu'] = 'russian buriat',
- ['rcr'] = 'r-cree',
- ['ria'] = 'riang',
- ['rms'] = 'rhaeto-romanic',
- ['rom'] = 'romanian',
- ['roy'] = 'romany',
- ['rsy'] = 'rusyn',
- ['rua'] = 'ruanda',
- ['rus'] = 'russian',
- ['sad'] = 'sadri',
- ['san'] = 'sanskrit',
- ['sat'] = 'santali',
- ['say'] = 'sayisi',
- ['sek'] = 'sekota',
- ['sel'] = 'selkup',
- ['sgo'] = 'sango',
- ['shn'] = 'shan',
- ['sib'] = 'sibe',
- ['sid'] = 'sidamo',
- ['sig'] = 'silte gurage',
- ['sks'] = 'skolt sami',
- ['sky'] = 'slovak',
- ['sla'] = 'slavey',
- ['slv'] = 'slovenian',
- ['sml'] = 'somali',
- ['smo'] = 'samoan',
- ['sna'] = 'sena',
- ['snd'] = 'sindhi',
- ['snh'] = 'sinhalese',
- ['snk'] = 'soninke',
- ['sog'] = 'sodo gurage',
- ['sot'] = 'sotho',
- ['sqi'] = 'albanian',
- ['srb'] = 'serbian',
- ['srk'] = 'saraiki',
- ['srr'] = 'serer',
- ['ssl'] = 'south slavey',
- ['ssm'] = 'southern sami',
- ['sur'] = 'suri',
- ['sva'] = 'svan',
- ['sve'] = 'swedish',
- ['swa'] = 'swadaya aramaic',
- ['swk'] = 'swahili',
- ['swz'] = 'swazi',
- ['sxt'] = 'sutu',
- ['syr'] = 'syriac',
- ['tab'] = 'tabasaran',
- ['taj'] = 'tajiki',
- ['tam'] = 'tamil',
- ['tat'] = 'tatar',
- ['tcr'] = 'th-cree',
- ['tel'] = 'telugu',
- ['tgn'] = 'tongan',
- ['tgr'] = 'tigre',
- ['tgy'] = 'tigrinya',
- ['tha'] = 'thai',
- ['tht'] = 'tahitian',
- ['tib'] = 'tibetan',
- ['tkm'] = 'turkmen',
- ['tmn'] = 'temne',
- ['tna'] = 'tswana',
- ['tne'] = 'tundra nenets',
- ['tng'] = 'tonga',
- ['tod'] = 'todo',
- ['trk'] = 'turkish',
- ['tsg'] = 'tsonga',
- ['tua'] = 'turoyo aramaic',
- ['tul'] = 'tulu',
- ['tuv'] = 'tuvin',
- ['twi'] = 'twi',
- ['udm'] = 'udmurt',
- ['ukr'] = 'ukrainian',
- ['urd'] = 'urdu',
- ['usb'] = 'upper sorbian',
- ['uyg'] = 'uyghur',
- ['uzb'] = 'uzbek',
- ['ven'] = 'venda',
- ['vit'] = 'vietnamese',
- ['wa' ] = 'wa',
- ['wag'] = 'wagdi',
- ['wcr'] = 'west-cree',
- ['wel'] = 'welsh',
- ['wlf'] = 'wolof',
- ['xbd'] = 'tai lue',
- ['xhs'] = 'xhosa',
- ['yak'] = 'yakut',
- ['yba'] = 'yoruba',
- ['ycr'] = 'y-cree',
- ['yic'] = 'yi classic',
- ['yim'] = 'yi modern',
- ['zhh'] = 'chinese hong kong',
- ['zhp'] = 'chinese phonetic',
- ['zhs'] = 'chinese simplified',
- ['zht'] = 'chinese traditional',
- ['znd'] = 'zande',
- ['zul'] = 'zulu'
-}
-
-local features = allocate {
- ['aalt'] = 'access all alternates',
- ['abvf'] = 'above-base forms',
- ['abvm'] = 'above-base mark positioning',
- ['abvs'] = 'above-base substitutions',
- ['afrc'] = 'alternative fractions',
- ['akhn'] = 'akhands',
- ['blwf'] = 'below-base forms',
- ['blwm'] = 'below-base mark positioning',
- ['blws'] = 'below-base substitutions',
- ['c2pc'] = 'petite capitals from capitals',
- ['c2sc'] = 'small capitals from capitals',
- ['calt'] = 'contextual alternates',
- ['case'] = 'case-sensitive forms',
- ['ccmp'] = 'glyph composition/decomposition',
- ['cjct'] = 'conjunct forms',
- ['clig'] = 'contextual ligatures',
- ['cpsp'] = 'capital spacing',
- ['cswh'] = 'contextual swash',
- ['curs'] = 'cursive positioning',
- ['dflt'] = 'default processing',
- ['dist'] = 'distances',
- ['dlig'] = 'discretionary ligatures',
- ['dnom'] = 'denominators',
- ['dtls'] = 'dotless forms', -- math
- ['expt'] = 'expert forms',
- ['falt'] = 'final glyph alternates',
- ['fin2'] = 'terminal forms #2',
- ['fin3'] = 'terminal forms #3',
- ['fina'] = 'terminal forms',
- ['flac'] = 'flattened accents over capitals', -- math
- ['frac'] = 'fractions',
- ['fwid'] = 'full width',
- ['half'] = 'half forms',
- ['haln'] = 'halant forms',
- ['halt'] = 'alternate half width',
- ['hist'] = 'historical forms',
- ['hkna'] = 'horizontal kana alternates',
- ['hlig'] = 'historical ligatures',
- ['hngl'] = 'hangul',
- ['hojo'] = 'hojo kanji forms',
- ['hwid'] = 'half width',
- ['init'] = 'initial forms',
- ['isol'] = 'isolated forms',
- ['ital'] = 'italics',
- ['jalt'] = 'justification alternatives',
- ['jp04'] = 'jis2004 forms',
- ['jp78'] = 'jis78 forms',
- ['jp83'] = 'jis83 forms',
- ['jp90'] = 'jis90 forms',
- ['kern'] = 'kerning',
- ['lfbd'] = 'left bounds',
- ['liga'] = 'standard ligatures',
- ['ljmo'] = 'leading jamo forms',
- ['lnum'] = 'lining figures',
- ['locl'] = 'localized forms',
- ['mark'] = 'mark positioning',
- ['med2'] = 'medial forms #2',
- ['medi'] = 'medial forms',
- ['mgrk'] = 'mathematical greek',
- ['mkmk'] = 'mark to mark positioning',
- ['mset'] = 'mark positioning via substitution',
- ['nalt'] = 'alternate annotation forms',
- ['nlck'] = 'nlc kanji forms',
- ['nukt'] = 'nukta forms',
- ['numr'] = 'numerators',
- ['onum'] = 'old style figures',
- ['opbd'] = 'optical bounds',
- ['ordn'] = 'ordinals',
- ['ornm'] = 'ornaments',
- ['palt'] = 'proportional alternate width',
- ['pcap'] = 'petite capitals',
- ['pnum'] = 'proportional figures',
- ['pref'] = 'pre-base forms',
- ['pres'] = 'pre-base substitutions',
- ['pstf'] = 'post-base forms',
- ['psts'] = 'post-base substitutions',
- ['pwid'] = 'proportional widths',
- ['qwid'] = 'quarter widths',
- ['rand'] = 'randomize',
- ['rkrf'] = 'rakar forms',
- ['rlig'] = 'required ligatures',
- ['rphf'] = 'reph form',
- ['rtbd'] = 'right bounds',
- ['rtla'] = 'right-to-left alternates',
- ['rtlm'] = 'right to left math', -- math
- ['ruby'] = 'ruby notation forms',
- ['salt'] = 'stylistic alternates',
- ['sinf'] = 'scientific inferiors',
- ['size'] = 'optical size',
- ['smcp'] = 'small capitals',
- ['smpl'] = 'simplified forms',
- -- ['ss01'] = 'stylistic set 1',
- -- ['ss02'] = 'stylistic set 2',
- -- ['ss03'] = 'stylistic set 3',
- -- ['ss04'] = 'stylistic set 4',
- -- ['ss05'] = 'stylistic set 5',
- -- ['ss06'] = 'stylistic set 6',
- -- ['ss07'] = 'stylistic set 7',
- -- ['ss08'] = 'stylistic set 8',
- -- ['ss09'] = 'stylistic set 9',
- -- ['ss10'] = 'stylistic set 10',
- -- ['ss11'] = 'stylistic set 11',
- -- ['ss12'] = 'stylistic set 12',
- -- ['ss13'] = 'stylistic set 13',
- -- ['ss14'] = 'stylistic set 14',
- -- ['ss15'] = 'stylistic set 15',
- -- ['ss16'] = 'stylistic set 16',
- -- ['ss17'] = 'stylistic set 17',
- -- ['ss18'] = 'stylistic set 18',
- -- ['ss19'] = 'stylistic set 19',
- -- ['ss20'] = 'stylistic set 20',
- ['ssty'] = 'script style', -- math
- ['subs'] = 'subscript',
- ['sups'] = 'superscript',
- ['swsh'] = 'swash',
- ['titl'] = 'titling',
- ['tjmo'] = 'trailing jamo forms',
- ['tnam'] = 'traditional name forms',
- ['tnum'] = 'tabular figures',
- ['trad'] = 'traditional forms',
- ['twid'] = 'third widths',
- ['unic'] = 'unicase',
- ['valt'] = 'alternate vertical metrics',
- ['vatu'] = 'vattu variants',
- ['vert'] = 'vertical writing',
- ['vhal'] = 'alternate vertical half metrics',
- ['vjmo'] = 'vowel jamo forms',
- ['vkna'] = 'vertical kana alternates',
- ['vkrn'] = 'vertical kerning',
- ['vpal'] = 'proportional alternate vertical metrics',
- ['vrt2'] = 'vertical rotation',
- ['zero'] = 'slashed zero',
-
- ['trep'] = 'traditional tex replacements',
- ['tlig'] = 'traditional tex ligatures',
-
- ['ss..'] = 'stylistic set ..',
- ['cv..'] = 'character variant ..',
- ['js..'] = 'justification ..',
-
- ["dv.."] = "devanagari ..",
-}
-
-local baselines = allocate {
- ['hang'] = 'hanging baseline',
- ['icfb'] = 'ideographic character face bottom edge baseline',
- ['icft'] = 'ideographic character face tope edige baseline',
- ['ideo'] = 'ideographic em-box bottom edge baseline',
- ['idtp'] = 'ideographic em-box top edge baseline',
- ['math'] = 'mathmatical centered baseline',
- ['romn'] = 'roman baseline'
-}
-
-tables.scripts = scripts
-tables.languages = languages
-tables.features = features
-tables.baselines = baselines
-
-local acceptscripts = true directives.register("otf.acceptscripts", function(v) acceptscripts = v end)
-local acceptlanguages = true directives.register("otf.acceptlanguages", function(v) acceptlanguages = v end)
-
-local report_checks = logs.reporter("fonts","checks")
-
--- hm, we overload the metatables
-
-if otffeatures.features then
- for k, v in next, otffeatures.features do
- features[k] = v
- end
- otffeatures.features = features
-end
-
-local function swapped(h)
- local r = { }
- for k, v in next, h do
- r[gsub(v,"[^a-z0-9]","")] = k -- is already lower
- end
- return r
-end
-
-local verbosescripts = allocate(swapped(scripts ))
-local verboselanguages = allocate(swapped(languages))
-local verbosefeatures = allocate(swapped(features ))
-local verbosebaselines = allocate(swapped(baselines))
-
--- lets forget about trailing spaces
-
-local function resolve(t,k)
- if k then
- k = gsub(lower(k),"[^a-z0-9]","")
- local v = rawget(t,k)
- if v then
- return v
- end
- end
-end
-
-setmetatableindex(verbosescripts, resolve)
-setmetatableindex(verboselanguages, resolve)
-setmetatableindex(verbosefeatures, resolve)
-setmetatableindex(verbosebaselines, resolve)
-
--- We could optimize the next lookups by using an extra metatable and storing
--- already found values but in practice there are not that many lookups so
--- it's never a bottleneck.
-
-setmetatableindex(scripts, function(t,k)
- if k then
- k = lower(k)
- if k == "dflt" then
- return k
- end
- local v = rawget(t,k)
- if v then
- return v
- end
- k = gsub(k," ","")
- v = rawget(t,v)
- if v then
- return v
- elseif acceptscripts then
- report_checks("registering extra script %a",k)
- rawset(t,k,k)
- return k
- end
- end
- return "dflt"
-end)
-
-setmetatableindex(languages, function(t,k)
- if k then
- k = lower(k)
- if k == "dflt" then
- return k
- end
- local v = rawget(t,k)
- if v then
- return v
- end
- k = gsub(k," ","")
- v = rawget(t,v)
- if v then
- return v
- elseif acceptlanguages then
- report_checks("registering extra language %a",k)
- rawset(t,k,k)
- return k
- end
- end
- return "dflt"
-end)
-
-setmetatablenewindex(languages, "ignore")
-setmetatablenewindex(baselines, "ignore")
-setmetatablenewindex(baselines, "ignore")
-
-local function resolve(t,k)
- if k then
- k = lower(k)
- local v = rawget(t,k)
- if v then
- return v
- end
- k = gsub(k," ","")
- local v = rawget(t,k)
- if v then
- return v
- end
- local tag, dd = match(k,"(..)(%d+)")
- if tag and dd then
- local v = rawget(t,tag)
- if v then
- return v -- return format(v,tonumber(dd)) -- old way
- else
- local v = rawget(t,tag.."..") -- nicer in overview
- if v then
- return (gsub(v,"%.%.",tonumber(dd))) -- new way
- end
- end
- end
- end
- return k -- "dflt"
-end
-
-setmetatableindex(features, resolve)
-
-local function assign(t,k,v)
- if k and v then
- v = lower(v)
- rawset(t,k,v) -- rawset ?
- -- rawset(features,gsub(v,"[^a-z0-9]",""),k) -- why ? old code
- end
-end
-
-setmetatablenewindex(features, assign)
-
-local checkers = {
- rand = function(v)
- return v == true and "random" or v
- end
-}
-
--- Keep this:
---
--- function otf.features.normalize(features)
--- if features then
--- local h = { }
--- for k, v in next, features do
--- k = lower(k)
--- if k == "language" then
--- v = gsub(lower(v),"[^a-z0-9]","")
--- h.language = rawget(verboselanguages,v) or (languages[v] and v) or "dflt" -- auto adds
--- elseif k == "script" then
--- v = gsub(lower(v),"[^a-z0-9]","")
--- h.script = rawget(verbosescripts,v) or (scripts[v] and v) or "dflt" -- auto adds
--- else
--- if type(v) == "string" then
--- local b = is_boolean(v)
--- if type(b) == "nil" then
--- v = tonumber(v) or lower(v)
--- else
--- v = b
--- end
--- end
--- if not rawget(features,k) then
--- k = rawget(verbosefeatures,k) or k
--- end
--- local c = checkers[k]
--- h[k] = c and c(v) or v
--- end
--- end
--- return h
--- end
--- end
-
--- inspect(fonts.handlers.otf.statistics.usedfeatures)
-
-if not storage then
- return
-end
-
-local usedfeatures = statistics.usedfeatures or { }
-statistics.usedfeatures = usedfeatures
-
-table.setmetatableindex(usedfeatures, function(t,k) if k then local v = { } t[k] = v return v end end) -- table.autotable
-
-storage.register("fonts/otf/usedfeatures", usedfeatures, "fonts.handlers.otf.statistics.usedfeatures" )
-
-function otf.features.normalize(features)
- if features then
- local h = { }
- for key, value in next, features do
- local k = lower(key)
- if k == "language" then
- local v = gsub(lower(value),"[^a-z0-9]","")
- h.language = rawget(verboselanguages,v) or (languages[v] and v) or "dflt" -- auto adds
- elseif k == "script" then
- local v = gsub(lower(value),"[^a-z0-9]","")
- h.script = rawget(verbosescripts,v) or (scripts[v] and v) or "dflt" -- auto adds
- else
- local uk = usedfeatures[key]
- local uv = uk[value]
- if uv then
- -- report_checks("feature value %a first seen at %a",value,key)
- else
- if type(value) == "string" then
- local b = is_boolean(value)
- if type(b) == "nil" then
- uv = tonumber(value) or lower(value)
- else
- uv = b
- end
- else
- uv = v
- end
- if not rawget(features,k) then
- k = rawget(verbosefeatures,k) or k
- end
- local c = checkers[k]
- if c then
- uv = c(uv) or vc
- end
- uk[value] = uv
- end
- h[k] = uv
- end
- end
- return h
- end
-end
-
---~ table.print(otf.features.normalize({ language = "dutch", liga = "yes", ss99 = true, aalt = 3, abcd = "yes" } ))
-
--- When I feel the need ...
-
---~ tables.aat = {
---~ [ 0] = {
---~ name = "allTypographicFeaturesType",
---~ [ 0] = "allTypeFeaturesOnSelector",
---~ [ 1] = "allTypeFeaturesOffSelector",
---~ },
---~ [ 1] = {
---~ name = "ligaturesType",
---~ [0 ] = "requiredLigaturesOnSelector",
---~ [1 ] = "requiredLigaturesOffSelector",
---~ [2 ] = "commonLigaturesOnSelector",
---~ [3 ] = "commonLigaturesOffSelector",
---~ [4 ] = "rareLigaturesOnSelector",
---~ [5 ] = "rareLigaturesOffSelector",
---~ [6 ] = "logosOnSelector ",
---~ [7 ] = "logosOffSelector ",
---~ [8 ] = "rebusPicturesOnSelector",
---~ [9 ] = "rebusPicturesOffSelector",
---~ [10] = "diphthongLigaturesOnSelector",
---~ [11] = "diphthongLigaturesOffSelector",
---~ [12] = "squaredLigaturesOnSelector",
---~ [13] = "squaredLigaturesOffSelector",
---~ [14] = "abbrevSquaredLigaturesOnSelector",
---~ [15] = "abbrevSquaredLigaturesOffSelector",
---~ },
---~ [ 2] = {
---~ name = "cursiveConnectionType",
---~ [ 0] = "unconnectedSelector",
---~ [ 1] = "partiallyConnectedSelector",
---~ [ 2] = "cursiveSelector ",
---~ },
---~ [ 3] = {
---~ name = "letterCaseType",
---~ [ 0] = "upperAndLowerCaseSelector",
---~ [ 1] = "allCapsSelector ",
---~ [ 2] = "allLowerCaseSelector",
---~ [ 3] = "smallCapsSelector ",
---~ [ 4] = "initialCapsSelector",
---~ [ 5] = "initialCapsAndSmallCapsSelector",
---~ },
---~ [ 4] = {
---~ name = "verticalSubstitutionType",
---~ [ 0] = "substituteVerticalFormsOnSelector",
---~ [ 1] = "substituteVerticalFormsOffSelector",
---~ },
---~ [ 5] = {
---~ name = "linguisticRearrangementType",
---~ [ 0] = "linguisticRearrangementOnSelector",
---~ [ 1] = "linguisticRearrangementOffSelector",
---~ },
---~ [ 6] = {
---~ name = "numberSpacingType",
---~ [ 0] = "monospacedNumbersSelector",
---~ [ 1] = "proportionalNumbersSelector",
---~ },
---~ [ 7] = {
---~ name = "appleReserved1Type",
---~ },
---~ [ 8] = {
---~ name = "smartSwashType",
---~ [ 0] = "wordInitialSwashesOnSelector",
---~ [ 1] = "wordInitialSwashesOffSelector",
---~ [ 2] = "wordFinalSwashesOnSelector",
---~ [ 3] = "wordFinalSwashesOffSelector",
---~ [ 4] = "lineInitialSwashesOnSelector",
---~ [ 5] = "lineInitialSwashesOffSelector",
---~ [ 6] = "lineFinalSwashesOnSelector",
---~ [ 7] = "lineFinalSwashesOffSelector",
---~ [ 8] = "nonFinalSwashesOnSelector",
---~ [ 9] = "nonFinalSwashesOffSelector",
---~ },
---~ [ 9] = {
---~ name = "diacriticsType",
---~ [ 0] = "showDiacriticsSelector",
---~ [ 1] = "hideDiacriticsSelector",
---~ [ 2] = "decomposeDiacriticsSelector",
---~ },
---~ [10] = {
---~ name = "verticalPositionType",
---~ [ 0] = "normalPositionSelector",
---~ [ 1] = "superiorsSelector ",
---~ [ 2] = "inferiorsSelector ",
---~ [ 3] = "ordinalsSelector ",
---~ },
---~ [11] = {
---~ name = "fractionsType",
---~ [ 0] = "noFractionsSelector",
---~ [ 1] = "verticalFractionsSelector",
---~ [ 2] = "diagonalFractionsSelector",
---~ },
---~ [12] = {
---~ name = "appleReserved2Type",
---~ },
---~ [13] = {
---~ name = "overlappingCharactersType",
---~ [ 0] = "preventOverlapOnSelector",
---~ [ 1] = "preventOverlapOffSelector",
---~ },
---~ [14] = {
---~ name = "typographicExtrasType",
---~ [0 ] = "hyphensToEmDashOnSelector",
---~ [1 ] = "hyphensToEmDashOffSelector",
---~ [2 ] = "hyphenToEnDashOnSelector",
---~ [3 ] = "hyphenToEnDashOffSelector",
---~ [4 ] = "unslashedZeroOnSelector",
---~ [5 ] = "unslashedZeroOffSelector",
---~ [6 ] = "formInterrobangOnSelector",
---~ [7 ] = "formInterrobangOffSelector",
---~ [8 ] = "smartQuotesOnSelector",
---~ [9 ] = "smartQuotesOffSelector",
---~ [10] = "periodsToEllipsisOnSelector",
---~ [11] = "periodsToEllipsisOffSelector",
---~ },
---~ [15] = {
---~ name = "mathematicalExtrasType",
---~ [ 0] = "hyphenToMinusOnSelector",
---~ [ 1] = "hyphenToMinusOffSelector",
---~ [ 2] = "asteriskToMultiplyOnSelector",
---~ [ 3] = "asteriskToMultiplyOffSelector",
---~ [ 4] = "slashToDivideOnSelector",
---~ [ 5] = "slashToDivideOffSelector",
---~ [ 6] = "inequalityLigaturesOnSelector",
---~ [ 7] = "inequalityLigaturesOffSelector",
---~ [ 8] = "exponentsOnSelector",
---~ [ 9] = "exponentsOffSelector",
---~ },
---~ [16] = {
---~ name = "ornamentSetsType",
---~ [ 0] = "noOrnamentsSelector",
---~ [ 1] = "dingbatsSelector ",
---~ [ 2] = "piCharactersSelector",
---~ [ 3] = "fleuronsSelector ",
---~ [ 4] = "decorativeBordersSelector",
---~ [ 5] = "internationalSymbolsSelector",
---~ [ 6] = "mathSymbolsSelector",
---~ },
---~ [17] = {
---~ name = "characterAlternativesType",
---~ [ 0] = "noAlternatesSelector",
---~ },
---~ [18] = {
---~ name = "designComplexityType",
---~ [ 0] = "designLevel1Selector",
---~ [ 1] = "designLevel2Selector",
---~ [ 2] = "designLevel3Selector",
---~ [ 3] = "designLevel4Selector",
---~ [ 4] = "designLevel5Selector",
---~ },
---~ [19] = {
---~ name = "styleOptionsType",
---~ [ 0] = "noStyleOptionsSelector",
---~ [ 1] = "displayTextSelector",
---~ [ 2] = "engravedTextSelector",
---~ [ 3] = "illuminatedCapsSelector",
---~ [ 4] = "titlingCapsSelector",
---~ [ 5] = "tallCapsSelector ",
---~ },
---~ [20] = {
---~ name = "characterShapeType",
---~ [0 ] = "traditionalCharactersSelector",
---~ [1 ] = "simplifiedCharactersSelector",
---~ [2 ] = "jis1978CharactersSelector",
---~ [3 ] = "jis1983CharactersSelector",
---~ [4 ] = "jis1990CharactersSelector",
---~ [5 ] = "traditionalAltOneSelector",
---~ [6 ] = "traditionalAltTwoSelector",
---~ [7 ] = "traditionalAltThreeSelector",
---~ [8 ] = "traditionalAltFourSelector",
---~ [9 ] = "traditionalAltFiveSelector",
---~ [10] = "expertCharactersSelector",
---~ },
---~ [21] = {
---~ name = "numberCaseType",
---~ [ 0] = "lowerCaseNumbersSelector",
---~ [ 1] = "upperCaseNumbersSelector",
---~ },
---~ [22] = {
---~ name = "textSpacingType",
---~ [ 0] = "proportionalTextSelector",
---~ [ 1] = "monospacedTextSelector",
---~ [ 2] = "halfWidthTextSelector",
---~ [ 3] = "normallySpacedTextSelector",
---~ },
---~ [23] = {
---~ name = "transliterationType",
---~ [ 0] = "noTransliterationSelector",
---~ [ 1] = "hanjaToHangulSelector",
---~ [ 2] = "hiraganaToKatakanaSelector",
---~ [ 3] = "katakanaToHiraganaSelector",
---~ [ 4] = "kanaToRomanizationSelector",
---~ [ 5] = "romanizationToHiraganaSelector",
---~ [ 6] = "romanizationToKatakanaSelector",
---~ [ 7] = "hanjaToHangulAltOneSelector",
---~ [ 8] = "hanjaToHangulAltTwoSelector",
---~ [ 9] = "hanjaToHangulAltThreeSelector",
---~ },
---~ [24] = {
---~ name = "annotationType",
---~ [ 0] = "noAnnotationSelector",
---~ [ 1] = "boxAnnotationSelector",
---~ [ 2] = "roundedBoxAnnotationSelector",
---~ [ 3] = "circleAnnotationSelector",
---~ [ 4] = "invertedCircleAnnotationSelector",
---~ [ 5] = "parenthesisAnnotationSelector",
---~ [ 6] = "periodAnnotationSelector",
---~ [ 7] = "romanNumeralAnnotationSelector",
---~ [ 8] = "diamondAnnotationSelector",
---~ },
---~ [25] = {
---~ name = "kanaSpacingType",
---~ [ 0] = "fullWidthKanaSelector",
---~ [ 1] = "proportionalKanaSelector",
---~ },
---~ [26] = {
---~ name = "ideographicSpacingType",
---~ [ 0] = "fullWidthIdeographsSelector",
---~ [ 1] = "proportionalIdeographsSelector",
---~ },
---~ [103] = {
---~ name = "cjkRomanSpacingType",
---~ [ 0] = "halfWidthCJKRomanSelector",
---~ [ 1] = "proportionalCJKRomanSelector",
---~ [ 2] = "defaultCJKRomanSelector",
---~ [ 3] = "fullWidthCJKRomanSelector",
---~ },
---~ }
+if not modules then modules = { } end modules ['font-ott'] = {
+ version = 1.001,
+ comment = "companion to font-otf.lua (tables)",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+ -- dataonly = true,
+}
+
+local type, next, tonumber, tostring, rawget, rawset = type, next, tonumber, tostring, rawget, rawset
+local gsub, lower, format, match = string.gsub, string.lower, string.format, string.match
+local is_boolean = string.is_boolean
+
+local setmetatableindex = table.setmetatableindex
+local setmetatablenewindex = table.setmetatablenewindex
+local allocate = utilities.storage.allocate
+
+local fonts = fonts
+local otf = fonts.handlers.otf
+local otffeatures = otf.features
+local registerotffeature = otffeatures.register
+
+local tables = otf.tables or { }
+otf.tables = tables
+
+local statistics = otf.statistics or { }
+otf.statistics = statistics
+
+local scripts = allocate {
+ ['arab'] = 'arabic',
+ ['armn'] = 'armenian',
+ ['bali'] = 'balinese',
+ ['beng'] = 'bengali',
+ ['bopo'] = 'bopomofo',
+ ['brai'] = 'braille',
+ ['bugi'] = 'buginese',
+ ['buhd'] = 'buhid',
+ ['byzm'] = 'byzantine music',
+ ['cans'] = 'canadian syllabics',
+ ['cher'] = 'cherokee',
+ ['copt'] = 'coptic',
+ ['cprt'] = 'cypriot syllabary',
+ ['cyrl'] = 'cyrillic',
+ ['deva'] = 'devanagari',
+ ['dsrt'] = 'deseret',
+ ['ethi'] = 'ethiopic',
+ ['geor'] = 'georgian',
+ ['glag'] = 'glagolitic',
+ ['goth'] = 'gothic',
+ ['grek'] = 'greek',
+ ['gujr'] = 'gujarati',
+ ['guru'] = 'gurmukhi',
+ ['hang'] = 'hangul',
+ ['hani'] = 'cjk ideographic',
+ ['hano'] = 'hanunoo',
+ ['hebr'] = 'hebrew',
+ ['ital'] = 'old italic',
+ ['jamo'] = 'hangul jamo',
+ ['java'] = 'javanese',
+ ['kana'] = 'hiragana and katakana',
+ ['khar'] = 'kharosthi',
+ ['khmr'] = 'khmer',
+ ['knda'] = 'kannada',
+ ['lao' ] = 'lao',
+ ['latn'] = 'latin',
+ ['limb'] = 'limbu',
+ ['linb'] = 'linear b',
+ ['math'] = 'mathematical alphanumeric symbols',
+ ['mlym'] = 'malayalam',
+ ['mong'] = 'mongolian',
+ ['musc'] = 'musical symbols',
+ ['mymr'] = 'myanmar',
+ ['nko' ] = "n'ko",
+ ['ogam'] = 'ogham',
+ ['orya'] = 'oriya',
+ ['osma'] = 'osmanya',
+ ['phag'] = 'phags-pa',
+ ['phnx'] = 'phoenician',
+ ['runr'] = 'runic',
+ ['shaw'] = 'shavian',
+ ['sinh'] = 'sinhala',
+ ['sylo'] = 'syloti nagri',
+ ['syrc'] = 'syriac',
+ ['tagb'] = 'tagbanwa',
+ ['tale'] = 'tai le',
+ ['talu'] = 'tai lu',
+ ['taml'] = 'tamil',
+ ['telu'] = 'telugu',
+ ['tfng'] = 'tifinagh',
+ ['tglg'] = 'tagalog',
+ ['thaa'] = 'thaana',
+ ['thai'] = 'thai',
+ ['tibt'] = 'tibetan',
+ ['ugar'] = 'ugaritic cuneiform',
+ ['xpeo'] = 'old persian cuneiform',
+ ['xsux'] = 'sumero-akkadian cuneiform',
+ ['yi' ] = 'yi',
+}
+
+local languages = allocate {
+ ['aba'] = 'abaza',
+ ['abk'] = 'abkhazian',
+ ['ady'] = 'adyghe',
+ ['afk'] = 'afrikaans',
+ ['afr'] = 'afar',
+ ['agw'] = 'agaw',
+ ['als'] = 'alsatian',
+ ['alt'] = 'altai',
+ ['amh'] = 'amharic',
+ ['ara'] = 'arabic',
+ ['ari'] = 'aari',
+ ['ark'] = 'arakanese',
+ ['asm'] = 'assamese',
+ ['ath'] = 'athapaskan',
+ ['avr'] = 'avar',
+ ['awa'] = 'awadhi',
+ ['aym'] = 'aymara',
+ ['aze'] = 'azeri',
+ ['bad'] = 'badaga',
+ ['bag'] = 'baghelkhandi',
+ ['bal'] = 'balkar',
+ ['bau'] = 'baule',
+ ['bbr'] = 'berber',
+ ['bch'] = 'bench',
+ ['bcr'] = 'bible cree',
+ ['bel'] = 'belarussian',
+ ['bem'] = 'bemba',
+ ['ben'] = 'bengali',
+ ['bgr'] = 'bulgarian',
+ ['bhi'] = 'bhili',
+ ['bho'] = 'bhojpuri',
+ ['bik'] = 'bikol',
+ ['bil'] = 'bilen',
+ ['bkf'] = 'blackfoot',
+ ['bli'] = 'balochi',
+ ['bln'] = 'balante',
+ ['blt'] = 'balti',
+ ['bmb'] = 'bambara',
+ ['bml'] = 'bamileke',
+ ['bos'] = 'bosnian',
+ ['bre'] = 'breton',
+ ['brh'] = 'brahui',
+ ['bri'] = 'braj bhasha',
+ ['brm'] = 'burmese',
+ ['bsh'] = 'bashkir',
+ ['bti'] = 'beti',
+ ['cat'] = 'catalan',
+ ['ceb'] = 'cebuano',
+ ['che'] = 'chechen',
+ ['chg'] = 'chaha gurage',
+ ['chh'] = 'chattisgarhi',
+ ['chi'] = 'chichewa',
+ ['chk'] = 'chukchi',
+ ['chp'] = 'chipewyan',
+ ['chr'] = 'cherokee',
+ ['chu'] = 'chuvash',
+ ['cmr'] = 'comorian',
+ ['cop'] = 'coptic',
+ ['cos'] = 'corsican',
+ ['cre'] = 'cree',
+ ['crr'] = 'carrier',
+ ['crt'] = 'crimean tatar',
+ ['csl'] = 'church slavonic',
+ ['csy'] = 'czech',
+ ['dan'] = 'danish',
+ ['dar'] = 'dargwa',
+ ['dcr'] = 'woods cree',
+ ['deu'] = 'german',
+ ['dgr'] = 'dogri',
+ ['div'] = 'divehi',
+ ['djr'] = 'djerma',
+ ['dng'] = 'dangme',
+ ['dnk'] = 'dinka',
+ ['dri'] = 'dari',
+ ['dun'] = 'dungan',
+ ['dzn'] = 'dzongkha',
+ ['ebi'] = 'ebira',
+ ['ecr'] = 'eastern cree',
+ ['edo'] = 'edo',
+ ['efi'] = 'efik',
+ ['ell'] = 'greek',
+ ['eng'] = 'english',
+ ['erz'] = 'erzya',
+ ['esp'] = 'spanish',
+ ['eti'] = 'estonian',
+ ['euq'] = 'basque',
+ ['evk'] = 'evenki',
+ ['evn'] = 'even',
+ ['ewe'] = 'ewe',
+ ['fan'] = 'french antillean',
+ ['far'] = 'farsi',
+ ['fin'] = 'finnish',
+ ['fji'] = 'fijian',
+ ['fle'] = 'flemish',
+ ['fne'] = 'forest nenets',
+ ['fon'] = 'fon',
+ ['fos'] = 'faroese',
+ ['fra'] = 'french',
+ ['fri'] = 'frisian',
+ ['frl'] = 'friulian',
+ ['fta'] = 'futa',
+ ['ful'] = 'fulani',
+ ['gad'] = 'ga',
+ ['gae'] = 'gaelic',
+ ['gag'] = 'gagauz',
+ ['gal'] = 'galician',
+ ['gar'] = 'garshuni',
+ ['gaw'] = 'garhwali',
+ ['gez'] = "ge'ez",
+ ['gil'] = 'gilyak',
+ ['gmz'] = 'gumuz',
+ ['gon'] = 'gondi',
+ ['grn'] = 'greenlandic',
+ ['gro'] = 'garo',
+ ['gua'] = 'guarani',
+ ['guj'] = 'gujarati',
+ ['hai'] = 'haitian',
+ ['hal'] = 'halam',
+ ['har'] = 'harauti',
+ ['hau'] = 'hausa',
+ ['haw'] = 'hawaiin',
+ ['hbn'] = 'hammer-banna',
+ ['hil'] = 'hiligaynon',
+ ['hin'] = 'hindi',
+ ['hma'] = 'high mari',
+ ['hnd'] = 'hindko',
+ ['ho'] = 'ho',
+ ['hri'] = 'harari',
+ ['hrv'] = 'croatian',
+ ['hun'] = 'hungarian',
+ ['hye'] = 'armenian',
+ ['ibo'] = 'igbo',
+ ['ijo'] = 'ijo',
+ ['ilo'] = 'ilokano',
+ ['ind'] = 'indonesian',
+ ['ing'] = 'ingush',
+ ['inu'] = 'inuktitut',
+ ['iri'] = 'irish',
+ ['irt'] = 'irish traditional',
+ ['isl'] = 'icelandic',
+ ['ism'] = 'inari sami',
+ ['ita'] = 'italian',
+ ['iwr'] = 'hebrew',
+ ['jan'] = 'japanese',
+ ['jav'] = 'javanese',
+ ['jii'] = 'yiddish',
+ ['jud'] = 'judezmo',
+ ['jul'] = 'jula',
+ ['kab'] = 'kabardian',
+ ['kac'] = 'kachchi',
+ ['kal'] = 'kalenjin',
+ ['kan'] = 'kannada',
+ ['kar'] = 'karachay',
+ ['kat'] = 'georgian',
+ ['kaz'] = 'kazakh',
+ ['keb'] = 'kebena',
+ ['kge'] = 'khutsuri georgian',
+ ['kha'] = 'khakass',
+ ['khk'] = 'khanty-kazim',
+ ['khm'] = 'khmer',
+ ['khs'] = 'khanty-shurishkar',
+ ['khv'] = 'khanty-vakhi',
+ ['khw'] = 'khowar',
+ ['kik'] = 'kikuyu',
+ ['kir'] = 'kirghiz',
+ ['kis'] = 'kisii',
+ ['kkn'] = 'kokni',
+ ['klm'] = 'kalmyk',
+ ['kmb'] = 'kamba',
+ ['kmn'] = 'kumaoni',
+ ['kmo'] = 'komo',
+ ['kms'] = 'komso',
+ ['knr'] = 'kanuri',
+ ['kod'] = 'kodagu',
+ ['koh'] = 'korean old hangul',
+ ['kok'] = 'konkani',
+ ['kon'] = 'kikongo',
+ ['kop'] = 'komi-permyak',
+ ['kor'] = 'korean',
+ ['koz'] = 'komi-zyrian',
+ ['kpl'] = 'kpelle',
+ ['kri'] = 'krio',
+ ['krk'] = 'karakalpak',
+ ['krl'] = 'karelian',
+ ['krm'] = 'karaim',
+ ['krn'] = 'karen',
+ ['krt'] = 'koorete',
+ ['ksh'] = 'kashmiri',
+ ['ksi'] = 'khasi',
+ ['ksm'] = 'kildin sami',
+ ['kui'] = 'kui',
+ ['kul'] = 'kulvi',
+ ['kum'] = 'kumyk',
+ ['kur'] = 'kurdish',
+ ['kuu'] = 'kurukh',
+ ['kuy'] = 'kuy',
+ ['kyk'] = 'koryak',
+ ['lad'] = 'ladin',
+ ['lah'] = 'lahuli',
+ ['lak'] = 'lak',
+ ['lam'] = 'lambani',
+ ['lao'] = 'lao',
+ ['lat'] = 'latin',
+ ['laz'] = 'laz',
+ ['lcr'] = 'l-cree',
+ ['ldk'] = 'ladakhi',
+ ['lez'] = 'lezgi',
+ ['lin'] = 'lingala',
+ ['lma'] = 'low mari',
+ ['lmb'] = 'limbu',
+ ['lmw'] = 'lomwe',
+ ['lsb'] = 'lower sorbian',
+ ['lsm'] = 'lule sami',
+ ['lth'] = 'lithuanian',
+ ['ltz'] = 'luxembourgish',
+ ['lub'] = 'luba',
+ ['lug'] = 'luganda',
+ ['luh'] = 'luhya',
+ ['luo'] = 'luo',
+ ['lvi'] = 'latvian',
+ ['maj'] = 'majang',
+ ['mak'] = 'makua',
+ ['mal'] = 'malayalam traditional',
+ ['man'] = 'mansi',
+ ['map'] = 'mapudungun',
+ ['mar'] = 'marathi',
+ ['maw'] = 'marwari',
+ ['mbn'] = 'mbundu',
+ ['mch'] = 'manchu',
+ ['mcr'] = 'moose cree',
+ ['mde'] = 'mende',
+ ['men'] = "me'en",
+ ['miz'] = 'mizo',
+ ['mkd'] = 'macedonian',
+ ['mle'] = 'male',
+ ['mlg'] = 'malagasy',
+ ['mln'] = 'malinke',
+ ['mlr'] = 'malayalam reformed',
+ ['mly'] = 'malay',
+ ['mnd'] = 'mandinka',
+ ['mng'] = 'mongolian',
+ ['mni'] = 'manipuri',
+ ['mnk'] = 'maninka',
+ ['mnx'] = 'manx gaelic',
+ ['moh'] = 'mohawk',
+ ['mok'] = 'moksha',
+ ['mol'] = 'moldavian',
+ ['mon'] = 'mon',
+ ['mor'] = 'moroccan',
+ ['mri'] = 'maori',
+ ['mth'] = 'maithili',
+ ['mts'] = 'maltese',
+ ['mun'] = 'mundari',
+ ['nag'] = 'naga-assamese',
+ ['nan'] = 'nanai',
+ ['nas'] = 'naskapi',
+ ['ncr'] = 'n-cree',
+ ['ndb'] = 'ndebele',
+ ['ndg'] = 'ndonga',
+ ['nep'] = 'nepali',
+ ['new'] = 'newari',
+ ['ngr'] = 'nagari',
+ ['nhc'] = 'norway house cree',
+ ['nis'] = 'nisi',
+ ['niu'] = 'niuean',
+ ['nkl'] = 'nkole',
+ ['nko'] = "n'ko",
+ ['nld'] = 'dutch',
+ ['nog'] = 'nogai',
+ ['nor'] = 'norwegian',
+ ['nsm'] = 'northern sami',
+ ['nta'] = 'northern tai',
+ ['nto'] = 'esperanto',
+ ['nyn'] = 'nynorsk',
+ ['oci'] = 'occitan',
+ ['ocr'] = 'oji-cree',
+ ['ojb'] = 'ojibway',
+ ['ori'] = 'oriya',
+ ['oro'] = 'oromo',
+ ['oss'] = 'ossetian',
+ ['paa'] = 'palestinian aramaic',
+ ['pal'] = 'pali',
+ ['pan'] = 'punjabi',
+ ['pap'] = 'palpa',
+ ['pas'] = 'pashto',
+ ['pgr'] = 'polytonic greek',
+ ['pil'] = 'pilipino',
+ ['plg'] = 'palaung',
+ ['plk'] = 'polish',
+ ['pro'] = 'provencal',
+ ['ptg'] = 'portuguese',
+ ['qin'] = 'chin',
+ ['raj'] = 'rajasthani',
+ ['rbu'] = 'russian buriat',
+ ['rcr'] = 'r-cree',
+ ['ria'] = 'riang',
+ ['rms'] = 'rhaeto-romanic',
+ ['rom'] = 'romanian',
+ ['roy'] = 'romany',
+ ['rsy'] = 'rusyn',
+ ['rua'] = 'ruanda',
+ ['rus'] = 'russian',
+ ['sad'] = 'sadri',
+ ['san'] = 'sanskrit',
+ ['sat'] = 'santali',
+ ['say'] = 'sayisi',
+ ['sek'] = 'sekota',
+ ['sel'] = 'selkup',
+ ['sgo'] = 'sango',
+ ['shn'] = 'shan',
+ ['sib'] = 'sibe',
+ ['sid'] = 'sidamo',
+ ['sig'] = 'silte gurage',
+ ['sks'] = 'skolt sami',
+ ['sky'] = 'slovak',
+ ['sla'] = 'slavey',
+ ['slv'] = 'slovenian',
+ ['sml'] = 'somali',
+ ['smo'] = 'samoan',
+ ['sna'] = 'sena',
+ ['snd'] = 'sindhi',
+ ['snh'] = 'sinhalese',
+ ['snk'] = 'soninke',
+ ['sog'] = 'sodo gurage',
+ ['sot'] = 'sotho',
+ ['sqi'] = 'albanian',
+ ['srb'] = 'serbian',
+ ['srk'] = 'saraiki',
+ ['srr'] = 'serer',
+ ['ssl'] = 'south slavey',
+ ['ssm'] = 'southern sami',
+ ['sur'] = 'suri',
+ ['sva'] = 'svan',
+ ['sve'] = 'swedish',
+ ['swa'] = 'swadaya aramaic',
+ ['swk'] = 'swahili',
+ ['swz'] = 'swazi',
+ ['sxt'] = 'sutu',
+ ['syr'] = 'syriac',
+ ['tab'] = 'tabasaran',
+ ['taj'] = 'tajiki',
+ ['tam'] = 'tamil',
+ ['tat'] = 'tatar',
+ ['tcr'] = 'th-cree',
+ ['tel'] = 'telugu',
+ ['tgn'] = 'tongan',
+ ['tgr'] = 'tigre',
+ ['tgy'] = 'tigrinya',
+ ['tha'] = 'thai',
+ ['tht'] = 'tahitian',
+ ['tib'] = 'tibetan',
+ ['tkm'] = 'turkmen',
+ ['tmn'] = 'temne',
+ ['tna'] = 'tswana',
+ ['tne'] = 'tundra nenets',
+ ['tng'] = 'tonga',
+ ['tod'] = 'todo',
+ ['trk'] = 'turkish',
+ ['tsg'] = 'tsonga',
+ ['tua'] = 'turoyo aramaic',
+ ['tul'] = 'tulu',
+ ['tuv'] = 'tuvin',
+ ['twi'] = 'twi',
+ ['udm'] = 'udmurt',
+ ['ukr'] = 'ukrainian',
+ ['urd'] = 'urdu',
+ ['usb'] = 'upper sorbian',
+ ['uyg'] = 'uyghur',
+ ['uzb'] = 'uzbek',
+ ['ven'] = 'venda',
+ ['vit'] = 'vietnamese',
+ ['wa' ] = 'wa',
+ ['wag'] = 'wagdi',
+ ['wcr'] = 'west-cree',
+ ['wel'] = 'welsh',
+ ['wlf'] = 'wolof',
+ ['xbd'] = 'tai lue',
+ ['xhs'] = 'xhosa',
+ ['yak'] = 'yakut',
+ ['yba'] = 'yoruba',
+ ['ycr'] = 'y-cree',
+ ['yic'] = 'yi classic',
+ ['yim'] = 'yi modern',
+ ['zhh'] = 'chinese hong kong',
+ ['zhp'] = 'chinese phonetic',
+ ['zhs'] = 'chinese simplified',
+ ['zht'] = 'chinese traditional',
+ ['znd'] = 'zande',
+ ['zul'] = 'zulu'
+}
+
+local features = allocate {
+ ['aalt'] = 'access all alternates',
+ ['abvf'] = 'above-base forms',
+ ['abvm'] = 'above-base mark positioning',
+ ['abvs'] = 'above-base substitutions',
+ ['afrc'] = 'alternative fractions',
+ ['akhn'] = 'akhands',
+ ['blwf'] = 'below-base forms',
+ ['blwm'] = 'below-base mark positioning',
+ ['blws'] = 'below-base substitutions',
+ ['c2pc'] = 'petite capitals from capitals',
+ ['c2sc'] = 'small capitals from capitals',
+ ['calt'] = 'contextual alternates',
+ ['case'] = 'case-sensitive forms',
+ ['ccmp'] = 'glyph composition/decomposition',
+ ['cjct'] = 'conjunct forms',
+ ['clig'] = 'contextual ligatures',
+ ['cpsp'] = 'capital spacing',
+ ['cswh'] = 'contextual swash',
+ ['curs'] = 'cursive positioning',
+ ['dflt'] = 'default processing',
+ ['dist'] = 'distances',
+ ['dlig'] = 'discretionary ligatures',
+ ['dnom'] = 'denominators',
+ ['dtls'] = 'dotless forms', -- math
+ ['expt'] = 'expert forms',
+ ['falt'] = 'final glyph alternates',
+ ['fin2'] = 'terminal forms #2',
+ ['fin3'] = 'terminal forms #3',
+ ['fina'] = 'terminal forms',
+ ['flac'] = 'flattened accents over capitals', -- math
+ ['frac'] = 'fractions',
+ ['fwid'] = 'full width',
+ ['half'] = 'half forms',
+ ['haln'] = 'halant forms',
+ ['halt'] = 'alternate half width',
+ ['hist'] = 'historical forms',
+ ['hkna'] = 'horizontal kana alternates',
+ ['hlig'] = 'historical ligatures',
+ ['hngl'] = 'hangul',
+ ['hojo'] = 'hojo kanji forms',
+ ['hwid'] = 'half width',
+ ['init'] = 'initial forms',
+ ['isol'] = 'isolated forms',
+ ['ital'] = 'italics',
+ ['jalt'] = 'justification alternatives',
+ ['jp04'] = 'jis2004 forms',
+ ['jp78'] = 'jis78 forms',
+ ['jp83'] = 'jis83 forms',
+ ['jp90'] = 'jis90 forms',
+ ['kern'] = 'kerning',
+ ['lfbd'] = 'left bounds',
+ ['liga'] = 'standard ligatures',
+ ['ljmo'] = 'leading jamo forms',
+ ['lnum'] = 'lining figures',
+ ['locl'] = 'localized forms',
+ ['mark'] = 'mark positioning',
+ ['med2'] = 'medial forms #2',
+ ['medi'] = 'medial forms',
+ ['mgrk'] = 'mathematical greek',
+ ['mkmk'] = 'mark to mark positioning',
+ ['mset'] = 'mark positioning via substitution',
+ ['nalt'] = 'alternate annotation forms',
+ ['nlck'] = 'nlc kanji forms',
+ ['nukt'] = 'nukta forms',
+ ['numr'] = 'numerators',
+ ['onum'] = 'old style figures',
+ ['opbd'] = 'optical bounds',
+ ['ordn'] = 'ordinals',
+ ['ornm'] = 'ornaments',
+ ['palt'] = 'proportional alternate width',
+ ['pcap'] = 'petite capitals',
+ ['pnum'] = 'proportional figures',
+ ['pref'] = 'pre-base forms',
+ ['pres'] = 'pre-base substitutions',
+ ['pstf'] = 'post-base forms',
+ ['psts'] = 'post-base substitutions',
+ ['pwid'] = 'proportional widths',
+ ['qwid'] = 'quarter widths',
+ ['rand'] = 'randomize',
+ ['rkrf'] = 'rakar forms',
+ ['rlig'] = 'required ligatures',
+ ['rphf'] = 'reph form',
+ ['rtbd'] = 'right bounds',
+ ['rtla'] = 'right-to-left alternates',
+ ['rtlm'] = 'right to left math', -- math
+ ['ruby'] = 'ruby notation forms',
+ ['salt'] = 'stylistic alternates',
+ ['sinf'] = 'scientific inferiors',
+ ['size'] = 'optical size',
+ ['smcp'] = 'small capitals',
+ ['smpl'] = 'simplified forms',
+ -- ['ss01'] = 'stylistic set 1',
+ -- ['ss02'] = 'stylistic set 2',
+ -- ['ss03'] = 'stylistic set 3',
+ -- ['ss04'] = 'stylistic set 4',
+ -- ['ss05'] = 'stylistic set 5',
+ -- ['ss06'] = 'stylistic set 6',
+ -- ['ss07'] = 'stylistic set 7',
+ -- ['ss08'] = 'stylistic set 8',
+ -- ['ss09'] = 'stylistic set 9',
+ -- ['ss10'] = 'stylistic set 10',
+ -- ['ss11'] = 'stylistic set 11',
+ -- ['ss12'] = 'stylistic set 12',
+ -- ['ss13'] = 'stylistic set 13',
+ -- ['ss14'] = 'stylistic set 14',
+ -- ['ss15'] = 'stylistic set 15',
+ -- ['ss16'] = 'stylistic set 16',
+ -- ['ss17'] = 'stylistic set 17',
+ -- ['ss18'] = 'stylistic set 18',
+ -- ['ss19'] = 'stylistic set 19',
+ -- ['ss20'] = 'stylistic set 20',
+ ['ssty'] = 'script style', -- math
+ ['subs'] = 'subscript',
+ ['sups'] = 'superscript',
+ ['swsh'] = 'swash',
+ ['titl'] = 'titling',
+ ['tjmo'] = 'trailing jamo forms',
+ ['tnam'] = 'traditional name forms',
+ ['tnum'] = 'tabular figures',
+ ['trad'] = 'traditional forms',
+ ['twid'] = 'third widths',
+ ['unic'] = 'unicase',
+ ['valt'] = 'alternate vertical metrics',
+ ['vatu'] = 'vattu variants',
+ ['vert'] = 'vertical writing',
+ ['vhal'] = 'alternate vertical half metrics',
+ ['vjmo'] = 'vowel jamo forms',
+ ['vkna'] = 'vertical kana alternates',
+ ['vkrn'] = 'vertical kerning',
+ ['vpal'] = 'proportional alternate vertical metrics',
+ ['vrt2'] = 'vertical rotation',
+ ['zero'] = 'slashed zero',
+
+ ['trep'] = 'traditional tex replacements',
+ ['tlig'] = 'traditional tex ligatures',
+
+ ['ss..'] = 'stylistic set ..',
+ ['cv..'] = 'character variant ..',
+ ['js..'] = 'justification ..',
+
+ ["dv.."] = "devanagari ..",
+}
+
+local baselines = allocate {
+ ['hang'] = 'hanging baseline',
+ ['icfb'] = 'ideographic character face bottom edge baseline',
+ ['icft'] = 'ideographic character face tope edige baseline',
+ ['ideo'] = 'ideographic em-box bottom edge baseline',
+ ['idtp'] = 'ideographic em-box top edge baseline',
+ ['math'] = 'mathmatical centered baseline',
+ ['romn'] = 'roman baseline'
+}
+
+tables.scripts = scripts
+tables.languages = languages
+tables.features = features
+tables.baselines = baselines
+
+local acceptscripts = true directives.register("otf.acceptscripts", function(v) acceptscripts = v end)
+local acceptlanguages = true directives.register("otf.acceptlanguages", function(v) acceptlanguages = v end)
+
+local report_checks = logs.reporter("fonts","checks")
+
+-- hm, we overload the metatables
+
+if otffeatures.features then
+ for k, v in next, otffeatures.features do
+ features[k] = v
+ end
+ otffeatures.features = features
+end
+
+local function swapped(h)
+ local r = { }
+ for k, v in next, h do
+ r[gsub(v,"[^a-z0-9]","")] = k -- is already lower
+ end
+ return r
+end
+
+local verbosescripts = allocate(swapped(scripts ))
+local verboselanguages = allocate(swapped(languages))
+local verbosefeatures = allocate(swapped(features ))
+local verbosebaselines = allocate(swapped(baselines))
+
+-- lets forget about trailing spaces
+
+local function resolve(t,k)
+ if k then
+ k = gsub(lower(k),"[^a-z0-9]","")
+ local v = rawget(t,k)
+ if v then
+ return v
+ end
+ end
+end
+
+setmetatableindex(verbosescripts, resolve)
+setmetatableindex(verboselanguages, resolve)
+setmetatableindex(verbosefeatures, resolve)
+setmetatableindex(verbosebaselines, resolve)
+
+-- We could optimize the next lookups by using an extra metatable and storing
+-- already found values but in practice there are not that many lookups so
+-- it's never a bottleneck.
+
+setmetatableindex(scripts, function(t,k)
+ if k then
+ k = lower(k)
+ if k == "dflt" then
+ return k
+ end
+ local v = rawget(t,k)
+ if v then
+ return v
+ end
+ k = gsub(k," ","")
+ v = rawget(t,v)
+ if v then
+ return v
+ elseif acceptscripts then
+ report_checks("registering extra script %a",k)
+ rawset(t,k,k)
+ return k
+ end
+ end
+ return "dflt"
+end)
+
+setmetatableindex(languages, function(t,k)
+ if k then
+ k = lower(k)
+ if k == "dflt" then
+ return k
+ end
+ local v = rawget(t,k)
+ if v then
+ return v
+ end
+ k = gsub(k," ","")
+ v = rawget(t,v)
+ if v then
+ return v
+ elseif acceptlanguages then
+ report_checks("registering extra language %a",k)
+ rawset(t,k,k)
+ return k
+ end
+ end
+ return "dflt"
+end)
+
+setmetatablenewindex(languages, "ignore")
+setmetatablenewindex(baselines, "ignore")
+setmetatablenewindex(baselines, "ignore")
+
+local function resolve(t,k)
+ if k then
+ k = lower(k)
+ local v = rawget(t,k)
+ if v then
+ return v
+ end
+ k = gsub(k," ","")
+ local v = rawget(t,k)
+ if v then
+ return v
+ end
+ local tag, dd = match(k,"(..)(%d+)")
+ if tag and dd then
+ local v = rawget(t,tag)
+ if v then
+ return v -- return format(v,tonumber(dd)) -- old way
+ else
+ local v = rawget(t,tag.."..") -- nicer in overview
+ if v then
+ return (gsub(v,"%.%.",tonumber(dd))) -- new way
+ end
+ end
+ end
+ end
+ return k -- "dflt"
+end
+
+setmetatableindex(features, resolve)
+
+local function assign(t,k,v)
+ if k and v then
+ v = lower(v)
+ rawset(t,k,v) -- rawset ?
+ -- rawset(features,gsub(v,"[^a-z0-9]",""),k) -- why ? old code
+ end
+end
+
+setmetatablenewindex(features, assign)
+
+local checkers = {
+ rand = function(v)
+ return v == true and "random" or v
+ end
+}
+
+-- Keep this:
+--
+-- function otf.features.normalize(features)
+-- if features then
+-- local h = { }
+-- for k, v in next, features do
+-- k = lower(k)
+-- if k == "language" then
+-- v = gsub(lower(v),"[^a-z0-9]","")
+-- h.language = rawget(verboselanguages,v) or (languages[v] and v) or "dflt" -- auto adds
+-- elseif k == "script" then
+-- v = gsub(lower(v),"[^a-z0-9]","")
+-- h.script = rawget(verbosescripts,v) or (scripts[v] and v) or "dflt" -- auto adds
+-- else
+-- if type(v) == "string" then
+-- local b = is_boolean(v)
+-- if type(b) == "nil" then
+-- v = tonumber(v) or lower(v)
+-- else
+-- v = b
+-- end
+-- end
+-- if not rawget(features,k) then
+-- k = rawget(verbosefeatures,k) or k
+-- end
+-- local c = checkers[k]
+-- h[k] = c and c(v) or v
+-- end
+-- end
+-- return h
+-- end
+-- end
+
+-- inspect(fonts.handlers.otf.statistics.usedfeatures)
+
+if not storage then
+ return
+end
+
+local usedfeatures = statistics.usedfeatures or { }
+statistics.usedfeatures = usedfeatures
+
+table.setmetatableindex(usedfeatures, function(t,k) if k then local v = { } t[k] = v return v end end) -- table.autotable
+
+storage.register("fonts/otf/usedfeatures", usedfeatures, "fonts.handlers.otf.statistics.usedfeatures" )
+
+function otf.features.normalize(features)
+ if features then
+ local h = { }
+ for key, value in next, features do
+ local k = lower(key)
+ if k == "language" then
+ local v = gsub(lower(value),"[^a-z0-9]","")
+ h.language = rawget(verboselanguages,v) or (languages[v] and v) or "dflt" -- auto adds
+ elseif k == "script" then
+ local v = gsub(lower(value),"[^a-z0-9]","")
+ h.script = rawget(verbosescripts,v) or (scripts[v] and v) or "dflt" -- auto adds
+ else
+ local uk = usedfeatures[key]
+ local uv = uk[value]
+ if uv then
+ -- report_checks("feature value %a first seen at %a",value,key)
+ else
+ if type(value) == "string" then
+ local b = is_boolean(value)
+ if type(b) == "nil" then
+ uv = tonumber(value) or lower(value)
+ else
+ uv = b
+ end
+ else
+ uv = v
+ end
+ if not rawget(features,k) then
+ k = rawget(verbosefeatures,k) or k
+ end
+ local c = checkers[k]
+ if c then
+ uv = c(uv) or vc
+ end
+ uk[value] = uv
+ end
+ h[k] = uv
+ end
+ end
+ return h
+ end
+end
+
+--~ table.print(otf.features.normalize({ language = "dutch", liga = "yes", ss99 = true, aalt = 3, abcd = "yes" } ))
+
+-- When I feel the need ...
+
+--~ tables.aat = {
+--~ [ 0] = {
+--~ name = "allTypographicFeaturesType",
+--~ [ 0] = "allTypeFeaturesOnSelector",
+--~ [ 1] = "allTypeFeaturesOffSelector",
+--~ },
+--~ [ 1] = {
+--~ name = "ligaturesType",
+--~ [0 ] = "requiredLigaturesOnSelector",
+--~ [1 ] = "requiredLigaturesOffSelector",
+--~ [2 ] = "commonLigaturesOnSelector",
+--~ [3 ] = "commonLigaturesOffSelector",
+--~ [4 ] = "rareLigaturesOnSelector",
+--~ [5 ] = "rareLigaturesOffSelector",
+--~ [6 ] = "logosOnSelector ",
+--~ [7 ] = "logosOffSelector ",
+--~ [8 ] = "rebusPicturesOnSelector",
+--~ [9 ] = "rebusPicturesOffSelector",
+--~ [10] = "diphthongLigaturesOnSelector",
+--~ [11] = "diphthongLigaturesOffSelector",
+--~ [12] = "squaredLigaturesOnSelector",
+--~ [13] = "squaredLigaturesOffSelector",
+--~ [14] = "abbrevSquaredLigaturesOnSelector",
+--~ [15] = "abbrevSquaredLigaturesOffSelector",
+--~ },
+--~ [ 2] = {
+--~ name = "cursiveConnectionType",
+--~ [ 0] = "unconnectedSelector",
+--~ [ 1] = "partiallyConnectedSelector",
+--~ [ 2] = "cursiveSelector ",
+--~ },
+--~ [ 3] = {
+--~ name = "letterCaseType",
+--~ [ 0] = "upperAndLowerCaseSelector",
+--~ [ 1] = "allCapsSelector ",
+--~ [ 2] = "allLowerCaseSelector",
+--~ [ 3] = "smallCapsSelector ",
+--~ [ 4] = "initialCapsSelector",
+--~ [ 5] = "initialCapsAndSmallCapsSelector",
+--~ },
+--~ [ 4] = {
+--~ name = "verticalSubstitutionType",
+--~ [ 0] = "substituteVerticalFormsOnSelector",
+--~ [ 1] = "substituteVerticalFormsOffSelector",
+--~ },
+--~ [ 5] = {
+--~ name = "linguisticRearrangementType",
+--~ [ 0] = "linguisticRearrangementOnSelector",
+--~ [ 1] = "linguisticRearrangementOffSelector",
+--~ },
+--~ [ 6] = {
+--~ name = "numberSpacingType",
+--~ [ 0] = "monospacedNumbersSelector",
+--~ [ 1] = "proportionalNumbersSelector",
+--~ },
+--~ [ 7] = {
+--~ name = "appleReserved1Type",
+--~ },
+--~ [ 8] = {
+--~ name = "smartSwashType",
+--~ [ 0] = "wordInitialSwashesOnSelector",
+--~ [ 1] = "wordInitialSwashesOffSelector",
+--~ [ 2] = "wordFinalSwashesOnSelector",
+--~ [ 3] = "wordFinalSwashesOffSelector",
+--~ [ 4] = "lineInitialSwashesOnSelector",
+--~ [ 5] = "lineInitialSwashesOffSelector",
+--~ [ 6] = "lineFinalSwashesOnSelector",
+--~ [ 7] = "lineFinalSwashesOffSelector",
+--~ [ 8] = "nonFinalSwashesOnSelector",
+--~ [ 9] = "nonFinalSwashesOffSelector",
+--~ },
+--~ [ 9] = {
+--~ name = "diacriticsType",
+--~ [ 0] = "showDiacriticsSelector",
+--~ [ 1] = "hideDiacriticsSelector",
+--~ [ 2] = "decomposeDiacriticsSelector",
+--~ },
+--~ [10] = {
+--~ name = "verticalPositionType",
+--~ [ 0] = "normalPositionSelector",
+--~ [ 1] = "superiorsSelector ",
+--~ [ 2] = "inferiorsSelector ",
+--~ [ 3] = "ordinalsSelector ",
+--~ },
+--~ [11] = {
+--~ name = "fractionsType",
+--~ [ 0] = "noFractionsSelector",
+--~ [ 1] = "verticalFractionsSelector",
+--~ [ 2] = "diagonalFractionsSelector",
+--~ },
+--~ [12] = {
+--~ name = "appleReserved2Type",
+--~ },
+--~ [13] = {
+--~ name = "overlappingCharactersType",
+--~ [ 0] = "preventOverlapOnSelector",
+--~ [ 1] = "preventOverlapOffSelector",
+--~ },
+--~ [14] = {
+--~ name = "typographicExtrasType",
+--~ [0 ] = "hyphensToEmDashOnSelector",
+--~ [1 ] = "hyphensToEmDashOffSelector",
+--~ [2 ] = "hyphenToEnDashOnSelector",
+--~ [3 ] = "hyphenToEnDashOffSelector",
+--~ [4 ] = "unslashedZeroOnSelector",
+--~ [5 ] = "unslashedZeroOffSelector",
+--~ [6 ] = "formInterrobangOnSelector",
+--~ [7 ] = "formInterrobangOffSelector",
+--~ [8 ] = "smartQuotesOnSelector",
+--~ [9 ] = "smartQuotesOffSelector",
+--~ [10] = "periodsToEllipsisOnSelector",
+--~ [11] = "periodsToEllipsisOffSelector",
+--~ },
+--~ [15] = {
+--~ name = "mathematicalExtrasType",
+--~ [ 0] = "hyphenToMinusOnSelector",
+--~ [ 1] = "hyphenToMinusOffSelector",
+--~ [ 2] = "asteriskToMultiplyOnSelector",
+--~ [ 3] = "asteriskToMultiplyOffSelector",
+--~ [ 4] = "slashToDivideOnSelector",
+--~ [ 5] = "slashToDivideOffSelector",
+--~ [ 6] = "inequalityLigaturesOnSelector",
+--~ [ 7] = "inequalityLigaturesOffSelector",
+--~ [ 8] = "exponentsOnSelector",
+--~ [ 9] = "exponentsOffSelector",
+--~ },
+--~ [16] = {
+--~ name = "ornamentSetsType",
+--~ [ 0] = "noOrnamentsSelector",
+--~ [ 1] = "dingbatsSelector ",
+--~ [ 2] = "piCharactersSelector",
+--~ [ 3] = "fleuronsSelector ",
+--~ [ 4] = "decorativeBordersSelector",
+--~ [ 5] = "internationalSymbolsSelector",
+--~ [ 6] = "mathSymbolsSelector",
+--~ },
+--~ [17] = {
+--~ name = "characterAlternativesType",
+--~ [ 0] = "noAlternatesSelector",
+--~ },
+--~ [18] = {
+--~ name = "designComplexityType",
+--~ [ 0] = "designLevel1Selector",
+--~ [ 1] = "designLevel2Selector",
+--~ [ 2] = "designLevel3Selector",
+--~ [ 3] = "designLevel4Selector",
+--~ [ 4] = "designLevel5Selector",
+--~ },
+--~ [19] = {
+--~ name = "styleOptionsType",
+--~ [ 0] = "noStyleOptionsSelector",
+--~ [ 1] = "displayTextSelector",
+--~ [ 2] = "engravedTextSelector",
+--~ [ 3] = "illuminatedCapsSelector",
+--~ [ 4] = "titlingCapsSelector",
+--~ [ 5] = "tallCapsSelector ",
+--~ },
+--~ [20] = {
+--~ name = "characterShapeType",
+--~ [0 ] = "traditionalCharactersSelector",
+--~ [1 ] = "simplifiedCharactersSelector",
+--~ [2 ] = "jis1978CharactersSelector",
+--~ [3 ] = "jis1983CharactersSelector",
+--~ [4 ] = "jis1990CharactersSelector",
+--~ [5 ] = "traditionalAltOneSelector",
+--~ [6 ] = "traditionalAltTwoSelector",
+--~ [7 ] = "traditionalAltThreeSelector",
+--~ [8 ] = "traditionalAltFourSelector",
+--~ [9 ] = "traditionalAltFiveSelector",
+--~ [10] = "expertCharactersSelector",
+--~ },
+--~ [21] = {
+--~ name = "numberCaseType",
+--~ [ 0] = "lowerCaseNumbersSelector",
+--~ [ 1] = "upperCaseNumbersSelector",
+--~ },
+--~ [22] = {
+--~ name = "textSpacingType",
+--~ [ 0] = "proportionalTextSelector",
+--~ [ 1] = "monospacedTextSelector",
+--~ [ 2] = "halfWidthTextSelector",
+--~ [ 3] = "normallySpacedTextSelector",
+--~ },
+--~ [23] = {
+--~ name = "transliterationType",
+--~ [ 0] = "noTransliterationSelector",
+--~ [ 1] = "hanjaToHangulSelector",
+--~ [ 2] = "hiraganaToKatakanaSelector",
+--~ [ 3] = "katakanaToHiraganaSelector",
+--~ [ 4] = "kanaToRomanizationSelector",
+--~ [ 5] = "romanizationToHiraganaSelector",
+--~ [ 6] = "romanizationToKatakanaSelector",
+--~ [ 7] = "hanjaToHangulAltOneSelector",
+--~ [ 8] = "hanjaToHangulAltTwoSelector",
+--~ [ 9] = "hanjaToHangulAltThreeSelector",
+--~ },
+--~ [24] = {
+--~ name = "annotationType",
+--~ [ 0] = "noAnnotationSelector",
+--~ [ 1] = "boxAnnotationSelector",
+--~ [ 2] = "roundedBoxAnnotationSelector",
+--~ [ 3] = "circleAnnotationSelector",
+--~ [ 4] = "invertedCircleAnnotationSelector",
+--~ [ 5] = "parenthesisAnnotationSelector",
+--~ [ 6] = "periodAnnotationSelector",
+--~ [ 7] = "romanNumeralAnnotationSelector",
+--~ [ 8] = "diamondAnnotationSelector",
+--~ },
+--~ [25] = {
+--~ name = "kanaSpacingType",
+--~ [ 0] = "fullWidthKanaSelector",
+--~ [ 1] = "proportionalKanaSelector",
+--~ },
+--~ [26] = {
+--~ name = "ideographicSpacingType",
+--~ [ 0] = "fullWidthIdeographsSelector",
+--~ [ 1] = "proportionalIdeographsSelector",
+--~ },
+--~ [103] = {
+--~ name = "cjkRomanSpacingType",
+--~ [ 0] = "halfWidthCJKRomanSelector",
+--~ [ 1] = "proportionalCJKRomanSelector",
+--~ [ 2] = "defaultCJKRomanSelector",
+--~ [ 3] = "fullWidthCJKRomanSelector",
+--~ },
+--~ }
diff --git a/tex/context/base/font-sol.lua b/tex/context/base/font-sol.lua
index b37ab8869..db2dd24c2 100644
--- a/tex/context/base/font-sol.lua
+++ b/tex/context/base/font-sol.lua
@@ -1,884 +1,884 @@
-if not modules then modules = { } end modules ['font-sol'] = { -- this was: node-spl
- version = 1.001,
- comment = "companion to font-sol.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- This module is dedicated to the oriental tex project and for
--- the moment is too experimental to be publicly supported.
---
--- We could cache solutions: say that we store the featureset and
--- all 'words' -> replacement ... so we create a large solution
--- database (per font)
---
--- This module can be optimized by using a dedicated dynamics handler
--- but I'll only do that when the rest of the code is stable.
---
--- Todo: bind setups to paragraph.
-
-local gmatch, concat, format, remove = string.gmatch, table.concat, string.format, table.remove
-local next, tostring, tonumber = next, tostring, tonumber
-local insert, remove = table.insert, table.remove
-local utfchar = utf.char
-local random = math.random
-
-local utilities, logs, statistics, fonts, trackers = utilities, logs, statistics, fonts, trackers
-local interfaces, commands, attributes = interfaces, commands, attributes
-local nodes, node, tex = nodes, node, tex
-
-local trace_split = false trackers.register("builders.paragraphs.solutions.splitters.splitter", function(v) trace_split = v end)
-local trace_optimize = false trackers.register("builders.paragraphs.solutions.splitters.optimizer", function(v) trace_optimize = v end)
-local trace_colors = false trackers.register("builders.paragraphs.solutions.splitters.colors", function(v) trace_colors = v end)
-local trace_goodies = false trackers.register("fonts.goodies", function(v) trace_goodies = v end)
-
-local report_solutions = logs.reporter("fonts","solutions")
-local report_splitters = logs.reporter("fonts","splitters")
-local report_optimizers = logs.reporter("fonts","optimizers")
-
-local variables = interfaces.variables
-
-local v_normal = variables.normal
-local v_reverse = variables.reverse
-local v_preroll = variables.preroll
-local v_random = variables.random
-local v_split = variables.split
-
-local settings_to_array = utilities.parsers.settings_to_array
-local settings_to_hash = utilities.parsers.settings_to_hash
-
-local find_node_tail = node.tail or node.slide
-local free_node = node.free
-local free_nodelist = node.flush_list
-local copy_nodelist = node.copy_list
-local traverse_nodes = node.traverse
-local traverse_ids = node.traverse_id
-local protect_glyphs = nodes.handlers.protectglyphs or node.protect_glyphs
-local hpack_nodes = node.hpack
-local insert_node_before = node.insert_before
-local insert_node_after = node.insert_after
-local repack_hlist = nodes.repackhlist
-local nodes_to_utf = nodes.listtoutf
-
-local setnodecolor = nodes.tracers.colors.set
-
-local nodecodes = nodes.nodecodes
-local whatsitcodes = nodes.whatsitcodes
-local kerncodes = nodes.kerncodes
-
-local glyph_code = nodecodes.glyph
-local disc_code = nodecodes.disc
-local kern_code = nodecodes.kern
-local hlist_code = nodecodes.hlist
-local whatsit_code = nodecodes.whatsit
-
-local fontkern_code = kerncodes.fontkern
-
-local localpar_code = whatsitcodes.localpar
-local dir_code = whatsitcodes.dir
-local userdefined_code = whatsitcodes.userdefined
-
-local nodepool = nodes.pool
-local tasks = nodes.tasks
-local usernodeids = nodepool.userids
-
-local new_textdir = nodepool.textdir
-local new_usernumber = nodepool.usernumber
-local new_glue = nodepool.glue
-local new_leftskip = nodepool.leftskip
-
-local starttiming = statistics.starttiming
-local stoptiming = statistics.stoptiming
-local process_characters = nodes.handlers.characters
-local inject_kerns = nodes.injections.handler
-
-local fonthashes = fonts.hashes
-local fontdata = fonthashes.identifiers
-local setfontdynamics = fonthashes.setdynamics
-local fontprocesses = fonthashes.processes
-
-local texsetattribute = tex.setattribute
-local unsetvalue = attributes.unsetvalue
-
-local parbuilders = builders.paragraphs
-parbuilders.solutions = parbuilders.solutions or { }
-local parsolutions = parbuilders.solutions
-parsolutions.splitters = parsolutions.splitters or { }
-local splitters = parsolutions.splitters
-
-local solutions = { } -- attribute sets
-local registered = { } -- backmapping
-splitters.registered = registered
-
-local a_split = attributes.private('splitter')
-
-local preroll = true
-local criterium = 0
-local randomseed = nil
-local optimize = nil -- set later
-local variant = v_normal
-local splitwords = true
-
-local cache = { }
-local variants = { }
-local max_less = 0
-local max_more = 0
-
-local stack = { }
-
-local dummy = {
- attribute = unsetvalue,
- randomseed = 0,
- criterium = 0,
- preroll = false,
- optimize = nil,
- splitwords = false,
- variant = v_normal,
-}
-
-local function checksettings(r,settings)
- local s = r.settings
- local method = settings_to_hash(settings.method or "")
- local optimize, preroll, splitwords
- for k, v in next, method do
- if k == v_preroll then
- preroll = true
- elseif k == v_split then
- splitwords = true
- elseif variants[k] then
- variant = k
- optimize = variants[k] -- last one wins
- end
- end
- r.randomseed = tonumber(settings.randomseed) or s.randomseed or r.randomseed or 0
- r.criterium = tonumber(settings.criterium ) or s.criterium or r.criterium or 0
- r.preroll = preroll or false
- r.splitwords = splitwords or false
- r.optimize = optimize or s.optimize or r.optimize or variants[v_normal]
-end
-
-local function pushsplitter(name,settings)
- local r = name and registered[name]
- if r then
- if settings then
- checksettings(r,settings)
- end
- else
- r = dummy
- end
- insert(stack,r)
- -- brr
- randomseed = r.randomseed or 0
- criterium = r.criterium or 0
- preroll = r.preroll or false
- optimize = r.optimize or nil
- splitwords = r.splitwords or nil
- --
- texsetattribute(a_split,r.attribute)
- return #stack
-end
-
-local function popsplitter()
- remove(stack)
- local n = #stack
- local r = stack[n] or dummy
- --
- randomseed = r.randomseed or 0
- criterium = r.criterium or 0
- preroll = r.preroll or false
- optimize = r.optimize or nil
- --
- texsetattribute(a_split,r.attribute)
- return n
-end
-
-local contextsetups = fonts.specifiers.contextsetups
-
-local function convert(featuresets,name,list)
- if list then
- local numbers = { }
- local nofnumbers = 0
- for i=1,#list do
- local feature = list[i]
- local fs = featuresets[feature]
- local fn = fs and fs.number
- if not fn then
- -- fall back on global features
- fs = contextsetups[feature]
- fn = fs and fs.number
- end
- if fn then
- nofnumbers = nofnumbers + 1
- numbers[nofnumbers] = fn
- if trace_goodies or trace_optimize then
- report_solutions("solution %a of %a uses feature %a with number %s",i,name,feature,fn)
- end
- else
- report_solutions("solution %a of %a has an invalid feature reference %a",i,name,feature)
- end
- end
- return nofnumbers > 0 and numbers
- end
-end
-
-local function initialize(goodies)
- local solutions = goodies.solutions
- if solutions then
- local featuresets = goodies.featuresets
- local goodiesname = goodies.name
- if trace_goodies or trace_optimize then
- report_solutions("checking solutions in %a",goodiesname)
- end
- for name, set in next, solutions do
- set.less = convert(featuresets,name,set.less)
- set.more = convert(featuresets,name,set.more)
- end
- end
-end
-
-fonts.goodies.register("solutions",initialize)
-
-function splitters.define(name,settings)
- local goodies = settings.goodies
- local solution = settings.solution
- local less = settings.less
- local more = settings.more
- local less_set, more_set
- local l = less and settings_to_array(less)
- local m = more and settings_to_array(more)
- if goodies then
- goodies = fonts.goodies.load(goodies) -- also in tfmdata
- if goodies then
- local featuresets = goodies.featuresets
- local solution = solution and goodies.solutions[solution]
- if l and #l > 0 then
- less_set = convert(featuresets,name,less) -- take from settings
- else
- less_set = solution and solution.less -- take from goodies
- end
- if m and #m > 0 then
- more_set = convert(featuresets,name,more) -- take from settings
- else
- more_set = solution and solution.more -- take from goodies
- end
- end
- else
- if l then
- local n = #less_set
- for i=1,#l do
- local ss = contextsetups[l[i]]
- if ss then
- n = n + 1
- less_set[n] = ss.number
- end
- end
- end
- if m then
- local n = #more_set
- for i=1,#m do
- local ss = contextsetups[m[i]]
- if ss then
- n = n + 1
- more_set[n] = ss.number
- end
- end
- end
- end
- if trace_optimize then
- report_solutions("defining solutions %a, less %a, more %a",name,concat(less_set or {}," "),concat(more_set or {}," "))
- end
- local nofsolutions = #solutions + 1
- local t = {
- solution = solution,
- less = less_set or { },
- more = more_set or { },
- settings = settings, -- for tracing
- attribute = nofsolutions,
- }
- solutions[nofsolutions] = t
- registered[name] = t
- return nofsolutions
-end
-
-local nofwords, noftries, nofadapted, nofkept, nofparagraphs = 0, 0, 0, 0, 0
-
-local splitter_one = usernodeids["splitters.one"]
-local splitter_two = usernodeids["splitters.two"]
-
-local a_word = attributes.private('word')
-local a_fontkern = attributes.private('fontkern')
-
-local encapsulate = false
-
-directives.register("builders.paragraphs.solutions.splitters.encapsulate", function(v)
- encapsulate = v
-end)
-
-function splitters.split(head)
- -- quite fast
- local current, done, rlmode, start, stop, attribute = head, false, false, nil, nil, 0
- cache, max_less, max_more = { }, 0, 0
- local function flush() -- we can move this
- local font = start.font
- local last = stop.next
- local list = last and copy_nodelist(start,last) or copy_nodelist(start)
- local n = #cache + 1
- if encapsulate then
- local user_one = new_usernumber(splitter_one,n)
- local user_two = new_usernumber(splitter_two,n)
- head, start = insert_node_before(head,start,user_one)
- insert_node_after(head,stop,user_two)
- else
- local current = start
- while true do
- current[a_word] = n
- if current == stop then
- break
- else
- current = current.next
- end
- end
- end
- if rlmode == "TRT" or rlmode == "+TRT" then
- local dirnode = new_textdir("+TRT")
- list.prev = dirnode
- dirnode.next = list
- list = dirnode
- end
- local c = {
- original = list,
- attribute = attribute,
- direction = rlmode,
- font = font
- }
- if trace_split then
- report_splitters("cached %4i: font %a, attribute %a, direction %a, word %a",
- n, font, attribute, nodes_to_utf(list,true), rlmode and "r2l" or "l2r")
- end
- cache[n] = c
- local solution = solutions[attribute]
- local l, m = #solution.less, #solution.more
- if l > max_less then max_less = l end
- if m > max_more then max_more = m end
- start, stop, done = nil, nil, true
- end
- while current do -- also nextid
- local next = current.next
- local id = current.id
- if id == glyph_code then
- if current.subtype < 256 then
- local a = current[a_split]
- if not a then
- start, stop = nil, nil
- elseif not start then
- start, stop, attribute = current, current, a
- elseif a ~= attribute then
- start, stop = nil, nil
- else
- stop = current
- end
- end
- elseif id == disc_code then
- if splitwords then
- if start then
- flush()
- end
- elseif start and next and next.id == glyph_code and next.subtype < 256 then
- -- beware: we can cross future lines
- stop = next
- else
- start, stop = nil, nil
- end
- elseif id == whatsit_code then
- if start then
- flush()
- end
- local subtype = current.subtype
- if subtype == dir_code or subtype == localpar_code then
- rlmode = current.dir
- end
- else
- if start then
- flush()
- end
- end
- current = next
- end
- if start then
- flush()
- end
- nofparagraphs = nofparagraphs + 1
- nofwords = nofwords + #cache
- return head, done
-end
-
-local function collect_words(list) -- can be made faster for attributes
- local words, w, word = { }, 0, nil
- if encapsulate then
- for current in traverse_ids(whatsit_code,list) do
- if current.subtype == userdefined_code then -- hm
- local user_id = current.user_id
- if user_id == splitter_one then
- word = { current.value, current, current }
- w = w + 1
- words[w] = word
- elseif user_id == splitter_two then
- if word then
- word[3] = current
- else
- -- something is wrong
- end
- end
- end
- end
- else
- local current, first, last, index = list, nil, nil, nil
- while current do
- -- todo: disc and kern
- local id = current.id
- if id == glyph_code or id == disc_code then
- local a = current[a_word]
- if a then
- if a == index then
- -- same word
- last = current
- elseif index then
- w = w + 1
- words[w] = { index, first, last }
- first = current
- last = current
- index = a
- elseif first then
- last = current
- index = a
- else
- first = current
- last = current
- index = a
- end
- elseif index then
- if first then
- w = w + 1
- words[w] = { index, first, last }
- end
- index = nil
- first = nil
- elseif trace_split then
- if id == disc_code then
- report_splitters("skipped: disc node")
- else
- report_splitters("skipped: %C",current.char)
- end
- end
- elseif id == kern_code and (current.subtype == fontkern_code or current[a_fontkern]) then
- if first then
- last = current
- else
- first = current
- last = current
- end
- elseif index then
- w = w + 1
- words[w] = { index, first, last }
- index = nil
- first = nil
- if id == disc_node then
- if trace_split then
- report_splitters("skipped: disc node")
- end
- end
- end
- current = current.next
- end
- if index then
- w = w + 1
- words[w] = { index, first, last }
- end
- if trace_split then
- for i=1,#words do
- local w = words[i]
- local n, f, l = w[1], w[2], w[3]
- local c = cache[n]
- if c then
- report_splitters("found %4i: word %a, cached %a",n,nodes_to_utf(f,true,true,l),nodes_to_utf(c.original,true))
- else
- report_splitters("found %4i: word %a, not in cache",n,nodes_to_utf(f,true,true,l))
- end
- end
- end
- end
- return words, list -- check for empty (elsewhere)
-end
-
--- we could avoid a hpack but hpack is not that slow
-
-local function doit(word,list,best,width,badness,line,set,listdir)
- local changed = 0
- local n = word[1]
- local found = cache[n]
- if found then
- local h, t
- if encapsulate then
- h = word[2].next -- head of current word
- t = word[3].prev -- tail of current word
- else
- h = word[2]
- t = word[3]
- end
- if splitwords then
- -- there are no lines crossed in a word
- else
- local ok = false
- local c = h
- while c do
- if c == t then
- ok = true
- break
- else
- c = c.next
- end
- end
- if not ok then
- report_solutions("skipping hyphenated word (for now)")
- -- todo: mark in words as skipped, saves a bit runtime
- return false, changed
- end
- end
- local original, attribute, direction = found.original, found.attribute, found.direction
- local solution = solutions[attribute]
- local features = solution and solution[set]
- if features then
- local featurenumber = features[best] -- not ok probably
- if featurenumber then
- noftries = noftries + 1
- local first = copy_nodelist(original)
- if not trace_colors then
- for n in traverse_nodes(first) do -- maybe fast force so no attr needed
- n[0] = featurenumber -- this forces dynamics
- end
- elseif set == "less" then
- for n in traverse_nodes(first) do
- setnodecolor(n,"font:isol") -- yellow
- n[0] = featurenumber
- end
- else
- for n in traverse_nodes(first) do
- setnodecolor(n,"font:medi") -- green
- n[0] = featurenumber
- end
- end
- local font = found.font
- local setdynamics = setfontdynamics[font]
- if setdynamics then
- local processes = setdynamics(font,featurenumber)
- for i=1,#processes do -- often more than 1
- first = processes[i](first,font,featurenumber)
- end
- else
- report_solutions("fatal error, no dynamics for font %a",font)
- end
- first = inject_kerns(first)
- if first.id == whatsit_code then
- local temp = first
- first = first.next
- free_node(temp)
- end
- local last = find_node_tail(first)
- -- replace [u]h->t by [u]first->last
- local prev = h.prev
- local next = t.next
- prev.next = first
- first.prev = prev
- if next then
- last.next = next
- next.prev = last
- end
- -- check new pack
- local temp, b = repack_hlist(list,width,'exactly',listdir)
- if b > badness then
- if trace_optimize then
- report_optimizers("line %a, badness before %a, after %a, criterium %a, verdict %a",line,badness,b,criterium,"quit")
- end
- -- remove last insert
- prev.next = h
- h.prev = prev
- if next then
- t.next = next
- next.prev = t
- else
- t.next = nil
- end
- last.next = nil
- free_nodelist(first)
- else
- if trace_optimize then
- report_optimizers("line %a, badness before: %a, after %a, criterium %a, verdict %a",line,badness,b,criterium,"continue")
- end
- -- free old h->t
- t.next = nil
- free_nodelist(h) -- somhow fails
- if not encapsulate then
- word[2] = first
- word[3] = last
- end
- changed, badness = changed + 1, b
- end
- if b <= criterium then
- return true, changed
- end
- end
- end
- end
- return false, changed
-end
-
--- We repeat some code but adding yet another layer of indirectness is not
--- making things better.
-
-variants[v_normal] = function(words,list,best,width,badness,line,set,listdir)
- local changed = 0
- for i=1,#words do
- local done, c = doit(words[i],list,best,width,badness,line,set,listdir)
- changed = changed + c
- if done then
- break
- end
- end
- if changed > 0 then
- nofadapted = nofadapted + 1
- -- todo: get rid of pack when ok because we already have packed and we only need the last b
- local list, b = repack_hlist(list,width,'exactly',listdir)
- return list, true, changed, b -- badness
- else
- nofkept = nofkept + 1
- return list, false, 0, badness
- end
-end
-
-variants[v_reverse] = function(words,list,best,width,badness,line,set,listdir)
- local changed = 0
- for i=#words,1,-1 do
- local done, c = doit(words[i],list,best,width,badness,line,set,listdir)
- changed = changed + c
- if done then
- break
- end
- end
- if changed > 0 then
- nofadapted = nofadapted + 1
- -- todo: get rid of pack when ok because we already have packed and we only need the last b
- local list, b = repack_hlist(list,width,'exactly',listdir)
- return list, true, changed, b -- badness
- else
- nofkept = nofkept + 1
- return list, false, 0, badness
- end
-end
-
-variants[v_random] = function(words,list,best,width,badness,line,set,listdir)
- local changed = 0
- while #words > 0 do
- local done, c = doit(remove(words,random(1,#words)),list,best,width,badness,line,set,listdir)
- changed = changed + c
- if done then
- break
- end
- end
- if changed > 0 then
- nofadapted = nofadapted + 1
- -- todo: get rid of pack when ok because we already have packed and we only need the last b
- local list, b = repack_hlist(list,width,'exactly',listdir)
- return list, true, changed, b -- badness
- else
- nofkept = nofkept + 1
- return list, false, 0, badness
- end
-end
-
-local function show_quality(current,what,line)
- local set = current.glue_set
- local sign = current.glue_sign
- local order = current.glue_order
- local amount = set * ((sign == 2 and -1) or 1)
- report_optimizers("line %a, category %a, amount %a, set %a, sign %a, how %a, order %a",line,what,amount,set,sign,how,order)
-end
-
-function splitters.optimize(head)
- if not optimize then
- report_optimizers("no optimizer set")
- return
- end
- local nc = #cache
- if nc == 0 then
- return
- end
- starttiming(splitters)
- local listdir = nil -- todo ! ! !
- if randomseed then
- math.setrandomseedi(randomseed)
- randomseed = nil
- end
- local line = 0
- local tex_hbadness, tex_hfuzz = tex.hbadness, tex.hfuzz
- tex.hbadness, tex.hfuzz = 10000, number.maxdimen
- if trace_optimize then
- report_optimizers("preroll %a, variant %a, criterium %a, cache size %a",preroll,variant,criterium,nc)
- end
- for current in traverse_ids(hlist_code,head) do
- -- report_splitters("before: [%s] => %s",current.dir,nodes.tosequence(current.list,nil))
- line = line + 1
- local sign, dir, list, width = current.glue_sign, current.dir, current.list, current.width
- if not encapsulate and list.id == glyph_code then
- -- nasty .. we always assume a prev being there .. future luatex will always have a leftskip set
- -- current.list, list = insert_node_before(list,list,new_glue(0))
- current.list, list = insert_node_before(list,list,new_leftskip(0))
- end
- local temp, badness = repack_hlist(list,width,'exactly',dir) -- it would be nice if the badness was stored in the node
- if badness > 0 then
- if sign == 0 then
- if trace_optimize then
- report_optimizers("line %a, badness %a, outcome %a, verdict %a",line,badness,"okay","okay")
- end
- else
- local set, max
- if sign == 1 then
- if trace_optimize then
- report_optimizers("line %a, badness %a, outcome %a, verdict %a",line,badness,"underfull","trying more")
- end
- set, max = "more", max_more
- else
- if trace_optimize then
- report_optimizers("line %a, badness %a, outcome %a, verdict %a",line,badness,"overfull","trying less")
- end
- set, max = "less", max_less
- end
- -- we can keep the best variants
- local lastbest, lastbadness = nil, badness
- if preroll then
- local bb, base
- for i=1,max do
- if base then
- free_nodelist(base)
- end
- base = copy_nodelist(list)
- local words = collect_words(base) -- beware: words is adapted
- for j=i,max do
- local temp, done, changes, b = optimize(words,base,j,width,badness,line,set,dir)
- base = temp
- if trace_optimize then
- report_optimizers("line %a, alternative %a.%a, changes %a, badness %a",line,i,j,changes,b)
- end
- bb = b
- if b <= criterium then
- break
- end
- -- if done then
- -- break
- -- end
- end
- if bb and bb > criterium then -- needs checking
- if not lastbest then
- lastbest, lastbadness = i, bb
- elseif bb > lastbadness then
- lastbest, lastbadness = i, bb
- end
- else
- break
- end
- end
- free_nodelist(base)
- end
- local words = collect_words(list)
- for best=lastbest or 1,max do
- local temp, done, changes, b = optimize(words,list,best,width,badness,line,set,dir)
- current.list = temp
- if trace_optimize then
- report_optimizers("line %a, alternative %a, changes %a, badness %a",line,best,changes,b)
- end
- if done then
- if b <= criterium then -- was == 0
- protect_glyphs(list)
- break
- end
- end
- end
- end
- else
- if trace_optimize then
- report_optimizers("line %a, verdict %a",line,"not bad enough")
- end
- end
- -- we pack inside the outer hpack and that way keep the original wd/ht/dp as bonus
- current.list = hpack_nodes(current.list,width,'exactly',listdir)
- -- report_splitters("after: [%s] => %s",temp.dir,nodes.tosequence(temp.list,nil))
- end
- for i=1,nc do
- local ci = cache[i]
- free_nodelist(ci.original)
- end
- cache = { }
- tex.hbadness, tex.hfuzz = tex_hbadness, tex_hfuzz
- stoptiming(splitters)
-end
-
-statistics.register("optimizer statistics", function()
- if nofwords > 0 then
- local elapsed = statistics.elapsedtime(splitters)
- local average = noftries/elapsed
- return format("%s words identified in %s paragraphs, %s words retried, %s lines tried, %0.3f seconds used, %s adapted, %0.1f lines per second",
- nofwords,nofparagraphs,noftries,nofadapted+nofkept,elapsed,nofadapted,average)
- end
-end)
-
--- we could use a stack
-
-local enableaction = tasks.enableaction
-local disableaction = tasks.disableaction
-
-local function enable()
- enableaction("processors", "builders.paragraphs.solutions.splitters.split")
- enableaction("finalizers", "builders.paragraphs.solutions.splitters.optimize")
-end
-
-local function disable()
- disableaction("processors", "builders.paragraphs.solutions.splitters.split")
- disableaction("finalizers", "builders.paragraphs.solutions.splitters.optimize")
-end
-
-function splitters.start(name,settings)
- if pushsplitter(name,settings) == 1 then
- enable()
- end
-end
-
-function splitters.stop()
- if popsplitter() == 0 then
- disable()
- end
-end
-
-function splitters.set(name,settings)
- if #stack > 0 then
- stack = { }
- else
- enable()
- end
- pushsplitter(name,settings) -- sets attribute etc
-end
-
-function splitters.reset()
- if #stack > 0 then
- stack = { }
- popsplitter() -- resets attribute etc
- disable()
- end
-end
-
--- interface
-
-commands.definefontsolution = splitters.define
-commands.startfontsolution = splitters.start
-commands.stopfontsolution = splitters.stop
-commands.setfontsolution = splitters.set
-commands.resetfontsolution = splitters.reset
+if not modules then modules = { } end modules ['font-sol'] = { -- this was: node-spl
+ version = 1.001,
+ comment = "companion to font-sol.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This module is dedicated to the oriental tex project and for
+-- the moment is too experimental to be publicly supported.
+--
+-- We could cache solutions: say that we store the featureset and
+-- all 'words' -> replacement ... so we create a large solution
+-- database (per font)
+--
+-- This module can be optimized by using a dedicated dynamics handler
+-- but I'll only do that when the rest of the code is stable.
+--
+-- Todo: bind setups to paragraph.
+
+local gmatch, concat, format, remove = string.gmatch, table.concat, string.format, table.remove
+local next, tostring, tonumber = next, tostring, tonumber
+local insert, remove = table.insert, table.remove
+local utfchar = utf.char
+local random = math.random
+
+local utilities, logs, statistics, fonts, trackers = utilities, logs, statistics, fonts, trackers
+local interfaces, commands, attributes = interfaces, commands, attributes
+local nodes, node, tex = nodes, node, tex
+
+local trace_split = false trackers.register("builders.paragraphs.solutions.splitters.splitter", function(v) trace_split = v end)
+local trace_optimize = false trackers.register("builders.paragraphs.solutions.splitters.optimizer", function(v) trace_optimize = v end)
+local trace_colors = false trackers.register("builders.paragraphs.solutions.splitters.colors", function(v) trace_colors = v end)
+local trace_goodies = false trackers.register("fonts.goodies", function(v) trace_goodies = v end)
+
+local report_solutions = logs.reporter("fonts","solutions")
+local report_splitters = logs.reporter("fonts","splitters")
+local report_optimizers = logs.reporter("fonts","optimizers")
+
+local variables = interfaces.variables
+
+local v_normal = variables.normal
+local v_reverse = variables.reverse
+local v_preroll = variables.preroll
+local v_random = variables.random
+local v_split = variables.split
+
+local settings_to_array = utilities.parsers.settings_to_array
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+local find_node_tail = node.tail or node.slide
+local free_node = node.free
+local free_nodelist = node.flush_list
+local copy_nodelist = node.copy_list
+local traverse_nodes = node.traverse
+local traverse_ids = node.traverse_id
+local protect_glyphs = nodes.handlers.protectglyphs or node.protect_glyphs
+local hpack_nodes = node.hpack
+local insert_node_before = node.insert_before
+local insert_node_after = node.insert_after
+local repack_hlist = nodes.repackhlist
+local nodes_to_utf = nodes.listtoutf
+
+local setnodecolor = nodes.tracers.colors.set
+
+local nodecodes = nodes.nodecodes
+local whatsitcodes = nodes.whatsitcodes
+local kerncodes = nodes.kerncodes
+
+local glyph_code = nodecodes.glyph
+local disc_code = nodecodes.disc
+local kern_code = nodecodes.kern
+local hlist_code = nodecodes.hlist
+local whatsit_code = nodecodes.whatsit
+
+local fontkern_code = kerncodes.fontkern
+
+local localpar_code = whatsitcodes.localpar
+local dir_code = whatsitcodes.dir
+local userdefined_code = whatsitcodes.userdefined
+
+local nodepool = nodes.pool
+local tasks = nodes.tasks
+local usernodeids = nodepool.userids
+
+local new_textdir = nodepool.textdir
+local new_usernumber = nodepool.usernumber
+local new_glue = nodepool.glue
+local new_leftskip = nodepool.leftskip
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+local process_characters = nodes.handlers.characters
+local inject_kerns = nodes.injections.handler
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+local setfontdynamics = fonthashes.setdynamics
+local fontprocesses = fonthashes.processes
+
+local texsetattribute = tex.setattribute
+local unsetvalue = attributes.unsetvalue
+
+local parbuilders = builders.paragraphs
+parbuilders.solutions = parbuilders.solutions or { }
+local parsolutions = parbuilders.solutions
+parsolutions.splitters = parsolutions.splitters or { }
+local splitters = parsolutions.splitters
+
+local solutions = { } -- attribute sets
+local registered = { } -- backmapping
+splitters.registered = registered
+
+local a_split = attributes.private('splitter')
+
+local preroll = true
+local criterium = 0
+local randomseed = nil
+local optimize = nil -- set later
+local variant = v_normal
+local splitwords = true
+
+local cache = { }
+local variants = { }
+local max_less = 0
+local max_more = 0
+
+local stack = { }
+
+local dummy = {
+ attribute = unsetvalue,
+ randomseed = 0,
+ criterium = 0,
+ preroll = false,
+ optimize = nil,
+ splitwords = false,
+ variant = v_normal,
+}
+
+local function checksettings(r,settings)
+ local s = r.settings
+ local method = settings_to_hash(settings.method or "")
+ local optimize, preroll, splitwords
+ for k, v in next, method do
+ if k == v_preroll then
+ preroll = true
+ elseif k == v_split then
+ splitwords = true
+ elseif variants[k] then
+ variant = k
+ optimize = variants[k] -- last one wins
+ end
+ end
+ r.randomseed = tonumber(settings.randomseed) or s.randomseed or r.randomseed or 0
+ r.criterium = tonumber(settings.criterium ) or s.criterium or r.criterium or 0
+ r.preroll = preroll or false
+ r.splitwords = splitwords or false
+ r.optimize = optimize or s.optimize or r.optimize or variants[v_normal]
+end
+
+local function pushsplitter(name,settings)
+ local r = name and registered[name]
+ if r then
+ if settings then
+ checksettings(r,settings)
+ end
+ else
+ r = dummy
+ end
+ insert(stack,r)
+ -- brr
+ randomseed = r.randomseed or 0
+ criterium = r.criterium or 0
+ preroll = r.preroll or false
+ optimize = r.optimize or nil
+ splitwords = r.splitwords or nil
+ --
+ texsetattribute(a_split,r.attribute)
+ return #stack
+end
+
+local function popsplitter()
+ remove(stack)
+ local n = #stack
+ local r = stack[n] or dummy
+ --
+ randomseed = r.randomseed or 0
+ criterium = r.criterium or 0
+ preroll = r.preroll or false
+ optimize = r.optimize or nil
+ --
+ texsetattribute(a_split,r.attribute)
+ return n
+end
+
+local contextsetups = fonts.specifiers.contextsetups
+
+local function convert(featuresets,name,list)
+ if list then
+ local numbers = { }
+ local nofnumbers = 0
+ for i=1,#list do
+ local feature = list[i]
+ local fs = featuresets[feature]
+ local fn = fs and fs.number
+ if not fn then
+ -- fall back on global features
+ fs = contextsetups[feature]
+ fn = fs and fs.number
+ end
+ if fn then
+ nofnumbers = nofnumbers + 1
+ numbers[nofnumbers] = fn
+ if trace_goodies or trace_optimize then
+ report_solutions("solution %a of %a uses feature %a with number %s",i,name,feature,fn)
+ end
+ else
+ report_solutions("solution %a of %a has an invalid feature reference %a",i,name,feature)
+ end
+ end
+ return nofnumbers > 0 and numbers
+ end
+end
+
+local function initialize(goodies)
+ local solutions = goodies.solutions
+ if solutions then
+ local featuresets = goodies.featuresets
+ local goodiesname = goodies.name
+ if trace_goodies or trace_optimize then
+ report_solutions("checking solutions in %a",goodiesname)
+ end
+ for name, set in next, solutions do
+ set.less = convert(featuresets,name,set.less)
+ set.more = convert(featuresets,name,set.more)
+ end
+ end
+end
+
+fonts.goodies.register("solutions",initialize)
+
+function splitters.define(name,settings)
+ local goodies = settings.goodies
+ local solution = settings.solution
+ local less = settings.less
+ local more = settings.more
+ local less_set, more_set
+ local l = less and settings_to_array(less)
+ local m = more and settings_to_array(more)
+ if goodies then
+ goodies = fonts.goodies.load(goodies) -- also in tfmdata
+ if goodies then
+ local featuresets = goodies.featuresets
+ local solution = solution and goodies.solutions[solution]
+ if l and #l > 0 then
+ less_set = convert(featuresets,name,less) -- take from settings
+ else
+ less_set = solution and solution.less -- take from goodies
+ end
+ if m and #m > 0 then
+ more_set = convert(featuresets,name,more) -- take from settings
+ else
+ more_set = solution and solution.more -- take from goodies
+ end
+ end
+ else
+ if l then
+ local n = #less_set
+ for i=1,#l do
+ local ss = contextsetups[l[i]]
+ if ss then
+ n = n + 1
+ less_set[n] = ss.number
+ end
+ end
+ end
+ if m then
+ local n = #more_set
+ for i=1,#m do
+ local ss = contextsetups[m[i]]
+ if ss then
+ n = n + 1
+ more_set[n] = ss.number
+ end
+ end
+ end
+ end
+ if trace_optimize then
+ report_solutions("defining solutions %a, less %a, more %a",name,concat(less_set or {}," "),concat(more_set or {}," "))
+ end
+ local nofsolutions = #solutions + 1
+ local t = {
+ solution = solution,
+ less = less_set or { },
+ more = more_set or { },
+ settings = settings, -- for tracing
+ attribute = nofsolutions,
+ }
+ solutions[nofsolutions] = t
+ registered[name] = t
+ return nofsolutions
+end
+
+local nofwords, noftries, nofadapted, nofkept, nofparagraphs = 0, 0, 0, 0, 0
+
+local splitter_one = usernodeids["splitters.one"]
+local splitter_two = usernodeids["splitters.two"]
+
+local a_word = attributes.private('word')
+local a_fontkern = attributes.private('fontkern')
+
+local encapsulate = false
+
+directives.register("builders.paragraphs.solutions.splitters.encapsulate", function(v)
+ encapsulate = v
+end)
+
+function splitters.split(head)
+ -- quite fast
+ local current, done, rlmode, start, stop, attribute = head, false, false, nil, nil, 0
+ cache, max_less, max_more = { }, 0, 0
+ local function flush() -- we can move this
+ local font = start.font
+ local last = stop.next
+ local list = last and copy_nodelist(start,last) or copy_nodelist(start)
+ local n = #cache + 1
+ if encapsulate then
+ local user_one = new_usernumber(splitter_one,n)
+ local user_two = new_usernumber(splitter_two,n)
+ head, start = insert_node_before(head,start,user_one)
+ insert_node_after(head,stop,user_two)
+ else
+ local current = start
+ while true do
+ current[a_word] = n
+ if current == stop then
+ break
+ else
+ current = current.next
+ end
+ end
+ end
+ if rlmode == "TRT" or rlmode == "+TRT" then
+ local dirnode = new_textdir("+TRT")
+ list.prev = dirnode
+ dirnode.next = list
+ list = dirnode
+ end
+ local c = {
+ original = list,
+ attribute = attribute,
+ direction = rlmode,
+ font = font
+ }
+ if trace_split then
+ report_splitters("cached %4i: font %a, attribute %a, direction %a, word %a",
+ n, font, attribute, nodes_to_utf(list,true), rlmode and "r2l" or "l2r")
+ end
+ cache[n] = c
+ local solution = solutions[attribute]
+ local l, m = #solution.less, #solution.more
+ if l > max_less then max_less = l end
+ if m > max_more then max_more = m end
+ start, stop, done = nil, nil, true
+ end
+ while current do -- also nextid
+ local next = current.next
+ local id = current.id
+ if id == glyph_code then
+ if current.subtype < 256 then
+ local a = current[a_split]
+ if not a then
+ start, stop = nil, nil
+ elseif not start then
+ start, stop, attribute = current, current, a
+ elseif a ~= attribute then
+ start, stop = nil, nil
+ else
+ stop = current
+ end
+ end
+ elseif id == disc_code then
+ if splitwords then
+ if start then
+ flush()
+ end
+ elseif start and next and next.id == glyph_code and next.subtype < 256 then
+ -- beware: we can cross future lines
+ stop = next
+ else
+ start, stop = nil, nil
+ end
+ elseif id == whatsit_code then
+ if start then
+ flush()
+ end
+ local subtype = current.subtype
+ if subtype == dir_code or subtype == localpar_code then
+ rlmode = current.dir
+ end
+ else
+ if start then
+ flush()
+ end
+ end
+ current = next
+ end
+ if start then
+ flush()
+ end
+ nofparagraphs = nofparagraphs + 1
+ nofwords = nofwords + #cache
+ return head, done
+end
+
+local function collect_words(list) -- can be made faster for attributes
+ local words, w, word = { }, 0, nil
+ if encapsulate then
+ for current in traverse_ids(whatsit_code,list) do
+ if current.subtype == userdefined_code then -- hm
+ local user_id = current.user_id
+ if user_id == splitter_one then
+ word = { current.value, current, current }
+ w = w + 1
+ words[w] = word
+ elseif user_id == splitter_two then
+ if word then
+ word[3] = current
+ else
+ -- something is wrong
+ end
+ end
+ end
+ end
+ else
+ local current, first, last, index = list, nil, nil, nil
+ while current do
+ -- todo: disc and kern
+ local id = current.id
+ if id == glyph_code or id == disc_code then
+ local a = current[a_word]
+ if a then
+ if a == index then
+ -- same word
+ last = current
+ elseif index then
+ w = w + 1
+ words[w] = { index, first, last }
+ first = current
+ last = current
+ index = a
+ elseif first then
+ last = current
+ index = a
+ else
+ first = current
+ last = current
+ index = a
+ end
+ elseif index then
+ if first then
+ w = w + 1
+ words[w] = { index, first, last }
+ end
+ index = nil
+ first = nil
+ elseif trace_split then
+ if id == disc_code then
+ report_splitters("skipped: disc node")
+ else
+ report_splitters("skipped: %C",current.char)
+ end
+ end
+ elseif id == kern_code and (current.subtype == fontkern_code or current[a_fontkern]) then
+ if first then
+ last = current
+ else
+ first = current
+ last = current
+ end
+ elseif index then
+ w = w + 1
+ words[w] = { index, first, last }
+ index = nil
+ first = nil
+ if id == disc_node then
+ if trace_split then
+ report_splitters("skipped: disc node")
+ end
+ end
+ end
+ current = current.next
+ end
+ if index then
+ w = w + 1
+ words[w] = { index, first, last }
+ end
+ if trace_split then
+ for i=1,#words do
+ local w = words[i]
+ local n, f, l = w[1], w[2], w[3]
+ local c = cache[n]
+ if c then
+ report_splitters("found %4i: word %a, cached %a",n,nodes_to_utf(f,true,true,l),nodes_to_utf(c.original,true))
+ else
+ report_splitters("found %4i: word %a, not in cache",n,nodes_to_utf(f,true,true,l))
+ end
+ end
+ end
+ end
+ return words, list -- check for empty (elsewhere)
+end
+
+-- we could avoid a hpack but hpack is not that slow
+
+local function doit(word,list,best,width,badness,line,set,listdir)
+ local changed = 0
+ local n = word[1]
+ local found = cache[n]
+ if found then
+ local h, t
+ if encapsulate then
+ h = word[2].next -- head of current word
+ t = word[3].prev -- tail of current word
+ else
+ h = word[2]
+ t = word[3]
+ end
+ if splitwords then
+ -- there are no lines crossed in a word
+ else
+ local ok = false
+ local c = h
+ while c do
+ if c == t then
+ ok = true
+ break
+ else
+ c = c.next
+ end
+ end
+ if not ok then
+ report_solutions("skipping hyphenated word (for now)")
+ -- todo: mark in words as skipped, saves a bit runtime
+ return false, changed
+ end
+ end
+ local original, attribute, direction = found.original, found.attribute, found.direction
+ local solution = solutions[attribute]
+ local features = solution and solution[set]
+ if features then
+ local featurenumber = features[best] -- not ok probably
+ if featurenumber then
+ noftries = noftries + 1
+ local first = copy_nodelist(original)
+ if not trace_colors then
+ for n in traverse_nodes(first) do -- maybe fast force so no attr needed
+ n[0] = featurenumber -- this forces dynamics
+ end
+ elseif set == "less" then
+ for n in traverse_nodes(first) do
+ setnodecolor(n,"font:isol") -- yellow
+ n[0] = featurenumber
+ end
+ else
+ for n in traverse_nodes(first) do
+ setnodecolor(n,"font:medi") -- green
+ n[0] = featurenumber
+ end
+ end
+ local font = found.font
+ local setdynamics = setfontdynamics[font]
+ if setdynamics then
+ local processes = setdynamics(font,featurenumber)
+ for i=1,#processes do -- often more than 1
+ first = processes[i](first,font,featurenumber)
+ end
+ else
+ report_solutions("fatal error, no dynamics for font %a",font)
+ end
+ first = inject_kerns(first)
+ if first.id == whatsit_code then
+ local temp = first
+ first = first.next
+ free_node(temp)
+ end
+ local last = find_node_tail(first)
+ -- replace [u]h->t by [u]first->last
+ local prev = h.prev
+ local next = t.next
+ prev.next = first
+ first.prev = prev
+ if next then
+ last.next = next
+ next.prev = last
+ end
+ -- check new pack
+ local temp, b = repack_hlist(list,width,'exactly',listdir)
+ if b > badness then
+ if trace_optimize then
+ report_optimizers("line %a, badness before %a, after %a, criterium %a, verdict %a",line,badness,b,criterium,"quit")
+ end
+ -- remove last insert
+ prev.next = h
+ h.prev = prev
+ if next then
+ t.next = next
+ next.prev = t
+ else
+ t.next = nil
+ end
+ last.next = nil
+ free_nodelist(first)
+ else
+ if trace_optimize then
+ report_optimizers("line %a, badness before: %a, after %a, criterium %a, verdict %a",line,badness,b,criterium,"continue")
+ end
+ -- free old h->t
+ t.next = nil
+ free_nodelist(h) -- somhow fails
+ if not encapsulate then
+ word[2] = first
+ word[3] = last
+ end
+ changed, badness = changed + 1, b
+ end
+ if b <= criterium then
+ return true, changed
+ end
+ end
+ end
+ end
+ return false, changed
+end
+
+-- We repeat some code but adding yet another layer of indirectness is not
+-- making things better.
+
+variants[v_normal] = function(words,list,best,width,badness,line,set,listdir)
+ local changed = 0
+ for i=1,#words do
+ local done, c = doit(words[i],list,best,width,badness,line,set,listdir)
+ changed = changed + c
+ if done then
+ break
+ end
+ end
+ if changed > 0 then
+ nofadapted = nofadapted + 1
+ -- todo: get rid of pack when ok because we already have packed and we only need the last b
+ local list, b = repack_hlist(list,width,'exactly',listdir)
+ return list, true, changed, b -- badness
+ else
+ nofkept = nofkept + 1
+ return list, false, 0, badness
+ end
+end
+
+variants[v_reverse] = function(words,list,best,width,badness,line,set,listdir)
+ local changed = 0
+ for i=#words,1,-1 do
+ local done, c = doit(words[i],list,best,width,badness,line,set,listdir)
+ changed = changed + c
+ if done then
+ break
+ end
+ end
+ if changed > 0 then
+ nofadapted = nofadapted + 1
+ -- todo: get rid of pack when ok because we already have packed and we only need the last b
+ local list, b = repack_hlist(list,width,'exactly',listdir)
+ return list, true, changed, b -- badness
+ else
+ nofkept = nofkept + 1
+ return list, false, 0, badness
+ end
+end
+
+variants[v_random] = function(words,list,best,width,badness,line,set,listdir)
+ local changed = 0
+ while #words > 0 do
+ local done, c = doit(remove(words,random(1,#words)),list,best,width,badness,line,set,listdir)
+ changed = changed + c
+ if done then
+ break
+ end
+ end
+ if changed > 0 then
+ nofadapted = nofadapted + 1
+ -- todo: get rid of pack when ok because we already have packed and we only need the last b
+ local list, b = repack_hlist(list,width,'exactly',listdir)
+ return list, true, changed, b -- badness
+ else
+ nofkept = nofkept + 1
+ return list, false, 0, badness
+ end
+end
+
+local function show_quality(current,what,line)
+ local set = current.glue_set
+ local sign = current.glue_sign
+ local order = current.glue_order
+ local amount = set * ((sign == 2 and -1) or 1)
+ report_optimizers("line %a, category %a, amount %a, set %a, sign %a, how %a, order %a",line,what,amount,set,sign,how,order)
+end
+
+function splitters.optimize(head)
+ if not optimize then
+ report_optimizers("no optimizer set")
+ return
+ end
+ local nc = #cache
+ if nc == 0 then
+ return
+ end
+ starttiming(splitters)
+ local listdir = nil -- todo ! ! !
+ if randomseed then
+ math.setrandomseedi(randomseed)
+ randomseed = nil
+ end
+ local line = 0
+ local tex_hbadness, tex_hfuzz = tex.hbadness, tex.hfuzz
+ tex.hbadness, tex.hfuzz = 10000, number.maxdimen
+ if trace_optimize then
+ report_optimizers("preroll %a, variant %a, criterium %a, cache size %a",preroll,variant,criterium,nc)
+ end
+ for current in traverse_ids(hlist_code,head) do
+ -- report_splitters("before: [%s] => %s",current.dir,nodes.tosequence(current.list,nil))
+ line = line + 1
+ local sign, dir, list, width = current.glue_sign, current.dir, current.list, current.width
+ if not encapsulate and list.id == glyph_code then
+ -- nasty .. we always assume a prev being there .. future luatex will always have a leftskip set
+ -- current.list, list = insert_node_before(list,list,new_glue(0))
+ current.list, list = insert_node_before(list,list,new_leftskip(0))
+ end
+ local temp, badness = repack_hlist(list,width,'exactly',dir) -- it would be nice if the badness was stored in the node
+ if badness > 0 then
+ if sign == 0 then
+ if trace_optimize then
+ report_optimizers("line %a, badness %a, outcome %a, verdict %a",line,badness,"okay","okay")
+ end
+ else
+ local set, max
+ if sign == 1 then
+ if trace_optimize then
+ report_optimizers("line %a, badness %a, outcome %a, verdict %a",line,badness,"underfull","trying more")
+ end
+ set, max = "more", max_more
+ else
+ if trace_optimize then
+ report_optimizers("line %a, badness %a, outcome %a, verdict %a",line,badness,"overfull","trying less")
+ end
+ set, max = "less", max_less
+ end
+ -- we can keep the best variants
+ local lastbest, lastbadness = nil, badness
+ if preroll then
+ local bb, base
+ for i=1,max do
+ if base then
+ free_nodelist(base)
+ end
+ base = copy_nodelist(list)
+ local words = collect_words(base) -- beware: words is adapted
+ for j=i,max do
+ local temp, done, changes, b = optimize(words,base,j,width,badness,line,set,dir)
+ base = temp
+ if trace_optimize then
+ report_optimizers("line %a, alternative %a.%a, changes %a, badness %a",line,i,j,changes,b)
+ end
+ bb = b
+ if b <= criterium then
+ break
+ end
+ -- if done then
+ -- break
+ -- end
+ end
+ if bb and bb > criterium then -- needs checking
+ if not lastbest then
+ lastbest, lastbadness = i, bb
+ elseif bb > lastbadness then
+ lastbest, lastbadness = i, bb
+ end
+ else
+ break
+ end
+ end
+ free_nodelist(base)
+ end
+ local words = collect_words(list)
+ for best=lastbest or 1,max do
+ local temp, done, changes, b = optimize(words,list,best,width,badness,line,set,dir)
+ current.list = temp
+ if trace_optimize then
+ report_optimizers("line %a, alternative %a, changes %a, badness %a",line,best,changes,b)
+ end
+ if done then
+ if b <= criterium then -- was == 0
+ protect_glyphs(list)
+ break
+ end
+ end
+ end
+ end
+ else
+ if trace_optimize then
+ report_optimizers("line %a, verdict %a",line,"not bad enough")
+ end
+ end
+ -- we pack inside the outer hpack and that way keep the original wd/ht/dp as bonus
+ current.list = hpack_nodes(current.list,width,'exactly',listdir)
+ -- report_splitters("after: [%s] => %s",temp.dir,nodes.tosequence(temp.list,nil))
+ end
+ for i=1,nc do
+ local ci = cache[i]
+ free_nodelist(ci.original)
+ end
+ cache = { }
+ tex.hbadness, tex.hfuzz = tex_hbadness, tex_hfuzz
+ stoptiming(splitters)
+end
+
+statistics.register("optimizer statistics", function()
+ if nofwords > 0 then
+ local elapsed = statistics.elapsedtime(splitters)
+ local average = noftries/elapsed
+ return format("%s words identified in %s paragraphs, %s words retried, %s lines tried, %0.3f seconds used, %s adapted, %0.1f lines per second",
+ nofwords,nofparagraphs,noftries,nofadapted+nofkept,elapsed,nofadapted,average)
+ end
+end)
+
+-- we could use a stack
+
+local enableaction = tasks.enableaction
+local disableaction = tasks.disableaction
+
+local function enable()
+ enableaction("processors", "builders.paragraphs.solutions.splitters.split")
+ enableaction("finalizers", "builders.paragraphs.solutions.splitters.optimize")
+end
+
+local function disable()
+ disableaction("processors", "builders.paragraphs.solutions.splitters.split")
+ disableaction("finalizers", "builders.paragraphs.solutions.splitters.optimize")
+end
+
+function splitters.start(name,settings)
+ if pushsplitter(name,settings) == 1 then
+ enable()
+ end
+end
+
+function splitters.stop()
+ if popsplitter() == 0 then
+ disable()
+ end
+end
+
+function splitters.set(name,settings)
+ if #stack > 0 then
+ stack = { }
+ else
+ enable()
+ end
+ pushsplitter(name,settings) -- sets attribute etc
+end
+
+function splitters.reset()
+ if #stack > 0 then
+ stack = { }
+ popsplitter() -- resets attribute etc
+ disable()
+ end
+end
+
+-- interface
+
+commands.definefontsolution = splitters.define
+commands.startfontsolution = splitters.start
+commands.stopfontsolution = splitters.stop
+commands.setfontsolution = splitters.set
+commands.resetfontsolution = splitters.reset
diff --git a/tex/context/base/font-syn.lua b/tex/context/base/font-syn.lua
index dd6c47a88..27176dade 100644
--- a/tex/context/base/font-syn.lua
+++ b/tex/context/base/font-syn.lua
@@ -1,1724 +1,1724 @@
-if not modules then modules = { } end modules ['font-syn'] = {
- 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"
-}
-
--- todo: subs in lookups requests
-
-local next, tonumber, type, tostring = next, tonumber, type, tostring
-local sub, gsub, lower, match, find, lower, upper = string.sub, string.gsub, string.lower, string.match, string.find, string.lower, string.upper
-local find, gmatch = string.find, string.gmatch
-local concat, sort, format = table.concat, table.sort, string.format
-local serialize = table.serialize
-local lpegmatch = lpeg.match
-local unpack = unpack or table.unpack
-local formatters = string.formatters
-
-local allocate = utilities.storage.allocate
-local sparse = utilities.storage.sparse
-
-local removesuffix = file.removesuffix
-local splitbase = file.splitbase
-local splitname = file.splitname
-local basename = file.basename
-local nameonly = file.nameonly
-local pathpart = file.pathpart
-local filejoin = file.join
-local is_qualified_path = file.is_qualified_path
-local exists = io.exists
-
-local findfile = resolvers.findfile
-local cleanpath = resolvers.cleanpath
-local resolveresolved = resolvers.resolve
-
-local trace_names = false trackers.register("fonts.names", function(v) trace_names = v end)
-local trace_warnings = false trackers.register("fonts.warnings", function(v) trace_warnings = v end)
-local trace_specifications = false trackers.register("fonts.specifications", function(v) trace_specifications = v end)
-
-local report_names = logs.reporter("fonts","names")
-
---[[ldx--
-<p>This module implements a name to filename resolver. Names are resolved
-using a table that has keys filtered from the font related files.</p>
---ldx]]--
-
-fonts = fonts or { } -- also used elsewhere
-
-local names = font.names or allocate { }
-fonts.names = names
-
-local filters = names.filters or { }
-names.filters = filters
-
-names.data = names.data or allocate { }
-
-names.version = 1.110
-names.basename = "names"
-names.saved = false
-names.loaded = false
-names.be_clever = true
-names.enabled = true
-names.cache = containers.define("fonts","data",names.version,true)
-
-local autoreload = true
-
-directives.register("fonts.autoreload", function(v) autoreload = toboolean(v) end)
-
---[[ldx--
-<p>A few helpers.</p>
---ldx]]--
-
-local P, C, Cc, Cs = lpeg.P, lpeg.C, lpeg.Cc, lpeg.Cs
-
--- what to do with 'thin'
-
-local weights = Cs ( -- not extra
- P("demibold")
- + P("semibold")
- + P("mediumbold")
- + P("ultrabold")
- + P("extrabold")
- + P("ultralight")
- + P("bold")
- + P("demi")
- + P("semi")
- + P("light")
- + P("medium")
- + P("heavy")
- + P("ultra")
- + P("black")
- + P("bol") -- / "bold"
- + P("regular") / "normal"
-)
-
-local normalized_weights = sparse {
- regular = "normal",
-}
-
-local styles = Cs (
- P("reverseoblique") / "reverseitalic"
- + P("regular") / "normal"
- + P("italic")
- + P("oblique") / "italic"
- + P("slanted")
- + P("roman") / "normal"
- + P("ital") / "italic"
- + P("ita") / "italic"
-)
-
-local normalized_styles = sparse {
- reverseoblique = "reverseitalic",
- regular = "normal",
- oblique = "italic",
-}
-
-local widths = Cs(
- P("condensed")
- + P("thin")
- + P("expanded")
- + P("cond") / "condensed"
- + P("normal")
- + P("book") / "normal"
-)
-
-local normalized_widths = sparse()
-
-local variants = Cs( -- fax casual
- P("smallcaps")
- + P("oldstyle")
- + P("caps") / "smallcaps"
-)
-
-local normalized_variants = sparse()
-
-names.knownweights = {
- "black",
- "bold",
- "demi",
- "demibold",
- "extrabold",
- "heavy",
- "light",
- "medium",
- "mediumbold",
- "normal",
- "regular",
- "semi",
- "semibold",
- "ultra",
- "ultrabold",
- "ultralight",
-}
-
-names.knownstyles = {
- "italic",
- "normal",
- "oblique",
- "regular",
- "reverseitalic",
- "reverseoblique",
- "roman",
- "slanted",
-}
-
-names.knownwidths = {
- "book",
- "condensed",
- "expanded",
- "normal",
- "thin",
-}
-
-names.knownvariants = {
- "normal",
- "oldstyle",
- "smallcaps",
-}
-
-local any = P(1)
-
-local analyzed_table
-
-local analyzer = Cs (
- (
- weights / function(s) analyzed_table[1] = s return "" end
- + styles / function(s) analyzed_table[2] = s return "" end
- + widths / function(s) analyzed_table[3] = s return "" end
- + variants / function(s) analyzed_table[4] = s return "" end
- + any
- )^0
-)
-
-local splitter = lpeg.splitat("-")
-
-function names.splitspec(askedname)
- local name, weight, style, width, variant = lpegmatch(splitter,askedname)
- weight = weight and lpegmatch(weights, weight) or weight
- style = style and lpegmatch(styles, style) or style
- width = width and lpegmatch(widths, width) or width
- variant = variant and lpegmatch(variants,variant) or variant
- if trace_names then
- report_names("requested name %a split in name %a, weight %a, style %a, width %a and variant %a",
- askedname,name,weight,style,width,variant)
- end
- if not weight or not weight or not width or not variant then
- weight, style, width, variant = weight or "normal", style or "normal", width or "normal", variant or "normal"
- if trace_names then
- report_names("request %a normalized to '%s-%s-%s-%s-%s'",
- askedname,name,weight,style,width,variant)
- end
- end
- return name or askedname, weight, style, width, variant
-end
-
-local function analyzespec(somename)
- if somename then
- analyzed_table = { }
- local name = lpegmatch(analyzer,somename)
- return name, analyzed_table[1], analyzed_table[2], analyzed_table[3], analyzed_table[4]
- end
-end
-
---[[ldx--
-<p>It would make sense to implement the filters in the related modules,
-but to keep the overview, we define them here.</p>
---ldx]]--
-
-filters.otf = fontloader.info
-filters.ttf = fontloader.info
-filters.ttc = fontloader.info
-filters.dfont = fontloader.info
-
-function fontloader.fullinfo(...) -- check with taco what we get / could get
- local ff = fontloader.open(...)
- if ff then
- local d = ff and fontloader.to_table(ff)
- d.glyphs, d.subfonts, d.gpos, d.gsub, d.lookups = nil, nil, nil, nil, nil
- fontloader.close(ff)
- return d
- else
- return nil, "error in loading font"
- end
-end
-
-filters.otf = fontloader.fullinfo
-
-function filters.afm(name)
- -- we could parse the afm file as well, and then report an error but
- -- it's not worth the trouble
- local pfbname = findfile(removesuffix(name)..".pfb","pfb") or ""
- if pfbname == "" then
- pfbname = findfile(nameonly(name)..".pfb","pfb") or ""
- end
- if pfbname ~= "" then
- local f = io.open(name)
- if f then
- local hash = { }
- for line in f:lines() do
- local key, value = match(line,"^(.+)%s+(.+)%s*$")
- if key and #key > 0 then
- hash[lower(key)] = value
- end
- if find(line,"StartCharMetrics") then
- break
- end
- end
- f:close()
- return hash
- end
- end
- return nil, "no matching pfb file"
-end
-
-function filters.pfb(name)
- return fontloader.info(name)
-end
-
---[[ldx--
-<p>The scanner loops over the filters using the information stored in
-the file databases. Watch how we check not only for the names, but also
-for combination with the weight of a font.</p>
---ldx]]--
-
-filters.list = {
- "otf", "ttf", "ttc", "dfont", "afm",
- -- "ttc", "otf", "ttf", "dfont", "afm",
-}
-
-names.fontconfigfile = "fonts.conf" -- a bit weird format, bonus feature
-names.osfontdirvariable = "OSFONTDIR" -- the official way, in minimals etc
-
-filters.paths = { }
-filters.names = { }
-
-function names.getpaths(trace)
- local hash, result, r = { }, { }, 0
- local function collect(t,where)
- for i=1,#t do
- local v = cleanpath(t[i])
- v = gsub(v,"/+$","") -- not needed any more
- local key = lower(v)
- report_names("%a specifies path %a",where,v)
- if not hash[key] then
- r = r + 1
- result[r] = v
- hash[key] = true
- end
- end
- end
- local path = names.osfontdirvariable or ""
- if path ~= "" then
- collect(resolvers.expandedpathlist(path),path)
- end
- if xml then
- local confname = resolvers.expansion("FONTCONFIG_FILE") or ""
- if confname == "" then
- confname = names.fontconfigfile or ""
- end
- if confname ~= "" then
- -- first look in the tex tree
- local name = findfile(confname,"fontconfig files") or ""
- if name == "" then
- -- after all, fontconfig is a unix thing
- name = filejoin("/etc",confname)
- if not lfs.isfile(name) then
- name = "" -- force quit
- end
- end
- if name ~= "" and lfs.isfile(name) then
- if trace_names then
- report_names("%s fontconfig file %a","loading",name)
- end
- local xmldata = xml.load(name)
- -- begin of untested mess
- xml.include(xmldata,"include","",true,function(incname)
- if not is_qualified_path(incname) then
- local path = pathpart(name) -- main name
- if path ~= "" then
- incname = filejoin(path,incname)
- end
- end
- if lfs.isfile(incname) then
- if trace_names then
- report_names("%s fontconfig file %a","merging included",incname)
- end
- return io.loaddata(incname)
- elseif trace_names then
- report_names("%s fontconfig file: %a","ignoring included",incname)
- end
- end)
- -- end of untested mess
- local fontdirs = xml.collect_texts(xmldata,"dir",true)
- if trace_names then
- report_names("%s dirs found in fontconfig",#fontdirs)
- end
- collect(fontdirs,"fontconfig file")
- end
- end
- end
- function names.getpaths()
- return result
- end
- return result
-end
-
-local function cleanname(name)
- return (gsub(lower(name),"[^%a%d]",""))
-end
-
-local function cleanfilename(fullname,defaultsuffix)
- local path, name, suffix = splitname(fullname)
- name = gsub(lower(name),"[^%a%d]","")
- if suffix and suffix ~= "" then
- return name .. ".".. suffix
- elseif defaultsuffix and defaultsuffix ~= "" then
- return name .. ".".. defaultsuffix
- else
- return name
- end
-end
-
-names.cleanname = cleanname
-names.cleanfilename = cleanfilename
-
-local function check_names(result)
- local names = result.names
- if names then
- for i=1,#names do
- local name = names[i]
- if name.lang == "English (US)" then
- return name.names
- end
- end
- end
-end
-
-local function walk_tree(pathlist,suffix,identify)
- if pathlist then
- for i=1,#pathlist do
- local path = pathlist[i]
- path = cleanpath(path .. "/")
- path = gsub(path,"/+","/")
- local pattern = path .. "**." .. suffix -- ** forces recurse
- report_names("globbing path %a",pattern)
- local t = dir.glob(pattern)
- sort(t,sorter)
- for j=1,#t do
- local completename = t[j]
- identify(completename,basename(completename),suffix,completename)
- end
- end
- end
-end
-
-local function check_name(data,result,filename,modification,suffix,subfont)
- -- shortcuts
- local specifications = data.specifications
- -- prepare
- local names = check_names(result)
- -- fetch
- local familyname = names and names.preffamilyname or result.familyname
- local fullname = names and names.fullname or result.fullname
- local fontname = result.fontname
- local subfamily = names and names.subfamily
- local modifiers = names and names.prefmodifiers
- local weight = names and names.weight or result.weight
- local italicangle = tonumber(result.italicangle)
- local subfont = subfont or nil
- local rawname = fullname or fontname or familyname
- -- normalize
- familyname = familyname and cleanname(familyname)
- fullname = fullname and cleanname(fullname)
- fontname = fontname and cleanname(fontname)
- subfamily = subfamily and cleanname(subfamily)
- modifiers = modifiers and cleanname(modifiers)
- weight = weight and cleanname(weight)
- italicangle = italicangle == 0 and nil
- -- analyze
- local a_name, a_weight, a_style, a_width, a_variant = analyzespec(fullname or fontname or familyname)
- -- check
- local width = a_width
- local variant = a_variant
- local style = modifiers and gsub(modifiers,"[^%a]","")
- if not style and italicangle then
- style = "italic"
- end
- if not variant or variant == "" then
- variant = "normal"
- end
- if not weight or weight == "" then
- weight = a_weight
- end
- if not style or style == "" then
- style = a_style
- end
- if not familyname then
- familyname = a_name
- end
- fontname = fontname or fullname or familyname or basename(filename)
- fullname = fullname or fontname
- familyname = familyname or fontname
- specifications[#specifications + 1] = {
- filename = filename, -- unresolved
- format = lower(suffix),
- subfont = subfont,
- rawname = rawname,
- familyname = familyname,
- fullname = fullname,
- fontname = fontname,
- subfamily = subfamily,
- modifiers = modifiers,
- weight = weight,
- style = style,
- width = width,
- variant = variant,
- minsize = result.design_range_bottom or 0,
- maxsize = result.design_range_top or 0,
- designsize = result.design_size or 0,
- modification = modification or 0,
- }
-end
-
-local function cleanupkeywords()
- local data = names.data
- local specifications = names.data.specifications
- if specifications then
- local weights = { }
- local styles = { }
- local widths = { }
- local variants = { }
- for i=1,#specifications do
- local s = specifications[i]
- -- fix (sofar styles are taken from the name, and widths from the specification)
- local _, b_weight, b_style, b_width, b_variant = analyzespec(s.weight)
- local _, c_weight, c_style, c_width, c_variant = analyzespec(s.style)
- local _, d_weight, d_style, d_width, d_variant = analyzespec(s.width)
- local _, e_weight, e_style, e_width, e_variant = analyzespec(s.variant)
- local _, f_weight, f_style, f_width, f_variant = analyzespec(s.fullname or "")
- local weight = b_weight or c_weight or d_weight or e_weight or f_weight or "normal"
- local style = b_style or c_style or d_style or e_style or f_style or "normal"
- local width = b_width or c_width or d_width or e_width or f_width or "normal"
- local variant = b_variant or c_variant or d_variant or e_variant or f_variant or "normal"
- if not weight or weight == "" then weight = "normal" end
- if not style or style == "" then style = "normal" end
- if not width or width == "" then width = "normal" end
- if not variant or variant == "" then variant = "normal" end
- weights [weight ] = (weights [weight ] or 0) + 1
- styles [style ] = (styles [style ] or 0) + 1
- widths [width ] = (widths [width ] or 0) + 1
- variants[variant] = (variants[variant] or 0) + 1
- if weight ~= s.weight then
- s.fontweight = s.weight
- end
- s.weight, s.style, s.width, s.variant = weight, style, width, variant
- end
- local stats = data.statistics
- stats.used_weights, stats.used_styles, stats.used_widths, stats.used_variants = weights, styles, widths, variants
- end
-end
-
-local function collectstatistics()
- local data = names.data
- local specifications = data.specifications
- if specifications then
- local weights = { }
- local styles = { }
- local widths = { }
- local variants = { }
- for i=1,#specifications do
- local s = specifications[i]
- local weight = s.weight
- local style = s.style
- local width = s.width
- local variant = s.variant
- if weight then weights [weight ] = (weights [weight ] or 0) + 1 end
- if style then styles [style ] = (styles [style ] or 0) + 1 end
- if width then widths [width ] = (widths [width ] or 0) + 1 end
- if variant then variants[variant] = (variants[variant] or 0) + 1 end
- end
- local stats = data.statistics
- stats.weights = weights
- stats.styles = styles
- stats.widths = widths
- stats.variants = variants
- stats.fonts = #specifications
- end
-end
-
-local function collecthashes()
- local data = names.data
- local mappings = data.mappings
- local fallbacks = data.fallbacks
- local specifications = data.specifications
- local nofmappings = 0
- local noffallbacks = 0
- if specifications then
- -- maybe multiple passes
- for index=1,#specifications do
- local s = specifications[index]
- local format, fullname, fontname, familyname, weight, subfamily = s.format, s.fullname, s.fontname, s.familyname, s.weight, s.subfamily
- local mf, ff = mappings[format], fallbacks[format]
- if fullname and not mf[fullname] then
- mf[fullname], nofmappings = index, nofmappings + 1
- end
- if fontname and not mf[fontname] then
- mf[fontname], nofmappings = index, nofmappings + 1
- end
- if familyname and weight and weight ~= sub(familyname,#familyname-#weight+1,#familyname) then
- local madename = familyname .. weight
- if not mf[madename] and not ff[madename] then
- ff[madename], noffallbacks = index, noffallbacks + 1
- end
- end
- if familyname and subfamily and subfamily ~= sub(familyname,#familyname-#subfamily+1,#familyname) then
- local extraname = familyname .. subfamily
- if not mf[extraname] and not ff[extraname] then
- ff[extraname], noffallbacks = index, noffallbacks + 1
- end
- end
- if familyname and not mf[familyname] and not ff[familyname] then
- ff[familyname], noffallbacks = index, noffallbacks + 1
- end
- end
- end
- return nofmappings, noffallbacks
-end
-
-local function collectfamilies()
- local data = names.data
- local specifications = data.specifications
- local families = data.families
- for index=1,#specifications do
- local familyname = specifications[index].familyname
- local family = families[familyname]
- if not family then
- families[familyname] = { index }
- else
- family[#family+1] = index
- end
- end
-end
-
-local function checkduplicate(where) -- fails on "Romantik" but that's a border case anyway
- local data = names.data
- local mapping = data[where]
- local specifications = data.specifications
- local loaded = { }
- if specifications and mapping then
- for _, m in next, mapping do
- for k, v in next, m do
- local s = specifications[v]
- local hash = formatters["%s-%s-%s-%s-%s"](s.familyname,s.weight or "*",s.style or "*",s.width or "*",s.variant or "*")
- local h = loaded[hash]
- if h then
- local ok = true
- local fn = s.filename
- for i=1,#h do
- local hn = s.filename
- if h[i] == fn then
- ok = false
- break
- end
- end
- if ok then
- h[#h+1] = fn
- end
- else
- loaded[hash] = { s.filename }
- end
- end
- end
- end
- local n = 0
- for k, v in table.sortedhash(loaded) do
- local nv = #v
- if nv > 1 then
- if trace_warnings then
- report_names("lookup %a clashes with %a",k,v)
- end
- n = n + nv
- end
- end
- report_names("%a double lookups in %a",n,where)
-end
-
-local function checkduplicates()
- checkduplicate("mappings")
- checkduplicate("fallbacks")
-end
-
-local sorter = function(a,b)
- return a > b -- to be checked
-end
-
-local function sorthashes()
- local data = names.data
- local list = filters.list
- local mappings = data.mappings
- local fallbacks = data.fallbacks
- local sorted_mappings = { }
- local sorted_fallbacks = { }
- data.sorted_mappings = sorted_mappings
- data.sorted_fallbacks = sorted_fallbacks
- for i=1,#list do
- local l = list[i]
- sorted_mappings [l] = table.keys(mappings[l])
- sorted_fallbacks[l] = table.keys(fallbacks[l])
- sort(sorted_mappings [l],sorter)
- sort(sorted_fallbacks[l],sorter)
- end
- data.sorted_families = table.keys(data.families)
- sort(data.sorted_families,sorter)
-end
-
-local function unpackreferences()
- local data = names.data
- local specifications = data.specifications
- if specifications then
- for k, v in next, data.families do
- for i=1,#v do
- v[i] = specifications[v[i]]
- end
- end
- local mappings = data.mappings
- if mappings then
- for _, m in next, mappings do
- for k, v in next, m do
- m[k] = specifications[v]
- end
- end
- end
- local fallbacks = data.fallbacks
- if fallbacks then
- for _, f in next, fallbacks do
- for k, v in next, f do
- f[k] = specifications[v]
- end
- end
- end
- end
-end
-
-local function analyzefiles(olddata)
- if not trace_warnings then
- report_names("warnings are disabled (tracker 'fonts.warnings')")
- end
- local data = names.data
- local done = { }
- local totalnofread = 0
- local totalnofskipped = 0
- local totalnofduplicates = 0
- local nofread = 0
- local nofskipped = 0
- local nofduplicates = 0
- local skip_paths = filters.paths
- local skip_names = filters.names
- local specifications = data.specifications
- local oldindices = olddata and olddata.indices or { }
- local oldspecifications = olddata and olddata.specifications or { }
- local oldrejected = olddata and olddata.rejected or { }
- local treatmentdata = fonts.treatments.data
- local function identify(completename,name,suffix,storedname)
- local pathpart, basepart = splitbase(completename)
- nofread = nofread + 1
- local treatment = treatmentdata[completename] or treatmentdata[basepart]
- if treatment and treatment.ignored then
- if trace_names then
- report_names("%s font %a is ignored, reason %a",suffix,completename,treatment.comment or "unknown")
- end
- nofskipped = nofskipped + 1
- elseif done[name] then
- -- already done (avoid otf afm clash)
- if trace_names then
- report_names("%s font %a already done",suffix,completename)
- end
- nofduplicates = nofduplicates + 1
- nofskipped = nofskipped + 1
- elseif not exists(completename) then
- -- weird error
- if trace_names then
- report_names("%s font %a does not really exist",suffix,completename)
- end
- nofskipped = nofskipped + 1
- elseif not is_qualified_path(completename) and findfile(completename,suffix) == "" then
- -- not locatable by backend anyway
- if trace_names then
- report_names("%s font %a cannot be found by backend",suffix,completename)
- end
- nofskipped = nofskipped + 1
- else
- if #skip_paths > 0 then
- for i=1,#skip_paths do
- if find(pathpart,skip_paths[i]) then
- if trace_names then
- report_names("rejecting path of %s font %a",suffix,completename)
- end
- nofskipped = nofskipped + 1
- return
- end
- end
- end
- if #skip_names > 0 then
- for i=1,#skip_paths do
- if find(basepart,skip_names[i]) then
- done[name] = true
- if trace_names then
- report_names("rejecting name of %s font %a",suffix,completename)
- end
- nofskipped = nofskipped + 1
- return
- end
- end
- end
- if trace_names then
- report_names("identifying %s font %a",suffix,completename)
- end
- local result = nil
- local modification = lfs.attributes(completename,"modification")
- if olddata and modification and modification > 0 then
- local oldindex = oldindices[storedname] -- index into specifications
- if oldindex then
- local oldspecification = oldspecifications[oldindex]
- if oldspecification and oldspecification.filename == storedname then -- double check for out of sync
- local oldmodification = oldspecification.modification
- if oldmodification == modification then
- result = oldspecification
- specifications[#specifications + 1] = result
- else
- end
- else
- end
- elseif oldrejected[storedname] == modification then
- result = false
- end
- end
- if result == nil then
- local result, message = filters[lower(suffix)](completename)
- if result then
- if result[1] then
- for r=1,#result do
- local ok = check_name(data,result[r],storedname,modification,suffix,r-1) -- subfonts start at zero
- -- if not ok then
- -- nofskipped = nofskipped + 1
- -- end
- end
- else
- local ok = check_name(data,result,storedname,modification,suffix)
- -- if not ok then
- -- nofskipped = nofskipped + 1
- -- end
- end
- if trace_warnings and message and message ~= "" then
- report_names("warning when identifying %s font %a, %s",suffix,completename,message)
- end
- elseif trace_warnings then
- nofskipped = nofskipped + 1
- report_names("error when identifying %s font %a, %s",suffix,completename,message or "unknown")
- end
- end
- done[name] = true
- end
- logs.flush() -- a bit overkill for each font, maybe not needed here
- end
- local function traverse(what, method)
- local list = filters.list
- for n=1,#list do
- local suffix = list[n]
- local t = os.gettimeofday() -- use elapser
- nofread, nofskipped, nofduplicates = 0, 0, 0
- suffix = lower(suffix)
- report_names("identifying %s font files with suffix %a",what,suffix)
- method(suffix)
- suffix = upper(suffix)
- report_names("identifying %s font files with suffix %a",what,suffix)
- method(suffix)
- totalnofread, totalnofskipped, totalnofduplicates = totalnofread + nofread, totalnofskipped + nofskipped, totalnofduplicates + nofduplicates
- local elapsed = os.gettimeofday() - t
- report_names("%s %s files identified, %s skipped, %s duplicates, %s hash entries added, runtime %0.3f seconds",nofread,what,nofskipped,nofduplicates,nofread-nofskipped,elapsed)
- end
- logs.flush()
- end
- -- problem .. this will not take care of duplicates
- local function withtree(suffix)
- resolvers.dowithfilesintree(".*%." .. suffix .. "$", function(method,root,path,name)
- if method == "file" or method == "tree" then
- local completename = root .."/" .. path .. "/" .. name
- completename = resolveresolved(completename) -- no shortcut
- identify(completename,name,suffix,name)
- return true
- end
- end, function(blobtype,blobpath,pattern)
- blobpath = resolveresolved(blobpath) -- no shortcut
- report_names("scanning path %a for %s files",blobpath,suffix)
- end, function(blobtype,blobpath,pattern,total,checked,done)
- blobpath = resolveresolved(blobpath) -- no shortcut
- report_names("%s entries found, %s %s files checked, %s okay",total,checked,suffix,done)
- end)
- end
- local function withlsr(suffix) -- all trees
- -- we do this only for a stupid names run, not used for context itself,
- -- using the vars is too clumsy so we just stick to a full scan instead
- local pathlist = resolvers.splitpath(resolvers.showpath("ls-R") or "")
- walk_tree(pathlist,suffix,identify)
- end
- local function withsystem(suffix) -- OSFONTDIR cum suis
- walk_tree(names.getpaths(trace),suffix,identify)
- end
- traverse("tree",withtree) -- TEXTREE only
- if texconfig.kpse_init then
- traverse("lsr", withlsr)
- else
- traverse("system", withsystem)
- end
- data.statistics.readfiles = totalnofread
- data.statistics.skippedfiles = totalnofskipped
- data.statistics.duplicatefiles = totalnofduplicates
-end
-
-local function addfilenames()
- local data = names.data
- local specifications = data.specifications
- local indices = { }
- local files = { }
- for i=1,#specifications do
- local fullname = specifications[i].filename
- files[cleanfilename(fullname)] = fullname
- indices[fullname] = i
- end
- data.files = files
- data.indices = indices
-end
-
-local function rejectclashes() -- just to be sure, so no explicit afm will be found then
- local specifications = names.data.specifications
- local used = { }
- local okay = { }
- local rejected = { } -- only keep modification
- local o = 0
- for i=1,#specifications do
- local s = specifications[i]
- local f = s.fontname
- if f then
- local fnd = used[f]
- local fnm = s.filename
- if fnd then
- if trace_warnings then
- report_names("fontname %a clashes, %a rejected in favor of %a",f,fnm,fnd)
- end
- rejected[f] = s.modification
- else
- used[f] = fnm
- o = o + 1
- okay[o] = s
- end
- else
- o = o + 1
- okay[o] = s
- end
- end
- local d = #specifications - #okay
- if d > 0 then
- report_names("%s files rejected due to clashes",d)
- end
- names.data.specifications = okay
- names.data.rejected = rejected
-end
-
-local function resetdata()
- local mappings = { }
- local fallbacks = { }
- for _, k in next, filters.list do
- mappings [k] = { }
- fallbacks[k] = { }
- end
- names.data = {
- version = names.version,
- mappings = mappings,
- fallbacks = fallbacks,
- specifications = { },
- families = { },
- statistics = { },
- names = { },
- indices = { },
- rejected = { },
- datastate = resolvers.datastate(),
- }
-end
-
-function names.identify(force)
- local starttime = os.gettimeofday() -- use elapser
- resetdata()
- analyzefiles(not force and names.readdata(names.basename))
- rejectclashes()
- collectfamilies()
- collectstatistics()
- cleanupkeywords()
- collecthashes()
- checkduplicates()
- addfilenames()
- -- sorthashes() -- will be resorted when saved
- report_names("total scan time %0.3f seconds",os.gettimeofday()-starttime)
-end
-
-function names.is_permitted(name)
- return containers.is_usable(names.cache, name)
-end
-function names.writedata(name,data)
- containers.write(names.cache,name,data)
-end
-function names.readdata(name)
- return containers.read(names.cache,name)
-end
-
-function names.load(reload,force)
- if not names.loaded then
- if reload then
- if names.is_permitted(names.basename) then
- names.identify(force)
- names.writedata(names.basename,names.data)
- else
- report_names("unable to access database cache")
- end
- names.saved = true
- end
- local data = names.readdata(names.basename)
- names.data = data
- if not names.saved then
- if not data or not next(data) or not data.specifications or not next(data.specifications) then
- names.load(true)
- end
- names.saved = true
- end
- if not data then
- report_names("accessing the data table failed")
- else
- unpackreferences()
- sorthashes()
- end
- names.loaded = true
- end
-end
-
-local function list_them(mapping,sorted,pattern,t,all)
- if mapping[pattern] then
- t[pattern] = mapping[pattern]
- else
- for k=1,#sorted do
- local v = sorted[k]
- if not t[v] and find(v,pattern) then
- t[v] = mapping[v]
- if not all then
- return
- end
- end
- end
- end
-end
-
-function names.list(pattern,reload,all) -- here?
- names.load() -- todo reload
- if names.loaded then
- local t = { }
- local data = names.data
- if data then
- local list = filters.list
- local mappings = data.mappings
- local sorted_mappings = data.sorted_mappings
- local fallbacks = data.fallbacks
- local sorted_fallbacks = data.sorted_fallbacks
- for i=1,#list do
- local format = list[i]
- list_them(mappings[format],sorted_mappings[format],pattern,t,all)
- if next(t) and not all then
- return t
- end
- list_them(fallbacks[format],sorted_fallbacks[format],pattern,t,all)
- if next(t) and not all then
- return t
- end
- end
- end
- return t
- end
-end
-
-local reloaded = false
-
-local function is_reloaded()
- if not reloaded then
- local data = names.data
- if autoreload then
- local c_status = serialize(resolvers.datastate())
- local f_status = serialize(data.datastate)
- if c_status == f_status then
- if trace_names then
- report_names("font database has matching configuration and file hashes")
- end
- return
- else
- report_names("font database has mismatching configuration and file hashes")
- end
- else
- report_names("font database is regenerated (controlled by directive 'fonts.autoreload')")
- end
- names.loaded = false
- reloaded = true
- logs.flush()
- names.load(true)
- end
-end
-
---[[ldx--
-<p>The resolver also checks if the cached names are loaded. Being clever
-here is for testing purposes only (it deals with names prefixed by an
-encoding name).</p>
---ldx]]--
-
-local function fuzzy(mapping,sorted,name,sub)
- local condensed = gsub(name,"[^%a%d]","")
- for k=1,#sorted do
- local v = sorted[k]
- if find(v,condensed) then
- return mapping[v], v
- end
- end
-end
-
--- we could cache a lookup .. maybe some day ... (only when auto loaded!)
-
-local function foundname(name,sub) -- sub is not used currently
- local data = names.data
- local mappings = data.mappings
- local sorted_mappings = data.sorted_mappings
- local fallbacks = data.fallbacks
- local sorted_fallbacks = data.sorted_fallbacks
- local list = filters.list
- -- dilemma: we lookup in the order otf ttf ttc ... afm but now an otf fallback
- -- can come after an afm match ... well, one should provide nice names anyway
- -- and having two lists is not an option
- for i=1,#list do
- local l = list[i]
- local found = mappings[l][name]
- if found then
- if trace_names then
- report_names("resolved via direct name match: %a",name)
- end
- return found
- end
- end
- for i=1,#list do
- local l = list[i]
- local found, fname = fuzzy(mappings[l],sorted_mappings[l],name,sub)
- if found then
- if trace_names then
- report_names("resolved via fuzzy name match: %a onto %a",name,fname)
- end
- return found
- end
- end
- for i=1,#list do
- local l = list[i]
- local found = fallbacks[l][name]
- if found then
- if trace_names then
- report_names("resolved via direct fallback match: %a",name)
- end
- return found
- end
- end
- for i=1,#list do
- local l = list[i]
- local found, fname = fuzzy(sorted_mappings[l],sorted_fallbacks[l],name,sub)
- if found then
- if trace_names then
- report_names("resolved via fuzzy fallback match: %a onto %a",name,fname)
- end
- return found
- end
- end
- if trace_names then
- report_names("font with name %a cannot be found",name)
- end
-end
-
-function names.resolvedspecification(askedname,sub)
- if askedname and askedname ~= "" and names.enabled then
- askedname = cleanname(askedname)
- names.load()
- local found = foundname(askedname,sub)
- if not found and is_reloaded() then
- found = foundname(askedname,sub)
- end
- return found
- end
-end
-
-function names.resolve(askedname,sub)
- local found = names.resolvedspecification(askedname,sub)
- if found then
- return found.filename, found.subfont and found.rawname
- end
-end
-
--- function names.getfilename(askedname,suffix) -- last resort, strip funny chars
--- names.load()
--- local files = names.data.files
--- askedname = files and files[cleanfilename(askedname,suffix)] or ""
--- if askedname == "" then
--- return ""
--- else -- never entered
--- return resolvers.findbinfile(askedname,suffix) or ""
--- end
--- end
-
-function names.getfilename(askedname,suffix) -- last resort, strip funny chars
- names.load()
- local files = names.data.files
- local cleanname = cleanfilename(askedname,suffix)
- local found = files and files[cleanname] or ""
- if found == "" and is_reloaded() then
- files = names.data.files
- found = files and files[cleanname] or ""
- end
- if found and found ~= "" then
- return resolvers.findbinfile(found,suffix) or "" -- we still need to locate it
- end
-end
-
--- specified search
-
-local function s_collect_weight_style_width_variant(found,done,all,weight,style,width,variant,family)
- if family then
- for i=1,#family do
- local f = family[i]
- if f and weight == f.weight and style == f.style and width == f.width and variant == f.variant then
- found[#found+1], done[f] = f, true
- if not all then return end
- end
- end
- end
-end
-local function m_collect_weight_style_width_variant(found,done,all,weight,style,width,variant,families,sorted,strictname)
- for i=1,#sorted do
- local k = sorted[i]
- local family = families[k]
- for i=1,#family do
- local f = family[i]
- if not done[f] and weight == f.weight and style == f.style and width == f.width and variant == f.variant and find(f.fontname,strictname) then
- found[#found+1], done[f] = f, true
- if not all then return end
- end
- end
- end
-end
-
-local function s_collect_weight_style_width(found,done,all,weight,style,width,family)
- if family then
- for i=1,#family do
- local f = family[i]
- if f and weight == f.weight and style == f.style and width == f.width then
- found[#found+1], done[f] = f, true
- if not all then return end
- end
- end
- end
-end
-local function m_collect_weight_style_width(found,done,all,weight,style,width,families,sorted,strictname)
- for i=1,#sorted do
- local k = sorted[i]
- local family = families[k]
- for i=1,#family do
- local f = family[i]
- if not done[f] and weight == f.weight and style == f.style and width == f.width and find(f.fontname,strictname) then
- found[#found+1], done[f] = f, true
- if not all then return end
- end
- end
- end
-end
-
-local function s_collect_weight_style(found,done,all,weight,style,family)
- if family then
- for i=1,#family do local f = family[i]
- if f and weight == f.weight and style == f.style then
- found[#found+1], done[f] = f, true
- if not all then return end
- end
- end
- end
-end
-local function m_collect_weight_style(found,done,all,weight,style,families,sorted,strictname)
- for i=1,#sorted do
- local k = sorted[i]
- local family = families[k]
- for i=1,#family do
- local f = family[i]
- if not done[f] and weight == f.weight and style == f.style and find(f.fontname,strictname) then
- found[#found+1], done[f] = f, true
- if not all then return end
- end
- end
- end
-end
-
-local function s_collect_style_width(found,done,all,style,width,family)
- if family then
- for i=1,#family do local f = family[i]
- if f and style == f.style and width == f.width then
- found[#found+1], done[f] = f, true
- if not all then return end
- end
- end
- end
-end
-local function m_collect_style_width(found,done,all,style,width,families,sorted,strictname)
- for i=1,#sorted do
- local k = sorted[i]
- local family = families[k]
- for i=1,#family do
- local f = family[i]
- if not done[f] and style == f.style and width == f.width and find(f.fontname,strictname) then
- found[#found+1], done[f] = f, true
- if not all then return end
- end
- end
- end
-end
-
-local function s_collect_weight(found,done,all,weight,family)
- if family then
- for i=1,#family do local f = family[i]
- if f and weight == f.weight then
- found[#found+1], done[f] = f, true
- if not all then return end
- end
- end
- end
-end
-local function m_collect_weight(found,done,all,weight,families,sorted,strictname)
- for i=1,#sorted do
- local k = sorted[i]
- local family = families[k]
- for i=1,#family do
- local f = family[i]
- if not done[f] and weight == f.weight and find(f.fontname,strictname) then
- found[#found+1], done[f] = f, true
- if not all then return end
- end
- end
- end
-end
-
-local function s_collect_style(found,done,all,style,family)
- if family then
- for i=1,#family do local f = family[i]
- if f and style == f.style then
- found[#found+1], done[f] = f, true
- if not all then return end
- end
- end
- end
-end
-local function m_collect_style(found,done,all,style,families,sorted,strictname)
- for i=1,#sorted do
- local k = sorted[i]
- local family = families[k]
- for i=1,#family do
- local f = family[i]
- if not done[f] and style == f.style and find(f.fontname,strictname) then
- found[#found+1], done[f] = f, true
- if not all then return end
- end
- end
- end
-end
-
-local function s_collect_width(found,done,all,width,family)
- if family then
- for i=1,#family do local f = family[i]
- if f and width == f.width then
- found[#found+1], done[f] = f, true
- if not all then return end
- end
- end
- end
-end
-local function m_collect_width(found,done,all,width,families,sorted,strictname)
- for i=1,#sorted do
- local k = sorted[i]
- local family = families[k]
- for i=1,#family do
- local f = family[i]
- if not done[f] and width == f.width and find(f.fontname,strictname) then
- found[#found+1], done[f] = f, true
- if not all then return end
- end
- end
- end
-end
-
-local function s_collect(found,done,all,family)
- if family then
- for i=1,#family do local f = family[i]
- if f then
- found[#found+1], done[f] = f, true
- if not all then return end
- end
- end
- end
-end
-local function m_collect(found,done,all,families,sorted,strictname)
- for i=1,#sorted do
- local k = sorted[i]
- local family = families[k]
- for i=1,#family do
- local f = family[i]
- if not done[f] and find(f.fontname,strictname) then
- found[#found+1], done[f] = f, true
- if not all then return end
- end
- end
- end
-end
-
-local function collect(stage,found,done,name,weight,style,width,variant,all)
- local data = names.data
- local families = data.families
- local sorted = data.sorted_families
- local strictname = "^".. name -- to be checked
- local family = families[name]
- if trace_names then
- report_names("resolving name %a, weight %a, style %a, width %a, variant %a",name,weight,style,width,variant)
- end
- if weight and weight ~= "" then
- if style and style ~= "" then
- if width and width ~= "" then
- if variant and variant ~= "" then
- if trace_names then
- report_names("resolving stage %s, name %a, weight %a, style %a, width %a, variant %a",stage,name,weight,style,width,variant)
- end
- s_collect_weight_style_width_variant(found,done,all,weight,style,width,variant,family)
- m_collect_weight_style_width_variant(found,done,all,weight,style,width,variant,families,sorted,strictname)
- else
- if trace_names then
- report_names("resolving stage %s, name %a, weight %a, style %a, width %a",stage,name,weight,style,width)
- end
- s_collect_weight_style_width(found,done,all,weight,style,width,family)
- m_collect_weight_style_width(found,done,all,weight,style,width,families,sorted,strictname)
- end
- else
- if trace_names then
- report_names("resolving stage %s, name %a, weight %a, style %a",stage,name,weight,style)
- end
- s_collect_weight_style(found,done,all,weight,style,family)
- m_collect_weight_style(found,done,all,weight,style,families,sorted,strictname)
- end
- else
- if trace_names then
- report_names("resolving stage %s, name %a, weight %a",stage,name,weight)
- end
- s_collect_weight(found,done,all,weight,family)
- m_collect_weight(found,done,all,weight,families,sorted,strictname)
- end
- elseif style and style ~= "" then
- if width and width ~= "" then
- if trace_names then
- report_names("resolving stage %s, name %a, style %a, width %a",stage,name,style,width)
- end
- s_collect_style_width(found,done,all,style,width,family)
- m_collect_style_width(found,done,all,style,width,families,sorted,strictname)
- else
- if trace_names then
- report_names("resolving stage %s, name %a, style %a",stage,name,style)
- end
- s_collect_style(found,done,all,style,family)
- m_collect_style(found,done,all,style,families,sorted,strictname)
- end
- elseif width and width ~= "" then
- if trace_names then
- report_names("resolving stage %s, name %a, width %a",stage,name,width)
- end
- s_collect_width(found,done,all,width,family)
- m_collect_width(found,done,all,width,families,sorted,strictname)
- else
- if trace_names then
- report_names("resolving stage %s, name %a",stage,name)
- end
- s_collect(found,done,all,family)
- m_collect(found,done,all,families,sorted,strictname)
- end
-end
-
-local function heuristic(name,weight,style,width,variant,all) -- todo: fallbacks
- local found, done = { }, { }
---~ print(name,weight,style,width,variant)
- weight, style, width, variant = weight or "normal", style or "normal", width or "normal", variant or "normal"
- name = cleanname(name)
- collect(1,found,done,name,weight,style,width,variant,all)
- -- still needed ?
- if #found == 0 and variant ~= "normal" then -- not weight
- variant = "normal"
- collect(4,found,done,name,weight,style,width,variant,all)
- end
- if #found == 0 and width ~= "normal" then
- width = "normal"
- collect(2,found,done,name,weight,style,width,variant,all)
- end
- if #found == 0 and weight ~= "normal" then -- not style
- weight = "normal"
- collect(3,found,done,name,weight,style,width,variant,all)
- end
- if #found == 0 and style ~= "normal" then -- not weight
- style = "normal"
- collect(4,found,done,name,weight,style,width,variant,all)
- end
- --
- local nf = #found
- if trace_names then
- if nf then
- local t = { }
- for i=1,nf do
- t[i] = formatters["%a"](found[i].fontname)
- end
- report_names("name %a resolved to %s instances: % t",name,nf,t)
- else
- report_names("name %a unresolved",name)
- end
- end
- if all then
- return nf > 0 and found
- else
- return found[1]
- end
-end
-
-function names.specification(askedname,weight,style,width,variant,reload,all)
- if askedname and askedname ~= "" and names.enabled then
- askedname = cleanname(askedname) -- or cleanname
- names.load(reload)
- local found = heuristic(askedname,weight,style,width,variant,all)
- if not found and is_reloaded() then
- found = heuristic(askedname,weight,style,width,variant,all)
- if not filename then
- found = foundname(askedname) -- old method
- end
- end
- return found
- end
-end
-
-function names.collect(askedname,weight,style,width,variant,reload,all)
- if askedname and askedname ~= "" and names.enabled then
- askedname = cleanname(askedname) -- or cleanname
- names.load(reload)
- local list = heuristic(askedname,weight,style,width,variant,true)
- if not list or #list == 0 and is_reloaded() then
- list = heuristic(askedname,weight,style,width,variant,true)
- end
- return list
- end
-end
-
-function names.collectspec(askedname,reload,all)
- local name, weight, style, width, variant = names.splitspec(askedname)
- return names.collect(name,weight,style,width,variant,reload,all)
-end
-
-function names.resolvespec(askedname,sub) -- redefined later
- local found = names.specification(names.splitspec(askedname))
- if found then
- return found.filename, found.subfont and found.rawname
- end
-end
-
-function names.collectfiles(askedname,reload) -- no all
- if askedname and askedname ~= "" and names.enabled then
- askedname = cleanname(askedname) -- or cleanname
- names.load(reload)
- local list = { }
- local specifications = names.data.specifications
- for i=1,#specifications do
- local s = specifications[i]
- if find(cleanname(basename(s.filename)),askedname) then
- list[#list+1] = s
- end
- end
- return list
- end
-end
-
--- todo:
---
--- blacklisted = {
--- ["cmr10.ttf"] = "completely messed up",
--- }
-
-function names.exists(name)
- local found = false
- local list = filters.list
- for k=1,#list do
- local v = list[k]
- found = (findfile(name,v) or "") ~= ""
- if found then
- return found
- end
- end
- return (findfile(name,"tfm") or "") ~= "" or (names.resolve(name) or "") ~= ""
-end
-
-local lastlookups, lastpattern = { }, ""
-
-function names.lookup(pattern,name,reload) -- todo: find
- if lastpattern ~= pattern then
- names.load(reload)
- local specifications = names.data.specifications
- local families = names.data.families
- local lookups = specifications
- if name then
- lookups = families[name]
- elseif not find(pattern,"=") then
- lookups = families[pattern]
- end
- if trace_names then
- report_names("starting with %s lookups for %a",#lookups,pattern)
- end
- if lookups then
- for key, value in gmatch(pattern,"([^=,]+)=([^=,]+)") do
- local t, n = { }, 0
- if find(value,"*") then
- value = string.topattern(value)
- for i=1,#lookups do
- local s = lookups[i]
- if find(s[key],value) then
- n = n + 1
- t[n] = lookups[i]
- end
- end
- else
- for i=1,#lookups do
- local s = lookups[i]
- if s[key] == value then
- n = n + 1
- t[n] = lookups[i]
- end
- end
- end
- if trace_names then
- report_names("%s matches for key %a with value %a",#t,key,value)
- end
- lookups = t
- end
- end
- lastpattern = pattern
- lastlookups = lookups or { }
- end
- return #lastlookups
-end
-
-function names.getlookupkey(key,n)
- local l = lastlookups[n or 1]
- return (l and l[key]) or ""
-end
-
-function names.noflookups()
- return #lastlookups
-end
-
-function names.getlookups(pattern,name,reload)
- if pattern then
- names.lookup(pattern,name,reload)
- end
- return lastlookups
-end
-
--- The following is new ... watch the overload!
-
-local specifications = allocate()
-names.specifications = specifications
-
--- files = {
--- name = "antykwapoltawskiego",
--- list = {
--- ["AntPoltLtCond-Regular.otf"] = {
--- -- name = "antykwapoltawskiego",
--- style = "regular",
--- weight = "light",
--- width = "condensed",
--- },
--- },
--- }
-
-function names.register(files)
- if files then
- local list, commonname = files.list, files.name
- if list then
- local n, m = 0, 0
- for filename, filespec in next, list do
- local name = lower(filespec.name or commonname)
- if name and name ~= "" then
- local style = normalized_styles [lower(filespec.style or "normal")]
- local width = normalized_widths [lower(filespec.width or "normal")]
- local weight = normalized_weights [lower(filespec.weight or "normal")]
- local variant = normalized_variants[lower(filespec.variant or "normal")]
- local weights = specifications[name ] if not weights then weights = { } specifications[name ] = weights end
- local styles = weights [weight] if not styles then styles = { } weights [weight] = styles end
- local widths = styles [style ] if not widths then widths = { } styles [style ] = widths end
- local variants = widths [width ] if not variants then variants = { } widths [width ] = variants end
- variants[variant] = filename
- n = n + 1
- else
- m = m + 1
- end
- end
- if trace_specifications then
- report_names("%s filenames registered, %s filenames rejected",n,m)
- end
- end
- end
-end
-
-function names.registered(name,weight,style,width,variant)
- local ok = specifications[name]
- ok = ok and (ok[(weight and weight ~= "" and weight ) or "normal"] or ok.normal)
- ok = ok and (ok[(style and style ~= "" and style ) or "normal"] or ok.normal)
- ok = ok and (ok[(width and width ~= "" and width ) or "normal"] or ok.normal)
- ok = ok and (ok[(variant and variant ~= "" and variant) or "normal"] or ok.normal)
- --
- -- todo: same fallbacks as with database
- --
- if ok then
- return {
- filename = ok,
- subname = "",
- -- rawname = nil,
- }
- end
-end
-
-function names.resolvespec(askedname,sub) -- overloads previous definition
- local name, weight, style, width, variant = names.splitspec(askedname)
- if trace_specifications then
- report_names("resolving specification: %a to name=%s, weight=%s, style=%s, width=%s, variant=%s",askedname,name,weight,style,width,variant)
- end
- local found = names.registered(name,weight,style,width,variant)
- if found and found.filename then
- if trace_specifications then
- report_names("resolved by registered names: %a to %s",askedname,found.filename)
- end
- return found.filename, found.subname, found.rawname
- else
- found = names.specification(name,weight,style,width,variant)
- if found and found.filename then
- if trace_specifications then
- report_names("resolved by font database: %a to %s",askedname,found.filename)
- end
- return found.filename, found.subfont and found.rawname
- end
- end
- if trace_specifications then
- report_names("unresolved: %s",askedname)
- end
-end
+if not modules then modules = { } end modules ['font-syn'] = {
+ 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"
+}
+
+-- todo: subs in lookups requests
+
+local next, tonumber, type, tostring = next, tonumber, type, tostring
+local sub, gsub, lower, match, find, lower, upper = string.sub, string.gsub, string.lower, string.match, string.find, string.lower, string.upper
+local find, gmatch = string.find, string.gmatch
+local concat, sort, format = table.concat, table.sort, string.format
+local serialize = table.serialize
+local lpegmatch = lpeg.match
+local unpack = unpack or table.unpack
+local formatters = string.formatters
+
+local allocate = utilities.storage.allocate
+local sparse = utilities.storage.sparse
+
+local removesuffix = file.removesuffix
+local splitbase = file.splitbase
+local splitname = file.splitname
+local basename = file.basename
+local nameonly = file.nameonly
+local pathpart = file.pathpart
+local filejoin = file.join
+local is_qualified_path = file.is_qualified_path
+local exists = io.exists
+
+local findfile = resolvers.findfile
+local cleanpath = resolvers.cleanpath
+local resolveresolved = resolvers.resolve
+
+local trace_names = false trackers.register("fonts.names", function(v) trace_names = v end)
+local trace_warnings = false trackers.register("fonts.warnings", function(v) trace_warnings = v end)
+local trace_specifications = false trackers.register("fonts.specifications", function(v) trace_specifications = v end)
+
+local report_names = logs.reporter("fonts","names")
+
+--[[ldx--
+<p>This module implements a name to filename resolver. Names are resolved
+using a table that has keys filtered from the font related files.</p>
+--ldx]]--
+
+fonts = fonts or { } -- also used elsewhere
+
+local names = font.names or allocate { }
+fonts.names = names
+
+local filters = names.filters or { }
+names.filters = filters
+
+names.data = names.data or allocate { }
+
+names.version = 1.110
+names.basename = "names"
+names.saved = false
+names.loaded = false
+names.be_clever = true
+names.enabled = true
+names.cache = containers.define("fonts","data",names.version,true)
+
+local autoreload = true
+
+directives.register("fonts.autoreload", function(v) autoreload = toboolean(v) end)
+
+--[[ldx--
+<p>A few helpers.</p>
+--ldx]]--
+
+local P, C, Cc, Cs = lpeg.P, lpeg.C, lpeg.Cc, lpeg.Cs
+
+-- what to do with 'thin'
+
+local weights = Cs ( -- not extra
+ P("demibold")
+ + P("semibold")
+ + P("mediumbold")
+ + P("ultrabold")
+ + P("extrabold")
+ + P("ultralight")
+ + P("bold")
+ + P("demi")
+ + P("semi")
+ + P("light")
+ + P("medium")
+ + P("heavy")
+ + P("ultra")
+ + P("black")
+ + P("bol") -- / "bold"
+ + P("regular") / "normal"
+)
+
+local normalized_weights = sparse {
+ regular = "normal",
+}
+
+local styles = Cs (
+ P("reverseoblique") / "reverseitalic"
+ + P("regular") / "normal"
+ + P("italic")
+ + P("oblique") / "italic"
+ + P("slanted")
+ + P("roman") / "normal"
+ + P("ital") / "italic"
+ + P("ita") / "italic"
+)
+
+local normalized_styles = sparse {
+ reverseoblique = "reverseitalic",
+ regular = "normal",
+ oblique = "italic",
+}
+
+local widths = Cs(
+ P("condensed")
+ + P("thin")
+ + P("expanded")
+ + P("cond") / "condensed"
+ + P("normal")
+ + P("book") / "normal"
+)
+
+local normalized_widths = sparse()
+
+local variants = Cs( -- fax casual
+ P("smallcaps")
+ + P("oldstyle")
+ + P("caps") / "smallcaps"
+)
+
+local normalized_variants = sparse()
+
+names.knownweights = {
+ "black",
+ "bold",
+ "demi",
+ "demibold",
+ "extrabold",
+ "heavy",
+ "light",
+ "medium",
+ "mediumbold",
+ "normal",
+ "regular",
+ "semi",
+ "semibold",
+ "ultra",
+ "ultrabold",
+ "ultralight",
+}
+
+names.knownstyles = {
+ "italic",
+ "normal",
+ "oblique",
+ "regular",
+ "reverseitalic",
+ "reverseoblique",
+ "roman",
+ "slanted",
+}
+
+names.knownwidths = {
+ "book",
+ "condensed",
+ "expanded",
+ "normal",
+ "thin",
+}
+
+names.knownvariants = {
+ "normal",
+ "oldstyle",
+ "smallcaps",
+}
+
+local any = P(1)
+
+local analyzed_table
+
+local analyzer = Cs (
+ (
+ weights / function(s) analyzed_table[1] = s return "" end
+ + styles / function(s) analyzed_table[2] = s return "" end
+ + widths / function(s) analyzed_table[3] = s return "" end
+ + variants / function(s) analyzed_table[4] = s return "" end
+ + any
+ )^0
+)
+
+local splitter = lpeg.splitat("-")
+
+function names.splitspec(askedname)
+ local name, weight, style, width, variant = lpegmatch(splitter,askedname)
+ weight = weight and lpegmatch(weights, weight) or weight
+ style = style and lpegmatch(styles, style) or style
+ width = width and lpegmatch(widths, width) or width
+ variant = variant and lpegmatch(variants,variant) or variant
+ if trace_names then
+ report_names("requested name %a split in name %a, weight %a, style %a, width %a and variant %a",
+ askedname,name,weight,style,width,variant)
+ end
+ if not weight or not weight or not width or not variant then
+ weight, style, width, variant = weight or "normal", style or "normal", width or "normal", variant or "normal"
+ if trace_names then
+ report_names("request %a normalized to '%s-%s-%s-%s-%s'",
+ askedname,name,weight,style,width,variant)
+ end
+ end
+ return name or askedname, weight, style, width, variant
+end
+
+local function analyzespec(somename)
+ if somename then
+ analyzed_table = { }
+ local name = lpegmatch(analyzer,somename)
+ return name, analyzed_table[1], analyzed_table[2], analyzed_table[3], analyzed_table[4]
+ end
+end
+
+--[[ldx--
+<p>It would make sense to implement the filters in the related modules,
+but to keep the overview, we define them here.</p>
+--ldx]]--
+
+filters.otf = fontloader.info
+filters.ttf = fontloader.info
+filters.ttc = fontloader.info
+filters.dfont = fontloader.info
+
+function fontloader.fullinfo(...) -- check with taco what we get / could get
+ local ff = fontloader.open(...)
+ if ff then
+ local d = ff and fontloader.to_table(ff)
+ d.glyphs, d.subfonts, d.gpos, d.gsub, d.lookups = nil, nil, nil, nil, nil
+ fontloader.close(ff)
+ return d
+ else
+ return nil, "error in loading font"
+ end
+end
+
+filters.otf = fontloader.fullinfo
+
+function filters.afm(name)
+ -- we could parse the afm file as well, and then report an error but
+ -- it's not worth the trouble
+ local pfbname = findfile(removesuffix(name)..".pfb","pfb") or ""
+ if pfbname == "" then
+ pfbname = findfile(nameonly(name)..".pfb","pfb") or ""
+ end
+ if pfbname ~= "" then
+ local f = io.open(name)
+ if f then
+ local hash = { }
+ for line in f:lines() do
+ local key, value = match(line,"^(.+)%s+(.+)%s*$")
+ if key and #key > 0 then
+ hash[lower(key)] = value
+ end
+ if find(line,"StartCharMetrics") then
+ break
+ end
+ end
+ f:close()
+ return hash
+ end
+ end
+ return nil, "no matching pfb file"
+end
+
+function filters.pfb(name)
+ return fontloader.info(name)
+end
+
+--[[ldx--
+<p>The scanner loops over the filters using the information stored in
+the file databases. Watch how we check not only for the names, but also
+for combination with the weight of a font.</p>
+--ldx]]--
+
+filters.list = {
+ "otf", "ttf", "ttc", "dfont", "afm",
+ -- "ttc", "otf", "ttf", "dfont", "afm",
+}
+
+names.fontconfigfile = "fonts.conf" -- a bit weird format, bonus feature
+names.osfontdirvariable = "OSFONTDIR" -- the official way, in minimals etc
+
+filters.paths = { }
+filters.names = { }
+
+function names.getpaths(trace)
+ local hash, result, r = { }, { }, 0
+ local function collect(t,where)
+ for i=1,#t do
+ local v = cleanpath(t[i])
+ v = gsub(v,"/+$","") -- not needed any more
+ local key = lower(v)
+ report_names("%a specifies path %a",where,v)
+ if not hash[key] then
+ r = r + 1
+ result[r] = v
+ hash[key] = true
+ end
+ end
+ end
+ local path = names.osfontdirvariable or ""
+ if path ~= "" then
+ collect(resolvers.expandedpathlist(path),path)
+ end
+ if xml then
+ local confname = resolvers.expansion("FONTCONFIG_FILE") or ""
+ if confname == "" then
+ confname = names.fontconfigfile or ""
+ end
+ if confname ~= "" then
+ -- first look in the tex tree
+ local name = findfile(confname,"fontconfig files") or ""
+ if name == "" then
+ -- after all, fontconfig is a unix thing
+ name = filejoin("/etc",confname)
+ if not lfs.isfile(name) then
+ name = "" -- force quit
+ end
+ end
+ if name ~= "" and lfs.isfile(name) then
+ if trace_names then
+ report_names("%s fontconfig file %a","loading",name)
+ end
+ local xmldata = xml.load(name)
+ -- begin of untested mess
+ xml.include(xmldata,"include","",true,function(incname)
+ if not is_qualified_path(incname) then
+ local path = pathpart(name) -- main name
+ if path ~= "" then
+ incname = filejoin(path,incname)
+ end
+ end
+ if lfs.isfile(incname) then
+ if trace_names then
+ report_names("%s fontconfig file %a","merging included",incname)
+ end
+ return io.loaddata(incname)
+ elseif trace_names then
+ report_names("%s fontconfig file: %a","ignoring included",incname)
+ end
+ end)
+ -- end of untested mess
+ local fontdirs = xml.collect_texts(xmldata,"dir",true)
+ if trace_names then
+ report_names("%s dirs found in fontconfig",#fontdirs)
+ end
+ collect(fontdirs,"fontconfig file")
+ end
+ end
+ end
+ function names.getpaths()
+ return result
+ end
+ return result
+end
+
+local function cleanname(name)
+ return (gsub(lower(name),"[^%a%d]",""))
+end
+
+local function cleanfilename(fullname,defaultsuffix)
+ local path, name, suffix = splitname(fullname)
+ name = gsub(lower(name),"[^%a%d]","")
+ if suffix and suffix ~= "" then
+ return name .. ".".. suffix
+ elseif defaultsuffix and defaultsuffix ~= "" then
+ return name .. ".".. defaultsuffix
+ else
+ return name
+ end
+end
+
+names.cleanname = cleanname
+names.cleanfilename = cleanfilename
+
+local function check_names(result)
+ local names = result.names
+ if names then
+ for i=1,#names do
+ local name = names[i]
+ if name.lang == "English (US)" then
+ return name.names
+ end
+ end
+ end
+end
+
+local function walk_tree(pathlist,suffix,identify)
+ if pathlist then
+ for i=1,#pathlist do
+ local path = pathlist[i]
+ path = cleanpath(path .. "/")
+ path = gsub(path,"/+","/")
+ local pattern = path .. "**." .. suffix -- ** forces recurse
+ report_names("globbing path %a",pattern)
+ local t = dir.glob(pattern)
+ sort(t,sorter)
+ for j=1,#t do
+ local completename = t[j]
+ identify(completename,basename(completename),suffix,completename)
+ end
+ end
+ end
+end
+
+local function check_name(data,result,filename,modification,suffix,subfont)
+ -- shortcuts
+ local specifications = data.specifications
+ -- prepare
+ local names = check_names(result)
+ -- fetch
+ local familyname = names and names.preffamilyname or result.familyname
+ local fullname = names and names.fullname or result.fullname
+ local fontname = result.fontname
+ local subfamily = names and names.subfamily
+ local modifiers = names and names.prefmodifiers
+ local weight = names and names.weight or result.weight
+ local italicangle = tonumber(result.italicangle)
+ local subfont = subfont or nil
+ local rawname = fullname or fontname or familyname
+ -- normalize
+ familyname = familyname and cleanname(familyname)
+ fullname = fullname and cleanname(fullname)
+ fontname = fontname and cleanname(fontname)
+ subfamily = subfamily and cleanname(subfamily)
+ modifiers = modifiers and cleanname(modifiers)
+ weight = weight and cleanname(weight)
+ italicangle = italicangle == 0 and nil
+ -- analyze
+ local a_name, a_weight, a_style, a_width, a_variant = analyzespec(fullname or fontname or familyname)
+ -- check
+ local width = a_width
+ local variant = a_variant
+ local style = modifiers and gsub(modifiers,"[^%a]","")
+ if not style and italicangle then
+ style = "italic"
+ end
+ if not variant or variant == "" then
+ variant = "normal"
+ end
+ if not weight or weight == "" then
+ weight = a_weight
+ end
+ if not style or style == "" then
+ style = a_style
+ end
+ if not familyname then
+ familyname = a_name
+ end
+ fontname = fontname or fullname or familyname or basename(filename)
+ fullname = fullname or fontname
+ familyname = familyname or fontname
+ specifications[#specifications + 1] = {
+ filename = filename, -- unresolved
+ format = lower(suffix),
+ subfont = subfont,
+ rawname = rawname,
+ familyname = familyname,
+ fullname = fullname,
+ fontname = fontname,
+ subfamily = subfamily,
+ modifiers = modifiers,
+ weight = weight,
+ style = style,
+ width = width,
+ variant = variant,
+ minsize = result.design_range_bottom or 0,
+ maxsize = result.design_range_top or 0,
+ designsize = result.design_size or 0,
+ modification = modification or 0,
+ }
+end
+
+local function cleanupkeywords()
+ local data = names.data
+ local specifications = names.data.specifications
+ if specifications then
+ local weights = { }
+ local styles = { }
+ local widths = { }
+ local variants = { }
+ for i=1,#specifications do
+ local s = specifications[i]
+ -- fix (sofar styles are taken from the name, and widths from the specification)
+ local _, b_weight, b_style, b_width, b_variant = analyzespec(s.weight)
+ local _, c_weight, c_style, c_width, c_variant = analyzespec(s.style)
+ local _, d_weight, d_style, d_width, d_variant = analyzespec(s.width)
+ local _, e_weight, e_style, e_width, e_variant = analyzespec(s.variant)
+ local _, f_weight, f_style, f_width, f_variant = analyzespec(s.fullname or "")
+ local weight = b_weight or c_weight or d_weight or e_weight or f_weight or "normal"
+ local style = b_style or c_style or d_style or e_style or f_style or "normal"
+ local width = b_width or c_width or d_width or e_width or f_width or "normal"
+ local variant = b_variant or c_variant or d_variant or e_variant or f_variant or "normal"
+ if not weight or weight == "" then weight = "normal" end
+ if not style or style == "" then style = "normal" end
+ if not width or width == "" then width = "normal" end
+ if not variant or variant == "" then variant = "normal" end
+ weights [weight ] = (weights [weight ] or 0) + 1
+ styles [style ] = (styles [style ] or 0) + 1
+ widths [width ] = (widths [width ] or 0) + 1
+ variants[variant] = (variants[variant] or 0) + 1
+ if weight ~= s.weight then
+ s.fontweight = s.weight
+ end
+ s.weight, s.style, s.width, s.variant = weight, style, width, variant
+ end
+ local stats = data.statistics
+ stats.used_weights, stats.used_styles, stats.used_widths, stats.used_variants = weights, styles, widths, variants
+ end
+end
+
+local function collectstatistics()
+ local data = names.data
+ local specifications = data.specifications
+ if specifications then
+ local weights = { }
+ local styles = { }
+ local widths = { }
+ local variants = { }
+ for i=1,#specifications do
+ local s = specifications[i]
+ local weight = s.weight
+ local style = s.style
+ local width = s.width
+ local variant = s.variant
+ if weight then weights [weight ] = (weights [weight ] or 0) + 1 end
+ if style then styles [style ] = (styles [style ] or 0) + 1 end
+ if width then widths [width ] = (widths [width ] or 0) + 1 end
+ if variant then variants[variant] = (variants[variant] or 0) + 1 end
+ end
+ local stats = data.statistics
+ stats.weights = weights
+ stats.styles = styles
+ stats.widths = widths
+ stats.variants = variants
+ stats.fonts = #specifications
+ end
+end
+
+local function collecthashes()
+ local data = names.data
+ local mappings = data.mappings
+ local fallbacks = data.fallbacks
+ local specifications = data.specifications
+ local nofmappings = 0
+ local noffallbacks = 0
+ if specifications then
+ -- maybe multiple passes
+ for index=1,#specifications do
+ local s = specifications[index]
+ local format, fullname, fontname, familyname, weight, subfamily = s.format, s.fullname, s.fontname, s.familyname, s.weight, s.subfamily
+ local mf, ff = mappings[format], fallbacks[format]
+ if fullname and not mf[fullname] then
+ mf[fullname], nofmappings = index, nofmappings + 1
+ end
+ if fontname and not mf[fontname] then
+ mf[fontname], nofmappings = index, nofmappings + 1
+ end
+ if familyname and weight and weight ~= sub(familyname,#familyname-#weight+1,#familyname) then
+ local madename = familyname .. weight
+ if not mf[madename] and not ff[madename] then
+ ff[madename], noffallbacks = index, noffallbacks + 1
+ end
+ end
+ if familyname and subfamily and subfamily ~= sub(familyname,#familyname-#subfamily+1,#familyname) then
+ local extraname = familyname .. subfamily
+ if not mf[extraname] and not ff[extraname] then
+ ff[extraname], noffallbacks = index, noffallbacks + 1
+ end
+ end
+ if familyname and not mf[familyname] and not ff[familyname] then
+ ff[familyname], noffallbacks = index, noffallbacks + 1
+ end
+ end
+ end
+ return nofmappings, noffallbacks
+end
+
+local function collectfamilies()
+ local data = names.data
+ local specifications = data.specifications
+ local families = data.families
+ for index=1,#specifications do
+ local familyname = specifications[index].familyname
+ local family = families[familyname]
+ if not family then
+ families[familyname] = { index }
+ else
+ family[#family+1] = index
+ end
+ end
+end
+
+local function checkduplicate(where) -- fails on "Romantik" but that's a border case anyway
+ local data = names.data
+ local mapping = data[where]
+ local specifications = data.specifications
+ local loaded = { }
+ if specifications and mapping then
+ for _, m in next, mapping do
+ for k, v in next, m do
+ local s = specifications[v]
+ local hash = formatters["%s-%s-%s-%s-%s"](s.familyname,s.weight or "*",s.style or "*",s.width or "*",s.variant or "*")
+ local h = loaded[hash]
+ if h then
+ local ok = true
+ local fn = s.filename
+ for i=1,#h do
+ local hn = s.filename
+ if h[i] == fn then
+ ok = false
+ break
+ end
+ end
+ if ok then
+ h[#h+1] = fn
+ end
+ else
+ loaded[hash] = { s.filename }
+ end
+ end
+ end
+ end
+ local n = 0
+ for k, v in table.sortedhash(loaded) do
+ local nv = #v
+ if nv > 1 then
+ if trace_warnings then
+ report_names("lookup %a clashes with %a",k,v)
+ end
+ n = n + nv
+ end
+ end
+ report_names("%a double lookups in %a",n,where)
+end
+
+local function checkduplicates()
+ checkduplicate("mappings")
+ checkduplicate("fallbacks")
+end
+
+local sorter = function(a,b)
+ return a > b -- to be checked
+end
+
+local function sorthashes()
+ local data = names.data
+ local list = filters.list
+ local mappings = data.mappings
+ local fallbacks = data.fallbacks
+ local sorted_mappings = { }
+ local sorted_fallbacks = { }
+ data.sorted_mappings = sorted_mappings
+ data.sorted_fallbacks = sorted_fallbacks
+ for i=1,#list do
+ local l = list[i]
+ sorted_mappings [l] = table.keys(mappings[l])
+ sorted_fallbacks[l] = table.keys(fallbacks[l])
+ sort(sorted_mappings [l],sorter)
+ sort(sorted_fallbacks[l],sorter)
+ end
+ data.sorted_families = table.keys(data.families)
+ sort(data.sorted_families,sorter)
+end
+
+local function unpackreferences()
+ local data = names.data
+ local specifications = data.specifications
+ if specifications then
+ for k, v in next, data.families do
+ for i=1,#v do
+ v[i] = specifications[v[i]]
+ end
+ end
+ local mappings = data.mappings
+ if mappings then
+ for _, m in next, mappings do
+ for k, v in next, m do
+ m[k] = specifications[v]
+ end
+ end
+ end
+ local fallbacks = data.fallbacks
+ if fallbacks then
+ for _, f in next, fallbacks do
+ for k, v in next, f do
+ f[k] = specifications[v]
+ end
+ end
+ end
+ end
+end
+
+local function analyzefiles(olddata)
+ if not trace_warnings then
+ report_names("warnings are disabled (tracker 'fonts.warnings')")
+ end
+ local data = names.data
+ local done = { }
+ local totalnofread = 0
+ local totalnofskipped = 0
+ local totalnofduplicates = 0
+ local nofread = 0
+ local nofskipped = 0
+ local nofduplicates = 0
+ local skip_paths = filters.paths
+ local skip_names = filters.names
+ local specifications = data.specifications
+ local oldindices = olddata and olddata.indices or { }
+ local oldspecifications = olddata and olddata.specifications or { }
+ local oldrejected = olddata and olddata.rejected or { }
+ local treatmentdata = fonts.treatments.data
+ local function identify(completename,name,suffix,storedname)
+ local pathpart, basepart = splitbase(completename)
+ nofread = nofread + 1
+ local treatment = treatmentdata[completename] or treatmentdata[basepart]
+ if treatment and treatment.ignored then
+ if trace_names then
+ report_names("%s font %a is ignored, reason %a",suffix,completename,treatment.comment or "unknown")
+ end
+ nofskipped = nofskipped + 1
+ elseif done[name] then
+ -- already done (avoid otf afm clash)
+ if trace_names then
+ report_names("%s font %a already done",suffix,completename)
+ end
+ nofduplicates = nofduplicates + 1
+ nofskipped = nofskipped + 1
+ elseif not exists(completename) then
+ -- weird error
+ if trace_names then
+ report_names("%s font %a does not really exist",suffix,completename)
+ end
+ nofskipped = nofskipped + 1
+ elseif not is_qualified_path(completename) and findfile(completename,suffix) == "" then
+ -- not locatable by backend anyway
+ if trace_names then
+ report_names("%s font %a cannot be found by backend",suffix,completename)
+ end
+ nofskipped = nofskipped + 1
+ else
+ if #skip_paths > 0 then
+ for i=1,#skip_paths do
+ if find(pathpart,skip_paths[i]) then
+ if trace_names then
+ report_names("rejecting path of %s font %a",suffix,completename)
+ end
+ nofskipped = nofskipped + 1
+ return
+ end
+ end
+ end
+ if #skip_names > 0 then
+ for i=1,#skip_paths do
+ if find(basepart,skip_names[i]) then
+ done[name] = true
+ if trace_names then
+ report_names("rejecting name of %s font %a",suffix,completename)
+ end
+ nofskipped = nofskipped + 1
+ return
+ end
+ end
+ end
+ if trace_names then
+ report_names("identifying %s font %a",suffix,completename)
+ end
+ local result = nil
+ local modification = lfs.attributes(completename,"modification")
+ if olddata and modification and modification > 0 then
+ local oldindex = oldindices[storedname] -- index into specifications
+ if oldindex then
+ local oldspecification = oldspecifications[oldindex]
+ if oldspecification and oldspecification.filename == storedname then -- double check for out of sync
+ local oldmodification = oldspecification.modification
+ if oldmodification == modification then
+ result = oldspecification
+ specifications[#specifications + 1] = result
+ else
+ end
+ else
+ end
+ elseif oldrejected[storedname] == modification then
+ result = false
+ end
+ end
+ if result == nil then
+ local result, message = filters[lower(suffix)](completename)
+ if result then
+ if result[1] then
+ for r=1,#result do
+ local ok = check_name(data,result[r],storedname,modification,suffix,r-1) -- subfonts start at zero
+ -- if not ok then
+ -- nofskipped = nofskipped + 1
+ -- end
+ end
+ else
+ local ok = check_name(data,result,storedname,modification,suffix)
+ -- if not ok then
+ -- nofskipped = nofskipped + 1
+ -- end
+ end
+ if trace_warnings and message and message ~= "" then
+ report_names("warning when identifying %s font %a, %s",suffix,completename,message)
+ end
+ elseif trace_warnings then
+ nofskipped = nofskipped + 1
+ report_names("error when identifying %s font %a, %s",suffix,completename,message or "unknown")
+ end
+ end
+ done[name] = true
+ end
+ logs.flush() -- a bit overkill for each font, maybe not needed here
+ end
+ local function traverse(what, method)
+ local list = filters.list
+ for n=1,#list do
+ local suffix = list[n]
+ local t = os.gettimeofday() -- use elapser
+ nofread, nofskipped, nofduplicates = 0, 0, 0
+ suffix = lower(suffix)
+ report_names("identifying %s font files with suffix %a",what,suffix)
+ method(suffix)
+ suffix = upper(suffix)
+ report_names("identifying %s font files with suffix %a",what,suffix)
+ method(suffix)
+ totalnofread, totalnofskipped, totalnofduplicates = totalnofread + nofread, totalnofskipped + nofskipped, totalnofduplicates + nofduplicates
+ local elapsed = os.gettimeofday() - t
+ report_names("%s %s files identified, %s skipped, %s duplicates, %s hash entries added, runtime %0.3f seconds",nofread,what,nofskipped,nofduplicates,nofread-nofskipped,elapsed)
+ end
+ logs.flush()
+ end
+ -- problem .. this will not take care of duplicates
+ local function withtree(suffix)
+ resolvers.dowithfilesintree(".*%." .. suffix .. "$", function(method,root,path,name)
+ if method == "file" or method == "tree" then
+ local completename = root .."/" .. path .. "/" .. name
+ completename = resolveresolved(completename) -- no shortcut
+ identify(completename,name,suffix,name)
+ return true
+ end
+ end, function(blobtype,blobpath,pattern)
+ blobpath = resolveresolved(blobpath) -- no shortcut
+ report_names("scanning path %a for %s files",blobpath,suffix)
+ end, function(blobtype,blobpath,pattern,total,checked,done)
+ blobpath = resolveresolved(blobpath) -- no shortcut
+ report_names("%s entries found, %s %s files checked, %s okay",total,checked,suffix,done)
+ end)
+ end
+ local function withlsr(suffix) -- all trees
+ -- we do this only for a stupid names run, not used for context itself,
+ -- using the vars is too clumsy so we just stick to a full scan instead
+ local pathlist = resolvers.splitpath(resolvers.showpath("ls-R") or "")
+ walk_tree(pathlist,suffix,identify)
+ end
+ local function withsystem(suffix) -- OSFONTDIR cum suis
+ walk_tree(names.getpaths(trace),suffix,identify)
+ end
+ traverse("tree",withtree) -- TEXTREE only
+ if texconfig.kpse_init then
+ traverse("lsr", withlsr)
+ else
+ traverse("system", withsystem)
+ end
+ data.statistics.readfiles = totalnofread
+ data.statistics.skippedfiles = totalnofskipped
+ data.statistics.duplicatefiles = totalnofduplicates
+end
+
+local function addfilenames()
+ local data = names.data
+ local specifications = data.specifications
+ local indices = { }
+ local files = { }
+ for i=1,#specifications do
+ local fullname = specifications[i].filename
+ files[cleanfilename(fullname)] = fullname
+ indices[fullname] = i
+ end
+ data.files = files
+ data.indices = indices
+end
+
+local function rejectclashes() -- just to be sure, so no explicit afm will be found then
+ local specifications = names.data.specifications
+ local used = { }
+ local okay = { }
+ local rejected = { } -- only keep modification
+ local o = 0
+ for i=1,#specifications do
+ local s = specifications[i]
+ local f = s.fontname
+ if f then
+ local fnd = used[f]
+ local fnm = s.filename
+ if fnd then
+ if trace_warnings then
+ report_names("fontname %a clashes, %a rejected in favor of %a",f,fnm,fnd)
+ end
+ rejected[f] = s.modification
+ else
+ used[f] = fnm
+ o = o + 1
+ okay[o] = s
+ end
+ else
+ o = o + 1
+ okay[o] = s
+ end
+ end
+ local d = #specifications - #okay
+ if d > 0 then
+ report_names("%s files rejected due to clashes",d)
+ end
+ names.data.specifications = okay
+ names.data.rejected = rejected
+end
+
+local function resetdata()
+ local mappings = { }
+ local fallbacks = { }
+ for _, k in next, filters.list do
+ mappings [k] = { }
+ fallbacks[k] = { }
+ end
+ names.data = {
+ version = names.version,
+ mappings = mappings,
+ fallbacks = fallbacks,
+ specifications = { },
+ families = { },
+ statistics = { },
+ names = { },
+ indices = { },
+ rejected = { },
+ datastate = resolvers.datastate(),
+ }
+end
+
+function names.identify(force)
+ local starttime = os.gettimeofday() -- use elapser
+ resetdata()
+ analyzefiles(not force and names.readdata(names.basename))
+ rejectclashes()
+ collectfamilies()
+ collectstatistics()
+ cleanupkeywords()
+ collecthashes()
+ checkduplicates()
+ addfilenames()
+ -- sorthashes() -- will be resorted when saved
+ report_names("total scan time %0.3f seconds",os.gettimeofday()-starttime)
+end
+
+function names.is_permitted(name)
+ return containers.is_usable(names.cache, name)
+end
+function names.writedata(name,data)
+ containers.write(names.cache,name,data)
+end
+function names.readdata(name)
+ return containers.read(names.cache,name)
+end
+
+function names.load(reload,force)
+ if not names.loaded then
+ if reload then
+ if names.is_permitted(names.basename) then
+ names.identify(force)
+ names.writedata(names.basename,names.data)
+ else
+ report_names("unable to access database cache")
+ end
+ names.saved = true
+ end
+ local data = names.readdata(names.basename)
+ names.data = data
+ if not names.saved then
+ if not data or not next(data) or not data.specifications or not next(data.specifications) then
+ names.load(true)
+ end
+ names.saved = true
+ end
+ if not data then
+ report_names("accessing the data table failed")
+ else
+ unpackreferences()
+ sorthashes()
+ end
+ names.loaded = true
+ end
+end
+
+local function list_them(mapping,sorted,pattern,t,all)
+ if mapping[pattern] then
+ t[pattern] = mapping[pattern]
+ else
+ for k=1,#sorted do
+ local v = sorted[k]
+ if not t[v] and find(v,pattern) then
+ t[v] = mapping[v]
+ if not all then
+ return
+ end
+ end
+ end
+ end
+end
+
+function names.list(pattern,reload,all) -- here?
+ names.load() -- todo reload
+ if names.loaded then
+ local t = { }
+ local data = names.data
+ if data then
+ local list = filters.list
+ local mappings = data.mappings
+ local sorted_mappings = data.sorted_mappings
+ local fallbacks = data.fallbacks
+ local sorted_fallbacks = data.sorted_fallbacks
+ for i=1,#list do
+ local format = list[i]
+ list_them(mappings[format],sorted_mappings[format],pattern,t,all)
+ if next(t) and not all then
+ return t
+ end
+ list_them(fallbacks[format],sorted_fallbacks[format],pattern,t,all)
+ if next(t) and not all then
+ return t
+ end
+ end
+ end
+ return t
+ end
+end
+
+local reloaded = false
+
+local function is_reloaded()
+ if not reloaded then
+ local data = names.data
+ if autoreload then
+ local c_status = serialize(resolvers.datastate())
+ local f_status = serialize(data.datastate)
+ if c_status == f_status then
+ if trace_names then
+ report_names("font database has matching configuration and file hashes")
+ end
+ return
+ else
+ report_names("font database has mismatching configuration and file hashes")
+ end
+ else
+ report_names("font database is regenerated (controlled by directive 'fonts.autoreload')")
+ end
+ names.loaded = false
+ reloaded = true
+ logs.flush()
+ names.load(true)
+ end
+end
+
+--[[ldx--
+<p>The resolver also checks if the cached names are loaded. Being clever
+here is for testing purposes only (it deals with names prefixed by an
+encoding name).</p>
+--ldx]]--
+
+local function fuzzy(mapping,sorted,name,sub)
+ local condensed = gsub(name,"[^%a%d]","")
+ for k=1,#sorted do
+ local v = sorted[k]
+ if find(v,condensed) then
+ return mapping[v], v
+ end
+ end
+end
+
+-- we could cache a lookup .. maybe some day ... (only when auto loaded!)
+
+local function foundname(name,sub) -- sub is not used currently
+ local data = names.data
+ local mappings = data.mappings
+ local sorted_mappings = data.sorted_mappings
+ local fallbacks = data.fallbacks
+ local sorted_fallbacks = data.sorted_fallbacks
+ local list = filters.list
+ -- dilemma: we lookup in the order otf ttf ttc ... afm but now an otf fallback
+ -- can come after an afm match ... well, one should provide nice names anyway
+ -- and having two lists is not an option
+ for i=1,#list do
+ local l = list[i]
+ local found = mappings[l][name]
+ if found then
+ if trace_names then
+ report_names("resolved via direct name match: %a",name)
+ end
+ return found
+ end
+ end
+ for i=1,#list do
+ local l = list[i]
+ local found, fname = fuzzy(mappings[l],sorted_mappings[l],name,sub)
+ if found then
+ if trace_names then
+ report_names("resolved via fuzzy name match: %a onto %a",name,fname)
+ end
+ return found
+ end
+ end
+ for i=1,#list do
+ local l = list[i]
+ local found = fallbacks[l][name]
+ if found then
+ if trace_names then
+ report_names("resolved via direct fallback match: %a",name)
+ end
+ return found
+ end
+ end
+ for i=1,#list do
+ local l = list[i]
+ local found, fname = fuzzy(sorted_mappings[l],sorted_fallbacks[l],name,sub)
+ if found then
+ if trace_names then
+ report_names("resolved via fuzzy fallback match: %a onto %a",name,fname)
+ end
+ return found
+ end
+ end
+ if trace_names then
+ report_names("font with name %a cannot be found",name)
+ end
+end
+
+function names.resolvedspecification(askedname,sub)
+ if askedname and askedname ~= "" and names.enabled then
+ askedname = cleanname(askedname)
+ names.load()
+ local found = foundname(askedname,sub)
+ if not found and is_reloaded() then
+ found = foundname(askedname,sub)
+ end
+ return found
+ end
+end
+
+function names.resolve(askedname,sub)
+ local found = names.resolvedspecification(askedname,sub)
+ if found then
+ return found.filename, found.subfont and found.rawname
+ end
+end
+
+-- function names.getfilename(askedname,suffix) -- last resort, strip funny chars
+-- names.load()
+-- local files = names.data.files
+-- askedname = files and files[cleanfilename(askedname,suffix)] or ""
+-- if askedname == "" then
+-- return ""
+-- else -- never entered
+-- return resolvers.findbinfile(askedname,suffix) or ""
+-- end
+-- end
+
+function names.getfilename(askedname,suffix) -- last resort, strip funny chars
+ names.load()
+ local files = names.data.files
+ local cleanname = cleanfilename(askedname,suffix)
+ local found = files and files[cleanname] or ""
+ if found == "" and is_reloaded() then
+ files = names.data.files
+ found = files and files[cleanname] or ""
+ end
+ if found and found ~= "" then
+ return resolvers.findbinfile(found,suffix) or "" -- we still need to locate it
+ end
+end
+
+-- specified search
+
+local function s_collect_weight_style_width_variant(found,done,all,weight,style,width,variant,family)
+ if family then
+ for i=1,#family do
+ local f = family[i]
+ if f and weight == f.weight and style == f.style and width == f.width and variant == f.variant then
+ found[#found+1], done[f] = f, true
+ if not all then return end
+ end
+ end
+ end
+end
+local function m_collect_weight_style_width_variant(found,done,all,weight,style,width,variant,families,sorted,strictname)
+ for i=1,#sorted do
+ local k = sorted[i]
+ local family = families[k]
+ for i=1,#family do
+ local f = family[i]
+ if not done[f] and weight == f.weight and style == f.style and width == f.width and variant == f.variant and find(f.fontname,strictname) then
+ found[#found+1], done[f] = f, true
+ if not all then return end
+ end
+ end
+ end
+end
+
+local function s_collect_weight_style_width(found,done,all,weight,style,width,family)
+ if family then
+ for i=1,#family do
+ local f = family[i]
+ if f and weight == f.weight and style == f.style and width == f.width then
+ found[#found+1], done[f] = f, true
+ if not all then return end
+ end
+ end
+ end
+end
+local function m_collect_weight_style_width(found,done,all,weight,style,width,families,sorted,strictname)
+ for i=1,#sorted do
+ local k = sorted[i]
+ local family = families[k]
+ for i=1,#family do
+ local f = family[i]
+ if not done[f] and weight == f.weight and style == f.style and width == f.width and find(f.fontname,strictname) then
+ found[#found+1], done[f] = f, true
+ if not all then return end
+ end
+ end
+ end
+end
+
+local function s_collect_weight_style(found,done,all,weight,style,family)
+ if family then
+ for i=1,#family do local f = family[i]
+ if f and weight == f.weight and style == f.style then
+ found[#found+1], done[f] = f, true
+ if not all then return end
+ end
+ end
+ end
+end
+local function m_collect_weight_style(found,done,all,weight,style,families,sorted,strictname)
+ for i=1,#sorted do
+ local k = sorted[i]
+ local family = families[k]
+ for i=1,#family do
+ local f = family[i]
+ if not done[f] and weight == f.weight and style == f.style and find(f.fontname,strictname) then
+ found[#found+1], done[f] = f, true
+ if not all then return end
+ end
+ end
+ end
+end
+
+local function s_collect_style_width(found,done,all,style,width,family)
+ if family then
+ for i=1,#family do local f = family[i]
+ if f and style == f.style and width == f.width then
+ found[#found+1], done[f] = f, true
+ if not all then return end
+ end
+ end
+ end
+end
+local function m_collect_style_width(found,done,all,style,width,families,sorted,strictname)
+ for i=1,#sorted do
+ local k = sorted[i]
+ local family = families[k]
+ for i=1,#family do
+ local f = family[i]
+ if not done[f] and style == f.style and width == f.width and find(f.fontname,strictname) then
+ found[#found+1], done[f] = f, true
+ if not all then return end
+ end
+ end
+ end
+end
+
+local function s_collect_weight(found,done,all,weight,family)
+ if family then
+ for i=1,#family do local f = family[i]
+ if f and weight == f.weight then
+ found[#found+1], done[f] = f, true
+ if not all then return end
+ end
+ end
+ end
+end
+local function m_collect_weight(found,done,all,weight,families,sorted,strictname)
+ for i=1,#sorted do
+ local k = sorted[i]
+ local family = families[k]
+ for i=1,#family do
+ local f = family[i]
+ if not done[f] and weight == f.weight and find(f.fontname,strictname) then
+ found[#found+1], done[f] = f, true
+ if not all then return end
+ end
+ end
+ end
+end
+
+local function s_collect_style(found,done,all,style,family)
+ if family then
+ for i=1,#family do local f = family[i]
+ if f and style == f.style then
+ found[#found+1], done[f] = f, true
+ if not all then return end
+ end
+ end
+ end
+end
+local function m_collect_style(found,done,all,style,families,sorted,strictname)
+ for i=1,#sorted do
+ local k = sorted[i]
+ local family = families[k]
+ for i=1,#family do
+ local f = family[i]
+ if not done[f] and style == f.style and find(f.fontname,strictname) then
+ found[#found+1], done[f] = f, true
+ if not all then return end
+ end
+ end
+ end
+end
+
+local function s_collect_width(found,done,all,width,family)
+ if family then
+ for i=1,#family do local f = family[i]
+ if f and width == f.width then
+ found[#found+1], done[f] = f, true
+ if not all then return end
+ end
+ end
+ end
+end
+local function m_collect_width(found,done,all,width,families,sorted,strictname)
+ for i=1,#sorted do
+ local k = sorted[i]
+ local family = families[k]
+ for i=1,#family do
+ local f = family[i]
+ if not done[f] and width == f.width and find(f.fontname,strictname) then
+ found[#found+1], done[f] = f, true
+ if not all then return end
+ end
+ end
+ end
+end
+
+local function s_collect(found,done,all,family)
+ if family then
+ for i=1,#family do local f = family[i]
+ if f then
+ found[#found+1], done[f] = f, true
+ if not all then return end
+ end
+ end
+ end
+end
+local function m_collect(found,done,all,families,sorted,strictname)
+ for i=1,#sorted do
+ local k = sorted[i]
+ local family = families[k]
+ for i=1,#family do
+ local f = family[i]
+ if not done[f] and find(f.fontname,strictname) then
+ found[#found+1], done[f] = f, true
+ if not all then return end
+ end
+ end
+ end
+end
+
+local function collect(stage,found,done,name,weight,style,width,variant,all)
+ local data = names.data
+ local families = data.families
+ local sorted = data.sorted_families
+ local strictname = "^".. name -- to be checked
+ local family = families[name]
+ if trace_names then
+ report_names("resolving name %a, weight %a, style %a, width %a, variant %a",name,weight,style,width,variant)
+ end
+ if weight and weight ~= "" then
+ if style and style ~= "" then
+ if width and width ~= "" then
+ if variant and variant ~= "" then
+ if trace_names then
+ report_names("resolving stage %s, name %a, weight %a, style %a, width %a, variant %a",stage,name,weight,style,width,variant)
+ end
+ s_collect_weight_style_width_variant(found,done,all,weight,style,width,variant,family)
+ m_collect_weight_style_width_variant(found,done,all,weight,style,width,variant,families,sorted,strictname)
+ else
+ if trace_names then
+ report_names("resolving stage %s, name %a, weight %a, style %a, width %a",stage,name,weight,style,width)
+ end
+ s_collect_weight_style_width(found,done,all,weight,style,width,family)
+ m_collect_weight_style_width(found,done,all,weight,style,width,families,sorted,strictname)
+ end
+ else
+ if trace_names then
+ report_names("resolving stage %s, name %a, weight %a, style %a",stage,name,weight,style)
+ end
+ s_collect_weight_style(found,done,all,weight,style,family)
+ m_collect_weight_style(found,done,all,weight,style,families,sorted,strictname)
+ end
+ else
+ if trace_names then
+ report_names("resolving stage %s, name %a, weight %a",stage,name,weight)
+ end
+ s_collect_weight(found,done,all,weight,family)
+ m_collect_weight(found,done,all,weight,families,sorted,strictname)
+ end
+ elseif style and style ~= "" then
+ if width and width ~= "" then
+ if trace_names then
+ report_names("resolving stage %s, name %a, style %a, width %a",stage,name,style,width)
+ end
+ s_collect_style_width(found,done,all,style,width,family)
+ m_collect_style_width(found,done,all,style,width,families,sorted,strictname)
+ else
+ if trace_names then
+ report_names("resolving stage %s, name %a, style %a",stage,name,style)
+ end
+ s_collect_style(found,done,all,style,family)
+ m_collect_style(found,done,all,style,families,sorted,strictname)
+ end
+ elseif width and width ~= "" then
+ if trace_names then
+ report_names("resolving stage %s, name %a, width %a",stage,name,width)
+ end
+ s_collect_width(found,done,all,width,family)
+ m_collect_width(found,done,all,width,families,sorted,strictname)
+ else
+ if trace_names then
+ report_names("resolving stage %s, name %a",stage,name)
+ end
+ s_collect(found,done,all,family)
+ m_collect(found,done,all,families,sorted,strictname)
+ end
+end
+
+local function heuristic(name,weight,style,width,variant,all) -- todo: fallbacks
+ local found, done = { }, { }
+--~ print(name,weight,style,width,variant)
+ weight, style, width, variant = weight or "normal", style or "normal", width or "normal", variant or "normal"
+ name = cleanname(name)
+ collect(1,found,done,name,weight,style,width,variant,all)
+ -- still needed ?
+ if #found == 0 and variant ~= "normal" then -- not weight
+ variant = "normal"
+ collect(4,found,done,name,weight,style,width,variant,all)
+ end
+ if #found == 0 and width ~= "normal" then
+ width = "normal"
+ collect(2,found,done,name,weight,style,width,variant,all)
+ end
+ if #found == 0 and weight ~= "normal" then -- not style
+ weight = "normal"
+ collect(3,found,done,name,weight,style,width,variant,all)
+ end
+ if #found == 0 and style ~= "normal" then -- not weight
+ style = "normal"
+ collect(4,found,done,name,weight,style,width,variant,all)
+ end
+ --
+ local nf = #found
+ if trace_names then
+ if nf then
+ local t = { }
+ for i=1,nf do
+ t[i] = formatters["%a"](found[i].fontname)
+ end
+ report_names("name %a resolved to %s instances: % t",name,nf,t)
+ else
+ report_names("name %a unresolved",name)
+ end
+ end
+ if all then
+ return nf > 0 and found
+ else
+ return found[1]
+ end
+end
+
+function names.specification(askedname,weight,style,width,variant,reload,all)
+ if askedname and askedname ~= "" and names.enabled then
+ askedname = cleanname(askedname) -- or cleanname
+ names.load(reload)
+ local found = heuristic(askedname,weight,style,width,variant,all)
+ if not found and is_reloaded() then
+ found = heuristic(askedname,weight,style,width,variant,all)
+ if not filename then
+ found = foundname(askedname) -- old method
+ end
+ end
+ return found
+ end
+end
+
+function names.collect(askedname,weight,style,width,variant,reload,all)
+ if askedname and askedname ~= "" and names.enabled then
+ askedname = cleanname(askedname) -- or cleanname
+ names.load(reload)
+ local list = heuristic(askedname,weight,style,width,variant,true)
+ if not list or #list == 0 and is_reloaded() then
+ list = heuristic(askedname,weight,style,width,variant,true)
+ end
+ return list
+ end
+end
+
+function names.collectspec(askedname,reload,all)
+ local name, weight, style, width, variant = names.splitspec(askedname)
+ return names.collect(name,weight,style,width,variant,reload,all)
+end
+
+function names.resolvespec(askedname,sub) -- redefined later
+ local found = names.specification(names.splitspec(askedname))
+ if found then
+ return found.filename, found.subfont and found.rawname
+ end
+end
+
+function names.collectfiles(askedname,reload) -- no all
+ if askedname and askedname ~= "" and names.enabled then
+ askedname = cleanname(askedname) -- or cleanname
+ names.load(reload)
+ local list = { }
+ local specifications = names.data.specifications
+ for i=1,#specifications do
+ local s = specifications[i]
+ if find(cleanname(basename(s.filename)),askedname) then
+ list[#list+1] = s
+ end
+ end
+ return list
+ end
+end
+
+-- todo:
+--
+-- blacklisted = {
+-- ["cmr10.ttf"] = "completely messed up",
+-- }
+
+function names.exists(name)
+ local found = false
+ local list = filters.list
+ for k=1,#list do
+ local v = list[k]
+ found = (findfile(name,v) or "") ~= ""
+ if found then
+ return found
+ end
+ end
+ return (findfile(name,"tfm") or "") ~= "" or (names.resolve(name) or "") ~= ""
+end
+
+local lastlookups, lastpattern = { }, ""
+
+function names.lookup(pattern,name,reload) -- todo: find
+ if lastpattern ~= pattern then
+ names.load(reload)
+ local specifications = names.data.specifications
+ local families = names.data.families
+ local lookups = specifications
+ if name then
+ lookups = families[name]
+ elseif not find(pattern,"=") then
+ lookups = families[pattern]
+ end
+ if trace_names then
+ report_names("starting with %s lookups for %a",#lookups,pattern)
+ end
+ if lookups then
+ for key, value in gmatch(pattern,"([^=,]+)=([^=,]+)") do
+ local t, n = { }, 0
+ if find(value,"*") then
+ value = string.topattern(value)
+ for i=1,#lookups do
+ local s = lookups[i]
+ if find(s[key],value) then
+ n = n + 1
+ t[n] = lookups[i]
+ end
+ end
+ else
+ for i=1,#lookups do
+ local s = lookups[i]
+ if s[key] == value then
+ n = n + 1
+ t[n] = lookups[i]
+ end
+ end
+ end
+ if trace_names then
+ report_names("%s matches for key %a with value %a",#t,key,value)
+ end
+ lookups = t
+ end
+ end
+ lastpattern = pattern
+ lastlookups = lookups or { }
+ end
+ return #lastlookups
+end
+
+function names.getlookupkey(key,n)
+ local l = lastlookups[n or 1]
+ return (l and l[key]) or ""
+end
+
+function names.noflookups()
+ return #lastlookups
+end
+
+function names.getlookups(pattern,name,reload)
+ if pattern then
+ names.lookup(pattern,name,reload)
+ end
+ return lastlookups
+end
+
+-- The following is new ... watch the overload!
+
+local specifications = allocate()
+names.specifications = specifications
+
+-- files = {
+-- name = "antykwapoltawskiego",
+-- list = {
+-- ["AntPoltLtCond-Regular.otf"] = {
+-- -- name = "antykwapoltawskiego",
+-- style = "regular",
+-- weight = "light",
+-- width = "condensed",
+-- },
+-- },
+-- }
+
+function names.register(files)
+ if files then
+ local list, commonname = files.list, files.name
+ if list then
+ local n, m = 0, 0
+ for filename, filespec in next, list do
+ local name = lower(filespec.name or commonname)
+ if name and name ~= "" then
+ local style = normalized_styles [lower(filespec.style or "normal")]
+ local width = normalized_widths [lower(filespec.width or "normal")]
+ local weight = normalized_weights [lower(filespec.weight or "normal")]
+ local variant = normalized_variants[lower(filespec.variant or "normal")]
+ local weights = specifications[name ] if not weights then weights = { } specifications[name ] = weights end
+ local styles = weights [weight] if not styles then styles = { } weights [weight] = styles end
+ local widths = styles [style ] if not widths then widths = { } styles [style ] = widths end
+ local variants = widths [width ] if not variants then variants = { } widths [width ] = variants end
+ variants[variant] = filename
+ n = n + 1
+ else
+ m = m + 1
+ end
+ end
+ if trace_specifications then
+ report_names("%s filenames registered, %s filenames rejected",n,m)
+ end
+ end
+ end
+end
+
+function names.registered(name,weight,style,width,variant)
+ local ok = specifications[name]
+ ok = ok and (ok[(weight and weight ~= "" and weight ) or "normal"] or ok.normal)
+ ok = ok and (ok[(style and style ~= "" and style ) or "normal"] or ok.normal)
+ ok = ok and (ok[(width and width ~= "" and width ) or "normal"] or ok.normal)
+ ok = ok and (ok[(variant and variant ~= "" and variant) or "normal"] or ok.normal)
+ --
+ -- todo: same fallbacks as with database
+ --
+ if ok then
+ return {
+ filename = ok,
+ subname = "",
+ -- rawname = nil,
+ }
+ end
+end
+
+function names.resolvespec(askedname,sub) -- overloads previous definition
+ local name, weight, style, width, variant = names.splitspec(askedname)
+ if trace_specifications then
+ report_names("resolving specification: %a to name=%s, weight=%s, style=%s, width=%s, variant=%s",askedname,name,weight,style,width,variant)
+ end
+ local found = names.registered(name,weight,style,width,variant)
+ if found and found.filename then
+ if trace_specifications then
+ report_names("resolved by registered names: %a to %s",askedname,found.filename)
+ end
+ return found.filename, found.subname, found.rawname
+ else
+ found = names.specification(name,weight,style,width,variant)
+ if found and found.filename then
+ if trace_specifications then
+ report_names("resolved by font database: %a to %s",askedname,found.filename)
+ end
+ return found.filename, found.subfont and found.rawname
+ end
+ end
+ if trace_specifications then
+ report_names("unresolved: %s",askedname)
+ end
+end
diff --git a/tex/context/base/font-tfm.lua b/tex/context/base/font-tfm.lua
index 316e11a65..316b947a3 100644
--- a/tex/context/base/font-tfm.lua
+++ b/tex/context/base/font-tfm.lua
@@ -1,152 +1,152 @@
-if not modules then modules = { } end modules ['font-tfm'] = {
- 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 next = next
-local match = string.match
-
-local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end)
-local trace_features = false trackers.register("tfm.features", function(v) trace_features = v end)
-
-local report_defining = logs.reporter("fonts","defining")
-local report_tfm = logs.reporter("fonts","tfm loading")
-
-local findbinfile = resolvers.findbinfile
-
-local fonts = fonts
-local handlers = fonts.handlers
-local readers = fonts.readers
-local constructors = fonts.constructors
-local encodings = fonts.encodings
-
-local tfm = constructors.newhandler("tfm")
-
-local tfmfeatures = constructors.newfeatures("tfm")
-local registertfmfeature = tfmfeatures.register
-
-constructors.resolvevirtualtoo = false -- wil be set in font-ctx.lua
-
-fonts.formats.tfm = "type1" -- we need to have at least a value here
-
---[[ldx--
-<p>The next function encapsulates the standard <l n='tfm'/> loader as
-supplied by <l n='luatex'/>.</p>
---ldx]]--
-
--- this might change: not scaling and then apply features and do scaling in the
--- usual way with dummy descriptions but on the other hand .. we no longer use
--- tfm so why bother
-
--- ofm directive blocks local path search unless set; btw, in context we
--- don't support ofm files anyway as this format is obsolete
-
-function tfm.setfeatures(tfmdata,features)
- local okay = constructors.initializefeatures("tfm",tfmdata,features,trace_features,report_tfm)
- if okay then
- return constructors.collectprocessors("tfm",tfmdata,features,trace_features,report_tfm)
- else
- return { } -- will become false
- end
-end
-
-local function read_from_tfm(specification)
- local filename = specification.filename
- local size = specification.size
- if trace_defining then
- report_defining("loading tfm file %a at size %s",filename,size)
- end
- local tfmdata = font.read_tfm(filename,size) -- not cached, fast enough
- if tfmdata then
- local features = specification.features and specification.features.normal or { }
- local resources = tfmdata.resources or { }
- local properties = tfmdata.properties or { }
- local parameters = tfmdata.parameters or { }
- local shared = tfmdata.shared or { }
- properties.name = tfmdata.name
- properties.fontname = tfmdata.fontname
- properties.psname = tfmdata.psname
- properties.filename = specification.filename
- parameters.size = size
- shared.rawdata = { }
- shared.features = features
- shared.processes = next(features) and tfm.setfeatures(tfmdata,features) or nil
- --
- tfmdata.properties = properties
- tfmdata.resources = resources
- tfmdata.parameters = parameters
- tfmdata.shared = shared
- --
- parameters.slant = parameters.slant or parameters[1] or 0
- parameters.space = parameters.space or parameters[2] or 0
- parameters.space_stretch = parameters.space_stretch or parameters[3] or 0
- parameters.space_shrink = parameters.space_shrink or parameters[4] or 0
- parameters.x_height = parameters.x_height or parameters[5] or 0
- parameters.quad = parameters.quad or parameters[6] or 0
- parameters.extra_space = parameters.extra_space or parameters[7] or 0
- --
- constructors.enhanceparameters(parameters) -- official copies for us
- --
- if constructors.resolvevirtualtoo then
- fonts.loggers.register(tfmdata,file.suffix(filename),specification) -- strange, why here
- local vfname = findbinfile(specification.name, 'ovf')
- if vfname and vfname ~= "" then
- local vfdata = font.read_vf(vfname,size) -- not cached, fast enough
- if vfdata then
- local chars = tfmdata.characters
- for k,v in next, vfdata.characters do
- chars[k].commands = v.commands
- end
- properties.virtualized = true
- tfmdata.fonts = vfdata.fonts
- end
- end
- end
- --
- local allfeatures = tfmdata.shared.features or specification.features.normal
- constructors.applymanipulators("tfm",tfmdata,allfeatures.normal,trace_features,report_tfm)
- if not features.encoding then
- local encoding, filename = match(properties.filename,"^(.-)%-(.*)$") -- context: encoding-name.*
- if filename and encoding and encodings.known[encoding] then
- features.encoding = encoding
- end
- end
- --
- return tfmdata
- end
-end
-
-local function check_tfm(specification,fullname) -- we could split up like afm/otf
- local foundname = findbinfile(fullname, 'tfm') or ""
- if foundname == "" then
- foundname = findbinfile(fullname, 'ofm') or "" -- not needed in context
- end
- if foundname == "" then
- foundname = fonts.names.getfilename(fullname,"tfm") or ""
- end
- if foundname ~= "" then
- specification.filename = foundname
- specification.format = "ofm"
- return read_from_tfm(specification)
- elseif trace_defining then
- report_defining("loading tfm with name %a fails",specification.name)
- end
-end
-
-readers.check_tfm = check_tfm
-
-function readers.tfm(specification)
- local fullname = specification.filename or ""
- if fullname == "" then
- local forced = specification.forced or ""
- if forced ~= "" then
- fullname = specification.name .. "." .. forced
- else
- fullname = specification.name
- end
- end
- return check_tfm(specification,fullname)
-end
+if not modules then modules = { } end modules ['font-tfm'] = {
+ 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 next = next
+local match = string.match
+
+local trace_defining = false trackers.register("fonts.defining", function(v) trace_defining = v end)
+local trace_features = false trackers.register("tfm.features", function(v) trace_features = v end)
+
+local report_defining = logs.reporter("fonts","defining")
+local report_tfm = logs.reporter("fonts","tfm loading")
+
+local findbinfile = resolvers.findbinfile
+
+local fonts = fonts
+local handlers = fonts.handlers
+local readers = fonts.readers
+local constructors = fonts.constructors
+local encodings = fonts.encodings
+
+local tfm = constructors.newhandler("tfm")
+
+local tfmfeatures = constructors.newfeatures("tfm")
+local registertfmfeature = tfmfeatures.register
+
+constructors.resolvevirtualtoo = false -- wil be set in font-ctx.lua
+
+fonts.formats.tfm = "type1" -- we need to have at least a value here
+
+--[[ldx--
+<p>The next function encapsulates the standard <l n='tfm'/> loader as
+supplied by <l n='luatex'/>.</p>
+--ldx]]--
+
+-- this might change: not scaling and then apply features and do scaling in the
+-- usual way with dummy descriptions but on the other hand .. we no longer use
+-- tfm so why bother
+
+-- ofm directive blocks local path search unless set; btw, in context we
+-- don't support ofm files anyway as this format is obsolete
+
+function tfm.setfeatures(tfmdata,features)
+ local okay = constructors.initializefeatures("tfm",tfmdata,features,trace_features,report_tfm)
+ if okay then
+ return constructors.collectprocessors("tfm",tfmdata,features,trace_features,report_tfm)
+ else
+ return { } -- will become false
+ end
+end
+
+local function read_from_tfm(specification)
+ local filename = specification.filename
+ local size = specification.size
+ if trace_defining then
+ report_defining("loading tfm file %a at size %s",filename,size)
+ end
+ local tfmdata = font.read_tfm(filename,size) -- not cached, fast enough
+ if tfmdata then
+ local features = specification.features and specification.features.normal or { }
+ local resources = tfmdata.resources or { }
+ local properties = tfmdata.properties or { }
+ local parameters = tfmdata.parameters or { }
+ local shared = tfmdata.shared or { }
+ properties.name = tfmdata.name
+ properties.fontname = tfmdata.fontname
+ properties.psname = tfmdata.psname
+ properties.filename = specification.filename
+ parameters.size = size
+ shared.rawdata = { }
+ shared.features = features
+ shared.processes = next(features) and tfm.setfeatures(tfmdata,features) or nil
+ --
+ tfmdata.properties = properties
+ tfmdata.resources = resources
+ tfmdata.parameters = parameters
+ tfmdata.shared = shared
+ --
+ parameters.slant = parameters.slant or parameters[1] or 0
+ parameters.space = parameters.space or parameters[2] or 0
+ parameters.space_stretch = parameters.space_stretch or parameters[3] or 0
+ parameters.space_shrink = parameters.space_shrink or parameters[4] or 0
+ parameters.x_height = parameters.x_height or parameters[5] or 0
+ parameters.quad = parameters.quad or parameters[6] or 0
+ parameters.extra_space = parameters.extra_space or parameters[7] or 0
+ --
+ constructors.enhanceparameters(parameters) -- official copies for us
+ --
+ if constructors.resolvevirtualtoo then
+ fonts.loggers.register(tfmdata,file.suffix(filename),specification) -- strange, why here
+ local vfname = findbinfile(specification.name, 'ovf')
+ if vfname and vfname ~= "" then
+ local vfdata = font.read_vf(vfname,size) -- not cached, fast enough
+ if vfdata then
+ local chars = tfmdata.characters
+ for k,v in next, vfdata.characters do
+ chars[k].commands = v.commands
+ end
+ properties.virtualized = true
+ tfmdata.fonts = vfdata.fonts
+ end
+ end
+ end
+ --
+ local allfeatures = tfmdata.shared.features or specification.features.normal
+ constructors.applymanipulators("tfm",tfmdata,allfeatures.normal,trace_features,report_tfm)
+ if not features.encoding then
+ local encoding, filename = match(properties.filename,"^(.-)%-(.*)$") -- context: encoding-name.*
+ if filename and encoding and encodings.known[encoding] then
+ features.encoding = encoding
+ end
+ end
+ --
+ return tfmdata
+ end
+end
+
+local function check_tfm(specification,fullname) -- we could split up like afm/otf
+ local foundname = findbinfile(fullname, 'tfm') or ""
+ if foundname == "" then
+ foundname = findbinfile(fullname, 'ofm') or "" -- not needed in context
+ end
+ if foundname == "" then
+ foundname = fonts.names.getfilename(fullname,"tfm") or ""
+ end
+ if foundname ~= "" then
+ specification.filename = foundname
+ specification.format = "ofm"
+ return read_from_tfm(specification)
+ elseif trace_defining then
+ report_defining("loading tfm with name %a fails",specification.name)
+ end
+end
+
+readers.check_tfm = check_tfm
+
+function readers.tfm(specification)
+ local fullname = specification.filename or ""
+ if fullname == "" then
+ local forced = specification.forced or ""
+ if forced ~= "" then
+ fullname = specification.name .. "." .. forced
+ else
+ fullname = specification.name
+ end
+ end
+ return check_tfm(specification,fullname)
+end
diff --git a/tex/context/base/font-trt.lua b/tex/context/base/font-trt.lua
index d382e62d7..6fc8028d1 100644
--- a/tex/context/base/font-trt.lua
+++ b/tex/context/base/font-trt.lua
@@ -1,57 +1,57 @@
-if not modules then modules = { } end modules ['font-trt'] = {
- 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 rawget, dofile, next = rawget, dofile, next
-
---[[ldx--
-<p>We provide a simple treatment mechanism (mostly because I want to demonstrate
-something in a manual). It's one of the few places where an lfg file gets loaded
-outside the goodies manager.</p>
---ldx]]--
-
-local treatments = utilities.storage.allocate()
-fonts.treatments = treatments
-local treatmentdata = { }
-treatments.data = treatmentdata
-treatments.filename = "treatments.lfg"
-
--- function treatments.load(name)
--- local filename = resolvers.findfile(name)
--- if filename and filename ~= "" then
--- local goodies = dofile(filename)
--- if goodies then
--- local treatments = goodies.treatments
--- if treatments then
--- for name, data in next, treatments do
--- treatmentdata[name] = data -- always wins
--- end
--- end
--- end
--- end
--- end
-
-table.setmetatableindex(treatmentdata,function(t,k)
- local files = resolvers.findfiles(treatments.filename)
- if files then
- for i=1,#files do
- local goodies = dofile(files[i])
- if goodies then
- local treatments = goodies.treatments
- if treatments then
- for name, data in next, treatments do
- if not rawget(t,name) then
- t[name] = data
- end
- end
- end
- end
- end
- end
- table.setmetatableindex(treatmentdata,nil)
- return treatmentdata[k]
-end)
+if not modules then modules = { } end modules ['font-trt'] = {
+ 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 rawget, dofile, next = rawget, dofile, next
+
+--[[ldx--
+<p>We provide a simple treatment mechanism (mostly because I want to demonstrate
+something in a manual). It's one of the few places where an lfg file gets loaded
+outside the goodies manager.</p>
+--ldx]]--
+
+local treatments = utilities.storage.allocate()
+fonts.treatments = treatments
+local treatmentdata = { }
+treatments.data = treatmentdata
+treatments.filename = "treatments.lfg"
+
+-- function treatments.load(name)
+-- local filename = resolvers.findfile(name)
+-- if filename and filename ~= "" then
+-- local goodies = dofile(filename)
+-- if goodies then
+-- local treatments = goodies.treatments
+-- if treatments then
+-- for name, data in next, treatments do
+-- treatmentdata[name] = data -- always wins
+-- end
+-- end
+-- end
+-- end
+-- end
+
+table.setmetatableindex(treatmentdata,function(t,k)
+ local files = resolvers.findfiles(treatments.filename)
+ if files then
+ for i=1,#files do
+ local goodies = dofile(files[i])
+ if goodies then
+ local treatments = goodies.treatments
+ if treatments then
+ for name, data in next, treatments do
+ if not rawget(t,name) then
+ t[name] = data
+ end
+ end
+ end
+ end
+ end
+ end
+ table.setmetatableindex(treatmentdata,nil)
+ return treatmentdata[k]
+end)
diff --git a/tex/context/base/font-vf.lua b/tex/context/base/font-vf.lua
index bc6ed400e..1fe6dd71c 100644
--- a/tex/context/base/font-vf.lua
+++ b/tex/context/base/font-vf.lua
@@ -1,205 +1,205 @@
-if not modules then modules = { } end modules ['font-vf'] = {
- 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>This is very experimental code! Not yet adapted to recent changes. This will change.</p>
---ldx]]--
-
--- present in the backend but unspecified:
---
--- vf.rule vf.special vf.right vf.push vf.down vf.char vf.node vf.fontid vf.pop vf.image vf.nop
-
-local next = next
-
-local allocate = utilities.storage.allocate
-local setmetatableindex = table.setmetatableindex
-local fastcopy = table.fastcopy
-
-local fonts = fonts
-local constructors = fonts.constructors
-local vf = constructors.newhandler("vf")
-
--- general code
-
-function vf.find(name)
- name = file.removesuffix(file.basename(name))
- if constructors.resolvevirtualtoo then
- local format = fonts.loggers.format(name)
- if format == 'tfm' or format == 'ofm' then
- if trace_defining then
- report_defining("locating vf for %a",name)
- end
- return findbinfile(name,"ovf")
- else
- if trace_defining then
- report_defining("vf for %a is already taken care of",name)
- end
- return nil -- ""
- end
- else
- if trace_defining then
- report_defining("locating vf for %a",name)
- end
- return findbinfile(name,"ovf")
- end
-end
-
---[[ldx--
-<p>We overload the <l n='vf'/> reader.</p>
---ldx]]--
-
-callbacks.register('find_vf_file', vf.find, "locating virtual fonts, insofar needed") -- not that relevant any more
-
--- specific code (will move to other module)
-
-local definers = fonts.definers
-local methods = definers.methods
-
-local variants = allocate()
-local combinations = { }
-local combiner = { }
-local whatever = allocate()
-local helpers = allocate()
-local predefined = allocate {
- dummy = { "comment" },
- push = { "push" },
- pop = { "pop" },
-}
-
-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, gc = f.characters, g.characters
- local fd, gd = f.descriptions, 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, gc = f.characters, g.characters
- local fd, gd = f.descriptions, 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, value = v[2], 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.load = combine_load
---~ combiner.assign = combine_assign
---~ combiner.process = combine_process
---~ combiner.names = combine_names
---~ combiner.feature = combine_feature
-
-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 = {
- virtualized = true,
- },
- fonts = {
- },
- characters = {
- },
- descriptions = {
- },
- specification = fastcopy(specification),
- }
- combine_process(g,combinations[tag])
- return g
-end
+if not modules then modules = { } end modules ['font-vf'] = {
+ 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>This is very experimental code! Not yet adapted to recent changes. This will change.</p>
+--ldx]]--
+
+-- present in the backend but unspecified:
+--
+-- vf.rule vf.special vf.right vf.push vf.down vf.char vf.node vf.fontid vf.pop vf.image vf.nop
+
+local next = next
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+local fastcopy = table.fastcopy
+
+local fonts = fonts
+local constructors = fonts.constructors
+local vf = constructors.newhandler("vf")
+
+-- general code
+
+function vf.find(name)
+ name = file.removesuffix(file.basename(name))
+ if constructors.resolvevirtualtoo then
+ local format = fonts.loggers.format(name)
+ if format == 'tfm' or format == 'ofm' then
+ if trace_defining then
+ report_defining("locating vf for %a",name)
+ end
+ return findbinfile(name,"ovf")
+ else
+ if trace_defining then
+ report_defining("vf for %a is already taken care of",name)
+ end
+ return nil -- ""
+ end
+ else
+ if trace_defining then
+ report_defining("locating vf for %a",name)
+ end
+ return findbinfile(name,"ovf")
+ end
+end
+
+--[[ldx--
+<p>We overload the <l n='vf'/> reader.</p>
+--ldx]]--
+
+callbacks.register('find_vf_file', vf.find, "locating virtual fonts, insofar needed") -- not that relevant any more
+
+-- specific code (will move to other module)
+
+local definers = fonts.definers
+local methods = definers.methods
+
+local variants = allocate()
+local combinations = { }
+local combiner = { }
+local whatever = allocate()
+local helpers = allocate()
+local predefined = allocate {
+ dummy = { "comment" },
+ push = { "push" },
+ pop = { "pop" },
+}
+
+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, gc = f.characters, g.characters
+ local fd, gd = f.descriptions, 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, gc = f.characters, g.characters
+ local fd, gd = f.descriptions, 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, value = v[2], 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.load = combine_load
+--~ combiner.assign = combine_assign
+--~ combiner.process = combine_process
+--~ combiner.names = combine_names
+--~ combiner.feature = combine_feature
+
+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 = {
+ virtualized = true,
+ },
+ fonts = {
+ },
+ characters = {
+ },
+ descriptions = {
+ },
+ specification = fastcopy(specification),
+ }
+ combine_process(g,combinations[tag])
+ return g
+end
diff --git a/tex/context/base/grph-epd.lua b/tex/context/base/grph-epd.lua
index 49022e464..4f9d46097 100644
--- a/tex/context/base/grph-epd.lua
+++ b/tex/context/base/grph-epd.lua
@@ -1,25 +1,25 @@
-if not modules then modules = { } end modules ['grph-epd'] = {
- version = 1.001,
- comment = "companion to grph-epd.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local variables = interfaces.variables
-local settings_to_hash = utilities.parsers.settings_to_hash
-
--- todo: page, name, file, url
-
-local codeinjections = backends.codeinjections
-
-function figures.mergegoodies(optionlist)
- local options = settings_to_hash(optionlist)
- local all = options[variables.all] or options[variables.yes]
- if all or options[variables.reference] then
- codeinjections.mergereferences()
- end
- if all or options[variables.layer] then
- codeinjections.mergeviewerlayers()
- end
-end
+if not modules then modules = { } end modules ['grph-epd'] = {
+ version = 1.001,
+ comment = "companion to grph-epd.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local variables = interfaces.variables
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+-- todo: page, name, file, url
+
+local codeinjections = backends.codeinjections
+
+function figures.mergegoodies(optionlist)
+ local options = settings_to_hash(optionlist)
+ local all = options[variables.all] or options[variables.yes]
+ if all or options[variables.reference] then
+ codeinjections.mergereferences()
+ end
+ if all or options[variables.layer] then
+ codeinjections.mergeviewerlayers()
+ end
+end
diff --git a/tex/context/base/grph-fil.lua b/tex/context/base/grph-fil.lua
index 9ee90b07a..3449f1779 100644
--- a/tex/context/base/grph-fil.lua
+++ b/tex/context/base/grph-fil.lua
@@ -1,71 +1,71 @@
-if not modules then modules = { } end modules ['grph-fil'] = {
- version = 1.001,
- comment = "companion to grph-fig.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local type = type
-
-local trace_run = false trackers.register("graphic.runfile",function(v) trace_run = v end)
-local report_run = logs.reporter("graphics","run")
-
--- Historically running files is part of graphics processing, so this is why it
--- sits here but is part of the job namespace.
-
-local allocate = utilities.storage.allocate
-
-local collected = allocate()
-local tobesaved = allocate()
-
-local jobfiles = {
- collected = collected,
- tobesaved = tobesaved,
- forcerun = false, -- maybe a directive some day
-}
-
-job.files = jobfiles
-
-local function initializer()
- tobesaved = jobfiles.tobesaved
- collected = jobfiles.collected
-end
-
-job.register('job.files.collected', tobesaved, initializer)
-
-function jobfiles.run(name,action)
- local oldchecksum = collected[name]
- local newchecksum = file.checksum(name)
- if jobfiles.forcerun or not oldchecksum or oldchecksum ~= newchecksum then
- if trace_run then
- report_run("processing file, changes in %a, processing forced",name)
- end
- local ta = type(action)
- if ta == "function" then
- action(name)
- elseif ta == "string" and action ~= "" then
- os.execute(action)
- else
- report_run("processing file, no action given for processing %a",name)
- end
- elseif trace_run then
- report_run("processing file, no changes in %a, not processed",name)
- end
- tobesaved[name] = newchecksum
-end
-
---
-
-function jobfiles.context(name,options)
- if type(name) == "table" then
- local result = { }
- for i=1,#name do
- result[#result+1] = jobfiles.context(name[i],options)
- end
- return result
- else
- jobfiles.run(name,"context ".. (options or "") .. " " .. name)
- return file.replacesuffix(name,"pdf")
- end
-end
+if not modules then modules = { } end modules ['grph-fil'] = {
+ version = 1.001,
+ comment = "companion to grph-fig.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local type = type
+
+local trace_run = false trackers.register("graphic.runfile",function(v) trace_run = v end)
+local report_run = logs.reporter("graphics","run")
+
+-- Historically running files is part of graphics processing, so this is why it
+-- sits here but is part of the job namespace.
+
+local allocate = utilities.storage.allocate
+
+local collected = allocate()
+local tobesaved = allocate()
+
+local jobfiles = {
+ collected = collected,
+ tobesaved = tobesaved,
+ forcerun = false, -- maybe a directive some day
+}
+
+job.files = jobfiles
+
+local function initializer()
+ tobesaved = jobfiles.tobesaved
+ collected = jobfiles.collected
+end
+
+job.register('job.files.collected', tobesaved, initializer)
+
+function jobfiles.run(name,action)
+ local oldchecksum = collected[name]
+ local newchecksum = file.checksum(name)
+ if jobfiles.forcerun or not oldchecksum or oldchecksum ~= newchecksum then
+ if trace_run then
+ report_run("processing file, changes in %a, processing forced",name)
+ end
+ local ta = type(action)
+ if ta == "function" then
+ action(name)
+ elseif ta == "string" and action ~= "" then
+ os.execute(action)
+ else
+ report_run("processing file, no action given for processing %a",name)
+ end
+ elseif trace_run then
+ report_run("processing file, no changes in %a, not processed",name)
+ end
+ tobesaved[name] = newchecksum
+end
+
+--
+
+function jobfiles.context(name,options)
+ if type(name) == "table" then
+ local result = { }
+ for i=1,#name do
+ result[#result+1] = jobfiles.context(name[i],options)
+ end
+ return result
+ else
+ jobfiles.run(name,"context ".. (options or "") .. " " .. name)
+ return file.replacesuffix(name,"pdf")
+ end
+end
diff --git a/tex/context/base/grph-inc.lua b/tex/context/base/grph-inc.lua
index ae4d5642d..9603419ae 100644
--- a/tex/context/base/grph-inc.lua
+++ b/tex/context/base/grph-inc.lua
@@ -1,1609 +1,1609 @@
-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: 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.
-]]--
-
-local format, lower, find, match, gsub, gmatch = string.format, string.lower, string.find, string.match, string.gsub, string.gmatch
-local texbox = tex.box
-local contains = table.contains
-local concat, insert, remove = table.concat, table.insert, table.remove
-local todimen = string.todimen
-local collapsepath = file.collapsepath
-local formatters = string.formatters
-local longtostring = string.longtostring
-local expandfilename = dir.expandname
-
-local P, lpegmatch = lpeg.P, 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 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 report_inclusion = logs.reporter("graphics","inclusion")
-
-local context, img = context, img
-
-local f_hash_part = formatters["%s->%s->%s"]
-local f_hash_full = formatters["%s->%s->%s->%s->%s->%s->%s"]
-
-local v_yes = variables.yes
-local v_low = variables.low
-local v_medium = variables.medium
-local v_high = variables.high
-local v_global = variables["global"]
-local v_local = variables["local"]
-local v_default = variables.default
-
-local maxdimen = 2^30-1
-
-function img.check(figure)
- if figure then
- local width = figure.width
- local height = figure.height
- if height > width then
- if height > maxdimen then
- figure.height = maxdimen
- figure.width = width * maxdimen/height
- report_inclusion("limiting natural dimensions of %a (%s)",figure.filename,"height")
- end
- elseif width > maxdimen then
- figure.width = maxdimen
- figure.height = height * maxdimen/width
- report_inclusion("limiting natural dimensions of %a (%s)",figure.filename,"width")
- end
- return figure
- end
-end
-
---- some extra img functions --- can become luat-img.lua
-
-local imgkeys = img.keys()
-
-function img.totable(imgtable)
- local result = { }
- for k=1,#imgkeys do
- local key = imgkeys[k]
- result[key] = imgtable[key]
- end
- return result
-end
-
-function img.serialize(i,...)
- return table.serialize(img.totable(i),...)
-end
-
-function img.print(i,...)
- return table.print(img.totable(i),...)
-end
-
-function img.clone(i,data)
- i.width = data.width or i.width
- i.height = data.height or i.height
- -- attr etc
- return i
-end
-
-local validsizes = table.tohash(img.boxes())
-local validtypes = table.tohash(img.types())
-
-function img.checksize(size)
- if size then
- size = gsub(size,"box","")
- return validsizes[size] and size or "crop"
- else
- return "crop"
- end
-end
-
-local indexed = { }
-
-function img.ofindex(n)
- return indexed[n]
-end
-
---- we can consider an grph-ini file
-
-figures = figures or { }
-local figures = figures
-
-figures.boxnumber = figures.boxnumber or 0
-figures.defaultsearch = true
-figures.defaultwidth = 0
-figures.defaultheight = 0
-figures.defaultdepth = 0
-figures.nofprocessed = 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 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+" } },
- ["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") },
-}
-
-figures.formats = figures_formats -- frozen
-figures.magics = figures_magics -- frozen
-figures.order = figures_order -- frozen
-
--- 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
-
-function figures.guess(filename)
- local f = io.open(filename,'rb')
- if f then
- local str = f:read(100)
- f:close()
- 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
-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,target,what)
- 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', target,suffix ) end
-function figures.registerpattern(pattern,target) register('pattern',target,pattern) end
-
-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
-
--- 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,
- 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
- request.width = w > 0 and w or nil
- request.height = h > 0 and h or nil
- --
- request.page = math.max(tonumber(request.page) or 1,1)
- request.size = img.checksize(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
- table.merge(figuredata.request,request)
- end
- return figuredata
-end
-
-function figures.push(request)
- statistics.starttiming(figures)
- local figuredata = figures.initialize(request)
- insert(callstack,figuredata)
- lastfiguredata = figuredata
- return figuredata
-end
-
-function figures.pop()
- lastfiguredata = remove(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
-
-figures.get = get
-
-function commands.figurevariable(category,tag,default)
- context(get(category,tag,default))
-end
-
-function commands.figurestatus (tag,default) context(get("status", tag,default)) end
-function commands.figurerequest(tag,default) context(get("request",tag,default)) end
-function commands.figureused (tag,default) context(get("used", tag,default)) end
-
-function commands.figurefilepath() context(file.dirname (get("used","fullname"))) end
-function commands.figurefilename() context(file.nameonly(get("used","fullname"))) end
-function commands.figurefiletype() context(file.extname (get("used","fullname"))) end
-
--- 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 register(askedname,specification)
- if not specification then
- specification = { }
- elseif forbiddenname(specification.fullname) then
- specification = { }
- else
- local format = specification.format
- if format then
- local conversion = specification.conversion
- local resolution = specification.resolution
- if conversion == "" then
- conversion = nil
- end
- if resolution == "" then
- resolution = nil
- end
- 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",
- askedname,specification.fullname,format,newformat,conversion or "default",resolution or "default")
- end
- -- quick hack
- local converter = (newformat ~= format or resolution) and converters[format]
- if converter then
- if converter[newformat] then
- converter = converter[newformat]
- else
- newformat = defaultformat
- if converter[newformat] then
- converter = converter[newformat]
- else
- converter = nil
- newformat = defaultformat
- end
- end
- elseif trace_conversion then
- report_inclusion("no converter for %a to %a",format,newformat)
- end
- if converter then
- 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 = fc
- else
- newbase = defaultprefix .. newbase
- end
- 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
- local subpath = specification.subpath or figures.cachepaths.subpath
- if subpath and subpath ~= "" and subpath ~= "." then
- newpath = newpath .. "/" .. subpath
- end
- local prefix = specification.prefix or figures.cachepaths.prefix
- if prefix and prefix ~= "" then
- newbase = prefix .. newbase
- end
- if resolution and resolution ~= "" then -- the order might change
- newbase = newbase .. "_" .. resolution
- 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)
- dir.makedirs(newpath)
- oldname = collapsepath(oldname)
- newname = collapsepath(newname)
- local oldtime = lfs.attributes(oldname,'modification') or 0
- local newtime = lfs.attributes(newname,'modification') 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 "")
- 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 thsi 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
- elseif io.exists(oldname) then
- specification.fullname = oldname -- was newname
- specification.converted = false
- end
- end
- end
- local found = figures_suffixes[format] -- validtypes[format]
- if not found then
- specification.found = false
- if trace_figures then
- report_inclusion("format %a is not supported",format)
- end
- else
- specification.found = true
- if trace_figures then
- if validtypes[format] then -- format?
- report_inclusion("format %a natively supported by backend",format)
- else
- report_inclusion("format %a supported by output file format",format)
- end
- end
- end
- end
- specification.foundname = specification.foundname or specification.fullname
- local askedhash = f_hash_part(askedname,specification.conversion or "default",specification.resolution or "default")
- figures_found[askedhash] = specification
- return specification
-end
-
-local resolve_too = false -- true
-
-local internalschemes = {
- file = 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
- local askedhash = f_hash_part(askedname,request.conversion or "default",request.resolution or "default")
- local foundname = figures_found[askedhash]
- if foundname then
- return foundname
- end
- --
- local askedcache = request.cache
- local askedconversion = request.conversion
- local askedresolution = request.resolution
- --
- if request.format == "" or request.format == "unknown" then
- request.format = nil
- end
- -- protocol check
- local hashed = url.hashed(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 lfs.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 askedformat = request.format or file.suffix(askedname) or ""
- 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,
- })
- end
- end
- -- we could use the hashed data instead
- local askedpath= file.is_rootbased_path(askedname)
- local askedbase = file.basename(askedname)
- local askedformat = request.format or file.suffix(askedname) or ""
- 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]
- break
- end
- end
- end
- if format then
- local foundname, quitscanning, forcedformat = 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,
- })
- elseif quitscanning then
- return register(askedname)
- end
- 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,
- })
- end
- else
- -- type given
- for i=1,#figure_paths do
- local path = figure_paths[i]
- 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 = check,
- format = askedformat,
- cache = askedcache,
- conversion = askedconversion,
- resolution = askedresolution,
- })
- 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,
- })
- 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,
- })
- 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 = figure_paths[i]
- local check = path .. "/" .. name
- local isfile = url.hashed(check).scheme == "file"
- 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,
- })
- 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 = figure_paths[i]
- 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,
- })
- 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,
- })
- end
- end
- end
- end
- end
- return register(askedname, { -- these two are needed for hashing 'found'
- conversion = askedconversion,
- resolution = askedresolution,
- })
-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.found and 10) or 0
- end
- return data
-end
-
-function figures.identify(data)
- data = data or callstack[#callstack] or lastfiguredata
- local list = identifiers.list -- defined at the end
- for i=1,#list do
- local identifier = list[i]
- data = identifier(data)
- if data.status.status > 0 then
- break
- 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
-
-function figures.include(data)
- data = data or callstack[#callstack] or lastfiguredata
- return (includers[data.status.format] or includers.generic)(data)
-end
-
-function figures.scale(data) -- will become lua code
- context.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 = texbox[nr]
- ds.width = box.width
- ds.height = box.height
- ds.xscale = ds.width /(du.width or 1)
- ds.yscale = ds.height/(du.height or 1)
- ds.page = ds.page or du.page or dr.page -- sort of redundant but can be limited
- 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 = node.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.dir = "TLT"
- box.width = du.width
- box.height = du.height
- box.depth = du.depth
- texbox[nr] = box -- hm, should be global (to be checked for consistency)
-end
-
--- -- -- generic -- -- --
-
-function existers.generic(askedname,resolve)
- -- not findbinfile
- local result
- if lfs.isfile(askedname) then
- result = askedname
- elseif resolve then
- result = resolvers.findbinfile(askedname) or ""
- if result == "" then result = false end
- 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
-
-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
- if not conversion or conversion == "" then
- conversion = "unknown"
- end
- if not resolution or resolution == "" then
- resolution = "unknown"
- end
- local hash = f_hash_full(name,page,size,color,conversion,resolution,mask)
- local figure = figures_loaded[hash]
- if figure == nil then
- figure = img.new {
- filename = name,
- page = page,
- pagebox = dr.size,
- -- visiblefilename = "", -- this prohibits the full filename ending up in the file
- }
- codeinjections.setfigurecolorspace(data,figure)
- codeinjections.setfiguremask(data,figure)
- figure = figure and img.check(img.scan(figure)) or false
- local f, d = codeinjections.setfigurealternative(data,figure)
- figure, data = f or figure, 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
- du.width = figure.width
- du.height = figure.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
- ds.private = figure
- ds.hash = hash
- end
- return data
-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
- if figure then
- figure = img.copy(figure)
- figure = figure and img.clone(figure,data.request) or false
- end
- figures_used[hash] = figure
- end
- if figure then
- local nr = figures.boxnumber
- -- it looks like we have a leak in attributes here .. todo
- local box = node.hpack(img.node(figure)) -- img.node(figure) not longer valid
- indexed[figure.index] = figure
- box.width, box.height, box.depth = figure.width, figure.height, 0 -- new, hm, tricky, we need to do that in tex (yet)
- texbox[nr] = box
- ds.objectnumber = figure.objnum
- context.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 job.objects.get("FIG::"..hash) then
- if type(command) == "function" then
- command()
- end
- context.dosetfigureobject(hash)
- end
- context.doboxfigureobject(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
- context.startfoundexternalfigure(width .. "sp",height .. "sp")
- context(function()
- nodeinjections.insertmovie {
- width = width,
- height = height,
- factor = number.dimenfactors.bp,
- ["repeat"] = dr["repeat"],
- controls = dr.controls,
- preview = dr.preview,
- label = dr.label,
- foundname = foundname,
- }
- end)
- context.stopfoundexternalfigure()
- return data
-end
-
-includers.mov = includers.nongeneric
-
--- -- -- mps -- -- --
-
-internalschemes.mprun = true
-
-local function internal(askedname)
- local spec, mprun, mpnum = match(lower(askedname),"mprun([:%.]?)(.-)%.(%d+)")
- 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
- 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() context.docheckfiguremprun(mprun,mpnum) end)
- else
- return checkers_nongeneric(data,function() context.docheckfiguremps(data.used.fullname) end)
- end
-end
-
-includers.mps = includers.nongeneric
-
--- -- -- tex -- -- --
-
-function existers.tex(askedname)
- askedname = resolvers.findfile(askedname)
- return askedname ~= "" and askedname or false
-end
-
-function checkers.tex(data)
- return checkers_nongeneric(data,function() context.docheckfiguretex(data.used.fullname) end)
-end
-
-includers.tex = includers.nongeneric
-
--- -- -- buffer -- -- --
-
-function existers.buffer(askedname)
- local name = file.nameonly(askedname)
- local okay = buffers.exists(name)
- return okay and name, true -- always quit scanning
-end
-
-function checkers.buffer(data)
- return checkers_nongeneric(data,function() context.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 -- -- --
-
-existers.cld = existers.tex
-
-function checkers.cld(data)
- return checkers_nongeneric(data,function() context.docheckfigurecld(data.used.fullname) end)
-end
-
-includers.cld = includers.nongeneric
-
--- -- -- converters -- -- --
-
-local function makeoptions(options)
- local to = type(options)
- return (to == "table" and concat(options," ")) or (to == "string" and options) or ""
-end
-
--- programs.makeoptions = makeoptions
-
-local function runprogram(binary,argument,variables)
- local binary = match(binary,"[%S]+") -- to be sure
- if type(argument) == "table" then
- argument = concat(argument," ") -- for old times sake
- end
- if not os.which(binary) then
- report_inclusion("program %a is not installed, not running command: %s",binary,command)
- elseif not argument or argument == "" then
- report_inclusion("nothing to run, unknown program %a",binary)
- else
- 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.spawn(command)
- end
-end
-
-programs.run = runprogram
-
--- -- -- eps & pdf -- -- --
---
--- \externalfigure[cow.eps]
--- \externalfigure[cow.pdf][conversion=stripped]
-
-local epsconverter = converters.eps or { }
-converters.eps = epsconverter
-converters.ps = epsconverter
-
-local epstopdf = {
- resolutions = {
- [v_low] = "screen",
- [v_medium] = "ebook",
- [v_high] = "prepress",
- },
- command = os.type == "windows" and "gswin32c" or "gs",
- -- -dProcessDSCComments=false
- argument = [[
- -q
- -sDEVICE=pdfwrite
- -dNOPAUSE
- -dNOCACHE
- -dBATCH
- -dAutoRotatePages=/None
- -dPDFSETTINGS=/%presets%
- -dEPSCrop
- -sOutputFile=%newname%
- %oldname%
- -c quit
- ]],
-}
-
-programs.epstopdf = epstopdf
-programs.gs = epstopdf
-
-function epsconverter.pdf(oldname,newname,resolution) -- the resolution interface might change
- local epstopdf = programs.epstopdf -- can be changed
- local presets = epstopdf.resolutions[resolution or ""] or epstopdf.resolutions.high
- runprogram(epstopdf.command, epstopdf.argument, {
- newname = newname,
- oldname = oldname,
- presets = presets,
- } )
-end
-
-epsconverter.default = epsconverter.pdf
-
-local pdfconverter = converters.pdf or { }
-converters.pdf = pdfconverter
-
-programs.pdftoeps = {
- command = "pdftops",
- argument = [[-eps "%oldname%" "%newname%]],
-}
-
-pdfconverter.stripped = function(oldname,newname)
- local pdftoeps = programs.pdftoeps -- can be changed
- local epstopdf = programs.epstopdf -- can be changed
- local presets = epstopdf.resolutions[resolution or ""] or epstopdf.resolutions.high
- local tmpname = newname .. ".tmp"
- runprogram(pdftoeps.command, pdftoeps.argument, { oldname = oldname, newname = tmpname, presets = presets })
- runprogram(epstopdf.command, epstopdf.argument, { oldname = tmpname, newname = newname, presets = presets })
- os.remove(tmpname)
-end
-
-figures.registersuffix("stripped","pdf")
-
--- -- -- svg -- -- --
-
-local svgconverter = { }
-converters.svg = svgconverter
-converters.svgz = svgconverter
-
--- inkscape on windows only works with complete paths
-
-programs.inkscape = {
- command = "inkscape",
- pdfargument = [[
- "%oldname%"
- --export-dpi=600
- -A
- "%newname%"
- ]],
- pngargument = [[
- "%oldname%"
- --export-dpi=600
- --export-png="%newname%"
- ]],
-}
-
-function svgconverter.pdf(oldname,newname)
- local inkscape = programs.inkscape -- can be changed
- runprogram(inkscape.command, inkscape.pdfargument, {
- newname = expandfilename(newname),
- oldname = expandfilename(oldname),
- } )
-end
-
-function svgconverter.png(oldname,newname)
- local inkscape = programs.inkscape
- runprogram(inkscape.command, inkscape.pngargument, {
- newname = expandfilename(newname),
- oldname = expandfilename(oldname),
- } )
-end
-
-svgconverter.default = svgconverter.pdf
-
--- -- -- gif -- -- --
--- -- -- tif -- -- --
-
-local gifconverter = converters.gif or { }
-local tifconverter = converters.tif or { }
-local bmpconverter = converters.bmp or { }
-
-converters.gif = gifconverter
-converters.tif = tifconverter
-converters.bmp = bmpconverter
-
-programs.convert = {
- command = "gm", -- graphicmagick
- argument = [[convert "%oldname%" "%newname%"]],
-}
-
-local function converter(oldname,newname)
- local convert = programs.convert
- runprogram(convert.command, convert.argument, {
- newname = newname,
- oldname = oldname,
- } )
-end
-
-tifconverter.pdf = converter
-gifconverter.pdf = converter
-bmpconverter.pdf = converter
-
-gifconverter.default = converter
-tifconverter.default = converter
-bmpconverter.default = converter
-
--- todo: lowres
-
--- -- -- 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
-
-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 = figure_paths[i]
- 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)
- 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
- return format("%s seconds including tex, %s processed images", statistics.elapsedtime(figures),nofprocessed)
- 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 fig = figures.push { name = pdffile }
--- figures.identify()
--- figures.check()
--- local nofpages = fig.used.pages
--- figures.pop()
-
--- interfacing
-
-commands.setfigurelookuporder = figures.setorder
+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: 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.
+]]--
+
+local format, lower, find, match, gsub, gmatch = string.format, string.lower, string.find, string.match, string.gsub, string.gmatch
+local texbox = tex.box
+local contains = table.contains
+local concat, insert, remove = table.concat, table.insert, table.remove
+local todimen = string.todimen
+local collapsepath = file.collapsepath
+local formatters = string.formatters
+local longtostring = string.longtostring
+local expandfilename = dir.expandname
+
+local P, lpegmatch = lpeg.P, 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 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 report_inclusion = logs.reporter("graphics","inclusion")
+
+local context, img = context, img
+
+local f_hash_part = formatters["%s->%s->%s"]
+local f_hash_full = formatters["%s->%s->%s->%s->%s->%s->%s"]
+
+local v_yes = variables.yes
+local v_low = variables.low
+local v_medium = variables.medium
+local v_high = variables.high
+local v_global = variables["global"]
+local v_local = variables["local"]
+local v_default = variables.default
+
+local maxdimen = 2^30-1
+
+function img.check(figure)
+ if figure then
+ local width = figure.width
+ local height = figure.height
+ if height > width then
+ if height > maxdimen then
+ figure.height = maxdimen
+ figure.width = width * maxdimen/height
+ report_inclusion("limiting natural dimensions of %a (%s)",figure.filename,"height")
+ end
+ elseif width > maxdimen then
+ figure.width = maxdimen
+ figure.height = height * maxdimen/width
+ report_inclusion("limiting natural dimensions of %a (%s)",figure.filename,"width")
+ end
+ return figure
+ end
+end
+
+--- some extra img functions --- can become luat-img.lua
+
+local imgkeys = img.keys()
+
+function img.totable(imgtable)
+ local result = { }
+ for k=1,#imgkeys do
+ local key = imgkeys[k]
+ result[key] = imgtable[key]
+ end
+ return result
+end
+
+function img.serialize(i,...)
+ return table.serialize(img.totable(i),...)
+end
+
+function img.print(i,...)
+ return table.print(img.totable(i),...)
+end
+
+function img.clone(i,data)
+ i.width = data.width or i.width
+ i.height = data.height or i.height
+ -- attr etc
+ return i
+end
+
+local validsizes = table.tohash(img.boxes())
+local validtypes = table.tohash(img.types())
+
+function img.checksize(size)
+ if size then
+ size = gsub(size,"box","")
+ return validsizes[size] and size or "crop"
+ else
+ return "crop"
+ end
+end
+
+local indexed = { }
+
+function img.ofindex(n)
+ return indexed[n]
+end
+
+--- we can consider an grph-ini file
+
+figures = figures or { }
+local figures = figures
+
+figures.boxnumber = figures.boxnumber or 0
+figures.defaultsearch = true
+figures.defaultwidth = 0
+figures.defaultheight = 0
+figures.defaultdepth = 0
+figures.nofprocessed = 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 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+" } },
+ ["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") },
+}
+
+figures.formats = figures_formats -- frozen
+figures.magics = figures_magics -- frozen
+figures.order = figures_order -- frozen
+
+-- 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
+
+function figures.guess(filename)
+ local f = io.open(filename,'rb')
+ if f then
+ local str = f:read(100)
+ f:close()
+ 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
+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,target,what)
+ 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', target,suffix ) end
+function figures.registerpattern(pattern,target) register('pattern',target,pattern) end
+
+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
+
+-- 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,
+ 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
+ request.width = w > 0 and w or nil
+ request.height = h > 0 and h or nil
+ --
+ request.page = math.max(tonumber(request.page) or 1,1)
+ request.size = img.checksize(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
+ table.merge(figuredata.request,request)
+ end
+ return figuredata
+end
+
+function figures.push(request)
+ statistics.starttiming(figures)
+ local figuredata = figures.initialize(request)
+ insert(callstack,figuredata)
+ lastfiguredata = figuredata
+ return figuredata
+end
+
+function figures.pop()
+ lastfiguredata = remove(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
+
+figures.get = get
+
+function commands.figurevariable(category,tag,default)
+ context(get(category,tag,default))
+end
+
+function commands.figurestatus (tag,default) context(get("status", tag,default)) end
+function commands.figurerequest(tag,default) context(get("request",tag,default)) end
+function commands.figureused (tag,default) context(get("used", tag,default)) end
+
+function commands.figurefilepath() context(file.dirname (get("used","fullname"))) end
+function commands.figurefilename() context(file.nameonly(get("used","fullname"))) end
+function commands.figurefiletype() context(file.extname (get("used","fullname"))) end
+
+-- 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 register(askedname,specification)
+ if not specification then
+ specification = { }
+ elseif forbiddenname(specification.fullname) then
+ specification = { }
+ else
+ local format = specification.format
+ if format then
+ local conversion = specification.conversion
+ local resolution = specification.resolution
+ if conversion == "" then
+ conversion = nil
+ end
+ if resolution == "" then
+ resolution = nil
+ end
+ 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",
+ askedname,specification.fullname,format,newformat,conversion or "default",resolution or "default")
+ end
+ -- quick hack
+ local converter = (newformat ~= format or resolution) and converters[format]
+ if converter then
+ if converter[newformat] then
+ converter = converter[newformat]
+ else
+ newformat = defaultformat
+ if converter[newformat] then
+ converter = converter[newformat]
+ else
+ converter = nil
+ newformat = defaultformat
+ end
+ end
+ elseif trace_conversion then
+ report_inclusion("no converter for %a to %a",format,newformat)
+ end
+ if converter then
+ 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 = fc
+ else
+ newbase = defaultprefix .. newbase
+ end
+ 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
+ local subpath = specification.subpath or figures.cachepaths.subpath
+ if subpath and subpath ~= "" and subpath ~= "." then
+ newpath = newpath .. "/" .. subpath
+ end
+ local prefix = specification.prefix or figures.cachepaths.prefix
+ if prefix and prefix ~= "" then
+ newbase = prefix .. newbase
+ end
+ if resolution and resolution ~= "" then -- the order might change
+ newbase = newbase .. "_" .. resolution
+ 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)
+ dir.makedirs(newpath)
+ oldname = collapsepath(oldname)
+ newname = collapsepath(newname)
+ local oldtime = lfs.attributes(oldname,'modification') or 0
+ local newtime = lfs.attributes(newname,'modification') 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 "")
+ 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 thsi 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
+ elseif io.exists(oldname) then
+ specification.fullname = oldname -- was newname
+ specification.converted = false
+ end
+ end
+ end
+ local found = figures_suffixes[format] -- validtypes[format]
+ if not found then
+ specification.found = false
+ if trace_figures then
+ report_inclusion("format %a is not supported",format)
+ end
+ else
+ specification.found = true
+ if trace_figures then
+ if validtypes[format] then -- format?
+ report_inclusion("format %a natively supported by backend",format)
+ else
+ report_inclusion("format %a supported by output file format",format)
+ end
+ end
+ end
+ end
+ specification.foundname = specification.foundname or specification.fullname
+ local askedhash = f_hash_part(askedname,specification.conversion or "default",specification.resolution or "default")
+ figures_found[askedhash] = specification
+ return specification
+end
+
+local resolve_too = false -- true
+
+local internalschemes = {
+ file = 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
+ local askedhash = f_hash_part(askedname,request.conversion or "default",request.resolution or "default")
+ local foundname = figures_found[askedhash]
+ if foundname then
+ return foundname
+ end
+ --
+ local askedcache = request.cache
+ local askedconversion = request.conversion
+ local askedresolution = request.resolution
+ --
+ if request.format == "" or request.format == "unknown" then
+ request.format = nil
+ end
+ -- protocol check
+ local hashed = url.hashed(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 lfs.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 askedformat = request.format or file.suffix(askedname) or ""
+ 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,
+ })
+ end
+ end
+ -- we could use the hashed data instead
+ local askedpath= file.is_rootbased_path(askedname)
+ local askedbase = file.basename(askedname)
+ local askedformat = request.format or file.suffix(askedname) or ""
+ 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]
+ break
+ end
+ end
+ end
+ if format then
+ local foundname, quitscanning, forcedformat = 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,
+ })
+ elseif quitscanning then
+ return register(askedname)
+ end
+ 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,
+ })
+ end
+ else
+ -- type given
+ for i=1,#figure_paths do
+ local path = figure_paths[i]
+ 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 = check,
+ format = askedformat,
+ cache = askedcache,
+ conversion = askedconversion,
+ resolution = askedresolution,
+ })
+ 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,
+ })
+ 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,
+ })
+ 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 = figure_paths[i]
+ local check = path .. "/" .. name
+ local isfile = url.hashed(check).scheme == "file"
+ 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,
+ })
+ 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 = figure_paths[i]
+ 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,
+ })
+ 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,
+ })
+ end
+ end
+ end
+ end
+ end
+ return register(askedname, { -- these two are needed for hashing 'found'
+ conversion = askedconversion,
+ resolution = askedresolution,
+ })
+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.found and 10) or 0
+ end
+ return data
+end
+
+function figures.identify(data)
+ data = data or callstack[#callstack] or lastfiguredata
+ local list = identifiers.list -- defined at the end
+ for i=1,#list do
+ local identifier = list[i]
+ data = identifier(data)
+ if data.status.status > 0 then
+ break
+ 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
+
+function figures.include(data)
+ data = data or callstack[#callstack] or lastfiguredata
+ return (includers[data.status.format] or includers.generic)(data)
+end
+
+function figures.scale(data) -- will become lua code
+ context.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 = texbox[nr]
+ ds.width = box.width
+ ds.height = box.height
+ ds.xscale = ds.width /(du.width or 1)
+ ds.yscale = ds.height/(du.height or 1)
+ ds.page = ds.page or du.page or dr.page -- sort of redundant but can be limited
+ 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 = node.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.dir = "TLT"
+ box.width = du.width
+ box.height = du.height
+ box.depth = du.depth
+ texbox[nr] = box -- hm, should be global (to be checked for consistency)
+end
+
+-- -- -- generic -- -- --
+
+function existers.generic(askedname,resolve)
+ -- not findbinfile
+ local result
+ if lfs.isfile(askedname) then
+ result = askedname
+ elseif resolve then
+ result = resolvers.findbinfile(askedname) or ""
+ if result == "" then result = false end
+ 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
+
+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
+ if not conversion or conversion == "" then
+ conversion = "unknown"
+ end
+ if not resolution or resolution == "" then
+ resolution = "unknown"
+ end
+ local hash = f_hash_full(name,page,size,color,conversion,resolution,mask)
+ local figure = figures_loaded[hash]
+ if figure == nil then
+ figure = img.new {
+ filename = name,
+ page = page,
+ pagebox = dr.size,
+ -- visiblefilename = "", -- this prohibits the full filename ending up in the file
+ }
+ codeinjections.setfigurecolorspace(data,figure)
+ codeinjections.setfiguremask(data,figure)
+ figure = figure and img.check(img.scan(figure)) or false
+ local f, d = codeinjections.setfigurealternative(data,figure)
+ figure, data = f or figure, 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
+ du.width = figure.width
+ du.height = figure.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
+ ds.private = figure
+ ds.hash = hash
+ end
+ return data
+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
+ if figure then
+ figure = img.copy(figure)
+ figure = figure and img.clone(figure,data.request) or false
+ end
+ figures_used[hash] = figure
+ end
+ if figure then
+ local nr = figures.boxnumber
+ -- it looks like we have a leak in attributes here .. todo
+ local box = node.hpack(img.node(figure)) -- img.node(figure) not longer valid
+ indexed[figure.index] = figure
+ box.width, box.height, box.depth = figure.width, figure.height, 0 -- new, hm, tricky, we need to do that in tex (yet)
+ texbox[nr] = box
+ ds.objectnumber = figure.objnum
+ context.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 job.objects.get("FIG::"..hash) then
+ if type(command) == "function" then
+ command()
+ end
+ context.dosetfigureobject(hash)
+ end
+ context.doboxfigureobject(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
+ context.startfoundexternalfigure(width .. "sp",height .. "sp")
+ context(function()
+ nodeinjections.insertmovie {
+ width = width,
+ height = height,
+ factor = number.dimenfactors.bp,
+ ["repeat"] = dr["repeat"],
+ controls = dr.controls,
+ preview = dr.preview,
+ label = dr.label,
+ foundname = foundname,
+ }
+ end)
+ context.stopfoundexternalfigure()
+ return data
+end
+
+includers.mov = includers.nongeneric
+
+-- -- -- mps -- -- --
+
+internalschemes.mprun = true
+
+local function internal(askedname)
+ local spec, mprun, mpnum = match(lower(askedname),"mprun([:%.]?)(.-)%.(%d+)")
+ 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
+ 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() context.docheckfiguremprun(mprun,mpnum) end)
+ else
+ return checkers_nongeneric(data,function() context.docheckfiguremps(data.used.fullname) end)
+ end
+end
+
+includers.mps = includers.nongeneric
+
+-- -- -- tex -- -- --
+
+function existers.tex(askedname)
+ askedname = resolvers.findfile(askedname)
+ return askedname ~= "" and askedname or false
+end
+
+function checkers.tex(data)
+ return checkers_nongeneric(data,function() context.docheckfiguretex(data.used.fullname) end)
+end
+
+includers.tex = includers.nongeneric
+
+-- -- -- buffer -- -- --
+
+function existers.buffer(askedname)
+ local name = file.nameonly(askedname)
+ local okay = buffers.exists(name)
+ return okay and name, true -- always quit scanning
+end
+
+function checkers.buffer(data)
+ return checkers_nongeneric(data,function() context.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 -- -- --
+
+existers.cld = existers.tex
+
+function checkers.cld(data)
+ return checkers_nongeneric(data,function() context.docheckfigurecld(data.used.fullname) end)
+end
+
+includers.cld = includers.nongeneric
+
+-- -- -- converters -- -- --
+
+local function makeoptions(options)
+ local to = type(options)
+ return (to == "table" and concat(options," ")) or (to == "string" and options) or ""
+end
+
+-- programs.makeoptions = makeoptions
+
+local function runprogram(binary,argument,variables)
+ local binary = match(binary,"[%S]+") -- to be sure
+ if type(argument) == "table" then
+ argument = concat(argument," ") -- for old times sake
+ end
+ if not os.which(binary) then
+ report_inclusion("program %a is not installed, not running command: %s",binary,command)
+ elseif not argument or argument == "" then
+ report_inclusion("nothing to run, unknown program %a",binary)
+ else
+ 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.spawn(command)
+ end
+end
+
+programs.run = runprogram
+
+-- -- -- eps & pdf -- -- --
+--
+-- \externalfigure[cow.eps]
+-- \externalfigure[cow.pdf][conversion=stripped]
+
+local epsconverter = converters.eps or { }
+converters.eps = epsconverter
+converters.ps = epsconverter
+
+local epstopdf = {
+ resolutions = {
+ [v_low] = "screen",
+ [v_medium] = "ebook",
+ [v_high] = "prepress",
+ },
+ command = os.type == "windows" and "gswin32c" or "gs",
+ -- -dProcessDSCComments=false
+ argument = [[
+ -q
+ -sDEVICE=pdfwrite
+ -dNOPAUSE
+ -dNOCACHE
+ -dBATCH
+ -dAutoRotatePages=/None
+ -dPDFSETTINGS=/%presets%
+ -dEPSCrop
+ -sOutputFile=%newname%
+ %oldname%
+ -c quit
+ ]],
+}
+
+programs.epstopdf = epstopdf
+programs.gs = epstopdf
+
+function epsconverter.pdf(oldname,newname,resolution) -- the resolution interface might change
+ local epstopdf = programs.epstopdf -- can be changed
+ local presets = epstopdf.resolutions[resolution or ""] or epstopdf.resolutions.high
+ runprogram(epstopdf.command, epstopdf.argument, {
+ newname = newname,
+ oldname = oldname,
+ presets = presets,
+ } )
+end
+
+epsconverter.default = epsconverter.pdf
+
+local pdfconverter = converters.pdf or { }
+converters.pdf = pdfconverter
+
+programs.pdftoeps = {
+ command = "pdftops",
+ argument = [[-eps "%oldname%" "%newname%]],
+}
+
+pdfconverter.stripped = function(oldname,newname)
+ local pdftoeps = programs.pdftoeps -- can be changed
+ local epstopdf = programs.epstopdf -- can be changed
+ local presets = epstopdf.resolutions[resolution or ""] or epstopdf.resolutions.high
+ local tmpname = newname .. ".tmp"
+ runprogram(pdftoeps.command, pdftoeps.argument, { oldname = oldname, newname = tmpname, presets = presets })
+ runprogram(epstopdf.command, epstopdf.argument, { oldname = tmpname, newname = newname, presets = presets })
+ os.remove(tmpname)
+end
+
+figures.registersuffix("stripped","pdf")
+
+-- -- -- svg -- -- --
+
+local svgconverter = { }
+converters.svg = svgconverter
+converters.svgz = svgconverter
+
+-- inkscape on windows only works with complete paths
+
+programs.inkscape = {
+ command = "inkscape",
+ pdfargument = [[
+ "%oldname%"
+ --export-dpi=600
+ -A
+ "%newname%"
+ ]],
+ pngargument = [[
+ "%oldname%"
+ --export-dpi=600
+ --export-png="%newname%"
+ ]],
+}
+
+function svgconverter.pdf(oldname,newname)
+ local inkscape = programs.inkscape -- can be changed
+ runprogram(inkscape.command, inkscape.pdfargument, {
+ newname = expandfilename(newname),
+ oldname = expandfilename(oldname),
+ } )
+end
+
+function svgconverter.png(oldname,newname)
+ local inkscape = programs.inkscape
+ runprogram(inkscape.command, inkscape.pngargument, {
+ newname = expandfilename(newname),
+ oldname = expandfilename(oldname),
+ } )
+end
+
+svgconverter.default = svgconverter.pdf
+
+-- -- -- gif -- -- --
+-- -- -- tif -- -- --
+
+local gifconverter = converters.gif or { }
+local tifconverter = converters.tif or { }
+local bmpconverter = converters.bmp or { }
+
+converters.gif = gifconverter
+converters.tif = tifconverter
+converters.bmp = bmpconverter
+
+programs.convert = {
+ command = "gm", -- graphicmagick
+ argument = [[convert "%oldname%" "%newname%"]],
+}
+
+local function converter(oldname,newname)
+ local convert = programs.convert
+ runprogram(convert.command, convert.argument, {
+ newname = newname,
+ oldname = oldname,
+ } )
+end
+
+tifconverter.pdf = converter
+gifconverter.pdf = converter
+bmpconverter.pdf = converter
+
+gifconverter.default = converter
+tifconverter.default = converter
+bmpconverter.default = converter
+
+-- todo: lowres
+
+-- -- -- 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
+
+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 = figure_paths[i]
+ 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)
+ 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
+ return format("%s seconds including tex, %s processed images", statistics.elapsedtime(figures),nofprocessed)
+ 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 fig = figures.push { name = pdffile }
+-- figures.identify()
+-- figures.check()
+-- local nofpages = fig.used.pages
+-- figures.pop()
+
+-- interfacing
+
+commands.setfigurelookuporder = figures.setorder
diff --git a/tex/context/base/grph-raw.lua b/tex/context/base/grph-raw.lua
index e2ffb689f..4c5b031ea 100644
--- a/tex/context/base/grph-raw.lua
+++ b/tex/context/base/grph-raw.lua
@@ -1,42 +1,42 @@
-if not modules then modules = { } end modules ['grph-raw'] = {
- version = 1.001,
- comment = "companion to grph-raw.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- This module is for Mojca, who wanted something like this for
--- her gnuplot project. It's somewhat premliminary code but it
--- works ok for that purpose.
-
-local tonumber = tonumber
-
-local report_bitmap = logs.reporter("graphics","bitmaps")
-
-local context = context
-local texsp = tex.sp
-
-function figures.bitmapimage(t)
- local data = t.data
- local xresolution = tonumber(t.xresolution)
- local yresolution = tonumber(t.yresolution)
- if data and xresolution and yresolution then
- local width, height = t.width or "", t.height or ""
- local n = backends.nodeinjections.injectbitmap {
- xresolution = xresolution,
- yresolution = yresolution,
- width = width ~= "" and texsp(width) or nil,
- height = height ~= "" and texsp(height) or nil,
- data = data,
- colorspace = t.colorspace,
- }
- if n then
- context.hbox(n)
- else
- report_bitmap("format no supported by backend")
- end
- else
- report_bitmap("invalid specification")
- end
-end
+if not modules then modules = { } end modules ['grph-raw'] = {
+ version = 1.001,
+ comment = "companion to grph-raw.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This module is for Mojca, who wanted something like this for
+-- her gnuplot project. It's somewhat premliminary code but it
+-- works ok for that purpose.
+
+local tonumber = tonumber
+
+local report_bitmap = logs.reporter("graphics","bitmaps")
+
+local context = context
+local texsp = tex.sp
+
+function figures.bitmapimage(t)
+ local data = t.data
+ local xresolution = tonumber(t.xresolution)
+ local yresolution = tonumber(t.yresolution)
+ if data and xresolution and yresolution then
+ local width, height = t.width or "", t.height or ""
+ local n = backends.nodeinjections.injectbitmap {
+ xresolution = xresolution,
+ yresolution = yresolution,
+ width = width ~= "" and texsp(width) or nil,
+ height = height ~= "" and texsp(height) or nil,
+ data = data,
+ colorspace = t.colorspace,
+ }
+ if n then
+ context.hbox(n)
+ else
+ report_bitmap("format no supported by backend")
+ end
+ else
+ report_bitmap("invalid specification")
+ end
+end
diff --git a/tex/context/base/grph-swf.lua b/tex/context/base/grph-swf.lua
index 58136f7fc..8c28b76af 100644
--- a/tex/context/base/grph-swf.lua
+++ b/tex/context/base/grph-swf.lua
@@ -1,94 +1,94 @@
-if not modules then modules = { } end modules ['grph-swf'] = {
- 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"
-}
-
--- maybe: backends.codeinjections.insertswf
-
-local sub, format, match, byte = string.sub, string.format, string.match, string.byte
-local concat = table.concat
-local floor = math.floor
-local tonumber = tonumber
-
-local readstring = io.readstring
-local readnumber = io.readnumber
-local tobitstring = number.tobitstring
-local todimen = number.todimen
-local nodeinjections = backends.nodeinjections
-local figures = figures
-local context = context
-
-local function getheader(name)
- local f = io.open(name,"rb")
- if not f then
- return
- end
- local signature = readstring(f,3) -- F=uncompressed, C=compressed (zlib)
- local version = readnumber(f,1)
- local filelength = readnumber(f,-4)
- local compressed = sub(signature,1,1) == "C"
- local buffer
- if compressed then
- buffer = zlib.decompress(f:read('*a'))
- else
- buffer = f:read(20) -- ('*a')
- end
- f:close()
- buffer = { match(buffer,"(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)") }
- for i=1,9 do
- buffer[i] = tobitstring(byte(buffer[i]))
- end
- local framebits = concat(buffer,"",1,9)
- local n = tonumber(sub(framebits,1,5),2)
- local frame = { } -- xmin xmax ymin ymax
- local xmin = tonumber(sub(framebits,6, 5 + n),2)
- local xmax = tonumber(sub(framebits,6 + 1*n,5 + 2*n),2)
- local ymin = tonumber(sub(framebits,6 + 2*n,5 + 3*n),2)
- local ymax = tonumber(sub(framebits,6 + 3*n,5 + 4*n),2)
- return {
- filename = name,
- version = version,
- filelength = filelength,
- framerate = tonumber(byte(buffer[10]) * 256 + byte(buffer[11])),
- framecount = tonumber(byte(buffer[12]) * 256 + byte(buffer[13])),
- -- framebits = framebits,
- compressed = compressed,
- width = floor((xmax - xmin) / 20),
- height = floor((ymax - ymin) / 20),
- rectangle = {
- xmin = xmin,
- xmax = xmax,
- ymin = ymin,
- ymax = ymax,
- }
- }
-end
-
-function figures.checkers.swf(data)
- local dr, du, ds = data.request, data.used, data.status
- local foundname = du.fullname
- local header = getheader(foundname)
- local width, height = figures.applyratio(dr.width,dr.height,header.width,header.height)
- dr.width, dr.height = width, height
- du.width, du.height, du.foundname = width, height, foundname
- context.startfoundexternalfigure(todimen(width),todimen(height))
- nodeinjections.insertswf {
- foundname = foundname,
- width = width,
- height = height,
- -- factor = number.dimenfactors.bp,
- display = dr.display,
- controls = dr.controls,
- -- label = dr.label,
- resources = dr.resources,
- }
- context.stopfoundexternalfigure()
- return data
-end
-
-figures.includers.swf = figures.includers.nongeneric
-
-figures.registersuffix("swf","swf")
+if not modules then modules = { } end modules ['grph-swf'] = {
+ 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"
+}
+
+-- maybe: backends.codeinjections.insertswf
+
+local sub, format, match, byte = string.sub, string.format, string.match, string.byte
+local concat = table.concat
+local floor = math.floor
+local tonumber = tonumber
+
+local readstring = io.readstring
+local readnumber = io.readnumber
+local tobitstring = number.tobitstring
+local todimen = number.todimen
+local nodeinjections = backends.nodeinjections
+local figures = figures
+local context = context
+
+local function getheader(name)
+ local f = io.open(name,"rb")
+ if not f then
+ return
+ end
+ local signature = readstring(f,3) -- F=uncompressed, C=compressed (zlib)
+ local version = readnumber(f,1)
+ local filelength = readnumber(f,-4)
+ local compressed = sub(signature,1,1) == "C"
+ local buffer
+ if compressed then
+ buffer = zlib.decompress(f:read('*a'))
+ else
+ buffer = f:read(20) -- ('*a')
+ end
+ f:close()
+ buffer = { match(buffer,"(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)") }
+ for i=1,9 do
+ buffer[i] = tobitstring(byte(buffer[i]))
+ end
+ local framebits = concat(buffer,"",1,9)
+ local n = tonumber(sub(framebits,1,5),2)
+ local frame = { } -- xmin xmax ymin ymax
+ local xmin = tonumber(sub(framebits,6, 5 + n),2)
+ local xmax = tonumber(sub(framebits,6 + 1*n,5 + 2*n),2)
+ local ymin = tonumber(sub(framebits,6 + 2*n,5 + 3*n),2)
+ local ymax = tonumber(sub(framebits,6 + 3*n,5 + 4*n),2)
+ return {
+ filename = name,
+ version = version,
+ filelength = filelength,
+ framerate = tonumber(byte(buffer[10]) * 256 + byte(buffer[11])),
+ framecount = tonumber(byte(buffer[12]) * 256 + byte(buffer[13])),
+ -- framebits = framebits,
+ compressed = compressed,
+ width = floor((xmax - xmin) / 20),
+ height = floor((ymax - ymin) / 20),
+ rectangle = {
+ xmin = xmin,
+ xmax = xmax,
+ ymin = ymin,
+ ymax = ymax,
+ }
+ }
+end
+
+function figures.checkers.swf(data)
+ local dr, du, ds = data.request, data.used, data.status
+ local foundname = du.fullname
+ local header = getheader(foundname)
+ local width, height = figures.applyratio(dr.width,dr.height,header.width,header.height)
+ dr.width, dr.height = width, height
+ du.width, du.height, du.foundname = width, height, foundname
+ context.startfoundexternalfigure(todimen(width),todimen(height))
+ nodeinjections.insertswf {
+ foundname = foundname,
+ width = width,
+ height = height,
+ -- factor = number.dimenfactors.bp,
+ display = dr.display,
+ controls = dr.controls,
+ -- label = dr.label,
+ resources = dr.resources,
+ }
+ context.stopfoundexternalfigure()
+ return data
+end
+
+figures.includers.swf = figures.includers.nongeneric
+
+figures.registersuffix("swf","swf")
diff --git a/tex/context/base/grph-u3d.lua b/tex/context/base/grph-u3d.lua
index d141dc080..6961c5503 100644
--- a/tex/context/base/grph-u3d.lua
+++ b/tex/context/base/grph-u3d.lua
@@ -1,51 +1,51 @@
-if not modules then modules = { } end modules ['grph-u3d'] = {
- 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"
-}
-
--- see lpdf-u3d.lua for comment
-
--- maybe: backends.codeinjections.insertu3d
-
-local trace_inclusion = false trackers.register("figures.inclusion", function(v) trace_inclusion = v end)
-
-local report_u3d = logs.reporter("graphics","u3d")
-
-local figures = figures
-local context = context
-local nodeinjections = backends.nodeinjections
-local todimen = string.todimen
-
-function figures.checkers.u3d(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_u3d("including u3d %a, width %p, height %p",foundname,width,height)
- end
- context.startfoundexternalfigure(width .. "sp",height .. "sp")
- context(function()
- nodeinjections.insertu3d {
- foundname = foundname,
- width = width,
- height = height,
- factor = number.dimenfactors.bp,
- display = dr.display,
- controls = dr.controls,
- label = dr.label,
- }
- end)
- context.stopfoundexternalfigure()
- return data
-end
-
-figures.includers.u3d = figures.includers.nongeneric
-
-figures.registersuffix("u3d","u3d")
-figures.registersuffix("prc","u3d")
+if not modules then modules = { } end modules ['grph-u3d'] = {
+ 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"
+}
+
+-- see lpdf-u3d.lua for comment
+
+-- maybe: backends.codeinjections.insertu3d
+
+local trace_inclusion = false trackers.register("figures.inclusion", function(v) trace_inclusion = v end)
+
+local report_u3d = logs.reporter("graphics","u3d")
+
+local figures = figures
+local context = context
+local nodeinjections = backends.nodeinjections
+local todimen = string.todimen
+
+function figures.checkers.u3d(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_u3d("including u3d %a, width %p, height %p",foundname,width,height)
+ end
+ context.startfoundexternalfigure(width .. "sp",height .. "sp")
+ context(function()
+ nodeinjections.insertu3d {
+ foundname = foundname,
+ width = width,
+ height = height,
+ factor = number.dimenfactors.bp,
+ display = dr.display,
+ controls = dr.controls,
+ label = dr.label,
+ }
+ end)
+ context.stopfoundexternalfigure()
+ return data
+end
+
+figures.includers.u3d = figures.includers.nongeneric
+
+figures.registersuffix("u3d","u3d")
+figures.registersuffix("prc","u3d")
diff --git a/tex/context/base/grph-wnd.lua b/tex/context/base/grph-wnd.lua
index 8b005b123..ebb9b1169 100644
--- a/tex/context/base/grph-wnd.lua
+++ b/tex/context/base/grph-wnd.lua
@@ -1,47 +1,47 @@
-if not modules then modules = { } end modules ['grph-wnd'] = {
- 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"
-}
-
--- Thanks to Luigi Scarso for making graphic magic work in luatex.
---
--- \externalfigure[hacker.jpeg][width=4cm,conversion=gray.jpg]
-
-local converters, suffixes = figures.converters, figures.suffixes
-
-local trace_conversion = false trackers.register("figures.conversion", function(v) trace_conversion = v end)
-
-local report_wand = logs.reporter("graphics","wand")
-
-local function togray(oldname,newname)
- if lfs.isfile(oldname) then
- require("gmwand")
- if trace_conversion then
- report_wand("converting %a to %a using gmwand",oldname,newname)
- end
- gmwand.InitializeMagick("./") -- What does this path do?
- local wand = gmwand.NewMagickWand()
- gmwand.MagickReadImage(wand,oldname)
- gmwand.MagickSetImageColorspace(wand,gmwand.GRAYColorspace)
- gmwand.MagickWriteImages(wand,newname,1)
- gmwand.DestroyMagickWand(wand)
- else
- report_wand("unable to convert %a to %a using gmwand",oldname,newname)
- end
-end
-
-local formats = { "png", "jpg", "gif" }
-
-for i=1,#formats do
- local oldformat = formats[i]
- local newformat = "gray." .. oldformat
- if trace_conversion then
- report_wand("installing converter for %a to %a",oldformat,newformat)
- end
- converters[oldformat] = converters[oldformat] or { }
- converters[oldformat][newformat] = togray
- suffixes [newformat] = oldformat
-end
+if not modules then modules = { } end modules ['grph-wnd'] = {
+ 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"
+}
+
+-- Thanks to Luigi Scarso for making graphic magic work in luatex.
+--
+-- \externalfigure[hacker.jpeg][width=4cm,conversion=gray.jpg]
+
+local converters, suffixes = figures.converters, figures.suffixes
+
+local trace_conversion = false trackers.register("figures.conversion", function(v) trace_conversion = v end)
+
+local report_wand = logs.reporter("graphics","wand")
+
+local function togray(oldname,newname)
+ if lfs.isfile(oldname) then
+ require("gmwand")
+ if trace_conversion then
+ report_wand("converting %a to %a using gmwand",oldname,newname)
+ end
+ gmwand.InitializeMagick("./") -- What does this path do?
+ local wand = gmwand.NewMagickWand()
+ gmwand.MagickReadImage(wand,oldname)
+ gmwand.MagickSetImageColorspace(wand,gmwand.GRAYColorspace)
+ gmwand.MagickWriteImages(wand,newname,1)
+ gmwand.DestroyMagickWand(wand)
+ else
+ report_wand("unable to convert %a to %a using gmwand",oldname,newname)
+ end
+end
+
+local formats = { "png", "jpg", "gif" }
+
+for i=1,#formats do
+ local oldformat = formats[i]
+ local newformat = "gray." .. oldformat
+ if trace_conversion then
+ report_wand("installing converter for %a to %a",oldformat,newformat)
+ end
+ converters[oldformat] = converters[oldformat] or { }
+ converters[oldformat][newformat] = togray
+ suffixes [newformat] = oldformat
+end
diff --git a/tex/context/base/java-ini.lua b/tex/context/base/java-ini.lua
index 3f1fbd6cf..321e4e24d 100644
--- a/tex/context/base/java-ini.lua
+++ b/tex/context/base/java-ini.lua
@@ -1,226 +1,226 @@
-if not modules then modules = { } end modules ['java-ini'] = {
- version = 1.001,
- comment = "companion to java-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 concat = table.concat
-local lpegmatch, P, S, C, Carg, Cc = lpeg.match, lpeg.P, lpeg.S, lpeg.C, lpeg.Carg, lpeg.Cc
-
-local allocate = utilities.storage.allocate
-local settings_to_array = utilities.parsers.settings_to_array
-local variables = interfaces.variables
-local formatters = string.formatters
-
--- todo: don't flush scripts if no JS key
-
-local trace_javascript = false trackers.register("backends.javascript", function(v) trace_javascript = v end)
-
-local report_javascripts = logs.reporter ("interactions","javascripts")
-local status_javascripts = logs.messenger("interactions","javascripts")
-
-interactions.javascripts = interactions.javascripts or { }
-local javascripts = interactions.javascripts
-
-javascripts.codes = allocate()
-javascripts.preambles = allocate()
-javascripts.functions = allocate()
-
-local codes, preambles, functions = javascripts.codes, javascripts.preambles, javascripts.functions
-
-local preambled = { }
-
-local function storefunction(s,preamble)
- if trace_javascript then
- report_javascripts("found function %a",s)
- end
- functions[s] = preamble
-end
-
-local uses = P("uses")
-local used = P("used")
-local left = P("{")
-local right = P("}")
-local space = S(" \r\n")
-local spaces = space^0
-local braced = left * C((1-right-space)^1) * right
-local unbraced = C((1-space)^1)
-local name = spaces * (braced + unbraced) * spaces
-local any = P(1)
-local script = C(any^1)
-local funct = P("function")
-local leftp = P("(")
-local rightp = P(")")
-local fname = spaces * funct * spaces * (C((1-space-left-leftp)^1) * Carg(1) / storefunction) * spaces * leftp
-
-local parsecode = name * ((uses * name) + Cc("")) * spaces * script
-local parsepreamble = name * ((used * name) + Cc("")) * spaces * script
-local parsefunctions = (fname + any)^0
-
-function javascripts.storecode(str)
- local name, uses, script = lpegmatch(parsecode,str)
- if name and name ~= "" then
- codes[name] = { uses, script }
- end
-end
-
-function javascripts.storepreamble(str) -- now later
- local name, used, script = lpegmatch(parsepreamble,str)
- if name and name ~= "" and not preambled[name] then
- local n = #preambles + 1
- preambles[n] = { name, used, script }
- preambled[name] = n
- if trace_javascript then
- report_javascripts("stored preamble %a, state %a, order %a",name,used,n)
- end
- lpegmatch(parsefunctions,script,1,n)
- end
-end
-
-function javascripts.setpreamble(name,script) -- now later
- if name and name ~= "" and not preambled[name] then
- local n = #preambles + 1
- preambles[n] = { name, "now", script }
- preambled[name] = n
- if trace_javascript then
- report_javascripts("adapted preamble %a, state %a, order %a",name,"now",n)
- end
- lpegmatch(parsefunctions,script,1,n)
- end
-end
-
-function javascripts.addtopreamble(name,script)
- if name and name ~= "" then
- local p = preambled[name]
- if p then
- preambles[p] = { "now", preambles[p] .. " ;\n" .. script }
- if trace_javascript then
- report_javascripts("extended preamble %a, state %a, order %a",name,"now",p)
- end
- else
- local n = #preambles + 1
- preambles[n] = { name, "now", script }
- preambled[name] = n
- if trace_javascript then
- report_javascripts("stored preamble %a, state %a, order %a",name,"now",n)
- end
- lpegmatch(parsefunctions,script,1,n)
- end
- end
-end
-
-function javascripts.usepreamblenow(name) -- now later
- if name and name ~= "" and name ~= variables.reset then -- todo: reset
- local names = settings_to_array(name)
- for i=1,#names do
- local somename = names[i]
- if not preambled[somename] then
- preambles[preambled[somename]][2] = "now"
- if trace_javascript then
- report_javascripts("used preamble %a, state %a, order %a",somename,"now","auto")
- end
- end
- end
- end
-end
-
-local splitter = lpeg.tsplitat(lpeg.patterns.commaspacer)
-
-local used, reported = false, { } -- we can cache more
-
-function javascripts.code(name,arguments)
- local c = codes[name]
- if c then
- local u, code = c[1], c[2]
- if u ~= "" then
- local p = preambled[u]
- if p then
- preambles[p][2] = "now"
- if trace_javascript and not reported[name] then
- reported[name] = true
- report_javascripts("used code %a, preamble %a",name,u)
- end
- elseif trace_javascript and not reported[name] then
- reported[name] = true
- report_javascripts("used code %a",name)
- end
- elseif trace_javascript and not reported[name] then
- reported[name] = true
- report_javascripts("used code %a",name)
- end
- used = true
- return code
- end
- local f = functions[name]
- if f then
- used = true
- if trace_javascript and not reported[name] then
- reported[name] = true
- report_javascripts("used function %a",name)
- end
- preambles[f][2] = "now" -- automatically tag preambles that define the function (as later)
- if arguments then
- local args = lpegmatch(splitter,arguments)
- for i=1,#args do -- can be a helper
- args[i] = formatters["%q"](args[i])
- end
- return formatters["%s(%s)"](name,concat(args,","))
- else
- return formatters["%s()"](name)
- end
- end
-end
-
-function javascripts.flushpreambles()
- local t = { }
--- if used then -- we want to be able to enforce inclusion
- for i=1,#preambles do
- local preamble = preambles[i]
- if preamble[2] == "now" then
- if trace_javascript then
- report_javascripts("flushed preamble %a",preamble[1])
- end
- t[#t+1] = { preamble[1], preamble[3] }
- end
- end
--- end
- return t
-end
-
-local patterns = { "java-imp-%s.mkiv", "java-imp-%s.tex", "java-%s.mkiv", "java-%s.tex" }
-
-local function action(name,foundname)
- context.startnointerference()
- context.startreadingfile()
- context.input(foundname)
- status_javascripts("loaded: library %a",name)
- context.stopreadingfile()
- context.stopnointerference()
-end
-
-local function failure(name)
- report_javascripts("unknown library %a",name)
-end
-
-function javascripts.usescripts(name)
- if name ~= variables.reset then -- reset is obsolete
- commands.uselibrary {
- name = name,
- patterns = patterns,
- action = action,
- failure = failure,
- onlyonce = true,
- }
- end
-end
-
--- interface
-
-commands.storejavascriptcode = interactions.javascripts.storecode
-commands.storejavascriptpreamble = interactions.javascripts.storepreamble
-commands.addtojavascriptpreamble = interactions.javascripts.addtopreamble
-commands.usejavascriptpreamble = interactions.javascripts.usepreamblenow
-commands.usejavascriptscripts = interactions.javascripts.usescripts
+if not modules then modules = { } end modules ['java-ini'] = {
+ version = 1.001,
+ comment = "companion to java-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 concat = table.concat
+local lpegmatch, P, S, C, Carg, Cc = lpeg.match, lpeg.P, lpeg.S, lpeg.C, lpeg.Carg, lpeg.Cc
+
+local allocate = utilities.storage.allocate
+local settings_to_array = utilities.parsers.settings_to_array
+local variables = interfaces.variables
+local formatters = string.formatters
+
+-- todo: don't flush scripts if no JS key
+
+local trace_javascript = false trackers.register("backends.javascript", function(v) trace_javascript = v end)
+
+local report_javascripts = logs.reporter ("interactions","javascripts")
+local status_javascripts = logs.messenger("interactions","javascripts")
+
+interactions.javascripts = interactions.javascripts or { }
+local javascripts = interactions.javascripts
+
+javascripts.codes = allocate()
+javascripts.preambles = allocate()
+javascripts.functions = allocate()
+
+local codes, preambles, functions = javascripts.codes, javascripts.preambles, javascripts.functions
+
+local preambled = { }
+
+local function storefunction(s,preamble)
+ if trace_javascript then
+ report_javascripts("found function %a",s)
+ end
+ functions[s] = preamble
+end
+
+local uses = P("uses")
+local used = P("used")
+local left = P("{")
+local right = P("}")
+local space = S(" \r\n")
+local spaces = space^0
+local braced = left * C((1-right-space)^1) * right
+local unbraced = C((1-space)^1)
+local name = spaces * (braced + unbraced) * spaces
+local any = P(1)
+local script = C(any^1)
+local funct = P("function")
+local leftp = P("(")
+local rightp = P(")")
+local fname = spaces * funct * spaces * (C((1-space-left-leftp)^1) * Carg(1) / storefunction) * spaces * leftp
+
+local parsecode = name * ((uses * name) + Cc("")) * spaces * script
+local parsepreamble = name * ((used * name) + Cc("")) * spaces * script
+local parsefunctions = (fname + any)^0
+
+function javascripts.storecode(str)
+ local name, uses, script = lpegmatch(parsecode,str)
+ if name and name ~= "" then
+ codes[name] = { uses, script }
+ end
+end
+
+function javascripts.storepreamble(str) -- now later
+ local name, used, script = lpegmatch(parsepreamble,str)
+ if name and name ~= "" and not preambled[name] then
+ local n = #preambles + 1
+ preambles[n] = { name, used, script }
+ preambled[name] = n
+ if trace_javascript then
+ report_javascripts("stored preamble %a, state %a, order %a",name,used,n)
+ end
+ lpegmatch(parsefunctions,script,1,n)
+ end
+end
+
+function javascripts.setpreamble(name,script) -- now later
+ if name and name ~= "" and not preambled[name] then
+ local n = #preambles + 1
+ preambles[n] = { name, "now", script }
+ preambled[name] = n
+ if trace_javascript then
+ report_javascripts("adapted preamble %a, state %a, order %a",name,"now",n)
+ end
+ lpegmatch(parsefunctions,script,1,n)
+ end
+end
+
+function javascripts.addtopreamble(name,script)
+ if name and name ~= "" then
+ local p = preambled[name]
+ if p then
+ preambles[p] = { "now", preambles[p] .. " ;\n" .. script }
+ if trace_javascript then
+ report_javascripts("extended preamble %a, state %a, order %a",name,"now",p)
+ end
+ else
+ local n = #preambles + 1
+ preambles[n] = { name, "now", script }
+ preambled[name] = n
+ if trace_javascript then
+ report_javascripts("stored preamble %a, state %a, order %a",name,"now",n)
+ end
+ lpegmatch(parsefunctions,script,1,n)
+ end
+ end
+end
+
+function javascripts.usepreamblenow(name) -- now later
+ if name and name ~= "" and name ~= variables.reset then -- todo: reset
+ local names = settings_to_array(name)
+ for i=1,#names do
+ local somename = names[i]
+ if not preambled[somename] then
+ preambles[preambled[somename]][2] = "now"
+ if trace_javascript then
+ report_javascripts("used preamble %a, state %a, order %a",somename,"now","auto")
+ end
+ end
+ end
+ end
+end
+
+local splitter = lpeg.tsplitat(lpeg.patterns.commaspacer)
+
+local used, reported = false, { } -- we can cache more
+
+function javascripts.code(name,arguments)
+ local c = codes[name]
+ if c then
+ local u, code = c[1], c[2]
+ if u ~= "" then
+ local p = preambled[u]
+ if p then
+ preambles[p][2] = "now"
+ if trace_javascript and not reported[name] then
+ reported[name] = true
+ report_javascripts("used code %a, preamble %a",name,u)
+ end
+ elseif trace_javascript and not reported[name] then
+ reported[name] = true
+ report_javascripts("used code %a",name)
+ end
+ elseif trace_javascript and not reported[name] then
+ reported[name] = true
+ report_javascripts("used code %a",name)
+ end
+ used = true
+ return code
+ end
+ local f = functions[name]
+ if f then
+ used = true
+ if trace_javascript and not reported[name] then
+ reported[name] = true
+ report_javascripts("used function %a",name)
+ end
+ preambles[f][2] = "now" -- automatically tag preambles that define the function (as later)
+ if arguments then
+ local args = lpegmatch(splitter,arguments)
+ for i=1,#args do -- can be a helper
+ args[i] = formatters["%q"](args[i])
+ end
+ return formatters["%s(%s)"](name,concat(args,","))
+ else
+ return formatters["%s()"](name)
+ end
+ end
+end
+
+function javascripts.flushpreambles()
+ local t = { }
+-- if used then -- we want to be able to enforce inclusion
+ for i=1,#preambles do
+ local preamble = preambles[i]
+ if preamble[2] == "now" then
+ if trace_javascript then
+ report_javascripts("flushed preamble %a",preamble[1])
+ end
+ t[#t+1] = { preamble[1], preamble[3] }
+ end
+ end
+-- end
+ return t
+end
+
+local patterns = { "java-imp-%s.mkiv", "java-imp-%s.tex", "java-%s.mkiv", "java-%s.tex" }
+
+local function action(name,foundname)
+ context.startnointerference()
+ context.startreadingfile()
+ context.input(foundname)
+ status_javascripts("loaded: library %a",name)
+ context.stopreadingfile()
+ context.stopnointerference()
+end
+
+local function failure(name)
+ report_javascripts("unknown library %a",name)
+end
+
+function javascripts.usescripts(name)
+ if name ~= variables.reset then -- reset is obsolete
+ commands.uselibrary {
+ name = name,
+ patterns = patterns,
+ action = action,
+ failure = failure,
+ onlyonce = true,
+ }
+ end
+end
+
+-- interface
+
+commands.storejavascriptcode = interactions.javascripts.storecode
+commands.storejavascriptpreamble = interactions.javascripts.storepreamble
+commands.addtojavascriptpreamble = interactions.javascripts.addtopreamble
+commands.usejavascriptpreamble = interactions.javascripts.usepreamblenow
+commands.usejavascriptscripts = interactions.javascripts.usescripts
diff --git a/tex/context/base/l-boolean.lua b/tex/context/base/l-boolean.lua
index ddac9b8a0..f087f1a4c 100644
--- a/tex/context/base/l-boolean.lua
+++ b/tex/context/base/l-boolean.lua
@@ -1,69 +1,69 @@
-if not modules then modules = { } end modules ['l-boolean'] = {
- 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 type, tonumber = type, tonumber
-
-boolean = boolean or { }
-local boolean = boolean
-
-function boolean.tonumber(b)
- if b then return 1 else return 0 end -- test and return or return
-end
-
-function toboolean(str,tolerant) -- global
- if str == nil then
- return false
- elseif str == false then
- return false
- elseif str == true then
- return true
- elseif str == "true" then
- return true
- elseif str == "false" then
- return false
- elseif not tolerant then
- return false
- elseif str == 0 then
- return false
- elseif (tonumber(str) or 0) > 0 then
- return true
- else
- return str == "yes" or str == "on" or str == "t"
- end
-end
-
-string.toboolean = toboolean
-
-function string.booleanstring(str)
- if str == "0" then
- return false
- elseif str == "1" then
- return true
- elseif str == "" then
- return false
- elseif str == "false" then
- return false
- elseif str == "true" then
- return true
- elseif (tonumber(str) or 0) > 0 then
- return true
- else
- return str == "yes" or str == "on" or str == "t"
- end
-end
-
-function string.is_boolean(str,default)
- if type(str) == "string" then
- if str == "true" or str == "yes" or str == "on" or str == "t" then
- return true
- elseif str == "false" or str == "no" or str == "off" or str == "f" then
- return false
- end
- end
- return default
-end
+if not modules then modules = { } end modules ['l-boolean'] = {
+ 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 type, tonumber = type, tonumber
+
+boolean = boolean or { }
+local boolean = boolean
+
+function boolean.tonumber(b)
+ if b then return 1 else return 0 end -- test and return or return
+end
+
+function toboolean(str,tolerant) -- global
+ if str == nil then
+ return false
+ elseif str == false then
+ return false
+ elseif str == true then
+ return true
+ elseif str == "true" then
+ return true
+ elseif str == "false" then
+ return false
+ elseif not tolerant then
+ return false
+ elseif str == 0 then
+ return false
+ elseif (tonumber(str) or 0) > 0 then
+ return true
+ else
+ return str == "yes" or str == "on" or str == "t"
+ end
+end
+
+string.toboolean = toboolean
+
+function string.booleanstring(str)
+ if str == "0" then
+ return false
+ elseif str == "1" then
+ return true
+ elseif str == "" then
+ return false
+ elseif str == "false" then
+ return false
+ elseif str == "true" then
+ return true
+ elseif (tonumber(str) or 0) > 0 then
+ return true
+ else
+ return str == "yes" or str == "on" or str == "t"
+ end
+end
+
+function string.is_boolean(str,default)
+ if type(str) == "string" then
+ if str == "true" or str == "yes" or str == "on" or str == "t" then
+ return true
+ elseif str == "false" or str == "no" or str == "off" or str == "f" then
+ return false
+ end
+ end
+ return default
+end
diff --git a/tex/context/base/l-dir.lua b/tex/context/base/l-dir.lua
index a58e5302e..3d0576eeb 100644
--- a/tex/context/base/l-dir.lua
+++ b/tex/context/base/l-dir.lua
@@ -1,470 +1,470 @@
-if not modules then modules = { } end modules ['l-dir'] = {
- 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"
-}
-
--- dir.expandname will be merged with cleanpath and collapsepath
-
-local type, select = type, select
-local find, gmatch, match, gsub = string.find, string.gmatch, string.match, string.gsub
-local concat, insert, remove, unpack = table.concat, table.insert, table.remove, table.unpack
-local lpegmatch = lpeg.match
-
-local P, S, R, C, Cc, Cs, Ct, Cv, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cv, lpeg.V
-
-dir = dir or { }
-local dir = dir
-local lfs = lfs
-
-local attributes = lfs.attributes
-local walkdir = lfs.dir
-local isdir = lfs.isdir
-local isfile = lfs.isfile
-local currentdir = lfs.currentdir
-local chdir = lfs.chdir
-
--- in case we load outside luatex
-
-if not isdir then
- function isdir(name)
- local a = attributes(name)
- return a and a.mode == "directory"
- end
- lfs.isdir = isdir
-end
-
-if not isfile then
- function isfile(name)
- local a = attributes(name)
- return a and a.mode == "file"
- end
- lfs.isfile = isfile
-end
-
--- handy
-
-function dir.current()
- return (gsub(currentdir(),"\\","/"))
-end
-
--- optimizing for no find (*) does not save time
-
---~ local function globpattern(path,patt,recurse,action) -- fails in recent luatex due to some change in lfs
---~ local ok, scanner
---~ if path == "/" then
---~ ok, scanner = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe
---~ else
---~ ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe
---~ end
---~ if ok and type(scanner) == "function" then
---~ if not find(path,"/$") then path = path .. '/' end
---~ for name in scanner do
---~ local full = path .. name
---~ local mode = attributes(full,'mode')
---~ if mode == 'file' then
---~ if find(full,patt) then
---~ action(full)
---~ end
---~ elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
---~ globpattern(full,patt,recurse,action)
---~ end
---~ end
---~ end
---~ end
-
-local lfsisdir = isdir
-
-local function isdir(path)
- path = gsub(path,"[/\\]+$","")
- return lfsisdir(path)
-end
-
-lfs.isdir = isdir
-
-local function globpattern(path,patt,recurse,action)
- if path == "/" then
- path = path .. "."
- elseif not find(path,"/$") then
- path = path .. '/'
- end
- if isdir(path) then -- lfs.isdir does not like trailing /
- for name in walkdir(path) do -- lfs.dir accepts trailing /
- local full = path .. name
- local mode = attributes(full,'mode')
- if mode == 'file' then
- if find(full,patt) then
- action(full)
- end
- elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
- globpattern(full,patt,recurse,action)
- end
- end
- end
-end
-
-dir.globpattern = globpattern
-
-local function collectpattern(path,patt,recurse,result)
- local ok, scanner
- result = result or { }
- if path == "/" then
- ok, scanner, first = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe
- else
- ok, scanner, first = xpcall(function() return walkdir(path) end, function() end) -- kepler safe
- end
- if ok and type(scanner) == "function" then
- if not find(path,"/$") then path = path .. '/' end
- for name in scanner, first do
- local full = path .. name
- local attr = attributes(full)
- local mode = attr.mode
- if mode == 'file' then
- if find(full,patt) then
- result[name] = attr
- end
- elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
- attr.list = collectpattern(full,patt,recurse)
- result[name] = attr
- end
- end
- end
- return result
-end
-
-dir.collectpattern = collectpattern
-
-local pattern = Ct {
- [1] = (C(P(".") + P("/")^1) + C(R("az","AZ") * P(":") * P("/")^0) + Cc("./")) * V(2) * V(3),
- [2] = C(((1-S("*?/"))^0 * P("/"))^0),
- [3] = C(P(1)^0)
-}
-
-local filter = Cs ( (
- P("**") / ".*" +
- P("*") / "[^/]*" +
- P("?") / "[^/]" +
- P(".") / "%%." +
- P("+") / "%%+" +
- P("-") / "%%-" +
- P(1)
-)^0 )
-
-local function glob(str,t)
- if type(t) == "function" then
- if type(str) == "table" then
- for s=1,#str do
- glob(str[s],t)
- end
- elseif isfile(str) then
- t(str)
- else
- local split = lpegmatch(pattern,str) -- we could use the file splitter
- if split then
- local root, path, base = split[1], split[2], split[3]
- local recurse = find(base,"%*%*")
- local start = root .. path
- local result = lpegmatch(filter,start .. base)
- globpattern(start,result,recurse,t)
- end
- end
- else
- if type(str) == "table" then
- local t = t or { }
- for s=1,#str do
- glob(str[s],t)
- end
- return t
- elseif isfile(str) then
- if t then
- t[#t+1] = str
- return t
- else
- return { str }
- end
- else
- local split = lpegmatch(pattern,str) -- we could use the file splitter
- if split then
- local t = t or { }
- local action = action or function(name) t[#t+1] = name end
- local root, path, base = split[1], split[2], split[3]
- local recurse = find(base,"%*%*")
- local start = root .. path
- local result = lpegmatch(filter,start .. base)
- globpattern(start,result,recurse,action)
- return t
- else
- return { }
- end
- end
- end
-end
-
-dir.glob = glob
-
---~ list = dir.glob("**/*.tif")
---~ list = dir.glob("/**/*.tif")
---~ list = dir.glob("./**/*.tif")
---~ list = dir.glob("oeps/**/*.tif")
---~ list = dir.glob("/oeps/**/*.tif")
-
-local function globfiles(path,recurse,func,files) -- func == pattern or function
- if type(func) == "string" then
- local s = func
- func = function(name) return find(name,s) end
- end
- files = files or { }
- local noffiles = #files
- for name in walkdir(path) do
- if find(name,"^%.") then
- --- skip
- else
- local mode = attributes(name,'mode')
- if mode == "directory" then
- if recurse then
- globfiles(path .. "/" .. name,recurse,func,files)
- end
- elseif mode == "file" then
- if not func or func(name) then
- noffiles = noffiles + 1
- files[noffiles] = path .. "/" .. name
- end
- end
- end
- end
- return files
-end
-
-dir.globfiles = globfiles
-
--- t = dir.glob("c:/data/develop/context/sources/**/????-*.tex")
--- t = dir.glob("c:/data/develop/tex/texmf/**/*.tex")
--- t = dir.glob("c:/data/develop/context/texmf/**/*.tex")
--- t = dir.glob("f:/minimal/tex/**/*")
--- print(dir.ls("f:/minimal/tex/**/*"))
--- print(dir.ls("*.tex"))
-
-function dir.ls(pattern)
- return concat(glob(pattern),"\n")
-end
-
---~ mkdirs("temp")
---~ mkdirs("a/b/c")
---~ mkdirs(".","/a/b/c")
---~ mkdirs("a","b","c")
-
-local make_indeed = true -- false
-
-local onwindows = os.type == "windows" or find(os.getenv("PATH"),";")
-
-if onwindows then
-
- function dir.mkdirs(...)
- local str, pth = "", ""
- for i=1,select("#",...) do
- local s = select(i,...)
- if s == "" then
- -- skip
- elseif str == "" then
- str = s
- else
- str = str .. "/" .. s
- end
- end
- local first, middle, last
- local drive = false
- first, middle, last = match(str,"^(//)(//*)(.*)$")
- if first then
- -- empty network path == local path
- else
- first, last = match(str,"^(//)/*(.-)$")
- if first then
- middle, last = match(str,"([^/]+)/+(.-)$")
- if middle then
- pth = "//" .. middle
- else
- pth = "//" .. last
- last = ""
- end
- else
- first, middle, last = match(str,"^([a-zA-Z]:)(/*)(.-)$")
- if first then
- pth, drive = first .. middle, true
- else
- middle, last = match(str,"^(/*)(.-)$")
- if not middle then
- last = str
- end
- end
- end
- end
- for s in gmatch(last,"[^/]+") do
- if pth == "" then
- pth = s
- elseif drive then
- pth, drive = pth .. s, false
- else
- pth = pth .. "/" .. s
- end
- if make_indeed and not isdir(pth) then
- lfs.mkdir(pth)
- end
- end
- return pth, (isdir(pth) == true)
- end
-
- --~ print(dir.mkdirs("","","a","c"))
- --~ print(dir.mkdirs("a"))
- --~ print(dir.mkdirs("a:"))
- --~ print(dir.mkdirs("a:/b/c"))
- --~ print(dir.mkdirs("a:b/c"))
- --~ print(dir.mkdirs("a:/bbb/c"))
- --~ print(dir.mkdirs("/a/b/c"))
- --~ print(dir.mkdirs("/aaa/b/c"))
- --~ print(dir.mkdirs("//a/b/c"))
- --~ print(dir.mkdirs("///a/b/c"))
- --~ print(dir.mkdirs("a/bbb//ccc/"))
-
-else
-
- function dir.mkdirs(...)
- local str, pth = "", ""
- for i=1,select("#",...) do
- local s = select(i,...)
- if s and s ~= "" then -- we catch nil and false
- if str ~= "" then
- str = str .. "/" .. s
- else
- str = s
- end
- end
- end
- str = gsub(str,"/+","/")
- if find(str,"^/") then
- pth = "/"
- for s in gmatch(str,"[^/]+") do
- local first = (pth == "/")
- if first then
- pth = pth .. s
- else
- pth = pth .. "/" .. s
- end
- if make_indeed and not first and not isdir(pth) then
- lfs.mkdir(pth)
- end
- end
- else
- pth = "."
- for s in gmatch(str,"[^/]+") do
- pth = pth .. "/" .. s
- if make_indeed and not isdir(pth) then
- lfs.mkdir(pth)
- end
- end
- end
- return pth, (isdir(pth) == true)
- end
-
- --~ print(dir.mkdirs("","","a","c"))
- --~ print(dir.mkdirs("a"))
- --~ print(dir.mkdirs("/a/b/c"))
- --~ print(dir.mkdirs("/aaa/b/c"))
- --~ print(dir.mkdirs("//a/b/c"))
- --~ print(dir.mkdirs("///a/b/c"))
- --~ print(dir.mkdirs("a/bbb//ccc/"))
-
-end
-
-dir.makedirs = dir.mkdirs
-
--- we can only define it here as it uses dir.current
-
-if onwindows then
-
- function dir.expandname(str) -- will be merged with cleanpath and collapsepath
- local first, nothing, last = match(str,"^(//)(//*)(.*)$")
- if first then
- first = dir.current() .. "/" -- dir.current sanitizes
- end
- if not first then
- first, last = match(str,"^(//)/*(.*)$")
- end
- if not first then
- first, last = match(str,"^([a-zA-Z]:)(.*)$")
- if first and not find(last,"^/") then
- local d = currentdir()
- if chdir(first) then
- first = dir.current()
- end
- chdir(d)
- end
- end
- if not first then
- first, last = dir.current(), str
- end
- last = gsub(last,"//","/")
- last = gsub(last,"/%./","/")
- last = gsub(last,"^/*","")
- first = gsub(first,"/*$","")
- if last == "" or last == "." then
- return first
- else
- return first .. "/" .. last
- end
- end
-
-else
-
- function dir.expandname(str) -- will be merged with cleanpath and collapsepath
- if not find(str,"^/") then
- str = currentdir() .. "/" .. str
- end
- str = gsub(str,"//","/")
- str = gsub(str,"/%./","/")
- str = gsub(str,"(.)/%.$","%1")
- return str
- end
-
-end
-
-file.expandname = dir.expandname -- for convenience
-
-local stack = { }
-
-function dir.push(newdir)
- insert(stack,currentdir())
- if newdir and newdir ~= "" then
- chdir(newdir)
- end
-end
-
-function dir.pop()
- local d = remove(stack)
- if d then
- chdir(d)
- end
- return d
-end
-
-local function found(...) -- can have nil entries
- for i=1,select("#",...) do
- local path = select(i,...)
- local kind = type(path)
- if kind == "string" then
- if isdir(path) then
- return path
- end
- elseif kind == "table" then
- -- here we asume no holes, i.e. an indexed table
- local path = found(unpack(path))
- if path then
- return path
- end
- end
- end
- -- return nil -- if we want print("crappath") to show something
-end
-
-dir.found = found
+if not modules then modules = { } end modules ['l-dir'] = {
+ 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"
+}
+
+-- dir.expandname will be merged with cleanpath and collapsepath
+
+local type, select = type, select
+local find, gmatch, match, gsub = string.find, string.gmatch, string.match, string.gsub
+local concat, insert, remove, unpack = table.concat, table.insert, table.remove, table.unpack
+local lpegmatch = lpeg.match
+
+local P, S, R, C, Cc, Cs, Ct, Cv, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cv, lpeg.V
+
+dir = dir or { }
+local dir = dir
+local lfs = lfs
+
+local attributes = lfs.attributes
+local walkdir = lfs.dir
+local isdir = lfs.isdir
+local isfile = lfs.isfile
+local currentdir = lfs.currentdir
+local chdir = lfs.chdir
+
+-- in case we load outside luatex
+
+if not isdir then
+ function isdir(name)
+ local a = attributes(name)
+ return a and a.mode == "directory"
+ end
+ lfs.isdir = isdir
+end
+
+if not isfile then
+ function isfile(name)
+ local a = attributes(name)
+ return a and a.mode == "file"
+ end
+ lfs.isfile = isfile
+end
+
+-- handy
+
+function dir.current()
+ return (gsub(currentdir(),"\\","/"))
+end
+
+-- optimizing for no find (*) does not save time
+
+--~ local function globpattern(path,patt,recurse,action) -- fails in recent luatex due to some change in lfs
+--~ local ok, scanner
+--~ if path == "/" then
+--~ ok, scanner = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe
+--~ else
+--~ ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe
+--~ end
+--~ if ok and type(scanner) == "function" then
+--~ if not find(path,"/$") then path = path .. '/' end
+--~ for name in scanner do
+--~ local full = path .. name
+--~ local mode = attributes(full,'mode')
+--~ if mode == 'file' then
+--~ if find(full,patt) then
+--~ action(full)
+--~ end
+--~ elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
+--~ globpattern(full,patt,recurse,action)
+--~ end
+--~ end
+--~ end
+--~ end
+
+local lfsisdir = isdir
+
+local function isdir(path)
+ path = gsub(path,"[/\\]+$","")
+ return lfsisdir(path)
+end
+
+lfs.isdir = isdir
+
+local function globpattern(path,patt,recurse,action)
+ if path == "/" then
+ path = path .. "."
+ elseif not find(path,"/$") then
+ path = path .. '/'
+ end
+ if isdir(path) then -- lfs.isdir does not like trailing /
+ for name in walkdir(path) do -- lfs.dir accepts trailing /
+ local full = path .. name
+ local mode = attributes(full,'mode')
+ if mode == 'file' then
+ if find(full,patt) then
+ action(full)
+ end
+ elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
+ globpattern(full,patt,recurse,action)
+ end
+ end
+ end
+end
+
+dir.globpattern = globpattern
+
+local function collectpattern(path,patt,recurse,result)
+ local ok, scanner
+ result = result or { }
+ if path == "/" then
+ ok, scanner, first = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe
+ else
+ ok, scanner, first = xpcall(function() return walkdir(path) end, function() end) -- kepler safe
+ end
+ if ok and type(scanner) == "function" then
+ if not find(path,"/$") then path = path .. '/' end
+ for name in scanner, first do
+ local full = path .. name
+ local attr = attributes(full)
+ local mode = attr.mode
+ if mode == 'file' then
+ if find(full,patt) then
+ result[name] = attr
+ end
+ elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then
+ attr.list = collectpattern(full,patt,recurse)
+ result[name] = attr
+ end
+ end
+ end
+ return result
+end
+
+dir.collectpattern = collectpattern
+
+local pattern = Ct {
+ [1] = (C(P(".") + P("/")^1) + C(R("az","AZ") * P(":") * P("/")^0) + Cc("./")) * V(2) * V(3),
+ [2] = C(((1-S("*?/"))^0 * P("/"))^0),
+ [3] = C(P(1)^0)
+}
+
+local filter = Cs ( (
+ P("**") / ".*" +
+ P("*") / "[^/]*" +
+ P("?") / "[^/]" +
+ P(".") / "%%." +
+ P("+") / "%%+" +
+ P("-") / "%%-" +
+ P(1)
+)^0 )
+
+local function glob(str,t)
+ if type(t) == "function" then
+ if type(str) == "table" then
+ for s=1,#str do
+ glob(str[s],t)
+ end
+ elseif isfile(str) then
+ t(str)
+ else
+ local split = lpegmatch(pattern,str) -- we could use the file splitter
+ if split then
+ local root, path, base = split[1], split[2], split[3]
+ local recurse = find(base,"%*%*")
+ local start = root .. path
+ local result = lpegmatch(filter,start .. base)
+ globpattern(start,result,recurse,t)
+ end
+ end
+ else
+ if type(str) == "table" then
+ local t = t or { }
+ for s=1,#str do
+ glob(str[s],t)
+ end
+ return t
+ elseif isfile(str) then
+ if t then
+ t[#t+1] = str
+ return t
+ else
+ return { str }
+ end
+ else
+ local split = lpegmatch(pattern,str) -- we could use the file splitter
+ if split then
+ local t = t or { }
+ local action = action or function(name) t[#t+1] = name end
+ local root, path, base = split[1], split[2], split[3]
+ local recurse = find(base,"%*%*")
+ local start = root .. path
+ local result = lpegmatch(filter,start .. base)
+ globpattern(start,result,recurse,action)
+ return t
+ else
+ return { }
+ end
+ end
+ end
+end
+
+dir.glob = glob
+
+--~ list = dir.glob("**/*.tif")
+--~ list = dir.glob("/**/*.tif")
+--~ list = dir.glob("./**/*.tif")
+--~ list = dir.glob("oeps/**/*.tif")
+--~ list = dir.glob("/oeps/**/*.tif")
+
+local function globfiles(path,recurse,func,files) -- func == pattern or function
+ if type(func) == "string" then
+ local s = func
+ func = function(name) return find(name,s) end
+ end
+ files = files or { }
+ local noffiles = #files
+ for name in walkdir(path) do
+ if find(name,"^%.") then
+ --- skip
+ else
+ local mode = attributes(name,'mode')
+ if mode == "directory" then
+ if recurse then
+ globfiles(path .. "/" .. name,recurse,func,files)
+ end
+ elseif mode == "file" then
+ if not func or func(name) then
+ noffiles = noffiles + 1
+ files[noffiles] = path .. "/" .. name
+ end
+ end
+ end
+ end
+ return files
+end
+
+dir.globfiles = globfiles
+
+-- t = dir.glob("c:/data/develop/context/sources/**/????-*.tex")
+-- t = dir.glob("c:/data/develop/tex/texmf/**/*.tex")
+-- t = dir.glob("c:/data/develop/context/texmf/**/*.tex")
+-- t = dir.glob("f:/minimal/tex/**/*")
+-- print(dir.ls("f:/minimal/tex/**/*"))
+-- print(dir.ls("*.tex"))
+
+function dir.ls(pattern)
+ return concat(glob(pattern),"\n")
+end
+
+--~ mkdirs("temp")
+--~ mkdirs("a/b/c")
+--~ mkdirs(".","/a/b/c")
+--~ mkdirs("a","b","c")
+
+local make_indeed = true -- false
+
+local onwindows = os.type == "windows" or find(os.getenv("PATH"),";")
+
+if onwindows then
+
+ function dir.mkdirs(...)
+ local str, pth = "", ""
+ for i=1,select("#",...) do
+ local s = select(i,...)
+ if s == "" then
+ -- skip
+ elseif str == "" then
+ str = s
+ else
+ str = str .. "/" .. s
+ end
+ end
+ local first, middle, last
+ local drive = false
+ first, middle, last = match(str,"^(//)(//*)(.*)$")
+ if first then
+ -- empty network path == local path
+ else
+ first, last = match(str,"^(//)/*(.-)$")
+ if first then
+ middle, last = match(str,"([^/]+)/+(.-)$")
+ if middle then
+ pth = "//" .. middle
+ else
+ pth = "//" .. last
+ last = ""
+ end
+ else
+ first, middle, last = match(str,"^([a-zA-Z]:)(/*)(.-)$")
+ if first then
+ pth, drive = first .. middle, true
+ else
+ middle, last = match(str,"^(/*)(.-)$")
+ if not middle then
+ last = str
+ end
+ end
+ end
+ end
+ for s in gmatch(last,"[^/]+") do
+ if pth == "" then
+ pth = s
+ elseif drive then
+ pth, drive = pth .. s, false
+ else
+ pth = pth .. "/" .. s
+ end
+ if make_indeed and not isdir(pth) then
+ lfs.mkdir(pth)
+ end
+ end
+ return pth, (isdir(pth) == true)
+ end
+
+ --~ print(dir.mkdirs("","","a","c"))
+ --~ print(dir.mkdirs("a"))
+ --~ print(dir.mkdirs("a:"))
+ --~ print(dir.mkdirs("a:/b/c"))
+ --~ print(dir.mkdirs("a:b/c"))
+ --~ print(dir.mkdirs("a:/bbb/c"))
+ --~ print(dir.mkdirs("/a/b/c"))
+ --~ print(dir.mkdirs("/aaa/b/c"))
+ --~ print(dir.mkdirs("//a/b/c"))
+ --~ print(dir.mkdirs("///a/b/c"))
+ --~ print(dir.mkdirs("a/bbb//ccc/"))
+
+else
+
+ function dir.mkdirs(...)
+ local str, pth = "", ""
+ for i=1,select("#",...) do
+ local s = select(i,...)
+ if s and s ~= "" then -- we catch nil and false
+ if str ~= "" then
+ str = str .. "/" .. s
+ else
+ str = s
+ end
+ end
+ end
+ str = gsub(str,"/+","/")
+ if find(str,"^/") then
+ pth = "/"
+ for s in gmatch(str,"[^/]+") do
+ local first = (pth == "/")
+ if first then
+ pth = pth .. s
+ else
+ pth = pth .. "/" .. s
+ end
+ if make_indeed and not first and not isdir(pth) then
+ lfs.mkdir(pth)
+ end
+ end
+ else
+ pth = "."
+ for s in gmatch(str,"[^/]+") do
+ pth = pth .. "/" .. s
+ if make_indeed and not isdir(pth) then
+ lfs.mkdir(pth)
+ end
+ end
+ end
+ return pth, (isdir(pth) == true)
+ end
+
+ --~ print(dir.mkdirs("","","a","c"))
+ --~ print(dir.mkdirs("a"))
+ --~ print(dir.mkdirs("/a/b/c"))
+ --~ print(dir.mkdirs("/aaa/b/c"))
+ --~ print(dir.mkdirs("//a/b/c"))
+ --~ print(dir.mkdirs("///a/b/c"))
+ --~ print(dir.mkdirs("a/bbb//ccc/"))
+
+end
+
+dir.makedirs = dir.mkdirs
+
+-- we can only define it here as it uses dir.current
+
+if onwindows then
+
+ function dir.expandname(str) -- will be merged with cleanpath and collapsepath
+ local first, nothing, last = match(str,"^(//)(//*)(.*)$")
+ if first then
+ first = dir.current() .. "/" -- dir.current sanitizes
+ end
+ if not first then
+ first, last = match(str,"^(//)/*(.*)$")
+ end
+ if not first then
+ first, last = match(str,"^([a-zA-Z]:)(.*)$")
+ if first and not find(last,"^/") then
+ local d = currentdir()
+ if chdir(first) then
+ first = dir.current()
+ end
+ chdir(d)
+ end
+ end
+ if not first then
+ first, last = dir.current(), str
+ end
+ last = gsub(last,"//","/")
+ last = gsub(last,"/%./","/")
+ last = gsub(last,"^/*","")
+ first = gsub(first,"/*$","")
+ if last == "" or last == "." then
+ return first
+ else
+ return first .. "/" .. last
+ end
+ end
+
+else
+
+ function dir.expandname(str) -- will be merged with cleanpath and collapsepath
+ if not find(str,"^/") then
+ str = currentdir() .. "/" .. str
+ end
+ str = gsub(str,"//","/")
+ str = gsub(str,"/%./","/")
+ str = gsub(str,"(.)/%.$","%1")
+ return str
+ end
+
+end
+
+file.expandname = dir.expandname -- for convenience
+
+local stack = { }
+
+function dir.push(newdir)
+ insert(stack,currentdir())
+ if newdir and newdir ~= "" then
+ chdir(newdir)
+ end
+end
+
+function dir.pop()
+ local d = remove(stack)
+ if d then
+ chdir(d)
+ end
+ return d
+end
+
+local function found(...) -- can have nil entries
+ for i=1,select("#",...) do
+ local path = select(i,...)
+ local kind = type(path)
+ if kind == "string" then
+ if isdir(path) then
+ return path
+ end
+ elseif kind == "table" then
+ -- here we asume no holes, i.e. an indexed table
+ local path = found(unpack(path))
+ if path then
+ return path
+ end
+ end
+ end
+ -- return nil -- if we want print("crappath") to show something
+end
+
+dir.found = found
diff --git a/tex/context/base/l-file.lua b/tex/context/base/l-file.lua
index 2e47a3d1f..f25490749 100644
--- a/tex/context/base/l-file.lua
+++ b/tex/context/base/l-file.lua
@@ -1,590 +1,590 @@
-if not modules then modules = { } end modules ['l-file'] = {
- 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"
-}
-
--- needs a cleanup
-
-file = file or { }
-local file = file
-
-if not lfs then
- lfs = optionalrequire("lfs")
-end
-
-if not lfs then
-
- lfs = {
- getcurrentdir = function()
- return "."
- end,
- attributes = function()
- return nil
- end,
- isfile = function(name)
- local f = io.open(name,'rb')
- if f then
- f:close()
- return true
- end
- end,
- isdir = function(name)
- print("you need to load lfs")
- return false
- end
- }
-
-elseif not lfs.isfile then
-
- local attributes = lfs.attributes
-
- function lfs.isdir(name)
- return attributes(name,"mode") == "directory"
- end
-
- function lfs.isfile(name)
- return attributes(name,"mode") == "file"
- end
-
- -- function lfs.isdir(name)
- -- local a = attributes(name)
- -- return a and a.mode == "directory"
- -- end
-
- -- function lfs.isfile(name)
- -- local a = attributes(name)
- -- return a and a.mode == "file"
- -- end
-
-end
-
-local insert, concat = table.insert, table.concat
-local match, find, gmatch = string.match, string.find, string.gmatch
-local lpegmatch = lpeg.match
-local getcurrentdir, attributes = lfs.currentdir, lfs.attributes
-local checkedsplit = string.checkedsplit
-
--- local patterns = file.patterns or { }
--- file.patterns = patterns
-
-local P, R, S, C, Cs, Cp, Cc, Ct = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Cp, lpeg.Cc, lpeg.Ct
-
-local colon = P(":")
-local period = P(".")
-local periods = P("..")
-local fwslash = P("/")
-local bwslash = P("\\")
-local slashes = S("\\/")
-local noperiod = 1-period
-local noslashes = 1-slashes
-local name = noperiod^1
-local suffix = period/"" * (1-period-slashes)^1 * -1
-
------ pattern = C((noslashes^0 * slashes^1)^1)
-local pattern = C((1 - (slashes^1 * noslashes^1 * -1))^1) * P(1) -- there must be a more efficient way
-
-local function pathpart(name,default)
- return name and lpegmatch(pattern,name) or default or ""
-end
-
-local pattern = (noslashes^0 * slashes)^1 * C(noslashes^1) * -1
-
-local function basename(name)
- return name and lpegmatch(pattern,name) or name
-end
-
--- print(pathpart("file"))
--- print(pathpart("dir/file"))
--- print(pathpart("/dir/file"))
--- print(basename("file"))
--- print(basename("dir/file"))
--- print(basename("/dir/file"))
-
-local pattern = (noslashes^0 * slashes^1)^0 * Cs((1-suffix)^1) * suffix^0
-
-local function nameonly(name)
- return name and lpegmatch(pattern,name) or name
-end
-
-local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * C(noperiod^1) * -1
-
-local function suffixonly(name)
- return name and lpegmatch(pattern,name) or ""
-end
-
-file.pathpart = pathpart
-file.basename = basename
-file.nameonly = nameonly
-file.suffixonly = suffixonly
-file.suffix = suffixonly
-
-file.dirname = pathpart -- obsolete
-file.extname = suffixonly -- obsolete
-
--- actually these are schemes
-
-local drive = C(R("az","AZ")) * colon
-local path = C((noslashes^0 * slashes)^0)
-local suffix = period * C(P(1-period)^0 * P(-1))
-local base = C((1-suffix)^0)
-local rest = C(P(1)^0)
-
-drive = drive + Cc("")
-path = path + Cc("")
-base = base + Cc("")
-suffix = suffix + Cc("")
-
-local pattern_a = drive * path * base * suffix
-local pattern_b = path * base * suffix
-local pattern_c = C(drive * path) * C(base * suffix) -- trick: two extra captures
-local pattern_d = path * rest
-
-function file.splitname(str,splitdrive)
- if not str then
- -- error
- elseif splitdrive then
- return lpegmatch(pattern_a,str) -- returns drive, path, base, suffix
- else
- return lpegmatch(pattern_b,str) -- returns path, base, suffix
- end
-end
-
-function file.splitbase(str)
- if str then
- return lpegmatch(pattern_d,str) -- returns path, base+suffix (path has / appended, might change at some point)
- else
- return "", str -- assume no path
- end
-end
-
----- stripslash = C((1 - P("/")^1*P(-1))^0)
-
-function file.nametotable(str,splitdrive)
- if str then
- local path, drive, subpath, name, base, suffix = lpegmatch(pattern_c,str)
- -- if path ~= "" then
- -- path = lpegmatch(stripslash,path) -- unfortunate hack, maybe this becomes default
- -- end
- if splitdrive then
- return {
- path = path,
- drive = drive,
- subpath = subpath,
- name = name,
- base = base,
- suffix = suffix,
- }
- else
- return {
- path = path,
- name = name,
- base = base,
- suffix = suffix,
- }
- end
- end
-end
-
--- print(file.splitname("file"))
--- print(file.splitname("dir/file"))
--- print(file.splitname("/dir/file"))
--- print(file.splitname("file"))
--- print(file.splitname("dir/file"))
--- print(file.splitname("/dir/file"))
-
--- inspect(file.nametotable("file.ext"))
--- inspect(file.nametotable("dir/file.ext"))
--- inspect(file.nametotable("/dir/file.ext"))
--- inspect(file.nametotable("file.ext"))
--- inspect(file.nametotable("dir/file.ext"))
--- inspect(file.nametotable("/dir/file.ext"))
-
------ pattern = Cs(((period * noperiod^1 * -1) / "" + 1)^1)
-local pattern = Cs(((period * (1-period-slashes)^1 * -1) / "" + 1)^1)
-
-function file.removesuffix(name)
- return name and lpegmatch(pattern,name)
-end
-
--- local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * Cp() * noperiod^1 * -1
---
--- function file.addsuffix(name, suffix)
--- local p = lpegmatch(pattern,name)
--- if p then
--- return name
--- else
--- return name .. "." .. suffix
--- end
--- end
-
-local suffix = period/"" * (1-period-slashes)^1 * -1
-local pattern = Cs((noslashes^0 * slashes^1)^0 * ((1-suffix)^1)) * Cs(suffix)
-
-function file.addsuffix(filename,suffix,criterium)
- if not filename or not suffix or suffix == "" then
- return filename
- elseif criterium == true then
- return filename .. "." .. suffix
- elseif not criterium then
- local n, s = lpegmatch(pattern,filename)
- if not s or s == "" then
- return filename .. "." .. suffix
- else
- return filename
- end
- else
- local n, s = lpegmatch(pattern,filename)
- if s and s ~= "" then
- local t = type(criterium)
- if t == "table" then
- -- keep if in criterium
- for i=1,#criterium do
- if s == criterium[i] then
- return filename
- end
- end
- elseif t == "string" then
- -- keep if criterium
- if s == criterium then
- return filename
- end
- end
- end
- return (n or filename) .. "." .. suffix
- end
-end
-
--- print("1 " .. file.addsuffix("name","new") .. " -> name.new")
--- print("2 " .. file.addsuffix("name.old","new") .. " -> name.old")
--- print("3 " .. file.addsuffix("name.old","new",true) .. " -> name.old.new")
--- print("4 " .. file.addsuffix("name.old","new","new") .. " -> name.new")
--- print("5 " .. file.addsuffix("name.old","new","old") .. " -> name.old")
--- print("6 " .. file.addsuffix("name.old","new","foo") .. " -> name.new")
--- print("7 " .. file.addsuffix("name.old","new",{"foo","bar"}) .. " -> name.new")
--- print("8 " .. file.addsuffix("name.old","new",{"old","bar"}) .. " -> name.old")
-
-local suffix = period * (1-period-slashes)^1 * -1
-local pattern = Cs((1-suffix)^0)
-
-function file.replacesuffix(name,suffix)
- if name and suffix and suffix ~= "" then
- return lpegmatch(pattern,name) .. "." .. suffix
- else
- return name
- end
-end
-
---
-
-local reslasher = lpeg.replacer(P("\\"),"/")
-
-function file.reslash(str)
- return str and lpegmatch(reslasher,str)
-end
-
--- We should be able to use:
---
--- local writable = P(1) * P("w") * Cc(true)
---
--- function file.is_writable(name)
--- local a = attributes(name) or attributes(pathpart(name,"."))
--- return a and lpegmatch(writable,a.permissions) or false
--- end
---
--- But after some testing Taco and I came up with the more robust
--- variant:
-
-function file.is_writable(name)
- if not name then
- -- error
- elseif lfs.isdir(name) then
- name = name .. "/m_t_x_t_e_s_t.tmp"
- local f = io.open(name,"wb")
- if f then
- f:close()
- os.remove(name)
- return true
- end
- elseif lfs.isfile(name) then
- local f = io.open(name,"ab")
- if f then
- f:close()
- return true
- end
- else
- local f = io.open(name,"ab")
- if f then
- f:close()
- os.remove(name)
- return true
- end
- end
- return false
-end
-
-local readable = P("r") * Cc(true)
-
-function file.is_readable(name)
- if name then
- local a = attributes(name)
- return a and lpegmatch(readable,a.permissions) or false
- else
- return false
- end
-end
-
-file.isreadable = file.is_readable -- depricated
-file.iswritable = file.is_writable -- depricated
-
-function file.size(name)
- if name then
- local a = attributes(name)
- return a and a.size or 0
- else
- return 0
- end
-end
-
-function file.splitpath(str,separator) -- string .. reslash is a bonus (we could do a direct split)
- return str and checkedsplit(lpegmatch(reslasher,str),separator or io.pathseparator)
-end
-
-function file.joinpath(tab,separator) -- table
- return tab and concat(tab,separator or io.pathseparator) -- can have trailing //
-end
-
-local stripper = Cs(P(fwslash)^0/"" * reslasher)
-local isnetwork = fwslash * fwslash * (1-fwslash) + (1-fwslash-colon)^1 * colon
-local isroot = fwslash^1 * -1
-local hasroot = fwslash^1
-
-local deslasher = lpeg.replacer(S("\\/")^1,"/")
-
--- If we have a network or prefix then there is a change that we end up with two
--- // in the middle ... we could prevent this if we (1) expand prefixes: and (2)
--- split and rebuild as url. Of course we could assume no network paths (which
--- makes sense) adn assume either mapped drives (windows) or mounts (unix) but
--- then we still have to deal with urls ... anyhow, multiple // are never a real
--- problem but just ugly.
-
-function file.join(...)
- local lst = { ... }
- local one = lst[1]
- if lpegmatch(isnetwork,one) then
- local two = lpegmatch(deslasher,concat(lst,"/",2))
- return one .. "/" .. two
- elseif lpegmatch(isroot,one) then
- local two = lpegmatch(deslasher,concat(lst,"/",2))
- if lpegmatch(hasroot,two) then
- return two
- else
- return "/" .. two
- end
- elseif one == "" then
- return lpegmatch(stripper,concat(lst,"/",2))
- else
- return lpegmatch(deslasher,concat(lst,"/"))
- end
-end
-
--- print(file.join("c:/whatever","name"))
--- print(file.join("//","/y"))
--- print(file.join("/","/y"))
--- print(file.join("","/y"))
--- print(file.join("/x/","/y"))
--- print(file.join("x/","/y"))
--- print(file.join("http://","/y"))
--- print(file.join("http://a","/y"))
--- print(file.join("http:///a","/y"))
--- print(file.join("//nas-1","/y"))
-
--- The previous one fails on "a.b/c" so Taco came up with a split based
--- variant. After some skyping we got it sort of compatible with the old
--- one. After that the anchoring to currentdir was added in a better way.
--- Of course there are some optimizations too. Finally we had to deal with
--- windows drive prefixes and things like sys://. Eventually gsubs and
--- finds were replaced by lpegs.
-
-local drivespec = R("az","AZ")^1 * colon
-local anchors = fwslash + drivespec
-local untouched = periods + (1-period)^1 * P(-1)
-local splitstarter = (Cs(drivespec * (bwslash/"/" + fwslash)^0) + Cc(false)) * Ct(lpeg.splitat(S("/\\")^1))
-local absolute = fwslash
-
-function file.collapsepath(str,anchor) -- anchor: false|nil, true, "."
- if not str then
- return
- end
- if anchor == true and not lpegmatch(anchors,str) then
- str = getcurrentdir() .. "/" .. str
- end
- if str == "" or str =="." then
- return "."
- elseif lpegmatch(untouched,str) then
- return lpegmatch(reslasher,str)
- end
- local starter, oldelements = lpegmatch(splitstarter,str)
- local newelements = { }
- local i = #oldelements
- while i > 0 do
- local element = oldelements[i]
- if element == '.' then
- -- do nothing
- elseif element == '..' then
- local n = i - 1
- while n > 0 do
- local element = oldelements[n]
- if element ~= '..' and element ~= '.' then
- oldelements[n] = '.'
- break
- else
- n = n - 1
- end
- end
- if n < 1 then
- insert(newelements,1,'..')
- end
- elseif element ~= "" then
- insert(newelements,1,element)
- end
- i = i - 1
- end
- if #newelements == 0 then
- return starter or "."
- elseif starter then
- return starter .. concat(newelements, '/')
- elseif lpegmatch(absolute,str) then
- return "/" .. concat(newelements,'/')
- else
- newelements = concat(newelements, '/')
- if anchor == "." and find(str,"^%./") then
- return "./" .. newelements
- else
- return newelements
- end
- end
-end
-
--- local function test(str,...)
--- print(string.format("%-20s %-15s %-30s %-20s",str,file.collapsepath(str),file.collapsepath(str,true),file.collapsepath(str,".")))
--- end
--- test("a/b.c/d") test("b.c/d") test("b.c/..")
--- test("/") test("c:/..") test("sys://..")
--- test("") test("./") test(".") test("..") test("./..") test("../..")
--- test("a") test("./a") test("/a") test("a/../..")
--- test("a/./b/..") test("a/aa/../b/bb") test("a/.././././b/..") test("a/./././b/..")
--- test("a/b/c/../..") test("./a/b/c/../..") test("a/b/c/../..")
--- test("./a")
-
-local validchars = R("az","09","AZ","--","..")
-local pattern_a = lpeg.replacer(1-validchars)
-local pattern_a = Cs((validchars + P(1)/"-")^1)
-local whatever = P("-")^0 / ""
-local pattern_b = Cs(whatever * (1 - whatever * -1)^1)
-
-function file.robustname(str,strict)
- if str then
- str = lpegmatch(pattern_a,str) or str
- if strict then
- return lpegmatch(pattern_b,str) or str -- two step is cleaner (less backtracking)
- else
- return str
- end
- end
-end
-
-file.readdata = io.loaddata
-file.savedata = io.savedata
-
-function file.copy(oldname,newname)
- if oldname and newname then
- local data = io.loaddata(oldname)
- if data and data ~= "" then
- file.savedata(newname,data)
- end
- end
-end
-
--- also rewrite previous
-
-local letter = R("az","AZ") + S("_-+")
-local separator = P("://")
-
-local qualified = period^0 * fwslash
- + letter * colon
- + letter^1 * separator
- + letter^1 * fwslash
-local rootbased = fwslash
- + letter * colon
-
-lpeg.patterns.qualified = qualified
-lpeg.patterns.rootbased = rootbased
-
--- ./name ../name /name c: :// name/name
-
-function file.is_qualified_path(filename)
- return filename and lpegmatch(qualified,filename) ~= nil
-end
-
-function file.is_rootbased_path(filename)
- return filename and lpegmatch(rootbased,filename) ~= nil
-end
-
--- function test(t) for k, v in next, t do print(v, "=>", file.splitname(v)) end end
---
--- test { "c:", "c:/aa", "c:/aa/bb", "c:/aa/bb/cc", "c:/aa/bb/cc.dd", "c:/aa/bb/cc.dd.ee" }
--- test { "c:", "c:aa", "c:aa/bb", "c:aa/bb/cc", "c:aa/bb/cc.dd", "c:aa/bb/cc.dd.ee" }
--- test { "/aa", "/aa/bb", "/aa/bb/cc", "/aa/bb/cc.dd", "/aa/bb/cc.dd.ee" }
--- test { "aa", "aa/bb", "aa/bb/cc", "aa/bb/cc.dd", "aa/bb/cc.dd.ee" }
-
--- -- maybe:
---
--- if os.type == "windows" then
--- local currentdir = getcurrentdir
--- function getcurrentdir()
--- return lpegmatch(reslasher,currentdir())
--- end
--- end
-
--- for myself:
-
-function file.strip(name,dir)
- if name then
- local b, a = match(name,"^(.-)" .. dir .. "(.*)$")
- return a ~= "" and a or name
- end
-end
-
--- local debuglist = {
--- "pathpart", "basename", "nameonly", "suffixonly", "suffix", "dirname", "extname",
--- "addsuffix", "removesuffix", "replacesuffix", "join",
--- "strip","collapsepath", "joinpath", "splitpath",
--- }
-
--- for i=1,#debuglist do
--- local name = debuglist[i]
--- local f = file[name]
--- file[name] = function(...)
--- print(name,f(...))
--- return f(...)
--- end
--- end
-
--- a goodie: a dumb version of mkdirs:
-
-function lfs.mkdirs(path)
- local full
- for sub in gmatch(path,"([^\\/]+)") do
- if full then
- full = full .. "/" .. sub
- else
- full = sub
- end
- if not lfs.isdir(full) then
- lfs.mkdir(full)
- end
- end
-end
+if not modules then modules = { } end modules ['l-file'] = {
+ 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"
+}
+
+-- needs a cleanup
+
+file = file or { }
+local file = file
+
+if not lfs then
+ lfs = optionalrequire("lfs")
+end
+
+if not lfs then
+
+ lfs = {
+ getcurrentdir = function()
+ return "."
+ end,
+ attributes = function()
+ return nil
+ end,
+ isfile = function(name)
+ local f = io.open(name,'rb')
+ if f then
+ f:close()
+ return true
+ end
+ end,
+ isdir = function(name)
+ print("you need to load lfs")
+ return false
+ end
+ }
+
+elseif not lfs.isfile then
+
+ local attributes = lfs.attributes
+
+ function lfs.isdir(name)
+ return attributes(name,"mode") == "directory"
+ end
+
+ function lfs.isfile(name)
+ return attributes(name,"mode") == "file"
+ end
+
+ -- function lfs.isdir(name)
+ -- local a = attributes(name)
+ -- return a and a.mode == "directory"
+ -- end
+
+ -- function lfs.isfile(name)
+ -- local a = attributes(name)
+ -- return a and a.mode == "file"
+ -- end
+
+end
+
+local insert, concat = table.insert, table.concat
+local match, find, gmatch = string.match, string.find, string.gmatch
+local lpegmatch = lpeg.match
+local getcurrentdir, attributes = lfs.currentdir, lfs.attributes
+local checkedsplit = string.checkedsplit
+
+-- local patterns = file.patterns or { }
+-- file.patterns = patterns
+
+local P, R, S, C, Cs, Cp, Cc, Ct = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Cp, lpeg.Cc, lpeg.Ct
+
+local colon = P(":")
+local period = P(".")
+local periods = P("..")
+local fwslash = P("/")
+local bwslash = P("\\")
+local slashes = S("\\/")
+local noperiod = 1-period
+local noslashes = 1-slashes
+local name = noperiod^1
+local suffix = period/"" * (1-period-slashes)^1 * -1
+
+----- pattern = C((noslashes^0 * slashes^1)^1)
+local pattern = C((1 - (slashes^1 * noslashes^1 * -1))^1) * P(1) -- there must be a more efficient way
+
+local function pathpart(name,default)
+ return name and lpegmatch(pattern,name) or default or ""
+end
+
+local pattern = (noslashes^0 * slashes)^1 * C(noslashes^1) * -1
+
+local function basename(name)
+ return name and lpegmatch(pattern,name) or name
+end
+
+-- print(pathpart("file"))
+-- print(pathpart("dir/file"))
+-- print(pathpart("/dir/file"))
+-- print(basename("file"))
+-- print(basename("dir/file"))
+-- print(basename("/dir/file"))
+
+local pattern = (noslashes^0 * slashes^1)^0 * Cs((1-suffix)^1) * suffix^0
+
+local function nameonly(name)
+ return name and lpegmatch(pattern,name) or name
+end
+
+local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * C(noperiod^1) * -1
+
+local function suffixonly(name)
+ return name and lpegmatch(pattern,name) or ""
+end
+
+file.pathpart = pathpart
+file.basename = basename
+file.nameonly = nameonly
+file.suffixonly = suffixonly
+file.suffix = suffixonly
+
+file.dirname = pathpart -- obsolete
+file.extname = suffixonly -- obsolete
+
+-- actually these are schemes
+
+local drive = C(R("az","AZ")) * colon
+local path = C((noslashes^0 * slashes)^0)
+local suffix = period * C(P(1-period)^0 * P(-1))
+local base = C((1-suffix)^0)
+local rest = C(P(1)^0)
+
+drive = drive + Cc("")
+path = path + Cc("")
+base = base + Cc("")
+suffix = suffix + Cc("")
+
+local pattern_a = drive * path * base * suffix
+local pattern_b = path * base * suffix
+local pattern_c = C(drive * path) * C(base * suffix) -- trick: two extra captures
+local pattern_d = path * rest
+
+function file.splitname(str,splitdrive)
+ if not str then
+ -- error
+ elseif splitdrive then
+ return lpegmatch(pattern_a,str) -- returns drive, path, base, suffix
+ else
+ return lpegmatch(pattern_b,str) -- returns path, base, suffix
+ end
+end
+
+function file.splitbase(str)
+ if str then
+ return lpegmatch(pattern_d,str) -- returns path, base+suffix (path has / appended, might change at some point)
+ else
+ return "", str -- assume no path
+ end
+end
+
+---- stripslash = C((1 - P("/")^1*P(-1))^0)
+
+function file.nametotable(str,splitdrive)
+ if str then
+ local path, drive, subpath, name, base, suffix = lpegmatch(pattern_c,str)
+ -- if path ~= "" then
+ -- path = lpegmatch(stripslash,path) -- unfortunate hack, maybe this becomes default
+ -- end
+ if splitdrive then
+ return {
+ path = path,
+ drive = drive,
+ subpath = subpath,
+ name = name,
+ base = base,
+ suffix = suffix,
+ }
+ else
+ return {
+ path = path,
+ name = name,
+ base = base,
+ suffix = suffix,
+ }
+ end
+ end
+end
+
+-- print(file.splitname("file"))
+-- print(file.splitname("dir/file"))
+-- print(file.splitname("/dir/file"))
+-- print(file.splitname("file"))
+-- print(file.splitname("dir/file"))
+-- print(file.splitname("/dir/file"))
+
+-- inspect(file.nametotable("file.ext"))
+-- inspect(file.nametotable("dir/file.ext"))
+-- inspect(file.nametotable("/dir/file.ext"))
+-- inspect(file.nametotable("file.ext"))
+-- inspect(file.nametotable("dir/file.ext"))
+-- inspect(file.nametotable("/dir/file.ext"))
+
+----- pattern = Cs(((period * noperiod^1 * -1) / "" + 1)^1)
+local pattern = Cs(((period * (1-period-slashes)^1 * -1) / "" + 1)^1)
+
+function file.removesuffix(name)
+ return name and lpegmatch(pattern,name)
+end
+
+-- local pattern = (noslashes^0 * slashes)^0 * (noperiod^1 * period)^1 * Cp() * noperiod^1 * -1
+--
+-- function file.addsuffix(name, suffix)
+-- local p = lpegmatch(pattern,name)
+-- if p then
+-- return name
+-- else
+-- return name .. "." .. suffix
+-- end
+-- end
+
+local suffix = period/"" * (1-period-slashes)^1 * -1
+local pattern = Cs((noslashes^0 * slashes^1)^0 * ((1-suffix)^1)) * Cs(suffix)
+
+function file.addsuffix(filename,suffix,criterium)
+ if not filename or not suffix or suffix == "" then
+ return filename
+ elseif criterium == true then
+ return filename .. "." .. suffix
+ elseif not criterium then
+ local n, s = lpegmatch(pattern,filename)
+ if not s or s == "" then
+ return filename .. "." .. suffix
+ else
+ return filename
+ end
+ else
+ local n, s = lpegmatch(pattern,filename)
+ if s and s ~= "" then
+ local t = type(criterium)
+ if t == "table" then
+ -- keep if in criterium
+ for i=1,#criterium do
+ if s == criterium[i] then
+ return filename
+ end
+ end
+ elseif t == "string" then
+ -- keep if criterium
+ if s == criterium then
+ return filename
+ end
+ end
+ end
+ return (n or filename) .. "." .. suffix
+ end
+end
+
+-- print("1 " .. file.addsuffix("name","new") .. " -> name.new")
+-- print("2 " .. file.addsuffix("name.old","new") .. " -> name.old")
+-- print("3 " .. file.addsuffix("name.old","new",true) .. " -> name.old.new")
+-- print("4 " .. file.addsuffix("name.old","new","new") .. " -> name.new")
+-- print("5 " .. file.addsuffix("name.old","new","old") .. " -> name.old")
+-- print("6 " .. file.addsuffix("name.old","new","foo") .. " -> name.new")
+-- print("7 " .. file.addsuffix("name.old","new",{"foo","bar"}) .. " -> name.new")
+-- print("8 " .. file.addsuffix("name.old","new",{"old","bar"}) .. " -> name.old")
+
+local suffix = period * (1-period-slashes)^1 * -1
+local pattern = Cs((1-suffix)^0)
+
+function file.replacesuffix(name,suffix)
+ if name and suffix and suffix ~= "" then
+ return lpegmatch(pattern,name) .. "." .. suffix
+ else
+ return name
+ end
+end
+
+--
+
+local reslasher = lpeg.replacer(P("\\"),"/")
+
+function file.reslash(str)
+ return str and lpegmatch(reslasher,str)
+end
+
+-- We should be able to use:
+--
+-- local writable = P(1) * P("w") * Cc(true)
+--
+-- function file.is_writable(name)
+-- local a = attributes(name) or attributes(pathpart(name,"."))
+-- return a and lpegmatch(writable,a.permissions) or false
+-- end
+--
+-- But after some testing Taco and I came up with the more robust
+-- variant:
+
+function file.is_writable(name)
+ if not name then
+ -- error
+ elseif lfs.isdir(name) then
+ name = name .. "/m_t_x_t_e_s_t.tmp"
+ local f = io.open(name,"wb")
+ if f then
+ f:close()
+ os.remove(name)
+ return true
+ end
+ elseif lfs.isfile(name) then
+ local f = io.open(name,"ab")
+ if f then
+ f:close()
+ return true
+ end
+ else
+ local f = io.open(name,"ab")
+ if f then
+ f:close()
+ os.remove(name)
+ return true
+ end
+ end
+ return false
+end
+
+local readable = P("r") * Cc(true)
+
+function file.is_readable(name)
+ if name then
+ local a = attributes(name)
+ return a and lpegmatch(readable,a.permissions) or false
+ else
+ return false
+ end
+end
+
+file.isreadable = file.is_readable -- depricated
+file.iswritable = file.is_writable -- depricated
+
+function file.size(name)
+ if name then
+ local a = attributes(name)
+ return a and a.size or 0
+ else
+ return 0
+ end
+end
+
+function file.splitpath(str,separator) -- string .. reslash is a bonus (we could do a direct split)
+ return str and checkedsplit(lpegmatch(reslasher,str),separator or io.pathseparator)
+end
+
+function file.joinpath(tab,separator) -- table
+ return tab and concat(tab,separator or io.pathseparator) -- can have trailing //
+end
+
+local stripper = Cs(P(fwslash)^0/"" * reslasher)
+local isnetwork = fwslash * fwslash * (1-fwslash) + (1-fwslash-colon)^1 * colon
+local isroot = fwslash^1 * -1
+local hasroot = fwslash^1
+
+local deslasher = lpeg.replacer(S("\\/")^1,"/")
+
+-- If we have a network or prefix then there is a change that we end up with two
+-- // in the middle ... we could prevent this if we (1) expand prefixes: and (2)
+-- split and rebuild as url. Of course we could assume no network paths (which
+-- makes sense) adn assume either mapped drives (windows) or mounts (unix) but
+-- then we still have to deal with urls ... anyhow, multiple // are never a real
+-- problem but just ugly.
+
+function file.join(...)
+ local lst = { ... }
+ local one = lst[1]
+ if lpegmatch(isnetwork,one) then
+ local two = lpegmatch(deslasher,concat(lst,"/",2))
+ return one .. "/" .. two
+ elseif lpegmatch(isroot,one) then
+ local two = lpegmatch(deslasher,concat(lst,"/",2))
+ if lpegmatch(hasroot,two) then
+ return two
+ else
+ return "/" .. two
+ end
+ elseif one == "" then
+ return lpegmatch(stripper,concat(lst,"/",2))
+ else
+ return lpegmatch(deslasher,concat(lst,"/"))
+ end
+end
+
+-- print(file.join("c:/whatever","name"))
+-- print(file.join("//","/y"))
+-- print(file.join("/","/y"))
+-- print(file.join("","/y"))
+-- print(file.join("/x/","/y"))
+-- print(file.join("x/","/y"))
+-- print(file.join("http://","/y"))
+-- print(file.join("http://a","/y"))
+-- print(file.join("http:///a","/y"))
+-- print(file.join("//nas-1","/y"))
+
+-- The previous one fails on "a.b/c" so Taco came up with a split based
+-- variant. After some skyping we got it sort of compatible with the old
+-- one. After that the anchoring to currentdir was added in a better way.
+-- Of course there are some optimizations too. Finally we had to deal with
+-- windows drive prefixes and things like sys://. Eventually gsubs and
+-- finds were replaced by lpegs.
+
+local drivespec = R("az","AZ")^1 * colon
+local anchors = fwslash + drivespec
+local untouched = periods + (1-period)^1 * P(-1)
+local splitstarter = (Cs(drivespec * (bwslash/"/" + fwslash)^0) + Cc(false)) * Ct(lpeg.splitat(S("/\\")^1))
+local absolute = fwslash
+
+function file.collapsepath(str,anchor) -- anchor: false|nil, true, "."
+ if not str then
+ return
+ end
+ if anchor == true and not lpegmatch(anchors,str) then
+ str = getcurrentdir() .. "/" .. str
+ end
+ if str == "" or str =="." then
+ return "."
+ elseif lpegmatch(untouched,str) then
+ return lpegmatch(reslasher,str)
+ end
+ local starter, oldelements = lpegmatch(splitstarter,str)
+ local newelements = { }
+ local i = #oldelements
+ while i > 0 do
+ local element = oldelements[i]
+ if element == '.' then
+ -- do nothing
+ elseif element == '..' then
+ local n = i - 1
+ while n > 0 do
+ local element = oldelements[n]
+ if element ~= '..' and element ~= '.' then
+ oldelements[n] = '.'
+ break
+ else
+ n = n - 1
+ end
+ end
+ if n < 1 then
+ insert(newelements,1,'..')
+ end
+ elseif element ~= "" then
+ insert(newelements,1,element)
+ end
+ i = i - 1
+ end
+ if #newelements == 0 then
+ return starter or "."
+ elseif starter then
+ return starter .. concat(newelements, '/')
+ elseif lpegmatch(absolute,str) then
+ return "/" .. concat(newelements,'/')
+ else
+ newelements = concat(newelements, '/')
+ if anchor == "." and find(str,"^%./") then
+ return "./" .. newelements
+ else
+ return newelements
+ end
+ end
+end
+
+-- local function test(str,...)
+-- print(string.format("%-20s %-15s %-30s %-20s",str,file.collapsepath(str),file.collapsepath(str,true),file.collapsepath(str,".")))
+-- end
+-- test("a/b.c/d") test("b.c/d") test("b.c/..")
+-- test("/") test("c:/..") test("sys://..")
+-- test("") test("./") test(".") test("..") test("./..") test("../..")
+-- test("a") test("./a") test("/a") test("a/../..")
+-- test("a/./b/..") test("a/aa/../b/bb") test("a/.././././b/..") test("a/./././b/..")
+-- test("a/b/c/../..") test("./a/b/c/../..") test("a/b/c/../..")
+-- test("./a")
+
+local validchars = R("az","09","AZ","--","..")
+local pattern_a = lpeg.replacer(1-validchars)
+local pattern_a = Cs((validchars + P(1)/"-")^1)
+local whatever = P("-")^0 / ""
+local pattern_b = Cs(whatever * (1 - whatever * -1)^1)
+
+function file.robustname(str,strict)
+ if str then
+ str = lpegmatch(pattern_a,str) or str
+ if strict then
+ return lpegmatch(pattern_b,str) or str -- two step is cleaner (less backtracking)
+ else
+ return str
+ end
+ end
+end
+
+file.readdata = io.loaddata
+file.savedata = io.savedata
+
+function file.copy(oldname,newname)
+ if oldname and newname then
+ local data = io.loaddata(oldname)
+ if data and data ~= "" then
+ file.savedata(newname,data)
+ end
+ end
+end
+
+-- also rewrite previous
+
+local letter = R("az","AZ") + S("_-+")
+local separator = P("://")
+
+local qualified = period^0 * fwslash
+ + letter * colon
+ + letter^1 * separator
+ + letter^1 * fwslash
+local rootbased = fwslash
+ + letter * colon
+
+lpeg.patterns.qualified = qualified
+lpeg.patterns.rootbased = rootbased
+
+-- ./name ../name /name c: :// name/name
+
+function file.is_qualified_path(filename)
+ return filename and lpegmatch(qualified,filename) ~= nil
+end
+
+function file.is_rootbased_path(filename)
+ return filename and lpegmatch(rootbased,filename) ~= nil
+end
+
+-- function test(t) for k, v in next, t do print(v, "=>", file.splitname(v)) end end
+--
+-- test { "c:", "c:/aa", "c:/aa/bb", "c:/aa/bb/cc", "c:/aa/bb/cc.dd", "c:/aa/bb/cc.dd.ee" }
+-- test { "c:", "c:aa", "c:aa/bb", "c:aa/bb/cc", "c:aa/bb/cc.dd", "c:aa/bb/cc.dd.ee" }
+-- test { "/aa", "/aa/bb", "/aa/bb/cc", "/aa/bb/cc.dd", "/aa/bb/cc.dd.ee" }
+-- test { "aa", "aa/bb", "aa/bb/cc", "aa/bb/cc.dd", "aa/bb/cc.dd.ee" }
+
+-- -- maybe:
+--
+-- if os.type == "windows" then
+-- local currentdir = getcurrentdir
+-- function getcurrentdir()
+-- return lpegmatch(reslasher,currentdir())
+-- end
+-- end
+
+-- for myself:
+
+function file.strip(name,dir)
+ if name then
+ local b, a = match(name,"^(.-)" .. dir .. "(.*)$")
+ return a ~= "" and a or name
+ end
+end
+
+-- local debuglist = {
+-- "pathpart", "basename", "nameonly", "suffixonly", "suffix", "dirname", "extname",
+-- "addsuffix", "removesuffix", "replacesuffix", "join",
+-- "strip","collapsepath", "joinpath", "splitpath",
+-- }
+
+-- for i=1,#debuglist do
+-- local name = debuglist[i]
+-- local f = file[name]
+-- file[name] = function(...)
+-- print(name,f(...))
+-- return f(...)
+-- end
+-- end
+
+-- a goodie: a dumb version of mkdirs:
+
+function lfs.mkdirs(path)
+ local full
+ for sub in gmatch(path,"([^\\/]+)") do
+ if full then
+ full = full .. "/" .. sub
+ else
+ full = sub
+ end
+ if not lfs.isdir(full) then
+ lfs.mkdir(full)
+ end
+ end
+end
diff --git a/tex/context/base/l-function.lua b/tex/context/base/l-function.lua
index cdb1d3def..7ded8ceec 100644
--- a/tex/context/base/l-function.lua
+++ b/tex/context/base/l-function.lua
@@ -1,11 +1,11 @@
-if not modules then modules = { } end modules ['l-functions'] = {
- 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"
-}
-
-functions = functions or { }
-
-function functions.dummy() end
+if not modules then modules = { } end modules ['l-functions'] = {
+ 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"
+}
+
+functions = functions or { }
+
+function functions.dummy() end
diff --git a/tex/context/base/l-io.lua b/tex/context/base/l-io.lua
index 2ddfacaee..06e1fb5ef 100644
--- a/tex/context/base/l-io.lua
+++ b/tex/context/base/l-io.lua
@@ -1,362 +1,362 @@
-if not modules then modules = { } end modules ['l-io'] = {
- 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 io = io
-local byte, find, gsub, format = string.byte, string.find, string.gsub, string.format
-local concat = table.concat
-local floor = math.floor
-local type = type
-
-if string.find(os.getenv("PATH"),";") then
- io.fileseparator, io.pathseparator = "\\", ";"
-else
- io.fileseparator, io.pathseparator = "/" , ":"
-end
-
-local function readall(f)
- return f:read("*all")
-end
-
--- The next one is upto 50% faster on large files and less memory consumption due
--- to less intermediate large allocations. This phenomena was discussed on the
--- luatex dev list.
-
-local function readall(f)
- local size = f:seek("end")
- if size == 0 then
- return ""
- elseif size < 1024*1024 then
- f:seek("set",0)
- return f:read('*all')
- else
- local done = f:seek("set",0)
- if size < 1024*1024 then
- step = 1024 * 1024
- elseif size > 16*1024*1024 then
- step = 16*1024*1024
- else
- step = floor(size/(1024*1024)) * 1024 * 1024 / 8
- end
- local data = { }
- while true do
- local r = f:read(step)
- if not r then
- return concat(data)
- else
- data[#data+1] = r
- end
- end
- end
-end
-
-io.readall = readall
-
-function io.loaddata(filename,textmode) -- return nil if empty
- local f = io.open(filename,(textmode and 'r') or 'rb')
- if f then
--- local data = f:read('*all')
- local data = readall(f)
- f:close()
- if #data > 0 then
- return data
- end
- end
-end
-
-function io.savedata(filename,data,joiner)
- local f = io.open(filename,"wb")
- if f then
- if type(data) == "table" then
- f:write(concat(data,joiner or ""))
- elseif type(data) == "function" then
- data(f)
- else
- f:write(data or "")
- end
- f:close()
- io.flush()
- return true
- else
- return false
- end
-end
-
--- we can also chunk this one if needed: io.lines(filename,chunksize,"*l")
-
-function io.loadlines(filename,n) -- return nil if empty
- local f = io.open(filename,'r')
- if not f then
- -- no file
- elseif n then
- local lines = { }
- for i=1,n do
- local line = f:read("*lines")
- if line then
- lines[#lines+1] = line
- else
- break
- end
- end
- f:close()
- lines = concat(lines,"\n")
- if #lines > 0 then
- return lines
- end
- else
- local line = f:read("*line") or ""
- f:close()
- if #line > 0 then
- return line
- end
- end
-end
-
-function io.loadchunk(filename,n)
- local f = io.open(filename,'rb')
- if f then
- local data = f:read(n or 1024)
- f:close()
- if #data > 0 then
- return data
- end
- end
-end
-
-function io.exists(filename)
- local f = io.open(filename)
- if f == nil then
- return false
- else
- f:close()
- return true
- end
-end
-
-function io.size(filename)
- local f = io.open(filename)
- if f == nil then
- return 0
- else
- local s = f:seek("end")
- f:close()
- return s
- end
-end
-
-function io.noflines(f)
- if type(f) == "string" then
- local f = io.open(filename)
- if f then
- local n = f and io.noflines(f) or 0
- f:close()
- return n
- else
- return 0
- end
- else
- local n = 0
- for _ in f:lines() do
- n = n + 1
- end
- f:seek('set',0)
- return n
- end
-end
-
-local nextchar = {
- [ 4] = function(f)
- return f:read(1,1,1,1)
- end,
- [ 2] = function(f)
- return f:read(1,1)
- end,
- [ 1] = function(f)
- return f:read(1)
- end,
- [-2] = function(f)
- local a, b = f:read(1,1)
- return b, a
- end,
- [-4] = function(f)
- local a, b, c, d = f:read(1,1,1,1)
- return d, c, b, a
- end
-}
-
-function io.characters(f,n)
- if f then
- return nextchar[n or 1], f
- end
-end
-
-local nextbyte = {
- [4] = function(f)
- local a, b, c, d = f:read(1,1,1,1)
- if d then
- return byte(a), byte(b), byte(c), byte(d)
- end
- end,
- [3] = function(f)
- local a, b, c = f:read(1,1,1)
- if b then
- return byte(a), byte(b), byte(c)
- end
- end,
- [2] = function(f)
- local a, b = f:read(1,1)
- if b then
- return byte(a), byte(b)
- end
- end,
- [1] = function (f)
- local a = f:read(1)
- if a then
- return byte(a)
- end
- end,
- [-2] = function (f)
- local a, b = f:read(1,1)
- if b then
- return byte(b), byte(a)
- end
- end,
- [-3] = function(f)
- local a, b, c = f:read(1,1,1)
- if b then
- return byte(c), byte(b), byte(a)
- end
- end,
- [-4] = function(f)
- local a, b, c, d = f:read(1,1,1,1)
- if d then
- return byte(d), byte(c), byte(b), byte(a)
- end
- end
-}
-
-function io.bytes(f,n)
- if f then
- return nextbyte[n or 1], f
- else
- return nil, nil
- end
-end
-
-function io.ask(question,default,options)
- while true do
- io.write(question)
- if options then
- io.write(format(" [%s]",concat(options,"|")))
- end
- if default then
- io.write(format(" [%s]",default))
- end
- io.write(format(" "))
- io.flush()
- local answer = io.read()
- answer = gsub(answer,"^%s*(.*)%s*$","%1")
- if answer == "" and default then
- return default
- elseif not options then
- return answer
- else
- for k=1,#options do
- if options[k] == answer then
- return answer
- end
- end
- local pattern = "^" .. answer
- for k=1,#options do
- local v = options[k]
- if find(v,pattern) then
- return v
- end
- end
- end
- end
-end
-
-local function readnumber(f,n,m)
- if m then
- f:seek("set",n)
- n = m
- end
- if n == 1 then
- return byte(f:read(1))
- elseif n == 2 then
- local a, b = byte(f:read(2),1,2)
- return 256 * a + b
- elseif n == 3 then
- local a, b, c = byte(f:read(3),1,3)
- return 256*256 * a + 256 * b + c
- elseif n == 4 then
- local a, b, c, d = byte(f:read(4),1,4)
- return 256*256*256 * a + 256*256 * b + 256 * c + d
- elseif n == 8 then
- local a, b = readnumber(f,4), readnumber(f,4)
- return 256 * a + b
- elseif n == 12 then
- local a, b, c = readnumber(f,4), readnumber(f,4), readnumber(f,4)
- return 256*256 * a + 256 * b + c
- elseif n == -2 then
- local b, a = byte(f:read(2),1,2)
- return 256*a + b
- elseif n == -3 then
- local c, b, a = byte(f:read(3),1,3)
- return 256*256 * a + 256 * b + c
- elseif n == -4 then
- local d, c, b, a = byte(f:read(4),1,4)
- return 256*256*256 * a + 256*256 * b + 256*c + d
- elseif n == -8 then
- local h, g, f, e, d, c, b, a = byte(f:read(8),1,8)
- return 256*256*256*256*256*256*256 * a +
- 256*256*256*256*256*256 * b +
- 256*256*256*256*256 * c +
- 256*256*256*256 * d +
- 256*256*256 * e +
- 256*256 * f +
- 256 * g +
- h
- else
- return 0
- end
-end
-
-io.readnumber = readnumber
-
-function io.readstring(f,n,m)
- if m then
- f:seek("set",n)
- n = m
- end
- local str = gsub(f:read(n),"\000","")
- return str
-end
-
---
-
-if not io.i_limiter then function io.i_limiter() end end -- dummy so we can test safely
-if not io.o_limiter then function io.o_limiter() end end -- dummy so we can test safely
-
--- This works quite ok:
---
--- function io.piped(command,writer)
--- local pipe = io.popen(command)
--- -- for line in pipe:lines() do
--- -- print(line)
--- -- end
--- while true do
--- local line = pipe:read(1)
--- if not line then
--- break
--- elseif line ~= "\n" then
--- writer(line)
--- end
--- end
--- return pipe:close() -- ok, status, (error)code
--- end
+if not modules then modules = { } end modules ['l-io'] = {
+ 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 io = io
+local byte, find, gsub, format = string.byte, string.find, string.gsub, string.format
+local concat = table.concat
+local floor = math.floor
+local type = type
+
+if string.find(os.getenv("PATH"),";") then
+ io.fileseparator, io.pathseparator = "\\", ";"
+else
+ io.fileseparator, io.pathseparator = "/" , ":"
+end
+
+local function readall(f)
+ return f:read("*all")
+end
+
+-- The next one is upto 50% faster on large files and less memory consumption due
+-- to less intermediate large allocations. This phenomena was discussed on the
+-- luatex dev list.
+
+local function readall(f)
+ local size = f:seek("end")
+ if size == 0 then
+ return ""
+ elseif size < 1024*1024 then
+ f:seek("set",0)
+ return f:read('*all')
+ else
+ local done = f:seek("set",0)
+ if size < 1024*1024 then
+ step = 1024 * 1024
+ elseif size > 16*1024*1024 then
+ step = 16*1024*1024
+ else
+ step = floor(size/(1024*1024)) * 1024 * 1024 / 8
+ end
+ local data = { }
+ while true do
+ local r = f:read(step)
+ if not r then
+ return concat(data)
+ else
+ data[#data+1] = r
+ end
+ end
+ end
+end
+
+io.readall = readall
+
+function io.loaddata(filename,textmode) -- return nil if empty
+ local f = io.open(filename,(textmode and 'r') or 'rb')
+ if f then
+-- local data = f:read('*all')
+ local data = readall(f)
+ f:close()
+ if #data > 0 then
+ return data
+ end
+ end
+end
+
+function io.savedata(filename,data,joiner)
+ local f = io.open(filename,"wb")
+ if f then
+ if type(data) == "table" then
+ f:write(concat(data,joiner or ""))
+ elseif type(data) == "function" then
+ data(f)
+ else
+ f:write(data or "")
+ end
+ f:close()
+ io.flush()
+ return true
+ else
+ return false
+ end
+end
+
+-- we can also chunk this one if needed: io.lines(filename,chunksize,"*l")
+
+function io.loadlines(filename,n) -- return nil if empty
+ local f = io.open(filename,'r')
+ if not f then
+ -- no file
+ elseif n then
+ local lines = { }
+ for i=1,n do
+ local line = f:read("*lines")
+ if line then
+ lines[#lines+1] = line
+ else
+ break
+ end
+ end
+ f:close()
+ lines = concat(lines,"\n")
+ if #lines > 0 then
+ return lines
+ end
+ else
+ local line = f:read("*line") or ""
+ f:close()
+ if #line > 0 then
+ return line
+ end
+ end
+end
+
+function io.loadchunk(filename,n)
+ local f = io.open(filename,'rb')
+ if f then
+ local data = f:read(n or 1024)
+ f:close()
+ if #data > 0 then
+ return data
+ end
+ end
+end
+
+function io.exists(filename)
+ local f = io.open(filename)
+ if f == nil then
+ return false
+ else
+ f:close()
+ return true
+ end
+end
+
+function io.size(filename)
+ local f = io.open(filename)
+ if f == nil then
+ return 0
+ else
+ local s = f:seek("end")
+ f:close()
+ return s
+ end
+end
+
+function io.noflines(f)
+ if type(f) == "string" then
+ local f = io.open(filename)
+ if f then
+ local n = f and io.noflines(f) or 0
+ f:close()
+ return n
+ else
+ return 0
+ end
+ else
+ local n = 0
+ for _ in f:lines() do
+ n = n + 1
+ end
+ f:seek('set',0)
+ return n
+ end
+end
+
+local nextchar = {
+ [ 4] = function(f)
+ return f:read(1,1,1,1)
+ end,
+ [ 2] = function(f)
+ return f:read(1,1)
+ end,
+ [ 1] = function(f)
+ return f:read(1)
+ end,
+ [-2] = function(f)
+ local a, b = f:read(1,1)
+ return b, a
+ end,
+ [-4] = function(f)
+ local a, b, c, d = f:read(1,1,1,1)
+ return d, c, b, a
+ end
+}
+
+function io.characters(f,n)
+ if f then
+ return nextchar[n or 1], f
+ end
+end
+
+local nextbyte = {
+ [4] = function(f)
+ local a, b, c, d = f:read(1,1,1,1)
+ if d then
+ return byte(a), byte(b), byte(c), byte(d)
+ end
+ end,
+ [3] = function(f)
+ local a, b, c = f:read(1,1,1)
+ if b then
+ return byte(a), byte(b), byte(c)
+ end
+ end,
+ [2] = function(f)
+ local a, b = f:read(1,1)
+ if b then
+ return byte(a), byte(b)
+ end
+ end,
+ [1] = function (f)
+ local a = f:read(1)
+ if a then
+ return byte(a)
+ end
+ end,
+ [-2] = function (f)
+ local a, b = f:read(1,1)
+ if b then
+ return byte(b), byte(a)
+ end
+ end,
+ [-3] = function(f)
+ local a, b, c = f:read(1,1,1)
+ if b then
+ return byte(c), byte(b), byte(a)
+ end
+ end,
+ [-4] = function(f)
+ local a, b, c, d = f:read(1,1,1,1)
+ if d then
+ return byte(d), byte(c), byte(b), byte(a)
+ end
+ end
+}
+
+function io.bytes(f,n)
+ if f then
+ return nextbyte[n or 1], f
+ else
+ return nil, nil
+ end
+end
+
+function io.ask(question,default,options)
+ while true do
+ io.write(question)
+ if options then
+ io.write(format(" [%s]",concat(options,"|")))
+ end
+ if default then
+ io.write(format(" [%s]",default))
+ end
+ io.write(format(" "))
+ io.flush()
+ local answer = io.read()
+ answer = gsub(answer,"^%s*(.*)%s*$","%1")
+ if answer == "" and default then
+ return default
+ elseif not options then
+ return answer
+ else
+ for k=1,#options do
+ if options[k] == answer then
+ return answer
+ end
+ end
+ local pattern = "^" .. answer
+ for k=1,#options do
+ local v = options[k]
+ if find(v,pattern) then
+ return v
+ end
+ end
+ end
+ end
+end
+
+local function readnumber(f,n,m)
+ if m then
+ f:seek("set",n)
+ n = m
+ end
+ if n == 1 then
+ return byte(f:read(1))
+ elseif n == 2 then
+ local a, b = byte(f:read(2),1,2)
+ return 256 * a + b
+ elseif n == 3 then
+ local a, b, c = byte(f:read(3),1,3)
+ return 256*256 * a + 256 * b + c
+ elseif n == 4 then
+ local a, b, c, d = byte(f:read(4),1,4)
+ return 256*256*256 * a + 256*256 * b + 256 * c + d
+ elseif n == 8 then
+ local a, b = readnumber(f,4), readnumber(f,4)
+ return 256 * a + b
+ elseif n == 12 then
+ local a, b, c = readnumber(f,4), readnumber(f,4), readnumber(f,4)
+ return 256*256 * a + 256 * b + c
+ elseif n == -2 then
+ local b, a = byte(f:read(2),1,2)
+ return 256*a + b
+ elseif n == -3 then
+ local c, b, a = byte(f:read(3),1,3)
+ return 256*256 * a + 256 * b + c
+ elseif n == -4 then
+ local d, c, b, a = byte(f:read(4),1,4)
+ return 256*256*256 * a + 256*256 * b + 256*c + d
+ elseif n == -8 then
+ local h, g, f, e, d, c, b, a = byte(f:read(8),1,8)
+ return 256*256*256*256*256*256*256 * a +
+ 256*256*256*256*256*256 * b +
+ 256*256*256*256*256 * c +
+ 256*256*256*256 * d +
+ 256*256*256 * e +
+ 256*256 * f +
+ 256 * g +
+ h
+ else
+ return 0
+ end
+end
+
+io.readnumber = readnumber
+
+function io.readstring(f,n,m)
+ if m then
+ f:seek("set",n)
+ n = m
+ end
+ local str = gsub(f:read(n),"\000","")
+ return str
+end
+
+--
+
+if not io.i_limiter then function io.i_limiter() end end -- dummy so we can test safely
+if not io.o_limiter then function io.o_limiter() end end -- dummy so we can test safely
+
+-- This works quite ok:
+--
+-- function io.piped(command,writer)
+-- local pipe = io.popen(command)
+-- -- for line in pipe:lines() do
+-- -- print(line)
+-- -- end
+-- while true do
+-- local line = pipe:read(1)
+-- if not line then
+-- break
+-- elseif line ~= "\n" then
+-- writer(line)
+-- end
+-- end
+-- return pipe:close() -- ok, status, (error)code
+-- end
diff --git a/tex/context/base/l-lpeg.lua b/tex/context/base/l-lpeg.lua
index 07926da86..323c73b69 100644
--- a/tex/context/base/l-lpeg.lua
+++ b/tex/context/base/l-lpeg.lua
@@ -1,852 +1,852 @@
-if not modules then modules = { } end modules ['l-lpeg'] = {
- 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"
-}
-
--- a new lpeg fails on a #(1-P(":")) test and really needs a + P(-1)
-
--- move utf -> l-unicode
--- move string -> l-string or keep it here
-
-lpeg = require("lpeg")
-
--- tracing (only used when we encounter a problem in integration of lpeg in luatex)
-
--- some code will move to unicode and string
-
--- local lpmatch = lpeg.match
--- local lpprint = lpeg.print
--- local lpp = lpeg.P
--- local lpr = lpeg.R
--- local lps = lpeg.S
--- local lpc = lpeg.C
--- local lpb = lpeg.B
--- local lpv = lpeg.V
--- local lpcf = lpeg.Cf
--- local lpcb = lpeg.Cb
--- local lpcg = lpeg.Cg
--- local lpct = lpeg.Ct
--- local lpcs = lpeg.Cs
--- local lpcc = lpeg.Cc
--- local lpcmt = lpeg.Cmt
--- local lpcarg = lpeg.Carg
-
--- function lpeg.match(l,...) print("LPEG MATCH") lpprint(l) return lpmatch(l,...) end
-
--- function lpeg.P (l) local p = lpp (l) print("LPEG P =") lpprint(l) return p end
--- function lpeg.R (l) local p = lpr (l) print("LPEG R =") lpprint(l) return p end
--- function lpeg.S (l) local p = lps (l) print("LPEG S =") lpprint(l) return p end
--- function lpeg.C (l) local p = lpc (l) print("LPEG C =") lpprint(l) return p end
--- function lpeg.B (l) local p = lpb (l) print("LPEG B =") lpprint(l) return p end
--- function lpeg.V (l) local p = lpv (l) print("LPEG V =") lpprint(l) return p end
--- function lpeg.Cf (l) local p = lpcf (l) print("LPEG Cf =") lpprint(l) return p end
--- function lpeg.Cb (l) local p = lpcb (l) print("LPEG Cb =") lpprint(l) return p end
--- function lpeg.Cg (l) local p = lpcg (l) print("LPEG Cg =") lpprint(l) return p end
--- function lpeg.Ct (l) local p = lpct (l) print("LPEG Ct =") lpprint(l) return p end
--- function lpeg.Cs (l) local p = lpcs (l) print("LPEG Cs =") lpprint(l) return p end
--- function lpeg.Cc (l) local p = lpcc (l) print("LPEG Cc =") lpprint(l) return p end
--- function lpeg.Cmt (l) local p = lpcmt (l) print("LPEG Cmt =") lpprint(l) return p end
--- function lpeg.Carg (l) local p = lpcarg(l) print("LPEG Carg =") lpprint(l) return p end
-
-local type, next, tostring = type, next, tostring
-local byte, char, gmatch, format = string.byte, string.char, string.gmatch, string.format
------ mod, div = math.mod, math.div
-local floor = math.floor
-
-local P, R, S, V, Ct, C, Cs, Cc, Cp, Cmt = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.Ct, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.Cp, lpeg.Cmt
-local lpegtype, lpegmatch, lpegprint = lpeg.type, lpeg.match, lpeg.print
-
--- let's start with an inspector:
-
-setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end)
-
--- Beware, we predefine a bunch of patterns here and one reason for doing so
--- is that we get consistent behaviour in some of the visualizers.
-
-lpeg.patterns = lpeg.patterns or { } -- so that we can share
-local patterns = lpeg.patterns
-
-
-local anything = P(1)
-local endofstring = P(-1)
-local alwaysmatched = P(true)
-
-patterns.anything = anything
-patterns.endofstring = endofstring
-patterns.beginofstring = alwaysmatched
-patterns.alwaysmatched = alwaysmatched
-
-local digit, sign = R('09'), S('+-')
-local cr, lf, crlf = P("\r"), P("\n"), P("\r\n")
-local newline = crlf + S("\r\n") -- cr + lf
-local escaped = P("\\") * anything
-local squote = P("'")
-local dquote = P('"')
-local space = P(" ")
-
-local utfbom_32_be = P('\000\000\254\255')
-local utfbom_32_le = P('\255\254\000\000')
-local utfbom_16_be = P('\255\254')
-local utfbom_16_le = P('\254\255')
-local utfbom_8 = P('\239\187\191')
-local utfbom = utfbom_32_be + utfbom_32_le
- + utfbom_16_be + utfbom_16_le
- + utfbom_8
-local utftype = utfbom_32_be * Cc("utf-32-be") + utfbom_32_le * Cc("utf-32-le")
- + utfbom_16_be * Cc("utf-16-be") + utfbom_16_le * Cc("utf-16-le")
- + utfbom_8 * Cc("utf-8") + alwaysmatched * Cc("utf-8") -- assume utf8
-local utfoffset = utfbom_32_be * Cc(4) + utfbom_32_le * Cc(4)
- + utfbom_16_be * Cc(2) + utfbom_16_le * Cc(2)
- + utfbom_8 * Cc(3) + Cc(0)
-
-local utf8next = R("\128\191")
-
-patterns.utf8one = R("\000\127")
-patterns.utf8two = R("\194\223") * utf8next
-patterns.utf8three = R("\224\239") * utf8next * utf8next
-patterns.utf8four = R("\240\244") * utf8next * utf8next * utf8next
-patterns.utfbom = utfbom
-patterns.utftype = utftype
-patterns.utfoffset = utfoffset
-
-local utf8char = patterns.utf8one + patterns.utf8two + patterns.utf8three + patterns.utf8four
-local validutf8char = utf8char^0 * endofstring * Cc(true) + Cc(false)
-
-local utf8character = P(1) * R("\128\191")^0 -- unchecked but fast
-
-patterns.utf8 = utf8char
-patterns.utf8char = utf8char
-patterns.utf8character = utf8character -- this one can be used in most cases so we might use that one
-patterns.validutf8 = validutf8char
-patterns.validutf8char = validutf8char
-
-local eol = S("\n\r")
-local spacer = S(" \t\f\v") -- + char(0xc2, 0xa0) if we want utf (cf mail roberto)
-local whitespace = eol + spacer
-local nonspacer = 1 - spacer
-local nonwhitespace = 1 - whitespace
-
-patterns.eol = eol
-patterns.spacer = spacer
-patterns.whitespace = whitespace
-patterns.nonspacer = nonspacer
-patterns.nonwhitespace = nonwhitespace
-
-local stripper = spacer^0 * C((spacer^0 * nonspacer^1)^0) -- from example by roberto
-
------ collapser = Cs(spacer^0/"" * ((spacer^1 * P(-1) / "") + (spacer^1/" ") + P(1))^0)
-local collapser = Cs(spacer^0/"" * nonspacer^0 * ((spacer^0/" " * nonspacer^1)^0))
-
-patterns.stripper = stripper
-patterns.collapser = collapser
-
-patterns.digit = digit
-patterns.sign = sign
-patterns.cardinal = sign^0 * digit^1
-patterns.integer = sign^0 * digit^1
-patterns.unsigned = digit^0 * P('.') * digit^1
-patterns.float = sign^0 * patterns.unsigned
-patterns.cunsigned = digit^0 * P(',') * digit^1
-patterns.cfloat = sign^0 * patterns.cunsigned
-patterns.number = patterns.float + patterns.integer
-patterns.cnumber = patterns.cfloat + patterns.integer
-patterns.oct = P("0") * R("07")^1
-patterns.octal = patterns.oct
-patterns.HEX = P("0x") * R("09","AF")^1
-patterns.hex = P("0x") * R("09","af")^1
-patterns.hexadecimal = P("0x") * R("09","AF","af")^1
-patterns.lowercase = R("az")
-patterns.uppercase = R("AZ")
-patterns.letter = patterns.lowercase + patterns.uppercase
-patterns.space = space
-patterns.tab = P("\t")
-patterns.spaceortab = patterns.space + patterns.tab
-patterns.newline = newline
-patterns.emptyline = newline^1
-patterns.equal = P("=")
-patterns.comma = P(",")
-patterns.commaspacer = P(",") * spacer^0
-patterns.period = P(".")
-patterns.colon = P(":")
-patterns.semicolon = P(";")
-patterns.underscore = P("_")
-patterns.escaped = escaped
-patterns.squote = squote
-patterns.dquote = dquote
-patterns.nosquote = (escaped + (1-squote))^0
-patterns.nodquote = (escaped + (1-dquote))^0
-patterns.unsingle = (squote/"") * patterns.nosquote * (squote/"") -- will change to C in the middle
-patterns.undouble = (dquote/"") * patterns.nodquote * (dquote/"") -- will change to C in the middle
-patterns.unquoted = patterns.undouble + patterns.unsingle -- more often undouble
-patterns.unspacer = ((patterns.spacer^1)/"")^0
-
-patterns.singlequoted = squote * patterns.nosquote * squote
-patterns.doublequoted = dquote * patterns.nodquote * dquote
-patterns.quoted = patterns.doublequoted + patterns.singlequoted
-
-patterns.propername = R("AZ","az","__") * R("09","AZ","az", "__")^0 * P(-1)
-
-patterns.somecontent = (anything - newline - space)^1 -- (utf8char - newline - space)^1
-patterns.beginline = #(1-newline)
-
-patterns.longtostring = Cs(whitespace^0/"" * nonwhitespace^0 * ((whitespace^0/" " * (patterns.quoted + nonwhitespace)^1)^0))
-
-local function anywhere(pattern) --slightly adapted from website
- return P { P(pattern) + 1 * V(1) }
-end
-
-lpeg.anywhere = anywhere
-
-function lpeg.instringchecker(p)
- p = anywhere(p)
- return function(str)
- return lpegmatch(p,str) and true or false
- end
-end
-
-function lpeg.splitter(pattern, action)
- return (((1-P(pattern))^1)/action+1)^0
-end
-
-function lpeg.tsplitter(pattern, action)
- return Ct((((1-P(pattern))^1)/action+1)^0)
-end
-
--- probleem: separator can be lpeg and that does not hash too well, but
--- it's quite okay as the key is then not garbage collected
-
-local splitters_s, splitters_m, splitters_t = { }, { }, { }
-
-local function splitat(separator,single)
- local splitter = (single and splitters_s[separator]) or splitters_m[separator]
- if not splitter then
- separator = P(separator)
- local other = C((1 - separator)^0)
- if single then
- local any = anything
- splitter = other * (separator * C(any^0) + "") -- ?
- splitters_s[separator] = splitter
- else
- splitter = other * (separator * other)^0
- splitters_m[separator] = splitter
- end
- end
- return splitter
-end
-
-local function tsplitat(separator)
- local splitter = splitters_t[separator]
- if not splitter then
- splitter = Ct(splitat(separator))
- splitters_t[separator] = splitter
- end
- return splitter
-end
-
-lpeg.splitat = splitat
-lpeg.tsplitat = tsplitat
-
-function string.splitup(str,separator)
- if not separator then
- separator = ","
- end
- return lpegmatch(splitters_m[separator] or splitat(separator),str)
-end
-
--- local p = splitat("->",false) print(lpegmatch(p,"oeps->what->more")) -- oeps what more
--- local p = splitat("->",true) print(lpegmatch(p,"oeps->what->more")) -- oeps what->more
--- local p = splitat("->",false) print(lpegmatch(p,"oeps")) -- oeps
--- local p = splitat("->",true) print(lpegmatch(p,"oeps")) -- oeps
-
-local cache = { }
-
-function lpeg.split(separator,str)
- local c = cache[separator]
- if not c then
- c = tsplitat(separator)
- cache[separator] = c
- end
- return lpegmatch(c,str)
-end
-
-function string.split(str,separator)
- if separator then
- local c = cache[separator]
- if not c then
- c = tsplitat(separator)
- cache[separator] = c
- end
- return lpegmatch(c,str)
- else
- return { str }
- end
-end
-
-local spacing = patterns.spacer^0 * newline -- sort of strip
-local empty = spacing * Cc("")
-local nonempty = Cs((1-spacing)^1) * spacing^-1
-local content = (empty + nonempty)^1
-
-patterns.textline = content
-
-local linesplitter = tsplitat(newline)
-
-patterns.linesplitter = linesplitter
-
-function string.splitlines(str)
- return lpegmatch(linesplitter,str)
-end
-
--- lpeg.splitters = cache -- no longer public
-
-local cache = { }
-
-function lpeg.checkedsplit(separator,str)
- local c = cache[separator]
- if not c then
- separator = P(separator)
- local other = C((1 - separator)^1)
- c = Ct(separator^0 * other * (separator^1 * other)^0)
- cache[separator] = c
- end
- return lpegmatch(c,str)
-end
-
-function string.checkedsplit(str,separator)
- local c = cache[separator]
- if not c then
- separator = P(separator)
- local other = C((1 - separator)^1)
- c = Ct(separator^0 * other * (separator^1 * other)^0)
- cache[separator] = c
- end
- return lpegmatch(c,str)
-end
-
--- from roberto's site:
-
-local function f2(s) local c1, c2 = byte(s,1,2) return c1 * 64 + c2 - 12416 end
-local function f3(s) local c1, c2, c3 = byte(s,1,3) return (c1 * 64 + c2) * 64 + c3 - 925824 end
-local function f4(s) local c1, c2, c3, c4 = byte(s,1,4) return ((c1 * 64 + c2) * 64 + c3) * 64 + c4 - 63447168 end
-
-local utf8byte = patterns.utf8one/byte + patterns.utf8two/f2 + patterns.utf8three/f3 + patterns.utf8four/f4
-
-patterns.utf8byte = utf8byte
-
---~ local str = " a b c d "
-
---~ local s = lpeg.stripper(lpeg.R("az")) print("["..lpegmatch(s,str).."]")
---~ local s = lpeg.keeper(lpeg.R("az")) print("["..lpegmatch(s,str).."]")
---~ local s = lpeg.stripper("ab") print("["..lpegmatch(s,str).."]")
---~ local s = lpeg.keeper("ab") print("["..lpegmatch(s,str).."]")
-
-local cache = { }
-
-function lpeg.stripper(str)
- if type(str) == "string" then
- local s = cache[str]
- if not s then
- s = Cs(((S(str)^1)/"" + 1)^0)
- cache[str] = s
- end
- return s
- else
- return Cs(((str^1)/"" + 1)^0)
- end
-end
-
-local cache = { }
-
-function lpeg.keeper(str)
- if type(str) == "string" then
- local s = cache[str]
- if not s then
- s = Cs((((1-S(str))^1)/"" + 1)^0)
- cache[str] = s
- end
- return s
- else
- return Cs((((1-str)^1)/"" + 1)^0)
- end
-end
-
-function lpeg.frontstripper(str) -- or pattern (yet undocumented)
- return (P(str) + P(true)) * Cs(anything^0)
-end
-
-function lpeg.endstripper(str) -- or pattern (yet undocumented)
- return Cs((1 - P(str) * endofstring)^0)
-end
-
--- Just for fun I looked at the used bytecode and
--- p = (p and p + pp) or pp gets one more (testset).
-
--- todo: cache when string
-
-function lpeg.replacer(one,two,makefunction,isutf) -- in principle we should sort the keys
- local pattern
- local u = isutf and utf8char or 1
- if type(one) == "table" then
- local no = #one
- local p = P(false)
- if no == 0 then
- for k, v in next, one do
- p = p + P(k) / v
- end
- pattern = Cs((p + u)^0)
- elseif no == 1 then
- local o = one[1]
- one, two = P(o[1]), o[2]
- -- pattern = Cs(((1-one)^1 + one/two)^0)
- pattern = Cs((one/two + u)^0)
- else
- for i=1,no do
- local o = one[i]
- p = p + P(o[1]) / o[2]
- end
- pattern = Cs((p + u)^0)
- end
- else
- pattern = Cs((P(one)/(two or "") + u)^0)
- end
- if makefunction then
- return function(str)
- return lpegmatch(pattern,str)
- end
- else
- return pattern
- end
-end
-
-function lpeg.finder(lst,makefunction)
- local pattern
- if type(lst) == "table" then
- pattern = P(false)
- if #lst == 0 then
- for k, v in next, lst do
- pattern = pattern + P(k) -- ignore key, so we can use a replacer table
- end
- else
- for i=1,#lst do
- pattern = pattern + P(lst[i])
- end
- end
- else
- pattern = P(lst)
- end
- pattern = (1-pattern)^0 * pattern
- if makefunction then
- return function(str)
- return lpegmatch(pattern,str)
- end
- else
- return pattern
- end
-end
-
--- print(lpeg.match(lpeg.replacer("e","a"),"test test"))
--- print(lpeg.match(lpeg.replacer{{"e","a"}},"test test"))
--- print(lpeg.match(lpeg.replacer({ e = "a", t = "x" }),"test test"))
-
-local splitters_f, splitters_s = { }, { }
-
-function lpeg.firstofsplit(separator) -- always return value
- local splitter = splitters_f[separator]
- if not splitter then
- separator = P(separator)
- splitter = C((1 - separator)^0)
- splitters_f[separator] = splitter
- end
- return splitter
-end
-
-function lpeg.secondofsplit(separator) -- nil if not split
- local splitter = splitters_s[separator]
- if not splitter then
- separator = P(separator)
- splitter = (1 - separator)^0 * separator * C(anything^0)
- splitters_s[separator] = splitter
- end
- return splitter
-end
-
-function lpeg.balancer(left,right)
- left, right = P(left), P(right)
- return P { left * ((1 - left - right) + V(1))^0 * right }
-end
-
--- print(1,lpegmatch(lpeg.firstofsplit(":"),"bc:de"))
--- print(2,lpegmatch(lpeg.firstofsplit(":"),":de")) -- empty
--- print(3,lpegmatch(lpeg.firstofsplit(":"),"bc"))
--- print(4,lpegmatch(lpeg.secondofsplit(":"),"bc:de"))
--- print(5,lpegmatch(lpeg.secondofsplit(":"),"bc:")) -- empty
--- print(6,lpegmatch(lpeg.secondofsplit(":",""),"bc"))
--- print(7,lpegmatch(lpeg.secondofsplit(":"),"bc"))
--- print(9,lpegmatch(lpeg.secondofsplit(":","123"),"bc"))
-
--- -- slower:
---
--- function lpeg.counter(pattern)
--- local n, pattern = 0, (lpeg.P(pattern)/function() n = n + 1 end + lpeg.anything)^0
--- return function(str) n = 0 ; lpegmatch(pattern,str) ; return n end
--- end
-
-local nany = utf8char/""
-
-function lpeg.counter(pattern)
- pattern = Cs((P(pattern)/" " + nany)^0)
- return function(str)
- return #lpegmatch(pattern,str)
- end
-end
-
--- utf extensies
-
-utf = utf or (unicode and unicode.utf8) or { }
-
-local utfcharacters = utf and utf.characters or string.utfcharacters
-local utfgmatch = utf and utf.gmatch
-local utfchar = utf and utf.char
-
-lpeg.UP = lpeg.P
-
-if utfcharacters then
-
- function lpeg.US(str)
- local p = P(false)
- for uc in utfcharacters(str) do
- p = p + P(uc)
- end
- return p
- end
-
-
-elseif utfgmatch then
-
- function lpeg.US(str)
- local p = P(false)
- for uc in utfgmatch(str,".") do
- p = p + P(uc)
- end
- return p
- end
-
-else
-
- function lpeg.US(str)
- local p = P(false)
- local f = function(uc)
- p = p + P(uc)
- end
- lpegmatch((utf8char/f)^0,str)
- return p
- end
-
-end
-
-local range = utf8byte * utf8byte + Cc(false) -- utf8byte is already a capture
-
-function lpeg.UR(str,more)
- local first, last
- if type(str) == "number" then
- first = str
- last = more or first
- else
- first, last = lpegmatch(range,str)
- if not last then
- return P(str)
- end
- end
- if first == last then
- return P(str)
- elseif utfchar and (last - first < 8) then -- a somewhat arbitrary criterium
- local p = P(false)
- for i=first,last do
- p = p + P(utfchar(i))
- end
- return p -- nil when invalid range
- else
- local f = function(b)
- return b >= first and b <= last
- end
- -- tricky, these nested captures
- return utf8byte / f -- nil when invalid range
- end
-end
-
--- print(lpeg.match(lpeg.Cs((C(lpeg.UR("αω"))/{ ["χ"] = "OEPS" })^0),"αωχαω"))
-
--- lpeg.print(lpeg.R("ab","cd","gh"))
--- lpeg.print(lpeg.P("a","b","c"))
--- lpeg.print(lpeg.S("a","b","c"))
-
--- print(lpeg.count("äáàa",lpeg.P("á") + lpeg.P("à")))
--- print(lpeg.count("äáàa",lpeg.UP("áà")))
--- print(lpeg.count("äáàa",lpeg.US("àá")))
--- print(lpeg.count("äáàa",lpeg.UR("aá")))
--- print(lpeg.count("äáàa",lpeg.UR("àá")))
--- print(lpeg.count("äáàa",lpeg.UR(0x0000,0xFFFF)))
-
-function lpeg.is_lpeg(p)
- return p and lpegtype(p) == "pattern"
-end
-
-function lpeg.oneof(list,...) -- lpeg.oneof("elseif","else","if","then") -- assume proper order
- if type(list) ~= "table" then
- list = { list, ... }
- end
- -- table.sort(list) -- longest match first
- local p = P(list[1])
- for l=2,#list do
- p = p + P(list[l])
- end
- return p
-end
-
--- For the moment here, but it might move to utilities. Beware, we need to
--- have the longest keyword first, so 'aaa' comes beforte 'aa' which is why we
--- loop back from the end cq. prepend.
-
-local sort = table.sort
-
-local function copyindexed(old)
- local new = { }
- for i=1,#old do
- new[i] = old
- end
- return new
-end
-
-local function sortedkeys(tab)
- local keys, s = { }, 0
- for key,_ in next, tab do
- s = s + 1
- keys[s] = key
- end
- sort(keys)
- return keys
-end
-
-function lpeg.append(list,pp,delayed,checked)
- local p = pp
- if #list > 0 then
- local keys = copyindexed(list)
- sort(keys)
- for i=#keys,1,-1 do
- local k = keys[i]
- if p then
- p = P(k) + p
- else
- p = P(k)
- end
- end
- elseif delayed then -- hm, it looks like the lpeg parser resolves anyway
- local keys = sortedkeys(list)
- if p then
- for i=1,#keys,1 do
- local k = keys[i]
- local v = list[k]
- p = P(k)/list + p
- end
- else
- for i=1,#keys do
- local k = keys[i]
- local v = list[k]
- if p then
- p = P(k) + p
- else
- p = P(k)
- end
- end
- if p then
- p = p / list
- end
- end
- elseif checked then
- -- problem: substitution gives a capture
- local keys = sortedkeys(list)
- for i=1,#keys do
- local k = keys[i]
- local v = list[k]
- if p then
- if k == v then
- p = P(k) + p
- else
- p = P(k)/v + p
- end
- else
- if k == v then
- p = P(k)
- else
- p = P(k)/v
- end
- end
- end
- else
- local keys = sortedkeys(list)
- for i=1,#keys do
- local k = keys[i]
- local v = list[k]
- if p then
- p = P(k)/v + p
- else
- p = P(k)/v
- end
- end
- end
- return p
-end
-
--- inspect(lpeg.append({ a = "1", aa = "1", aaa = "1" } ,nil,true))
--- inspect(lpeg.append({ ["degree celsius"] = "1", celsius = "1", degree = "1" } ,nil,true))
-
--- function lpeg.exact_match(words,case_insensitive)
--- local pattern = concat(words)
--- if case_insensitive then
--- local pattern = S(upper(characters)) + S(lower(characters))
--- local list = { }
--- for i=1,#words do
--- list[lower(words[i])] = true
--- end
--- return Cmt(pattern^1, function(_,i,s)
--- return list[lower(s)] and i
--- end)
--- else
--- local pattern = S(concat(words))
--- local list = { }
--- for i=1,#words do
--- list[words[i]] = true
--- end
--- return Cmt(pattern^1, function(_,i,s)
--- return list[s] and i
--- end)
--- end
--- end
-
--- experiment:
-
-local function make(t)
- local p
- local keys = sortedkeys(t)
- for i=1,#keys do
- local k = keys[i]
- local v = t[k]
- if not p then
- if next(v) then
- p = P(k) * make(v)
- else
- p = P(k)
- end
- else
- if next(v) then
- p = p + P(k) * make(v)
- else
- p = p + P(k)
- end
- end
- end
- return p
-end
-
-function lpeg.utfchartabletopattern(list) -- goes to util-lpg
- local tree = { }
- for i=1,#list do
- local t = tree
- for c in gmatch(list[i],".") do
- if not t[c] then
- t[c] = { }
- end
- t = t[c]
- end
- end
- return make(tree)
-end
-
--- inspect ( lpeg.utfchartabletopattern {
--- utfchar(0x00A0), -- nbsp
--- utfchar(0x2000), -- enquad
--- utfchar(0x2001), -- emquad
--- utfchar(0x2002), -- enspace
--- utfchar(0x2003), -- emspace
--- utfchar(0x2004), -- threeperemspace
--- utfchar(0x2005), -- fourperemspace
--- utfchar(0x2006), -- sixperemspace
--- utfchar(0x2007), -- figurespace
--- utfchar(0x2008), -- punctuationspace
--- utfchar(0x2009), -- breakablethinspace
--- utfchar(0x200A), -- hairspace
--- utfchar(0x200B), -- zerowidthspace
--- utfchar(0x202F), -- narrownobreakspace
--- utfchar(0x205F), -- math thinspace
--- } )
-
--- a few handy ones:
---
--- faster than find(str,"[\n\r]") when match and # > 7 and always faster when # > 3
-
-patterns.containseol = lpeg.finder(eol) -- (1-eol)^0 * eol
-
--- The next pattern^n variant is based on an approach suggested
--- by Roberto: constructing a big repetition in chunks.
---
--- Being sparse is not needed, and only complicate matters and
--- the number of redundant entries is not that large.
-
-local function nextstep(n,step,result)
- local m = n % step -- mod(n,step)
- local d = floor(n/step) -- div(n,step)
- if d > 0 then
- local v = V(tostring(step))
- local s = result.start
- for i=1,d do
- if s then
- s = v * s
- else
- s = v
- end
- end
- result.start = s
- end
- if step > 1 and result.start then
- local v = V(tostring(step/2))
- result[tostring(step)] = v * v
- end
- if step > 0 then
- return nextstep(m,step/2,result)
- else
- return result
- end
-end
-
-function lpeg.times(pattern,n)
- return P(nextstep(n,2^16,{ "start", ["1"] = pattern }))
-end
-
--- local p = lpeg.Cs((1 - lpeg.times(lpeg.P("AB"),25))^1)
--- local s = "12" .. string.rep("AB",20) .. "34" .. string.rep("AB",30) .. "56"
--- inspect(p)
--- print(lpeg.match(p,s))
-
--- moved here (before util-str)
-
-local digit = R("09")
-local period = P(".")
-local zero = P("0")
-local trailingzeros = zero^0 * -digit -- suggested by Roberto R
-local case_1 = period * trailingzeros / ""
-local case_2 = period * (digit - trailingzeros)^1 * (trailingzeros / "")
-local number = digit^1 * (case_1 + case_2)
-local stripper = Cs((number + 1)^0)
-
-lpeg.patterns.stripzeros = stripper
-
--- local sample = "bla 11.00 bla 11 bla 0.1100 bla 1.00100 bla 0.00 bla 0.001 bla 1.1100 bla 0.100100100 bla 0.00100100100"
--- collectgarbage("collect")
--- str = string.rep(sample,10000)
--- local ts = os.clock()
--- lpegmatch(stripper,str)
--- print(#str, os.clock()-ts, lpegmatch(stripper,sample))
-
+if not modules then modules = { } end modules ['l-lpeg'] = {
+ 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"
+}
+
+-- a new lpeg fails on a #(1-P(":")) test and really needs a + P(-1)
+
+-- move utf -> l-unicode
+-- move string -> l-string or keep it here
+
+lpeg = require("lpeg")
+
+-- tracing (only used when we encounter a problem in integration of lpeg in luatex)
+
+-- some code will move to unicode and string
+
+-- local lpmatch = lpeg.match
+-- local lpprint = lpeg.print
+-- local lpp = lpeg.P
+-- local lpr = lpeg.R
+-- local lps = lpeg.S
+-- local lpc = lpeg.C
+-- local lpb = lpeg.B
+-- local lpv = lpeg.V
+-- local lpcf = lpeg.Cf
+-- local lpcb = lpeg.Cb
+-- local lpcg = lpeg.Cg
+-- local lpct = lpeg.Ct
+-- local lpcs = lpeg.Cs
+-- local lpcc = lpeg.Cc
+-- local lpcmt = lpeg.Cmt
+-- local lpcarg = lpeg.Carg
+
+-- function lpeg.match(l,...) print("LPEG MATCH") lpprint(l) return lpmatch(l,...) end
+
+-- function lpeg.P (l) local p = lpp (l) print("LPEG P =") lpprint(l) return p end
+-- function lpeg.R (l) local p = lpr (l) print("LPEG R =") lpprint(l) return p end
+-- function lpeg.S (l) local p = lps (l) print("LPEG S =") lpprint(l) return p end
+-- function lpeg.C (l) local p = lpc (l) print("LPEG C =") lpprint(l) return p end
+-- function lpeg.B (l) local p = lpb (l) print("LPEG B =") lpprint(l) return p end
+-- function lpeg.V (l) local p = lpv (l) print("LPEG V =") lpprint(l) return p end
+-- function lpeg.Cf (l) local p = lpcf (l) print("LPEG Cf =") lpprint(l) return p end
+-- function lpeg.Cb (l) local p = lpcb (l) print("LPEG Cb =") lpprint(l) return p end
+-- function lpeg.Cg (l) local p = lpcg (l) print("LPEG Cg =") lpprint(l) return p end
+-- function lpeg.Ct (l) local p = lpct (l) print("LPEG Ct =") lpprint(l) return p end
+-- function lpeg.Cs (l) local p = lpcs (l) print("LPEG Cs =") lpprint(l) return p end
+-- function lpeg.Cc (l) local p = lpcc (l) print("LPEG Cc =") lpprint(l) return p end
+-- function lpeg.Cmt (l) local p = lpcmt (l) print("LPEG Cmt =") lpprint(l) return p end
+-- function lpeg.Carg (l) local p = lpcarg(l) print("LPEG Carg =") lpprint(l) return p end
+
+local type, next, tostring = type, next, tostring
+local byte, char, gmatch, format = string.byte, string.char, string.gmatch, string.format
+----- mod, div = math.mod, math.div
+local floor = math.floor
+
+local P, R, S, V, Ct, C, Cs, Cc, Cp, Cmt = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.Ct, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.Cp, lpeg.Cmt
+local lpegtype, lpegmatch, lpegprint = lpeg.type, lpeg.match, lpeg.print
+
+-- let's start with an inspector:
+
+setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end)
+
+-- Beware, we predefine a bunch of patterns here and one reason for doing so
+-- is that we get consistent behaviour in some of the visualizers.
+
+lpeg.patterns = lpeg.patterns or { } -- so that we can share
+local patterns = lpeg.patterns
+
+
+local anything = P(1)
+local endofstring = P(-1)
+local alwaysmatched = P(true)
+
+patterns.anything = anything
+patterns.endofstring = endofstring
+patterns.beginofstring = alwaysmatched
+patterns.alwaysmatched = alwaysmatched
+
+local digit, sign = R('09'), S('+-')
+local cr, lf, crlf = P("\r"), P("\n"), P("\r\n")
+local newline = crlf + S("\r\n") -- cr + lf
+local escaped = P("\\") * anything
+local squote = P("'")
+local dquote = P('"')
+local space = P(" ")
+
+local utfbom_32_be = P('\000\000\254\255')
+local utfbom_32_le = P('\255\254\000\000')
+local utfbom_16_be = P('\255\254')
+local utfbom_16_le = P('\254\255')
+local utfbom_8 = P('\239\187\191')
+local utfbom = utfbom_32_be + utfbom_32_le
+ + utfbom_16_be + utfbom_16_le
+ + utfbom_8
+local utftype = utfbom_32_be * Cc("utf-32-be") + utfbom_32_le * Cc("utf-32-le")
+ + utfbom_16_be * Cc("utf-16-be") + utfbom_16_le * Cc("utf-16-le")
+ + utfbom_8 * Cc("utf-8") + alwaysmatched * Cc("utf-8") -- assume utf8
+local utfoffset = utfbom_32_be * Cc(4) + utfbom_32_le * Cc(4)
+ + utfbom_16_be * Cc(2) + utfbom_16_le * Cc(2)
+ + utfbom_8 * Cc(3) + Cc(0)
+
+local utf8next = R("\128\191")
+
+patterns.utf8one = R("\000\127")
+patterns.utf8two = R("\194\223") * utf8next
+patterns.utf8three = R("\224\239") * utf8next * utf8next
+patterns.utf8four = R("\240\244") * utf8next * utf8next * utf8next
+patterns.utfbom = utfbom
+patterns.utftype = utftype
+patterns.utfoffset = utfoffset
+
+local utf8char = patterns.utf8one + patterns.utf8two + patterns.utf8three + patterns.utf8four
+local validutf8char = utf8char^0 * endofstring * Cc(true) + Cc(false)
+
+local utf8character = P(1) * R("\128\191")^0 -- unchecked but fast
+
+patterns.utf8 = utf8char
+patterns.utf8char = utf8char
+patterns.utf8character = utf8character -- this one can be used in most cases so we might use that one
+patterns.validutf8 = validutf8char
+patterns.validutf8char = validutf8char
+
+local eol = S("\n\r")
+local spacer = S(" \t\f\v") -- + char(0xc2, 0xa0) if we want utf (cf mail roberto)
+local whitespace = eol + spacer
+local nonspacer = 1 - spacer
+local nonwhitespace = 1 - whitespace
+
+patterns.eol = eol
+patterns.spacer = spacer
+patterns.whitespace = whitespace
+patterns.nonspacer = nonspacer
+patterns.nonwhitespace = nonwhitespace
+
+local stripper = spacer^0 * C((spacer^0 * nonspacer^1)^0) -- from example by roberto
+
+----- collapser = Cs(spacer^0/"" * ((spacer^1 * P(-1) / "") + (spacer^1/" ") + P(1))^0)
+local collapser = Cs(spacer^0/"" * nonspacer^0 * ((spacer^0/" " * nonspacer^1)^0))
+
+patterns.stripper = stripper
+patterns.collapser = collapser
+
+patterns.digit = digit
+patterns.sign = sign
+patterns.cardinal = sign^0 * digit^1
+patterns.integer = sign^0 * digit^1
+patterns.unsigned = digit^0 * P('.') * digit^1
+patterns.float = sign^0 * patterns.unsigned
+patterns.cunsigned = digit^0 * P(',') * digit^1
+patterns.cfloat = sign^0 * patterns.cunsigned
+patterns.number = patterns.float + patterns.integer
+patterns.cnumber = patterns.cfloat + patterns.integer
+patterns.oct = P("0") * R("07")^1
+patterns.octal = patterns.oct
+patterns.HEX = P("0x") * R("09","AF")^1
+patterns.hex = P("0x") * R("09","af")^1
+patterns.hexadecimal = P("0x") * R("09","AF","af")^1
+patterns.lowercase = R("az")
+patterns.uppercase = R("AZ")
+patterns.letter = patterns.lowercase + patterns.uppercase
+patterns.space = space
+patterns.tab = P("\t")
+patterns.spaceortab = patterns.space + patterns.tab
+patterns.newline = newline
+patterns.emptyline = newline^1
+patterns.equal = P("=")
+patterns.comma = P(",")
+patterns.commaspacer = P(",") * spacer^0
+patterns.period = P(".")
+patterns.colon = P(":")
+patterns.semicolon = P(";")
+patterns.underscore = P("_")
+patterns.escaped = escaped
+patterns.squote = squote
+patterns.dquote = dquote
+patterns.nosquote = (escaped + (1-squote))^0
+patterns.nodquote = (escaped + (1-dquote))^0
+patterns.unsingle = (squote/"") * patterns.nosquote * (squote/"") -- will change to C in the middle
+patterns.undouble = (dquote/"") * patterns.nodquote * (dquote/"") -- will change to C in the middle
+patterns.unquoted = patterns.undouble + patterns.unsingle -- more often undouble
+patterns.unspacer = ((patterns.spacer^1)/"")^0
+
+patterns.singlequoted = squote * patterns.nosquote * squote
+patterns.doublequoted = dquote * patterns.nodquote * dquote
+patterns.quoted = patterns.doublequoted + patterns.singlequoted
+
+patterns.propername = R("AZ","az","__") * R("09","AZ","az", "__")^0 * P(-1)
+
+patterns.somecontent = (anything - newline - space)^1 -- (utf8char - newline - space)^1
+patterns.beginline = #(1-newline)
+
+patterns.longtostring = Cs(whitespace^0/"" * nonwhitespace^0 * ((whitespace^0/" " * (patterns.quoted + nonwhitespace)^1)^0))
+
+local function anywhere(pattern) --slightly adapted from website
+ return P { P(pattern) + 1 * V(1) }
+end
+
+lpeg.anywhere = anywhere
+
+function lpeg.instringchecker(p)
+ p = anywhere(p)
+ return function(str)
+ return lpegmatch(p,str) and true or false
+ end
+end
+
+function lpeg.splitter(pattern, action)
+ return (((1-P(pattern))^1)/action+1)^0
+end
+
+function lpeg.tsplitter(pattern, action)
+ return Ct((((1-P(pattern))^1)/action+1)^0)
+end
+
+-- probleem: separator can be lpeg and that does not hash too well, but
+-- it's quite okay as the key is then not garbage collected
+
+local splitters_s, splitters_m, splitters_t = { }, { }, { }
+
+local function splitat(separator,single)
+ local splitter = (single and splitters_s[separator]) or splitters_m[separator]
+ if not splitter then
+ separator = P(separator)
+ local other = C((1 - separator)^0)
+ if single then
+ local any = anything
+ splitter = other * (separator * C(any^0) + "") -- ?
+ splitters_s[separator] = splitter
+ else
+ splitter = other * (separator * other)^0
+ splitters_m[separator] = splitter
+ end
+ end
+ return splitter
+end
+
+local function tsplitat(separator)
+ local splitter = splitters_t[separator]
+ if not splitter then
+ splitter = Ct(splitat(separator))
+ splitters_t[separator] = splitter
+ end
+ return splitter
+end
+
+lpeg.splitat = splitat
+lpeg.tsplitat = tsplitat
+
+function string.splitup(str,separator)
+ if not separator then
+ separator = ","
+ end
+ return lpegmatch(splitters_m[separator] or splitat(separator),str)
+end
+
+-- local p = splitat("->",false) print(lpegmatch(p,"oeps->what->more")) -- oeps what more
+-- local p = splitat("->",true) print(lpegmatch(p,"oeps->what->more")) -- oeps what->more
+-- local p = splitat("->",false) print(lpegmatch(p,"oeps")) -- oeps
+-- local p = splitat("->",true) print(lpegmatch(p,"oeps")) -- oeps
+
+local cache = { }
+
+function lpeg.split(separator,str)
+ local c = cache[separator]
+ if not c then
+ c = tsplitat(separator)
+ cache[separator] = c
+ end
+ return lpegmatch(c,str)
+end
+
+function string.split(str,separator)
+ if separator then
+ local c = cache[separator]
+ if not c then
+ c = tsplitat(separator)
+ cache[separator] = c
+ end
+ return lpegmatch(c,str)
+ else
+ return { str }
+ end
+end
+
+local spacing = patterns.spacer^0 * newline -- sort of strip
+local empty = spacing * Cc("")
+local nonempty = Cs((1-spacing)^1) * spacing^-1
+local content = (empty + nonempty)^1
+
+patterns.textline = content
+
+local linesplitter = tsplitat(newline)
+
+patterns.linesplitter = linesplitter
+
+function string.splitlines(str)
+ return lpegmatch(linesplitter,str)
+end
+
+-- lpeg.splitters = cache -- no longer public
+
+local cache = { }
+
+function lpeg.checkedsplit(separator,str)
+ local c = cache[separator]
+ if not c then
+ separator = P(separator)
+ local other = C((1 - separator)^1)
+ c = Ct(separator^0 * other * (separator^1 * other)^0)
+ cache[separator] = c
+ end
+ return lpegmatch(c,str)
+end
+
+function string.checkedsplit(str,separator)
+ local c = cache[separator]
+ if not c then
+ separator = P(separator)
+ local other = C((1 - separator)^1)
+ c = Ct(separator^0 * other * (separator^1 * other)^0)
+ cache[separator] = c
+ end
+ return lpegmatch(c,str)
+end
+
+-- from roberto's site:
+
+local function f2(s) local c1, c2 = byte(s,1,2) return c1 * 64 + c2 - 12416 end
+local function f3(s) local c1, c2, c3 = byte(s,1,3) return (c1 * 64 + c2) * 64 + c3 - 925824 end
+local function f4(s) local c1, c2, c3, c4 = byte(s,1,4) return ((c1 * 64 + c2) * 64 + c3) * 64 + c4 - 63447168 end
+
+local utf8byte = patterns.utf8one/byte + patterns.utf8two/f2 + patterns.utf8three/f3 + patterns.utf8four/f4
+
+patterns.utf8byte = utf8byte
+
+--~ local str = " a b c d "
+
+--~ local s = lpeg.stripper(lpeg.R("az")) print("["..lpegmatch(s,str).."]")
+--~ local s = lpeg.keeper(lpeg.R("az")) print("["..lpegmatch(s,str).."]")
+--~ local s = lpeg.stripper("ab") print("["..lpegmatch(s,str).."]")
+--~ local s = lpeg.keeper("ab") print("["..lpegmatch(s,str).."]")
+
+local cache = { }
+
+function lpeg.stripper(str)
+ if type(str) == "string" then
+ local s = cache[str]
+ if not s then
+ s = Cs(((S(str)^1)/"" + 1)^0)
+ cache[str] = s
+ end
+ return s
+ else
+ return Cs(((str^1)/"" + 1)^0)
+ end
+end
+
+local cache = { }
+
+function lpeg.keeper(str)
+ if type(str) == "string" then
+ local s = cache[str]
+ if not s then
+ s = Cs((((1-S(str))^1)/"" + 1)^0)
+ cache[str] = s
+ end
+ return s
+ else
+ return Cs((((1-str)^1)/"" + 1)^0)
+ end
+end
+
+function lpeg.frontstripper(str) -- or pattern (yet undocumented)
+ return (P(str) + P(true)) * Cs(anything^0)
+end
+
+function lpeg.endstripper(str) -- or pattern (yet undocumented)
+ return Cs((1 - P(str) * endofstring)^0)
+end
+
+-- Just for fun I looked at the used bytecode and
+-- p = (p and p + pp) or pp gets one more (testset).
+
+-- todo: cache when string
+
+function lpeg.replacer(one,two,makefunction,isutf) -- in principle we should sort the keys
+ local pattern
+ local u = isutf and utf8char or 1
+ if type(one) == "table" then
+ local no = #one
+ local p = P(false)
+ if no == 0 then
+ for k, v in next, one do
+ p = p + P(k) / v
+ end
+ pattern = Cs((p + u)^0)
+ elseif no == 1 then
+ local o = one[1]
+ one, two = P(o[1]), o[2]
+ -- pattern = Cs(((1-one)^1 + one/two)^0)
+ pattern = Cs((one/two + u)^0)
+ else
+ for i=1,no do
+ local o = one[i]
+ p = p + P(o[1]) / o[2]
+ end
+ pattern = Cs((p + u)^0)
+ end
+ else
+ pattern = Cs((P(one)/(two or "") + u)^0)
+ end
+ if makefunction then
+ return function(str)
+ return lpegmatch(pattern,str)
+ end
+ else
+ return pattern
+ end
+end
+
+function lpeg.finder(lst,makefunction)
+ local pattern
+ if type(lst) == "table" then
+ pattern = P(false)
+ if #lst == 0 then
+ for k, v in next, lst do
+ pattern = pattern + P(k) -- ignore key, so we can use a replacer table
+ end
+ else
+ for i=1,#lst do
+ pattern = pattern + P(lst[i])
+ end
+ end
+ else
+ pattern = P(lst)
+ end
+ pattern = (1-pattern)^0 * pattern
+ if makefunction then
+ return function(str)
+ return lpegmatch(pattern,str)
+ end
+ else
+ return pattern
+ end
+end
+
+-- print(lpeg.match(lpeg.replacer("e","a"),"test test"))
+-- print(lpeg.match(lpeg.replacer{{"e","a"}},"test test"))
+-- print(lpeg.match(lpeg.replacer({ e = "a", t = "x" }),"test test"))
+
+local splitters_f, splitters_s = { }, { }
+
+function lpeg.firstofsplit(separator) -- always return value
+ local splitter = splitters_f[separator]
+ if not splitter then
+ separator = P(separator)
+ splitter = C((1 - separator)^0)
+ splitters_f[separator] = splitter
+ end
+ return splitter
+end
+
+function lpeg.secondofsplit(separator) -- nil if not split
+ local splitter = splitters_s[separator]
+ if not splitter then
+ separator = P(separator)
+ splitter = (1 - separator)^0 * separator * C(anything^0)
+ splitters_s[separator] = splitter
+ end
+ return splitter
+end
+
+function lpeg.balancer(left,right)
+ left, right = P(left), P(right)
+ return P { left * ((1 - left - right) + V(1))^0 * right }
+end
+
+-- print(1,lpegmatch(lpeg.firstofsplit(":"),"bc:de"))
+-- print(2,lpegmatch(lpeg.firstofsplit(":"),":de")) -- empty
+-- print(3,lpegmatch(lpeg.firstofsplit(":"),"bc"))
+-- print(4,lpegmatch(lpeg.secondofsplit(":"),"bc:de"))
+-- print(5,lpegmatch(lpeg.secondofsplit(":"),"bc:")) -- empty
+-- print(6,lpegmatch(lpeg.secondofsplit(":",""),"bc"))
+-- print(7,lpegmatch(lpeg.secondofsplit(":"),"bc"))
+-- print(9,lpegmatch(lpeg.secondofsplit(":","123"),"bc"))
+
+-- -- slower:
+--
+-- function lpeg.counter(pattern)
+-- local n, pattern = 0, (lpeg.P(pattern)/function() n = n + 1 end + lpeg.anything)^0
+-- return function(str) n = 0 ; lpegmatch(pattern,str) ; return n end
+-- end
+
+local nany = utf8char/""
+
+function lpeg.counter(pattern)
+ pattern = Cs((P(pattern)/" " + nany)^0)
+ return function(str)
+ return #lpegmatch(pattern,str)
+ end
+end
+
+-- utf extensies
+
+utf = utf or (unicode and unicode.utf8) or { }
+
+local utfcharacters = utf and utf.characters or string.utfcharacters
+local utfgmatch = utf and utf.gmatch
+local utfchar = utf and utf.char
+
+lpeg.UP = lpeg.P
+
+if utfcharacters then
+
+ function lpeg.US(str)
+ local p = P(false)
+ for uc in utfcharacters(str) do
+ p = p + P(uc)
+ end
+ return p
+ end
+
+
+elseif utfgmatch then
+
+ function lpeg.US(str)
+ local p = P(false)
+ for uc in utfgmatch(str,".") do
+ p = p + P(uc)
+ end
+ return p
+ end
+
+else
+
+ function lpeg.US(str)
+ local p = P(false)
+ local f = function(uc)
+ p = p + P(uc)
+ end
+ lpegmatch((utf8char/f)^0,str)
+ return p
+ end
+
+end
+
+local range = utf8byte * utf8byte + Cc(false) -- utf8byte is already a capture
+
+function lpeg.UR(str,more)
+ local first, last
+ if type(str) == "number" then
+ first = str
+ last = more or first
+ else
+ first, last = lpegmatch(range,str)
+ if not last then
+ return P(str)
+ end
+ end
+ if first == last then
+ return P(str)
+ elseif utfchar and (last - first < 8) then -- a somewhat arbitrary criterium
+ local p = P(false)
+ for i=first,last do
+ p = p + P(utfchar(i))
+ end
+ return p -- nil when invalid range
+ else
+ local f = function(b)
+ return b >= first and b <= last
+ end
+ -- tricky, these nested captures
+ return utf8byte / f -- nil when invalid range
+ end
+end
+
+-- print(lpeg.match(lpeg.Cs((C(lpeg.UR("αω"))/{ ["χ"] = "OEPS" })^0),"αωχαω"))
+
+-- lpeg.print(lpeg.R("ab","cd","gh"))
+-- lpeg.print(lpeg.P("a","b","c"))
+-- lpeg.print(lpeg.S("a","b","c"))
+
+-- print(lpeg.count("äáàa",lpeg.P("á") + lpeg.P("à")))
+-- print(lpeg.count("äáàa",lpeg.UP("áà")))
+-- print(lpeg.count("äáàa",lpeg.US("àá")))
+-- print(lpeg.count("äáàa",lpeg.UR("aá")))
+-- print(lpeg.count("äáàa",lpeg.UR("àá")))
+-- print(lpeg.count("äáàa",lpeg.UR(0x0000,0xFFFF)))
+
+function lpeg.is_lpeg(p)
+ return p and lpegtype(p) == "pattern"
+end
+
+function lpeg.oneof(list,...) -- lpeg.oneof("elseif","else","if","then") -- assume proper order
+ if type(list) ~= "table" then
+ list = { list, ... }
+ end
+ -- table.sort(list) -- longest match first
+ local p = P(list[1])
+ for l=2,#list do
+ p = p + P(list[l])
+ end
+ return p
+end
+
+-- For the moment here, but it might move to utilities. Beware, we need to
+-- have the longest keyword first, so 'aaa' comes beforte 'aa' which is why we
+-- loop back from the end cq. prepend.
+
+local sort = table.sort
+
+local function copyindexed(old)
+ local new = { }
+ for i=1,#old do
+ new[i] = old
+ end
+ return new
+end
+
+local function sortedkeys(tab)
+ local keys, s = { }, 0
+ for key,_ in next, tab do
+ s = s + 1
+ keys[s] = key
+ end
+ sort(keys)
+ return keys
+end
+
+function lpeg.append(list,pp,delayed,checked)
+ local p = pp
+ if #list > 0 then
+ local keys = copyindexed(list)
+ sort(keys)
+ for i=#keys,1,-1 do
+ local k = keys[i]
+ if p then
+ p = P(k) + p
+ else
+ p = P(k)
+ end
+ end
+ elseif delayed then -- hm, it looks like the lpeg parser resolves anyway
+ local keys = sortedkeys(list)
+ if p then
+ for i=1,#keys,1 do
+ local k = keys[i]
+ local v = list[k]
+ p = P(k)/list + p
+ end
+ else
+ for i=1,#keys do
+ local k = keys[i]
+ local v = list[k]
+ if p then
+ p = P(k) + p
+ else
+ p = P(k)
+ end
+ end
+ if p then
+ p = p / list
+ end
+ end
+ elseif checked then
+ -- problem: substitution gives a capture
+ local keys = sortedkeys(list)
+ for i=1,#keys do
+ local k = keys[i]
+ local v = list[k]
+ if p then
+ if k == v then
+ p = P(k) + p
+ else
+ p = P(k)/v + p
+ end
+ else
+ if k == v then
+ p = P(k)
+ else
+ p = P(k)/v
+ end
+ end
+ end
+ else
+ local keys = sortedkeys(list)
+ for i=1,#keys do
+ local k = keys[i]
+ local v = list[k]
+ if p then
+ p = P(k)/v + p
+ else
+ p = P(k)/v
+ end
+ end
+ end
+ return p
+end
+
+-- inspect(lpeg.append({ a = "1", aa = "1", aaa = "1" } ,nil,true))
+-- inspect(lpeg.append({ ["degree celsius"] = "1", celsius = "1", degree = "1" } ,nil,true))
+
+-- function lpeg.exact_match(words,case_insensitive)
+-- local pattern = concat(words)
+-- if case_insensitive then
+-- local pattern = S(upper(characters)) + S(lower(characters))
+-- local list = { }
+-- for i=1,#words do
+-- list[lower(words[i])] = true
+-- end
+-- return Cmt(pattern^1, function(_,i,s)
+-- return list[lower(s)] and i
+-- end)
+-- else
+-- local pattern = S(concat(words))
+-- local list = { }
+-- for i=1,#words do
+-- list[words[i]] = true
+-- end
+-- return Cmt(pattern^1, function(_,i,s)
+-- return list[s] and i
+-- end)
+-- end
+-- end
+
+-- experiment:
+
+local function make(t)
+ local p
+ local keys = sortedkeys(t)
+ for i=1,#keys do
+ local k = keys[i]
+ local v = t[k]
+ if not p then
+ if next(v) then
+ p = P(k) * make(v)
+ else
+ p = P(k)
+ end
+ else
+ if next(v) then
+ p = p + P(k) * make(v)
+ else
+ p = p + P(k)
+ end
+ end
+ end
+ return p
+end
+
+function lpeg.utfchartabletopattern(list) -- goes to util-lpg
+ local tree = { }
+ for i=1,#list do
+ local t = tree
+ for c in gmatch(list[i],".") do
+ if not t[c] then
+ t[c] = { }
+ end
+ t = t[c]
+ end
+ end
+ return make(tree)
+end
+
+-- inspect ( lpeg.utfchartabletopattern {
+-- utfchar(0x00A0), -- nbsp
+-- utfchar(0x2000), -- enquad
+-- utfchar(0x2001), -- emquad
+-- utfchar(0x2002), -- enspace
+-- utfchar(0x2003), -- emspace
+-- utfchar(0x2004), -- threeperemspace
+-- utfchar(0x2005), -- fourperemspace
+-- utfchar(0x2006), -- sixperemspace
+-- utfchar(0x2007), -- figurespace
+-- utfchar(0x2008), -- punctuationspace
+-- utfchar(0x2009), -- breakablethinspace
+-- utfchar(0x200A), -- hairspace
+-- utfchar(0x200B), -- zerowidthspace
+-- utfchar(0x202F), -- narrownobreakspace
+-- utfchar(0x205F), -- math thinspace
+-- } )
+
+-- a few handy ones:
+--
+-- faster than find(str,"[\n\r]") when match and # > 7 and always faster when # > 3
+
+patterns.containseol = lpeg.finder(eol) -- (1-eol)^0 * eol
+
+-- The next pattern^n variant is based on an approach suggested
+-- by Roberto: constructing a big repetition in chunks.
+--
+-- Being sparse is not needed, and only complicate matters and
+-- the number of redundant entries is not that large.
+
+local function nextstep(n,step,result)
+ local m = n % step -- mod(n,step)
+ local d = floor(n/step) -- div(n,step)
+ if d > 0 then
+ local v = V(tostring(step))
+ local s = result.start
+ for i=1,d do
+ if s then
+ s = v * s
+ else
+ s = v
+ end
+ end
+ result.start = s
+ end
+ if step > 1 and result.start then
+ local v = V(tostring(step/2))
+ result[tostring(step)] = v * v
+ end
+ if step > 0 then
+ return nextstep(m,step/2,result)
+ else
+ return result
+ end
+end
+
+function lpeg.times(pattern,n)
+ return P(nextstep(n,2^16,{ "start", ["1"] = pattern }))
+end
+
+-- local p = lpeg.Cs((1 - lpeg.times(lpeg.P("AB"),25))^1)
+-- local s = "12" .. string.rep("AB",20) .. "34" .. string.rep("AB",30) .. "56"
+-- inspect(p)
+-- print(lpeg.match(p,s))
+
+-- moved here (before util-str)
+
+local digit = R("09")
+local period = P(".")
+local zero = P("0")
+local trailingzeros = zero^0 * -digit -- suggested by Roberto R
+local case_1 = period * trailingzeros / ""
+local case_2 = period * (digit - trailingzeros)^1 * (trailingzeros / "")
+local number = digit^1 * (case_1 + case_2)
+local stripper = Cs((number + 1)^0)
+
+lpeg.patterns.stripzeros = stripper
+
+-- local sample = "bla 11.00 bla 11 bla 0.1100 bla 1.00100 bla 0.00 bla 0.001 bla 1.1100 bla 0.100100100 bla 0.00100100100"
+-- collectgarbage("collect")
+-- str = string.rep(sample,10000)
+-- local ts = os.clock()
+-- lpegmatch(stripper,str)
+-- print(#str, os.clock()-ts, lpegmatch(stripper,sample))
+
diff --git a/tex/context/base/l-lua.lua b/tex/context/base/l-lua.lua
index 486c14a5f..fc05afa67 100644
--- a/tex/context/base/l-lua.lua
+++ b/tex/context/base/l-lua.lua
@@ -1,150 +1,150 @@
-if not modules then modules = { } end modules ['l-lua'] = {
- 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"
-}
-
--- compatibility hacksand helpers
-
-local major, minor = string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$")
-
-_MAJORVERSION = tonumber(major) or 5
-_MINORVERSION = tonumber(minor) or 1
-_LUAVERSION = _MAJORVERSION + _MINORVERSION/10
-
--- lpeg
-
-if not lpeg then
- lpeg = require("lpeg")
-end
-
--- basics:
-
-if loadstring then
-
- local loadnormal = load
-
- function load(first,...)
- if type(first) == "string" then
- return loadstring(first,...)
- else
- return loadnormal(first,...)
- end
- end
-
-else
-
- loadstring = load
-
-end
-
--- table:
-
--- At some point it was announced that i[pairs would be dropped, which makes
--- sense. As we already used the for loop and # in most places the impact on
--- ConTeXt was not that large; the remaining ipairs already have been replaced.
--- Hm, actually ipairs was retained, but we no longer use it anyway (nor
--- pairs).
---
--- Just in case, we provide the fallbacks as discussed in Programming
--- in Lua (http://www.lua.org/pil/7.3.html):
-
-if not ipairs then
-
- -- for k, v in ipairs(t) do ... end
- -- for k=1,#t do local v = t[k] ... end
-
- local function iterate(a,i)
- i = i + 1
- local v = a[i]
- if v ~= nil then
- return i, v --, nil
- end
- end
-
- function ipairs(a)
- return iterate, a, 0
- end
-
-end
-
-if not pairs then
-
- -- for k, v in pairs(t) do ... end
- -- for k, v in next, t do ... end
-
- function pairs(t)
- return next, t -- , nil
- end
-
-end
-
--- The unpack function has been moved to the table table, and for compatiility
--- reasons we provide both now.
-
-if not table.unpack then
-
- table.unpack = _G.unpack
-
-elseif not unpack then
-
- _G.unpack = table.unpack
-
-end
-
--- package:
-
--- if not package.seachers then
---
--- package.searchers = package.loaders -- 5.2
---
--- elseif not package.loaders then
---
--- package.loaders = package.searchers
---
--- end
-
-if not package.loaders then -- brr, searchers is a special "loadlib function" userdata type
-
- package.loaders = package.searchers
-
-end
-
--- moved from util-deb to here:
-
-local print, select, tostring = print, select, tostring
-
-local inspectors = { }
-
-function setinspector(inspector) -- global function
- inspectors[#inspectors+1] = inspector
-end
-
-function inspect(...) -- global function
- for s=1,select("#",...) do
- local value = select(s,...)
- local done = false
- for i=1,#inspectors do
- done = inspectors[i](value)
- if done then
- break
- end
- end
- if not done then
- print(tostring(value))
- end
- end
-end
-
---
-
-local dummy = function() end
-
-function optionalrequire(...)
- local ok, result = xpcall(require,dummy,...)
- if ok then
- return result
- end
-end
+if not modules then modules = { } end modules ['l-lua'] = {
+ 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"
+}
+
+-- compatibility hacksand helpers
+
+local major, minor = string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$")
+
+_MAJORVERSION = tonumber(major) or 5
+_MINORVERSION = tonumber(minor) or 1
+_LUAVERSION = _MAJORVERSION + _MINORVERSION/10
+
+-- lpeg
+
+if not lpeg then
+ lpeg = require("lpeg")
+end
+
+-- basics:
+
+if loadstring then
+
+ local loadnormal = load
+
+ function load(first,...)
+ if type(first) == "string" then
+ return loadstring(first,...)
+ else
+ return loadnormal(first,...)
+ end
+ end
+
+else
+
+ loadstring = load
+
+end
+
+-- table:
+
+-- At some point it was announced that i[pairs would be dropped, which makes
+-- sense. As we already used the for loop and # in most places the impact on
+-- ConTeXt was not that large; the remaining ipairs already have been replaced.
+-- Hm, actually ipairs was retained, but we no longer use it anyway (nor
+-- pairs).
+--
+-- Just in case, we provide the fallbacks as discussed in Programming
+-- in Lua (http://www.lua.org/pil/7.3.html):
+
+if not ipairs then
+
+ -- for k, v in ipairs(t) do ... end
+ -- for k=1,#t do local v = t[k] ... end
+
+ local function iterate(a,i)
+ i = i + 1
+ local v = a[i]
+ if v ~= nil then
+ return i, v --, nil
+ end
+ end
+
+ function ipairs(a)
+ return iterate, a, 0
+ end
+
+end
+
+if not pairs then
+
+ -- for k, v in pairs(t) do ... end
+ -- for k, v in next, t do ... end
+
+ function pairs(t)
+ return next, t -- , nil
+ end
+
+end
+
+-- The unpack function has been moved to the table table, and for compatiility
+-- reasons we provide both now.
+
+if not table.unpack then
+
+ table.unpack = _G.unpack
+
+elseif not unpack then
+
+ _G.unpack = table.unpack
+
+end
+
+-- package:
+
+-- if not package.seachers then
+--
+-- package.searchers = package.loaders -- 5.2
+--
+-- elseif not package.loaders then
+--
+-- package.loaders = package.searchers
+--
+-- end
+
+if not package.loaders then -- brr, searchers is a special "loadlib function" userdata type
+
+ package.loaders = package.searchers
+
+end
+
+-- moved from util-deb to here:
+
+local print, select, tostring = print, select, tostring
+
+local inspectors = { }
+
+function setinspector(inspector) -- global function
+ inspectors[#inspectors+1] = inspector
+end
+
+function inspect(...) -- global function
+ for s=1,select("#",...) do
+ local value = select(s,...)
+ local done = false
+ for i=1,#inspectors do
+ done = inspectors[i](value)
+ if done then
+ break
+ end
+ end
+ if not done then
+ print(tostring(value))
+ end
+ end
+end
+
+--
+
+local dummy = function() end
+
+function optionalrequire(...)
+ local ok, result = xpcall(require,dummy,...)
+ if ok then
+ return result
+ end
+end
diff --git a/tex/context/base/l-math.lua b/tex/context/base/l-math.lua
index fb6bbbf5d..43f60b56b 100644
--- a/tex/context/base/l-math.lua
+++ b/tex/context/base/l-math.lua
@@ -1,34 +1,34 @@
-if not modules then modules = { } end modules ['l-math'] = {
- 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 floor, sin, cos, tan = math.floor, math.sin, math.cos, math.tan
-
-if not math.round then
- function math.round(x) return floor(x + 0.5) end
-end
-
-if not math.div then
- function math.div(n,m) return floor(n/m) end
-end
-
-if not math.mod then
- function math.mod(n,m) return n % m end
-end
-
-local pipi = 2*math.pi/360
-
-if not math.sind then
- function math.sind(d) return sin(d*pipi) end
- function math.cosd(d) return cos(d*pipi) end
- function math.tand(d) return tan(d*pipi) end
-end
-
-if not math.odd then
- function math.odd (n) return n % 2 ~= 0 end
- function math.even(n) return n % 2 == 0 end
-end
+if not modules then modules = { } end modules ['l-math'] = {
+ 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 floor, sin, cos, tan = math.floor, math.sin, math.cos, math.tan
+
+if not math.round then
+ function math.round(x) return floor(x + 0.5) end
+end
+
+if not math.div then
+ function math.div(n,m) return floor(n/m) end
+end
+
+if not math.mod then
+ function math.mod(n,m) return n % m end
+end
+
+local pipi = 2*math.pi/360
+
+if not math.sind then
+ function math.sind(d) return sin(d*pipi) end
+ function math.cosd(d) return cos(d*pipi) end
+ function math.tand(d) return tan(d*pipi) end
+end
+
+if not math.odd then
+ function math.odd (n) return n % 2 ~= 0 end
+ function math.even(n) return n % 2 == 0 end
+end
diff --git a/tex/context/base/l-md5.lua b/tex/context/base/l-md5.lua
index 731dc3fbe..8ac20a5a5 100644
--- a/tex/context/base/l-md5.lua
+++ b/tex/context/base/l-md5.lua
@@ -1,117 +1,117 @@
-if not modules then modules = { } end modules ['l-md5'] = {
- version = 1.001,
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- This also provides file checksums and checkers.
-
-if not md5 then
- md5 = optionalrequire("md5")
-end
-
-if not md5 then
- md5 = {
- sum = function(str) print("error: md5 is not loaded (sum ignored)") return str end,
- sumhexa = function(str) print("error: md5 is not loaded (sumhexa ignored)") return str end,
- }
-end
-
-local md5, file = md5, file
-local gsub, format, byte = string.gsub, string.format, string.byte
-local md5sum = md5.sum
-
-local function convert(str,fmt)
- return (gsub(md5sum(str),".",function(chr) return format(fmt,byte(chr)) end))
-end
-
-if not md5.HEX then function md5.HEX(str) return convert(str,"%02X") end end
-if not md5.hex then function md5.hex(str) return convert(str,"%02x") end end
-if not md5.dec then function md5.dec(str) return convert(str,"%03i") end end
-
--- local P, Cs, lpegmatch = lpeg.P, lpeg.Cs,lpeg.match
---
--- if not md5.HEX then
--- local function remap(chr) return format("%02X",byte(chr)) end
--- function md5.HEX(str) return (gsub(md5.sum(str),".",remap)) end
--- end
---
--- if not md5.hex then
--- local function remap(chr) return format("%02x",byte(chr)) end
--- function md5.hex(str) return (gsub(md5.sum(str),".",remap)) end
--- end
---
--- if not md5.dec then
--- local function remap(chr) return format("%03i",byte(chr)) end
--- function md5.dec(str) return (gsub(md5.sum(str),".",remap)) end
--- end
-
--- if not md5.HEX then
--- local pattern_HEX = Cs( ( P(1) / function(chr) return format("%02X",byte(chr)) end)^0 )
--- function md5.HEX(str) return lpegmatch(pattern_HEX,md5.sum(str)) end
--- end
---
--- if not md5.hex then
--- local pattern_hex = Cs( ( P(1) / function(chr) return format("%02x",byte(chr)) end)^0 )
--- function md5.hex(str) return lpegmatch(pattern_hex,md5.sum(str)) end
--- end
---
--- if not md5.dec then
--- local pattern_dec = Cs( ( P(1) / function(chr) return format("%02i",byte(chr)) end)^0 )
--- function md5.dec(str) return lpegmatch(pattern_dec,md5.sum(str)) end
--- end
-
-function file.needsupdating(oldname,newname,threshold) -- size modification access change
- local oldtime = lfs.attributes(oldname,"modification")
- if oldtime then
- local newtime = lfs.attributes(newname,"modification")
- if not newtime then
- return true -- no new file, so no updating needed
- elseif newtime >= oldtime then
- return false -- new file definitely needs updating
- elseif oldtime - newtime < (threshold or 1) then
- return false -- new file is probably still okay
- else
- return true -- new file has to be updated
- end
- else
- return false -- no old file, so no updating needed
- end
-end
-
-file.needs_updating = file.needsupdating
-
-function file.syncmtimes(oldname,newname)
- local oldtime = lfs.attributes(oldname,"modification")
- if oldtime and lfs.isfile(newname) then
- lfs.touch(newname,oldtime,oldtime)
- end
-end
-
-function file.checksum(name)
- if md5 then
- local data = io.loaddata(name)
- if data then
- return md5.HEX(data)
- end
- end
- return nil
-end
-
-function file.loadchecksum(name)
- if md5 then
- local data = io.loaddata(name .. ".md5")
- return data and (gsub(data,"%s",""))
- end
- return nil
-end
-
-function file.savechecksum(name,checksum)
- if not checksum then checksum = file.checksum(name) end
- if checksum then
- io.savedata(name .. ".md5",checksum)
- return checksum
- end
- return nil
-end
+if not modules then modules = { } end modules ['l-md5'] = {
+ version = 1.001,
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This also provides file checksums and checkers.
+
+if not md5 then
+ md5 = optionalrequire("md5")
+end
+
+if not md5 then
+ md5 = {
+ sum = function(str) print("error: md5 is not loaded (sum ignored)") return str end,
+ sumhexa = function(str) print("error: md5 is not loaded (sumhexa ignored)") return str end,
+ }
+end
+
+local md5, file = md5, file
+local gsub, format, byte = string.gsub, string.format, string.byte
+local md5sum = md5.sum
+
+local function convert(str,fmt)
+ return (gsub(md5sum(str),".",function(chr) return format(fmt,byte(chr)) end))
+end
+
+if not md5.HEX then function md5.HEX(str) return convert(str,"%02X") end end
+if not md5.hex then function md5.hex(str) return convert(str,"%02x") end end
+if not md5.dec then function md5.dec(str) return convert(str,"%03i") end end
+
+-- local P, Cs, lpegmatch = lpeg.P, lpeg.Cs,lpeg.match
+--
+-- if not md5.HEX then
+-- local function remap(chr) return format("%02X",byte(chr)) end
+-- function md5.HEX(str) return (gsub(md5.sum(str),".",remap)) end
+-- end
+--
+-- if not md5.hex then
+-- local function remap(chr) return format("%02x",byte(chr)) end
+-- function md5.hex(str) return (gsub(md5.sum(str),".",remap)) end
+-- end
+--
+-- if not md5.dec then
+-- local function remap(chr) return format("%03i",byte(chr)) end
+-- function md5.dec(str) return (gsub(md5.sum(str),".",remap)) end
+-- end
+
+-- if not md5.HEX then
+-- local pattern_HEX = Cs( ( P(1) / function(chr) return format("%02X",byte(chr)) end)^0 )
+-- function md5.HEX(str) return lpegmatch(pattern_HEX,md5.sum(str)) end
+-- end
+--
+-- if not md5.hex then
+-- local pattern_hex = Cs( ( P(1) / function(chr) return format("%02x",byte(chr)) end)^0 )
+-- function md5.hex(str) return lpegmatch(pattern_hex,md5.sum(str)) end
+-- end
+--
+-- if not md5.dec then
+-- local pattern_dec = Cs( ( P(1) / function(chr) return format("%02i",byte(chr)) end)^0 )
+-- function md5.dec(str) return lpegmatch(pattern_dec,md5.sum(str)) end
+-- end
+
+function file.needsupdating(oldname,newname,threshold) -- size modification access change
+ local oldtime = lfs.attributes(oldname,"modification")
+ if oldtime then
+ local newtime = lfs.attributes(newname,"modification")
+ if not newtime then
+ return true -- no new file, so no updating needed
+ elseif newtime >= oldtime then
+ return false -- new file definitely needs updating
+ elseif oldtime - newtime < (threshold or 1) then
+ return false -- new file is probably still okay
+ else
+ return true -- new file has to be updated
+ end
+ else
+ return false -- no old file, so no updating needed
+ end
+end
+
+file.needs_updating = file.needsupdating
+
+function file.syncmtimes(oldname,newname)
+ local oldtime = lfs.attributes(oldname,"modification")
+ if oldtime and lfs.isfile(newname) then
+ lfs.touch(newname,oldtime,oldtime)
+ end
+end
+
+function file.checksum(name)
+ if md5 then
+ local data = io.loaddata(name)
+ if data then
+ return md5.HEX(data)
+ end
+ end
+ return nil
+end
+
+function file.loadchecksum(name)
+ if md5 then
+ local data = io.loaddata(name .. ".md5")
+ return data and (gsub(data,"%s",""))
+ end
+ return nil
+end
+
+function file.savechecksum(name,checksum)
+ if not checksum then checksum = file.checksum(name) end
+ if checksum then
+ io.savedata(name .. ".md5",checksum)
+ return checksum
+ end
+ return nil
+end
diff --git a/tex/context/base/l-number.lua b/tex/context/base/l-number.lua
index 7db82173c..001ca31f7 100644
--- a/tex/context/base/l-number.lua
+++ b/tex/context/base/l-number.lua
@@ -1,207 +1,207 @@
-if not modules then modules = { } end modules ['l-number'] = {
- 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"
-}
-
--- this module will be replaced when we have the bit library .. the number based sets
--- might go away
-
-local tostring, tonumber = tostring, tonumber
-local format, floor, match, rep = string.format, math.floor, string.match, string.rep
-local concat, insert = table.concat, table.insert
-local lpegmatch = lpeg.match
-
-number = number or { }
-local number = number
-
-if bit32 then -- I wonder if this is faster
-
- local btest, bor = bit32.btest, bit32.bor
-
- function number.bit(p)
- return 2 ^ (p - 1) -- 1-based indexing
- end
-
- number.hasbit = btest
- number.setbit = bor
-
- function number.setbit(x,p) -- why not bor?
- return btest(x,p) and x or x + p
- end
-
- function number.clearbit(x,p)
- return btest(x,p) and x - p or x
- end
-
-else
-
- -- http://ricilake.blogspot.com/2007/10/iterating-bits-in-lua.html
-
- function number.bit(p)
- return 2 ^ (p - 1) -- 1-based indexing
- end
-
- function number.hasbit(x, p) -- typical call: if hasbit(x, bit(3)) then ...
- return x % (p + p) >= p
- end
-
- function number.setbit(x, p)
- return (x % (p + p) >= p) and x or x + p
- end
-
- function number.clearbit(x, p)
- return (x % (p + p) >= p) and x - p or x
- end
-
-end
-
--- print(number.tobitstring(8))
--- print(number.tobitstring(14))
--- print(number.tobitstring(66))
--- print(number.tobitstring(0x00))
--- print(number.tobitstring(0xFF))
--- print(number.tobitstring(46260767936,4))
-
-if bit32 then
-
- local bextract = bit32.extract
-
- local t = {
- "0", "0", "0", "0", "0", "0", "0", "0",
- "0", "0", "0", "0", "0", "0", "0", "0",
- "0", "0", "0", "0", "0", "0", "0", "0",
- "0", "0", "0", "0", "0", "0", "0", "0",
- }
-
- function number.tobitstring(b,m)
- -- if really needed we can speed this one up
- -- because small numbers need less extraction
- local n = 32
- for i=0,31 do
- local v = bextract(b,i)
- local k = 32 - i
- if v == 1 then
- n = k
- t[k] = "1"
- else
- t[k] = "0"
- end
- end
- if m then
- m = 33 - m * 8
- if m < 1 then
- m = 1
- end
- return concat(t,"",m)
- elseif n < 8 then
- return concat(t)
- elseif n < 16 then
- return concat(t,"",9)
- elseif n < 24 then
- return concat(t,"",17)
- else
- return concat(t,"",25)
- end
- end
-
-else
-
- function number.tobitstring(n,m)
- if n > 0 then
- local t = { }
- while n > 0 do
- insert(t,1,n % 2 > 0 and 1 or 0)
- n = floor(n/2)
- end
- local nn = 8 - #t % 8
- if nn > 0 and nn < 8 then
- for i=1,nn do
- insert(t,1,0)
- end
- end
- if m then
- m = m * 8 - #t
- if m > 0 then
- insert(t,1,rep("0",m))
- end
- end
- return concat(t)
- elseif m then
- rep("00000000",m)
- else
- return "00000000"
- end
- end
-
-end
-
-function number.valid(str,default)
- return tonumber(str) or default or nil
-end
-
-function number.toevenhex(n)
- local s = format("%X",n)
- if #s % 2 == 0 then
- return s
- else
- return "0" .. s
- end
-end
-
--- a,b,c,d,e,f = number.toset(100101)
---
--- function number.toset(n)
--- return match(tostring(n),"(.?)(.?)(.?)(.?)(.?)(.?)(.?)(.?)")
--- end
---
--- -- the lpeg way is slower on 8 digits, but faster on 4 digits, some 7.5%
--- -- on
---
--- for i=1,1000000 do
--- local a,b,c,d,e,f,g,h = number.toset(12345678)
--- local a,b,c,d = number.toset(1234)
--- local a,b,c = number.toset(123)
--- local a,b,c = number.toset("123")
--- end
-
-local one = lpeg.C(1-lpeg.S('')/tonumber)^1
-
-function number.toset(n)
- return lpegmatch(one,tostring(n))
-end
-
--- function number.bits(n,zero)
--- local t, i = { }, (zero and 0) or 1
--- while n > 0 do
--- local m = n % 2
--- if m > 0 then
--- insert(t,1,i)
--- end
--- n = floor(n/2)
--- i = i + 1
--- end
--- return t
--- end
---
--- -- a bit faster
-
-local function bits(n,i,...)
- if n > 0 then
- local m = n % 2
- local n = floor(n/2)
- if m > 0 then
- return bits(n, i+1, i, ...)
- else
- return bits(n, i+1, ...)
- end
- else
- return ...
- end
-end
-
-function number.bits(n)
- return { bits(n,1) }
-end
+if not modules then modules = { } end modules ['l-number'] = {
+ 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"
+}
+
+-- this module will be replaced when we have the bit library .. the number based sets
+-- might go away
+
+local tostring, tonumber = tostring, tonumber
+local format, floor, match, rep = string.format, math.floor, string.match, string.rep
+local concat, insert = table.concat, table.insert
+local lpegmatch = lpeg.match
+
+number = number or { }
+local number = number
+
+if bit32 then -- I wonder if this is faster
+
+ local btest, bor = bit32.btest, bit32.bor
+
+ function number.bit(p)
+ return 2 ^ (p - 1) -- 1-based indexing
+ end
+
+ number.hasbit = btest
+ number.setbit = bor
+
+ function number.setbit(x,p) -- why not bor?
+ return btest(x,p) and x or x + p
+ end
+
+ function number.clearbit(x,p)
+ return btest(x,p) and x - p or x
+ end
+
+else
+
+ -- http://ricilake.blogspot.com/2007/10/iterating-bits-in-lua.html
+
+ function number.bit(p)
+ return 2 ^ (p - 1) -- 1-based indexing
+ end
+
+ function number.hasbit(x, p) -- typical call: if hasbit(x, bit(3)) then ...
+ return x % (p + p) >= p
+ end
+
+ function number.setbit(x, p)
+ return (x % (p + p) >= p) and x or x + p
+ end
+
+ function number.clearbit(x, p)
+ return (x % (p + p) >= p) and x - p or x
+ end
+
+end
+
+-- print(number.tobitstring(8))
+-- print(number.tobitstring(14))
+-- print(number.tobitstring(66))
+-- print(number.tobitstring(0x00))
+-- print(number.tobitstring(0xFF))
+-- print(number.tobitstring(46260767936,4))
+
+if bit32 then
+
+ local bextract = bit32.extract
+
+ local t = {
+ "0", "0", "0", "0", "0", "0", "0", "0",
+ "0", "0", "0", "0", "0", "0", "0", "0",
+ "0", "0", "0", "0", "0", "0", "0", "0",
+ "0", "0", "0", "0", "0", "0", "0", "0",
+ }
+
+ function number.tobitstring(b,m)
+ -- if really needed we can speed this one up
+ -- because small numbers need less extraction
+ local n = 32
+ for i=0,31 do
+ local v = bextract(b,i)
+ local k = 32 - i
+ if v == 1 then
+ n = k
+ t[k] = "1"
+ else
+ t[k] = "0"
+ end
+ end
+ if m then
+ m = 33 - m * 8
+ if m < 1 then
+ m = 1
+ end
+ return concat(t,"",m)
+ elseif n < 8 then
+ return concat(t)
+ elseif n < 16 then
+ return concat(t,"",9)
+ elseif n < 24 then
+ return concat(t,"",17)
+ else
+ return concat(t,"",25)
+ end
+ end
+
+else
+
+ function number.tobitstring(n,m)
+ if n > 0 then
+ local t = { }
+ while n > 0 do
+ insert(t,1,n % 2 > 0 and 1 or 0)
+ n = floor(n/2)
+ end
+ local nn = 8 - #t % 8
+ if nn > 0 and nn < 8 then
+ for i=1,nn do
+ insert(t,1,0)
+ end
+ end
+ if m then
+ m = m * 8 - #t
+ if m > 0 then
+ insert(t,1,rep("0",m))
+ end
+ end
+ return concat(t)
+ elseif m then
+ rep("00000000",m)
+ else
+ return "00000000"
+ end
+ end
+
+end
+
+function number.valid(str,default)
+ return tonumber(str) or default or nil
+end
+
+function number.toevenhex(n)
+ local s = format("%X",n)
+ if #s % 2 == 0 then
+ return s
+ else
+ return "0" .. s
+ end
+end
+
+-- a,b,c,d,e,f = number.toset(100101)
+--
+-- function number.toset(n)
+-- return match(tostring(n),"(.?)(.?)(.?)(.?)(.?)(.?)(.?)(.?)")
+-- end
+--
+-- -- the lpeg way is slower on 8 digits, but faster on 4 digits, some 7.5%
+-- -- on
+--
+-- for i=1,1000000 do
+-- local a,b,c,d,e,f,g,h = number.toset(12345678)
+-- local a,b,c,d = number.toset(1234)
+-- local a,b,c = number.toset(123)
+-- local a,b,c = number.toset("123")
+-- end
+
+local one = lpeg.C(1-lpeg.S('')/tonumber)^1
+
+function number.toset(n)
+ return lpegmatch(one,tostring(n))
+end
+
+-- function number.bits(n,zero)
+-- local t, i = { }, (zero and 0) or 1
+-- while n > 0 do
+-- local m = n % 2
+-- if m > 0 then
+-- insert(t,1,i)
+-- end
+-- n = floor(n/2)
+-- i = i + 1
+-- end
+-- return t
+-- end
+--
+-- -- a bit faster
+
+local function bits(n,i,...)
+ if n > 0 then
+ local m = n % 2
+ local n = floor(n/2)
+ if m > 0 then
+ return bits(n, i+1, i, ...)
+ else
+ return bits(n, i+1, ...)
+ end
+ else
+ return ...
+ end
+end
+
+function number.bits(n)
+ return { bits(n,1) }
+end
diff --git a/tex/context/base/l-os.lua b/tex/context/base/l-os.lua
index 6b9ae12f9..05ca0acdc 100644
--- a/tex/context/base/l-os.lua
+++ b/tex/context/base/l-os.lua
@@ -1,474 +1,474 @@
-if not modules then modules = { } end modules ['l-os'] = {
- 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"
-}
-
--- This file deals with some operating system issues. Please don't bother me
--- with the pros and cons of operating systems as they all have their flaws
--- and benefits. Bashing one of them won't help solving problems and fixing
--- bugs faster and is a waste of time and energy.
---
--- path separators: / or \ ... we can use / everywhere
--- suffixes : dll so exe <none> ... no big deal
--- quotes : we can use "" in most cases
--- expansion : unless "" are used * might give side effects
--- piping/threads : somewhat different for each os
--- locations : specific user file locations and settings can change over time
---
--- os.type : windows | unix (new, we already guessed os.platform)
--- os.name : windows | msdos | linux | macosx | solaris | .. | generic (new)
--- os.platform : extended os.name with architecture
-
--- os.sleep() => socket.sleep()
--- math.randomseed(tonumber(string.sub(string.reverse(tostring(math.floor(socket.gettime()*10000))),1,6)))
-
--- maybe build io.flush in os.execute
-
-local os = os
-local date, time = os.date, os.time
-local find, format, gsub, upper, gmatch = string.find, string.format, string.gsub, string.upper, string.gmatch
-local concat = table.concat
-local random, ceil, randomseed = math.random, math.ceil, math.randomseed
-local rawget, rawset, type, getmetatable, setmetatable, tonumber, tostring = rawget, rawset, type, getmetatable, setmetatable, tonumber, tostring
-
--- The following code permits traversing the environment table, at least
--- in luatex. Internally all environment names are uppercase.
-
--- The randomseed in Lua is not that random, although this depends on the operating system as well
--- as the binary (Luatex is normally okay). But to be sure we set the seed anyway.
-
-math.initialseed = tonumber(string.sub(string.reverse(tostring(ceil(socket and socket.gettime()*10000 or time()))),1,6))
-
-randomseed(math.initialseed)
-
-if not os.__getenv__ then
-
- os.__getenv__ = os.getenv
- os.__setenv__ = os.setenv
-
- if os.env then
-
- local osgetenv = os.getenv
- local ossetenv = os.setenv
- local osenv = os.env local _ = osenv.PATH -- initialize the table
-
- function os.setenv(k,v)
- if v == nil then
- v = ""
- end
- local K = upper(k)
- osenv[K] = v
- if type(v) == "table" then
- v = concat(v,";") -- path
- end
- ossetenv(K,v)
- end
-
- function os.getenv(k)
- local K = upper(k)
- local v = osenv[K] or osenv[k] or osgetenv(K) or osgetenv(k)
- if v == "" then
- return nil
- else
- return v
- end
- end
-
- else
-
- local ossetenv = os.setenv
- local osgetenv = os.getenv
- local osenv = { }
-
- function os.setenv(k,v)
- if v == nil then
- v = ""
- end
- local K = upper(k)
- osenv[K] = v
- end
-
- function os.getenv(k)
- local K = upper(k)
- local v = osenv[K] or osgetenv(K) or osgetenv(k)
- if v == "" then
- return nil
- else
- return v
- end
- end
-
- local function __index(t,k)
- return os.getenv(k)
- end
- local function __newindex(t,k,v)
- os.setenv(k,v)
- end
-
- os.env = { }
-
- setmetatable(os.env, { __index = __index, __newindex = __newindex } )
-
- end
-
-end
-
--- end of environment hack
-
-local execute, spawn, exec, iopopen, ioflush = os.execute, os.spawn or os.execute, os.exec or os.execute, io.popen, io.flush
-
-function os.execute(...) ioflush() return execute(...) end
-function os.spawn (...) ioflush() return spawn (...) end
-function os.exec (...) ioflush() return exec (...) end
-function io.popen (...) ioflush() return iopopen(...) end
-
-function os.resultof(command)
- local handle = io.popen(command,"r")
- return handle and handle:read("*all") or ""
-end
-
-if not io.fileseparator then
- if find(os.getenv("PATH"),";") then
- io.fileseparator, io.pathseparator, os.type = "\\", ";", os.type or "mswin"
- else
- io.fileseparator, io.pathseparator, os.type = "/" , ":", os.type or "unix"
- end
-end
-
-os.type = os.type or (io.pathseparator == ";" and "windows") or "unix"
-os.name = os.name or (os.type == "windows" and "mswin" ) or "linux"
-
-if os.type == "windows" then
- os.libsuffix, os.binsuffix, os.binsuffixes = 'dll', 'exe', { 'exe', 'cmd', 'bat' }
-else
- os.libsuffix, os.binsuffix, os.binsuffixes = 'so', '', { '' }
-end
-
-local launchers = {
- windows = "start %s",
- macosx = "open %s",
- unix = "$BROWSER %s &> /dev/null &",
-}
-
-function os.launch(str)
- os.execute(format(launchers[os.name] or launchers.unix,str))
-end
-
-if not os.times then -- ?
- -- utime = user time
- -- stime = system time
- -- cutime = children user time
- -- cstime = children system time
- function os.times()
- return {
- utime = os.gettimeofday(), -- user
- stime = 0, -- system
- cutime = 0, -- children user
- cstime = 0, -- children system
- }
- end
-end
-
-os.gettimeofday = os.gettimeofday or os.clock
-
-local startuptime = os.gettimeofday()
-
-function os.runtime()
- return os.gettimeofday() - startuptime
-end
-
---~ print(os.gettimeofday()-os.time())
---~ os.sleep(1.234)
---~ print (">>",os.runtime())
---~ print(os.date("%H:%M:%S",os.gettimeofday()))
---~ print(os.date("%H:%M:%S",os.time()))
-
--- no need for function anymore as we have more clever code and helpers now
--- this metatable trickery might as well disappear
-
-os.resolvers = os.resolvers or { } -- will become private
-
-local resolvers = os.resolvers
-
-setmetatable(os, { __index = function(t,k)
- local r = resolvers[k]
- return r and r(t,k) or nil -- no memoize
-end })
-
--- we can use HOSTTYPE on some platforms
-
-local name, platform = os.name or "linux", os.getenv("MTX_PLATFORM") or ""
-
-local function guess()
- local architecture = os.resultof("uname -m") or ""
- if architecture ~= "" then
- return architecture
- end
- architecture = os.getenv("HOSTTYPE") or ""
- if architecture ~= "" then
- return architecture
- end
- return os.resultof("echo $HOSTTYPE") or ""
-end
-
-if platform ~= "" then
-
- os.platform = platform
-
-elseif os.type == "windows" then
-
- -- we could set the variable directly, no function needed here
-
- function os.resolvers.platform(t,k)
- local platform, architecture = "", os.getenv("PROCESSOR_ARCHITECTURE") or ""
- if find(architecture,"AMD64") then
- platform = "mswin-64"
- else
- platform = "mswin"
- end
- os.setenv("MTX_PLATFORM",platform)
- os.platform = platform
- return platform
- end
-
-elseif name == "linux" then
-
- function os.resolvers.platform(t,k)
- -- we sometimes have HOSTTYPE set so let's check that first
- local platform, architecture = "", os.getenv("HOSTTYPE") or os.resultof("uname -m") or ""
- if find(architecture,"x86_64") then
- platform = "linux-64"
- elseif find(architecture,"ppc") then
- platform = "linux-ppc"
- else
- platform = "linux"
- end
- os.setenv("MTX_PLATFORM",platform)
- os.platform = platform
- return platform
- end
-
-elseif name == "macosx" then
-
- --[[
- Identifying the architecture of OSX is quite a mess and this
- is the best we can come up with. For some reason $HOSTTYPE is
- a kind of pseudo environment variable, not known to the current
- environment. And yes, uname cannot be trusted either, so there
- is a change that you end up with a 32 bit run on a 64 bit system.
- Also, some proper 64 bit intel macs are too cheap (low-end) and
- therefore not permitted to run the 64 bit kernel.
- ]]--
-
- function os.resolvers.platform(t,k)
- -- local platform, architecture = "", os.getenv("HOSTTYPE") or ""
- -- if architecture == "" then
- -- architecture = os.resultof("echo $HOSTTYPE") or ""
- -- end
- local platform, architecture = "", os.resultof("echo $HOSTTYPE") or ""
- if architecture == "" then
- -- print("\nI have no clue what kind of OSX you're running so let's assume an 32 bit intel.\n")
- platform = "osx-intel"
- elseif find(architecture,"i386") then
- platform = "osx-intel"
- elseif find(architecture,"x86_64") then
- platform = "osx-64"
- else
- platform = "osx-ppc"
- end
- os.setenv("MTX_PLATFORM",platform)
- os.platform = platform
- return platform
- end
-
-elseif name == "sunos" then
-
- function os.resolvers.platform(t,k)
- local platform, architecture = "", os.resultof("uname -m") or ""
- if find(architecture,"sparc") then
- platform = "solaris-sparc"
- else -- if architecture == 'i86pc'
- platform = "solaris-intel"
- end
- os.setenv("MTX_PLATFORM",platform)
- os.platform = platform
- return platform
- end
-
-elseif name == "freebsd" then
-
- function os.resolvers.platform(t,k)
- local platform, architecture = "", os.resultof("uname -m") or ""
- if find(architecture,"amd64") then
- platform = "freebsd-amd64"
- else
- platform = "freebsd"
- end
- os.setenv("MTX_PLATFORM",platform)
- os.platform = platform
- return platform
- end
-
-elseif name == "kfreebsd" then
-
- function os.resolvers.platform(t,k)
- -- we sometimes have HOSTTYPE set so let's check that first
- local platform, architecture = "", os.getenv("HOSTTYPE") or os.resultof("uname -m") or ""
- if find(architecture,"x86_64") then
- platform = "kfreebsd-amd64"
- else
- platform = "kfreebsd-i386"
- end
- os.setenv("MTX_PLATFORM",platform)
- os.platform = platform
- return platform
- end
-
-else
-
- -- platform = "linux"
- -- os.setenv("MTX_PLATFORM",platform)
- -- os.platform = platform
-
- function os.resolvers.platform(t,k)
- local platform = "linux"
- os.setenv("MTX_PLATFORM",platform)
- os.platform = platform
- return platform
- end
-
-end
-
--- beware, we set the randomseed
-
--- from wikipedia: Version 4 UUIDs use a scheme relying only on random numbers. This algorithm sets the
--- version number as well as two reserved bits. All other bits are set using a random or pseudorandom
--- data source. Version 4 UUIDs have the form xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx with hexadecimal
--- digits x and hexadecimal digits 8, 9, A, or B for y. e.g. f47ac10b-58cc-4372-a567-0e02b2c3d479.
---
--- as we don't call this function too often there is not so much risk on repetition
-
-local t = { 8, 9, "a", "b" }
-
-function os.uuid()
- return format("%04x%04x-4%03x-%s%03x-%04x-%04x%04x%04x",
- random(0xFFFF),random(0xFFFF),
- random(0x0FFF),
- t[ceil(random(4))] or 8,random(0x0FFF),
- random(0xFFFF),
- random(0xFFFF),random(0xFFFF),random(0xFFFF)
- )
-end
-
-local d
-
-function os.timezone(delta)
- d = d or tonumber(tonumber(date("%H")-date("!%H")))
- if delta then
- if d > 0 then
- return format("+%02i:00",d)
- else
- return format("-%02i:00",-d)
- end
- else
- return 1
- end
-end
-
-local timeformat = format("%%s%s",os.timezone(true))
-local dateformat = "!%Y-%m-%d %H:%M:%S"
-
-function os.fulltime(t,default)
- t = tonumber(t) or 0
- if t > 0 then
- -- valid time
- elseif default then
- return default
- else
- t = nil
- end
- return format(timeformat,date(dateformat,t))
-end
-
-local dateformat = "%Y-%m-%d %H:%M:%S"
-
-function os.localtime(t,default)
- t = tonumber(t) or 0
- if t > 0 then
- -- valid time
- elseif default then
- return default
- else
- t = nil
- end
- return date(dateformat,t)
-end
-
-function os.converttime(t,default)
- local t = tonumber(t)
- if t and t > 0 then
- return date(dateformat,t)
- else
- return default or "-"
- end
-end
-
-local memory = { }
-
-local function which(filename)
- local fullname = memory[filename]
- if fullname == nil then
- local suffix = file.suffix(filename)
- local suffixes = suffix == "" and os.binsuffixes or { suffix }
- for directory in gmatch(os.getenv("PATH"),"[^" .. io.pathseparator .."]+") do
- local df = file.join(directory,filename)
- for i=1,#suffixes do
- local dfs = file.addsuffix(df,suffixes[i])
- if io.exists(dfs) then
- fullname = dfs
- break
- end
- end
- end
- if not fullname then
- fullname = false
- end
- memory[filename] = fullname
- end
- return fullname
-end
-
-os.which = which
-os.where = which
-
-function os.today()
- return date("!*t") -- table with values
-end
-
-function os.now()
- return date("!%Y-%m-%d %H:%M:%S") -- 2011-12-04 14:59:12
-end
-
--- if not os.sleep and socket then
--- os.sleep = socket.sleep
--- end
-
-if not os.sleep then
- local socket = socket
- function os.sleep(n)
- if not socket then
- -- so we delay ... if os.sleep is really needed then one should also
- -- be sure that socket can be found
- socket = require("socket")
- end
- socket.sleep(n)
- end
-end
-
--- print(os.which("inkscape.exe"))
--- print(os.which("inkscape"))
--- print(os.which("gs.exe"))
--- print(os.which("ps2pdf"))
+if not modules then modules = { } end modules ['l-os'] = {
+ 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"
+}
+
+-- This file deals with some operating system issues. Please don't bother me
+-- with the pros and cons of operating systems as they all have their flaws
+-- and benefits. Bashing one of them won't help solving problems and fixing
+-- bugs faster and is a waste of time and energy.
+--
+-- path separators: / or \ ... we can use / everywhere
+-- suffixes : dll so exe <none> ... no big deal
+-- quotes : we can use "" in most cases
+-- expansion : unless "" are used * might give side effects
+-- piping/threads : somewhat different for each os
+-- locations : specific user file locations and settings can change over time
+--
+-- os.type : windows | unix (new, we already guessed os.platform)
+-- os.name : windows | msdos | linux | macosx | solaris | .. | generic (new)
+-- os.platform : extended os.name with architecture
+
+-- os.sleep() => socket.sleep()
+-- math.randomseed(tonumber(string.sub(string.reverse(tostring(math.floor(socket.gettime()*10000))),1,6)))
+
+-- maybe build io.flush in os.execute
+
+local os = os
+local date, time = os.date, os.time
+local find, format, gsub, upper, gmatch = string.find, string.format, string.gsub, string.upper, string.gmatch
+local concat = table.concat
+local random, ceil, randomseed = math.random, math.ceil, math.randomseed
+local rawget, rawset, type, getmetatable, setmetatable, tonumber, tostring = rawget, rawset, type, getmetatable, setmetatable, tonumber, tostring
+
+-- The following code permits traversing the environment table, at least
+-- in luatex. Internally all environment names are uppercase.
+
+-- The randomseed in Lua is not that random, although this depends on the operating system as well
+-- as the binary (Luatex is normally okay). But to be sure we set the seed anyway.
+
+math.initialseed = tonumber(string.sub(string.reverse(tostring(ceil(socket and socket.gettime()*10000 or time()))),1,6))
+
+randomseed(math.initialseed)
+
+if not os.__getenv__ then
+
+ os.__getenv__ = os.getenv
+ os.__setenv__ = os.setenv
+
+ if os.env then
+
+ local osgetenv = os.getenv
+ local ossetenv = os.setenv
+ local osenv = os.env local _ = osenv.PATH -- initialize the table
+
+ function os.setenv(k,v)
+ if v == nil then
+ v = ""
+ end
+ local K = upper(k)
+ osenv[K] = v
+ if type(v) == "table" then
+ v = concat(v,";") -- path
+ end
+ ossetenv(K,v)
+ end
+
+ function os.getenv(k)
+ local K = upper(k)
+ local v = osenv[K] or osenv[k] or osgetenv(K) or osgetenv(k)
+ if v == "" then
+ return nil
+ else
+ return v
+ end
+ end
+
+ else
+
+ local ossetenv = os.setenv
+ local osgetenv = os.getenv
+ local osenv = { }
+
+ function os.setenv(k,v)
+ if v == nil then
+ v = ""
+ end
+ local K = upper(k)
+ osenv[K] = v
+ end
+
+ function os.getenv(k)
+ local K = upper(k)
+ local v = osenv[K] or osgetenv(K) or osgetenv(k)
+ if v == "" then
+ return nil
+ else
+ return v
+ end
+ end
+
+ local function __index(t,k)
+ return os.getenv(k)
+ end
+ local function __newindex(t,k,v)
+ os.setenv(k,v)
+ end
+
+ os.env = { }
+
+ setmetatable(os.env, { __index = __index, __newindex = __newindex } )
+
+ end
+
+end
+
+-- end of environment hack
+
+local execute, spawn, exec, iopopen, ioflush = os.execute, os.spawn or os.execute, os.exec or os.execute, io.popen, io.flush
+
+function os.execute(...) ioflush() return execute(...) end
+function os.spawn (...) ioflush() return spawn (...) end
+function os.exec (...) ioflush() return exec (...) end
+function io.popen (...) ioflush() return iopopen(...) end
+
+function os.resultof(command)
+ local handle = io.popen(command,"r")
+ return handle and handle:read("*all") or ""
+end
+
+if not io.fileseparator then
+ if find(os.getenv("PATH"),";") then
+ io.fileseparator, io.pathseparator, os.type = "\\", ";", os.type or "mswin"
+ else
+ io.fileseparator, io.pathseparator, os.type = "/" , ":", os.type or "unix"
+ end
+end
+
+os.type = os.type or (io.pathseparator == ";" and "windows") or "unix"
+os.name = os.name or (os.type == "windows" and "mswin" ) or "linux"
+
+if os.type == "windows" then
+ os.libsuffix, os.binsuffix, os.binsuffixes = 'dll', 'exe', { 'exe', 'cmd', 'bat' }
+else
+ os.libsuffix, os.binsuffix, os.binsuffixes = 'so', '', { '' }
+end
+
+local launchers = {
+ windows = "start %s",
+ macosx = "open %s",
+ unix = "$BROWSER %s &> /dev/null &",
+}
+
+function os.launch(str)
+ os.execute(format(launchers[os.name] or launchers.unix,str))
+end
+
+if not os.times then -- ?
+ -- utime = user time
+ -- stime = system time
+ -- cutime = children user time
+ -- cstime = children system time
+ function os.times()
+ return {
+ utime = os.gettimeofday(), -- user
+ stime = 0, -- system
+ cutime = 0, -- children user
+ cstime = 0, -- children system
+ }
+ end
+end
+
+os.gettimeofday = os.gettimeofday or os.clock
+
+local startuptime = os.gettimeofday()
+
+function os.runtime()
+ return os.gettimeofday() - startuptime
+end
+
+--~ print(os.gettimeofday()-os.time())
+--~ os.sleep(1.234)
+--~ print (">>",os.runtime())
+--~ print(os.date("%H:%M:%S",os.gettimeofday()))
+--~ print(os.date("%H:%M:%S",os.time()))
+
+-- no need for function anymore as we have more clever code and helpers now
+-- this metatable trickery might as well disappear
+
+os.resolvers = os.resolvers or { } -- will become private
+
+local resolvers = os.resolvers
+
+setmetatable(os, { __index = function(t,k)
+ local r = resolvers[k]
+ return r and r(t,k) or nil -- no memoize
+end })
+
+-- we can use HOSTTYPE on some platforms
+
+local name, platform = os.name or "linux", os.getenv("MTX_PLATFORM") or ""
+
+local function guess()
+ local architecture = os.resultof("uname -m") or ""
+ if architecture ~= "" then
+ return architecture
+ end
+ architecture = os.getenv("HOSTTYPE") or ""
+ if architecture ~= "" then
+ return architecture
+ end
+ return os.resultof("echo $HOSTTYPE") or ""
+end
+
+if platform ~= "" then
+
+ os.platform = platform
+
+elseif os.type == "windows" then
+
+ -- we could set the variable directly, no function needed here
+
+ function os.resolvers.platform(t,k)
+ local platform, architecture = "", os.getenv("PROCESSOR_ARCHITECTURE") or ""
+ if find(architecture,"AMD64") then
+ platform = "mswin-64"
+ else
+ platform = "mswin"
+ end
+ os.setenv("MTX_PLATFORM",platform)
+ os.platform = platform
+ return platform
+ end
+
+elseif name == "linux" then
+
+ function os.resolvers.platform(t,k)
+ -- we sometimes have HOSTTYPE set so let's check that first
+ local platform, architecture = "", os.getenv("HOSTTYPE") or os.resultof("uname -m") or ""
+ if find(architecture,"x86_64") then
+ platform = "linux-64"
+ elseif find(architecture,"ppc") then
+ platform = "linux-ppc"
+ else
+ platform = "linux"
+ end
+ os.setenv("MTX_PLATFORM",platform)
+ os.platform = platform
+ return platform
+ end
+
+elseif name == "macosx" then
+
+ --[[
+ Identifying the architecture of OSX is quite a mess and this
+ is the best we can come up with. For some reason $HOSTTYPE is
+ a kind of pseudo environment variable, not known to the current
+ environment. And yes, uname cannot be trusted either, so there
+ is a change that you end up with a 32 bit run on a 64 bit system.
+ Also, some proper 64 bit intel macs are too cheap (low-end) and
+ therefore not permitted to run the 64 bit kernel.
+ ]]--
+
+ function os.resolvers.platform(t,k)
+ -- local platform, architecture = "", os.getenv("HOSTTYPE") or ""
+ -- if architecture == "" then
+ -- architecture = os.resultof("echo $HOSTTYPE") or ""
+ -- end
+ local platform, architecture = "", os.resultof("echo $HOSTTYPE") or ""
+ if architecture == "" then
+ -- print("\nI have no clue what kind of OSX you're running so let's assume an 32 bit intel.\n")
+ platform = "osx-intel"
+ elseif find(architecture,"i386") then
+ platform = "osx-intel"
+ elseif find(architecture,"x86_64") then
+ platform = "osx-64"
+ else
+ platform = "osx-ppc"
+ end
+ os.setenv("MTX_PLATFORM",platform)
+ os.platform = platform
+ return platform
+ end
+
+elseif name == "sunos" then
+
+ function os.resolvers.platform(t,k)
+ local platform, architecture = "", os.resultof("uname -m") or ""
+ if find(architecture,"sparc") then
+ platform = "solaris-sparc"
+ else -- if architecture == 'i86pc'
+ platform = "solaris-intel"
+ end
+ os.setenv("MTX_PLATFORM",platform)
+ os.platform = platform
+ return platform
+ end
+
+elseif name == "freebsd" then
+
+ function os.resolvers.platform(t,k)
+ local platform, architecture = "", os.resultof("uname -m") or ""
+ if find(architecture,"amd64") then
+ platform = "freebsd-amd64"
+ else
+ platform = "freebsd"
+ end
+ os.setenv("MTX_PLATFORM",platform)
+ os.platform = platform
+ return platform
+ end
+
+elseif name == "kfreebsd" then
+
+ function os.resolvers.platform(t,k)
+ -- we sometimes have HOSTTYPE set so let's check that first
+ local platform, architecture = "", os.getenv("HOSTTYPE") or os.resultof("uname -m") or ""
+ if find(architecture,"x86_64") then
+ platform = "kfreebsd-amd64"
+ else
+ platform = "kfreebsd-i386"
+ end
+ os.setenv("MTX_PLATFORM",platform)
+ os.platform = platform
+ return platform
+ end
+
+else
+
+ -- platform = "linux"
+ -- os.setenv("MTX_PLATFORM",platform)
+ -- os.platform = platform
+
+ function os.resolvers.platform(t,k)
+ local platform = "linux"
+ os.setenv("MTX_PLATFORM",platform)
+ os.platform = platform
+ return platform
+ end
+
+end
+
+-- beware, we set the randomseed
+
+-- from wikipedia: Version 4 UUIDs use a scheme relying only on random numbers. This algorithm sets the
+-- version number as well as two reserved bits. All other bits are set using a random or pseudorandom
+-- data source. Version 4 UUIDs have the form xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx with hexadecimal
+-- digits x and hexadecimal digits 8, 9, A, or B for y. e.g. f47ac10b-58cc-4372-a567-0e02b2c3d479.
+--
+-- as we don't call this function too often there is not so much risk on repetition
+
+local t = { 8, 9, "a", "b" }
+
+function os.uuid()
+ return format("%04x%04x-4%03x-%s%03x-%04x-%04x%04x%04x",
+ random(0xFFFF),random(0xFFFF),
+ random(0x0FFF),
+ t[ceil(random(4))] or 8,random(0x0FFF),
+ random(0xFFFF),
+ random(0xFFFF),random(0xFFFF),random(0xFFFF)
+ )
+end
+
+local d
+
+function os.timezone(delta)
+ d = d or tonumber(tonumber(date("%H")-date("!%H")))
+ if delta then
+ if d > 0 then
+ return format("+%02i:00",d)
+ else
+ return format("-%02i:00",-d)
+ end
+ else
+ return 1
+ end
+end
+
+local timeformat = format("%%s%s",os.timezone(true))
+local dateformat = "!%Y-%m-%d %H:%M:%S"
+
+function os.fulltime(t,default)
+ t = tonumber(t) or 0
+ if t > 0 then
+ -- valid time
+ elseif default then
+ return default
+ else
+ t = nil
+ end
+ return format(timeformat,date(dateformat,t))
+end
+
+local dateformat = "%Y-%m-%d %H:%M:%S"
+
+function os.localtime(t,default)
+ t = tonumber(t) or 0
+ if t > 0 then
+ -- valid time
+ elseif default then
+ return default
+ else
+ t = nil
+ end
+ return date(dateformat,t)
+end
+
+function os.converttime(t,default)
+ local t = tonumber(t)
+ if t and t > 0 then
+ return date(dateformat,t)
+ else
+ return default or "-"
+ end
+end
+
+local memory = { }
+
+local function which(filename)
+ local fullname = memory[filename]
+ if fullname == nil then
+ local suffix = file.suffix(filename)
+ local suffixes = suffix == "" and os.binsuffixes or { suffix }
+ for directory in gmatch(os.getenv("PATH"),"[^" .. io.pathseparator .."]+") do
+ local df = file.join(directory,filename)
+ for i=1,#suffixes do
+ local dfs = file.addsuffix(df,suffixes[i])
+ if io.exists(dfs) then
+ fullname = dfs
+ break
+ end
+ end
+ end
+ if not fullname then
+ fullname = false
+ end
+ memory[filename] = fullname
+ end
+ return fullname
+end
+
+os.which = which
+os.where = which
+
+function os.today()
+ return date("!*t") -- table with values
+end
+
+function os.now()
+ return date("!%Y-%m-%d %H:%M:%S") -- 2011-12-04 14:59:12
+end
+
+-- if not os.sleep and socket then
+-- os.sleep = socket.sleep
+-- end
+
+if not os.sleep then
+ local socket = socket
+ function os.sleep(n)
+ if not socket then
+ -- so we delay ... if os.sleep is really needed then one should also
+ -- be sure that socket can be found
+ socket = require("socket")
+ end
+ socket.sleep(n)
+ end
+end
+
+-- print(os.which("inkscape.exe"))
+-- print(os.which("inkscape"))
+-- print(os.which("gs.exe"))
+-- print(os.which("ps2pdf"))
diff --git a/tex/context/base/l-package.lua b/tex/context/base/l-package.lua
index 51da9f25d..579fd3941 100644
--- a/tex/context/base/l-package.lua
+++ b/tex/context/base/l-package.lua
@@ -1,340 +1,340 @@
-if not modules then modules = { } end modules ['l-package'] = {
- 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"
-}
-
--- Code moved from data-lua and changed into a plug-in.
-
--- We overload the regular loader. We do so because we operate mostly in
--- tds and use our own loader code. Alternatively we could use a more
--- extensive definition of package.path and package.cpath but even then
--- we're not done. Also, we now have better tracing.
---
--- -- local mylib = require("libtest")
--- -- local mysql = require("luasql.mysql")
-
-local type = type
-local gsub, format = string.gsub, string.format
-
-local P, S, Cs, lpegmatch = lpeg.P, lpeg.S, lpeg.Cs, lpeg.match
-
-local package = package
-local searchers = package.searchers or package.loaders
-
--- dummies
-
-local filejoin = file and file.join or function(path,name) return path .. "/" .. name end
-local isreadable = file and file.is_readable or function(name) local f = io.open(name) if f then f:close() return true end end
-local addsuffix = file and file.addsuffix or function(name,suffix) return name .. "." .. suffix end
-
--- local separator, concatinator, placeholder, pathofexecutable, ignorebefore = string.match(package.config,"(.-)\n(.-)\n(.-)\n(.-)\n(.-)\n")
---
--- local config = {
--- separator = separator, -- \ or /
--- concatinator = concatinator, -- ;
--- placeholder = placeholder, -- ? becomes name
--- pathofexecutable = pathofexecutable, -- ! becomes executables dir (on windows)
--- ignorebefore = ignorebefore, -- - remove all before this when making lua_open
--- }
-
-local function cleanpath(path) -- hm, don't we have a helper for this?
- return path
-end
-
-local pattern = Cs((((1-S("\\/"))^0 * (S("\\/")^1/"/"))^0 * (P(".")^1/"/"+P(1))^1) * -1)
-
-local function lualibfile(name)
- return lpegmatch(pattern,name) or name
-end
-
-local offset = luarocks and 1 or 0 -- todo: also check other extras
-
-local helpers = package.helpers or {
- cleanpath = cleanpath,
- lualibfile = lualibfile,
- trace = false,
- report = function(...) print(format(...)) end,
- builtin = {
- ["preload table"] = searchers[1+offset], -- special case, built-in libs
- ["path specification"] = searchers[2+offset],
- ["cpath specification"] = searchers[3+offset],
- ["all in one fallback"] = searchers[4+offset], -- special case, combined libs
- },
- methods = {
- },
- sequence = {
- "already loaded",
- "preload table",
- "lua extra list",
- "lib extra list",
- "path specification",
- "cpath specification",
- "all in one fallback",
- "not loaded",
- }
-}
-
-package.helpers = helpers
-
-local methods = helpers.methods
-local builtin = helpers.builtin
-
--- extra tds/ctx paths ... a bit of overhead for efficient tracing
-
-local extraluapaths = { }
-local extralibpaths = { }
-local luapaths = nil -- delayed
-local libpaths = nil -- delayed
-local oldluapath = nil
-local oldlibpath = nil
-
-local nofextralua = -1
-local nofextralib = -1
-local nofpathlua = -1
-local nofpathlib = -1
-
-local function listpaths(what,paths)
- local nofpaths = #paths
- if nofpaths > 0 then
- for i=1,nofpaths do
- helpers.report("using %s path %i: %s",what,i,paths[i])
- end
- else
- helpers.report("no %s paths defined",what)
- end
- return nofpaths
-end
-
-local function getextraluapaths()
- if helpers.trace and #extraluapaths ~= nofextralua then
- nofextralua = listpaths("extra lua",extraluapaths)
- end
- return extraluapaths
-end
-
-local function getextralibpaths()
- if helpers.trace and #extralibpaths ~= nofextralib then
- nofextralib = listpaths("extra lib",extralibpaths)
- end
- return extralibpaths
-end
-
-local function getluapaths()
- local luapath = package.path or ""
- if oldluapath ~= luapath then
- luapaths = file.splitpath(luapath,";")
- oldluapath = luapath
- nofpathlua = -1
- end
- if helpers.trace and #luapaths ~= nofpathlua then
- nofpathlua = listpaths("builtin lua",luapaths)
- end
- return luapaths
-end
-
-local function getlibpaths()
- local libpath = package.cpath or ""
- if oldlibpath ~= libpath then
- libpaths = file.splitpath(libpath,";")
- oldlibpath = libpath
- nofpathlib = -1
- end
- if helpers.trace and #libpaths ~= nofpathlib then
- nofpathlib = listpaths("builtin lib",libpaths)
- end
- return libpaths
-end
-
-package.luapaths = getluapaths
-package.libpaths = getlibpaths
-package.extraluapaths = getextraluapaths
-package.extralibpaths = getextralibpaths
-
-local hashes = {
- lua = { },
- lib = { },
-}
-
-local function registerpath(tag,what,target,...)
- local pathlist = { ... }
- local cleanpath = helpers.cleanpath
- local trace = helpers.trace
- local report = helpers.report
- local hash = hashes[what]
- --
- local function add(path)
- local path = cleanpath(path)
- if not hash[path] then
- target[#target+1] = path
- hash[path] = true
- if trace then
- report("registered %s path %s: %s",tag,#target,path)
- end
- else
- if trace then
- report("duplicate %s path: %s",tag,path)
- end
- end
- end
- --
- for p=1,#pathlist do
- local path = pathlist[p]
- if type(path) == "table" then
- for i=1,#path do
- add(path[i])
- end
- else
- add(path)
- end
- end
- return paths
-end
-
-helpers.registerpath = registerpath
-
-function package.extraluapath(...)
- registerpath("extra lua","lua",extraluapaths,...)
-end
-
-function package.extralibpath(...)
- registerpath("extra lib","lib",extralibpaths,...)
-end
-
--- lib loader (used elsewhere)
-
-local function loadedaslib(resolved,rawname) -- todo: strip all before first -
- local base = gsub(rawname,"%.","_")
- -- so, we can do a require("foo/bar") and initialize bar
- -- local base = gsub(file.basename(rawname),"%.","_")
- local init = "luaopen_" .. gsub(base,"%.","_")
- if helpers.trace then
- helpers.report("calling loadlib with '%s' with init '%s'",resolved,init)
- end
- return package.loadlib(resolved,init)
-end
-
-helpers.loadedaslib = loadedaslib
-
--- wrapped and new loaders
-
-local function loadedbypath(name,rawname,paths,islib,what)
- local trace = helpers.trace
- for p=1,#paths do
- local path = paths[p]
- local resolved = filejoin(path,name)
- if trace then
- helpers.report("%s path, identifying '%s' on '%s'",what,name,path)
- end
- if isreadable(resolved) then
- if trace then
- helpers.report("%s path, '%s' found on '%s'",what,name,resolved)
- end
- if islib then
- return loadedaslib(resolved,rawname)
- else
- return loadfile(resolved)
- end
- end
- end
-end
-
-helpers.loadedbypath = loadedbypath
-
-methods["already loaded"] = function(name)
- return package.loaded[name]
-end
-
-methods["preload table"] = function(name)
- return builtin["preload table"](name)
-end
-
-methods["lua extra list"] = function(name)
- return loadedbypath(addsuffix(lualibfile(name),"lua" ),name,getextraluapaths(),false,"lua")
-end
-
-methods["lib extra list"] = function(name)
- return loadedbypath(addsuffix(lualibfile(name),os.libsuffix),name,getextralibpaths(),true, "lib")
-end
-
-methods["path specification"] = function(name)
- getluapaths() -- triggers list building and tracing
- return builtin["path specification"](name)
-end
-
-methods["cpath specification"] = function(name)
- getlibpaths() -- triggers list building and tracing
- return builtin["cpath specification"](name)
-end
-
-methods["all in one fallback"] = function(name)
- return builtin["all in one fallback"](name)
-end
-
-methods["not loaded"] = function(name)
- if helpers.trace then
- helpers.report("unable to locate '%s'",name or "?")
- end
- return nil
-end
-
-local level = 0
-local used = { }
-
-helpers.traceused = false
-
-function helpers.loaded(name)
- local sequence = helpers.sequence
- level = level + 1
- for i=1,#sequence do
- local method = sequence[i]
- if helpers.trace then
- helpers.report("%s, level '%s', method '%s', name '%s'","locating",level,method,name)
- end
- local result, rest = methods[method](name)
- if type(result) == "function" then
- if helpers.trace then
- helpers.report("%s, level '%s', method '%s', name '%s'","found",level,method,name)
- end
- if helpers.traceused then
- used[#used+1] = { level = level, name = name }
- end
- level = level - 1
- return result, rest
- end
- end
- -- safeguard, we never come here
- level = level - 1
- return nil
-end
-
-function helpers.showused()
- local n = #used
- if n > 0 then
- helpers.report("%s libraries loaded:",n)
- helpers.report()
- for i=1,n do
- local u = used[i]
- helpers.report("%i %a",u.level,u.name)
- end
- helpers.report()
- end
-end
-
-function helpers.unload(name)
- if helpers.trace then
- if package.loaded[name] then
- helpers.report("unloading, name '%s', %s",name,"done")
- else
- helpers.report("unloading, name '%s', %s",name,"not loaded")
- end
- end
- package.loaded[name] = nil
-end
-
--- overloading require does not work out well so we need to push it in
--- front ..
-
-table.insert(searchers,1,helpers.loaded)
+if not modules then modules = { } end modules ['l-package'] = {
+ 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"
+}
+
+-- Code moved from data-lua and changed into a plug-in.
+
+-- We overload the regular loader. We do so because we operate mostly in
+-- tds and use our own loader code. Alternatively we could use a more
+-- extensive definition of package.path and package.cpath but even then
+-- we're not done. Also, we now have better tracing.
+--
+-- -- local mylib = require("libtest")
+-- -- local mysql = require("luasql.mysql")
+
+local type = type
+local gsub, format = string.gsub, string.format
+
+local P, S, Cs, lpegmatch = lpeg.P, lpeg.S, lpeg.Cs, lpeg.match
+
+local package = package
+local searchers = package.searchers or package.loaders
+
+-- dummies
+
+local filejoin = file and file.join or function(path,name) return path .. "/" .. name end
+local isreadable = file and file.is_readable or function(name) local f = io.open(name) if f then f:close() return true end end
+local addsuffix = file and file.addsuffix or function(name,suffix) return name .. "." .. suffix end
+
+-- local separator, concatinator, placeholder, pathofexecutable, ignorebefore = string.match(package.config,"(.-)\n(.-)\n(.-)\n(.-)\n(.-)\n")
+--
+-- local config = {
+-- separator = separator, -- \ or /
+-- concatinator = concatinator, -- ;
+-- placeholder = placeholder, -- ? becomes name
+-- pathofexecutable = pathofexecutable, -- ! becomes executables dir (on windows)
+-- ignorebefore = ignorebefore, -- - remove all before this when making lua_open
+-- }
+
+local function cleanpath(path) -- hm, don't we have a helper for this?
+ return path
+end
+
+local pattern = Cs((((1-S("\\/"))^0 * (S("\\/")^1/"/"))^0 * (P(".")^1/"/"+P(1))^1) * -1)
+
+local function lualibfile(name)
+ return lpegmatch(pattern,name) or name
+end
+
+local offset = luarocks and 1 or 0 -- todo: also check other extras
+
+local helpers = package.helpers or {
+ cleanpath = cleanpath,
+ lualibfile = lualibfile,
+ trace = false,
+ report = function(...) print(format(...)) end,
+ builtin = {
+ ["preload table"] = searchers[1+offset], -- special case, built-in libs
+ ["path specification"] = searchers[2+offset],
+ ["cpath specification"] = searchers[3+offset],
+ ["all in one fallback"] = searchers[4+offset], -- special case, combined libs
+ },
+ methods = {
+ },
+ sequence = {
+ "already loaded",
+ "preload table",
+ "lua extra list",
+ "lib extra list",
+ "path specification",
+ "cpath specification",
+ "all in one fallback",
+ "not loaded",
+ }
+}
+
+package.helpers = helpers
+
+local methods = helpers.methods
+local builtin = helpers.builtin
+
+-- extra tds/ctx paths ... a bit of overhead for efficient tracing
+
+local extraluapaths = { }
+local extralibpaths = { }
+local luapaths = nil -- delayed
+local libpaths = nil -- delayed
+local oldluapath = nil
+local oldlibpath = nil
+
+local nofextralua = -1
+local nofextralib = -1
+local nofpathlua = -1
+local nofpathlib = -1
+
+local function listpaths(what,paths)
+ local nofpaths = #paths
+ if nofpaths > 0 then
+ for i=1,nofpaths do
+ helpers.report("using %s path %i: %s",what,i,paths[i])
+ end
+ else
+ helpers.report("no %s paths defined",what)
+ end
+ return nofpaths
+end
+
+local function getextraluapaths()
+ if helpers.trace and #extraluapaths ~= nofextralua then
+ nofextralua = listpaths("extra lua",extraluapaths)
+ end
+ return extraluapaths
+end
+
+local function getextralibpaths()
+ if helpers.trace and #extralibpaths ~= nofextralib then
+ nofextralib = listpaths("extra lib",extralibpaths)
+ end
+ return extralibpaths
+end
+
+local function getluapaths()
+ local luapath = package.path or ""
+ if oldluapath ~= luapath then
+ luapaths = file.splitpath(luapath,";")
+ oldluapath = luapath
+ nofpathlua = -1
+ end
+ if helpers.trace and #luapaths ~= nofpathlua then
+ nofpathlua = listpaths("builtin lua",luapaths)
+ end
+ return luapaths
+end
+
+local function getlibpaths()
+ local libpath = package.cpath or ""
+ if oldlibpath ~= libpath then
+ libpaths = file.splitpath(libpath,";")
+ oldlibpath = libpath
+ nofpathlib = -1
+ end
+ if helpers.trace and #libpaths ~= nofpathlib then
+ nofpathlib = listpaths("builtin lib",libpaths)
+ end
+ return libpaths
+end
+
+package.luapaths = getluapaths
+package.libpaths = getlibpaths
+package.extraluapaths = getextraluapaths
+package.extralibpaths = getextralibpaths
+
+local hashes = {
+ lua = { },
+ lib = { },
+}
+
+local function registerpath(tag,what,target,...)
+ local pathlist = { ... }
+ local cleanpath = helpers.cleanpath
+ local trace = helpers.trace
+ local report = helpers.report
+ local hash = hashes[what]
+ --
+ local function add(path)
+ local path = cleanpath(path)
+ if not hash[path] then
+ target[#target+1] = path
+ hash[path] = true
+ if trace then
+ report("registered %s path %s: %s",tag,#target,path)
+ end
+ else
+ if trace then
+ report("duplicate %s path: %s",tag,path)
+ end
+ end
+ end
+ --
+ for p=1,#pathlist do
+ local path = pathlist[p]
+ if type(path) == "table" then
+ for i=1,#path do
+ add(path[i])
+ end
+ else
+ add(path)
+ end
+ end
+ return paths
+end
+
+helpers.registerpath = registerpath
+
+function package.extraluapath(...)
+ registerpath("extra lua","lua",extraluapaths,...)
+end
+
+function package.extralibpath(...)
+ registerpath("extra lib","lib",extralibpaths,...)
+end
+
+-- lib loader (used elsewhere)
+
+local function loadedaslib(resolved,rawname) -- todo: strip all before first -
+ local base = gsub(rawname,"%.","_")
+ -- so, we can do a require("foo/bar") and initialize bar
+ -- local base = gsub(file.basename(rawname),"%.","_")
+ local init = "luaopen_" .. gsub(base,"%.","_")
+ if helpers.trace then
+ helpers.report("calling loadlib with '%s' with init '%s'",resolved,init)
+ end
+ return package.loadlib(resolved,init)
+end
+
+helpers.loadedaslib = loadedaslib
+
+-- wrapped and new loaders
+
+local function loadedbypath(name,rawname,paths,islib,what)
+ local trace = helpers.trace
+ for p=1,#paths do
+ local path = paths[p]
+ local resolved = filejoin(path,name)
+ if trace then
+ helpers.report("%s path, identifying '%s' on '%s'",what,name,path)
+ end
+ if isreadable(resolved) then
+ if trace then
+ helpers.report("%s path, '%s' found on '%s'",what,name,resolved)
+ end
+ if islib then
+ return loadedaslib(resolved,rawname)
+ else
+ return loadfile(resolved)
+ end
+ end
+ end
+end
+
+helpers.loadedbypath = loadedbypath
+
+methods["already loaded"] = function(name)
+ return package.loaded[name]
+end
+
+methods["preload table"] = function(name)
+ return builtin["preload table"](name)
+end
+
+methods["lua extra list"] = function(name)
+ return loadedbypath(addsuffix(lualibfile(name),"lua" ),name,getextraluapaths(),false,"lua")
+end
+
+methods["lib extra list"] = function(name)
+ return loadedbypath(addsuffix(lualibfile(name),os.libsuffix),name,getextralibpaths(),true, "lib")
+end
+
+methods["path specification"] = function(name)
+ getluapaths() -- triggers list building and tracing
+ return builtin["path specification"](name)
+end
+
+methods["cpath specification"] = function(name)
+ getlibpaths() -- triggers list building and tracing
+ return builtin["cpath specification"](name)
+end
+
+methods["all in one fallback"] = function(name)
+ return builtin["all in one fallback"](name)
+end
+
+methods["not loaded"] = function(name)
+ if helpers.trace then
+ helpers.report("unable to locate '%s'",name or "?")
+ end
+ return nil
+end
+
+local level = 0
+local used = { }
+
+helpers.traceused = false
+
+function helpers.loaded(name)
+ local sequence = helpers.sequence
+ level = level + 1
+ for i=1,#sequence do
+ local method = sequence[i]
+ if helpers.trace then
+ helpers.report("%s, level '%s', method '%s', name '%s'","locating",level,method,name)
+ end
+ local result, rest = methods[method](name)
+ if type(result) == "function" then
+ if helpers.trace then
+ helpers.report("%s, level '%s', method '%s', name '%s'","found",level,method,name)
+ end
+ if helpers.traceused then
+ used[#used+1] = { level = level, name = name }
+ end
+ level = level - 1
+ return result, rest
+ end
+ end
+ -- safeguard, we never come here
+ level = level - 1
+ return nil
+end
+
+function helpers.showused()
+ local n = #used
+ if n > 0 then
+ helpers.report("%s libraries loaded:",n)
+ helpers.report()
+ for i=1,n do
+ local u = used[i]
+ helpers.report("%i %a",u.level,u.name)
+ end
+ helpers.report()
+ end
+end
+
+function helpers.unload(name)
+ if helpers.trace then
+ if package.loaded[name] then
+ helpers.report("unloading, name '%s', %s",name,"done")
+ else
+ helpers.report("unloading, name '%s', %s",name,"not loaded")
+ end
+ end
+ package.loaded[name] = nil
+end
+
+-- overloading require does not work out well so we need to push it in
+-- front ..
+
+table.insert(searchers,1,helpers.loaded)
diff --git a/tex/context/base/l-pdfview.lua b/tex/context/base/l-pdfview.lua
index 643d538e7..80033900f 100644
--- a/tex/context/base/l-pdfview.lua
+++ b/tex/context/base/l-pdfview.lua
@@ -1,143 +1,143 @@
-if not modules then modules = { } end modules ['l-pdfview'] = {
- version = 1.001,
- comment = "companion to mtx-context.lua",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- Todo: figure out pdfopen/pdfclose on linux. Calling e.g. okular directly
--- doesn't work in linux when issued from scite as it blocks the editor (no
--- & possible or so). Unfortunately pdfopen keeps changing with not keeping
--- downward compatibility (command line arguments and so).
-
--- no 2>&1 any more, needs checking on windows
-
-local format, concat = string.format, table.concat
-
-pdfview = pdfview or { }
-
-local opencalls, closecalls, allcalls, runner
-
-if os.type == "windows" then
-
- opencalls = {
- ['default'] = "pdfopen --rxi --file",
- ['acrobat'] = "pdfopen --rxi --file",
- ['fullacrobat'] = "pdfopen --axi --file",
- ['okular'] = 'start "test" "c:/data/system/kde/bin/okular.exe" --unique', -- todo!
- ['sumatra'] = 'start "test" "c:/data/system/sumatrapdf/sumatrapdf.exe" -reuse-instance',
- ['okular'] = 'start "test" "okular.exe" --unique',
- ['sumatra'] = 'start "test" "sumatrapdf.exe" -reuse-instance -bg-color 0xCCCCCC',
- }
- closecalls= {
- ['default'] = "pdfclose --file",
- ['acrobat'] = "pdfclose --file",
- ['okular'] = false,
- ['sumatra'] = false,
- }
- allcalls = {
- ['default'] = "pdfclose --all",
- ['acrobat'] = "pdfclose --all",
- ['okular'] = false,
- ['sumatra'] = false,
- }
-
- pdfview.method = "acrobat"
-
- runner = function(...)
--- os.spawn(...)
- os.execute(...)
- end
-
-else
-
- opencalls = {
- ['default'] = "pdfopen", -- we could pass the default here
- ['okular'] = 'okular --unique'
- }
- closecalls= {
- ['default'] = "pdfclose --file",
- ['okular'] = false,
- }
- allcalls = {
- ['default'] = "pdfclose --all",
- ['okular'] = false,
- }
-
- pdfview.method = "okular"
-
- runner = function(...)
- os.spawn(...)
- end
-
-end
-
-directives.register("pdfview.method", function(v)
- pdfview.method = (opencalls[v] and v) or 'default'
-end)
-
-function pdfview.setmethod(method)
- if method and opencalls[method] then
- pdfview.method = method
- end
-end
-
-function pdfview.methods()
- return concat(table.sortedkeys(opencalls), " ")
-end
-
-function pdfview.status()
- return format("pdfview methods: %s, current method: %s (directives_pdfview_method)",pdfview.methods(),tostring(pdfview.method))
-end
-
--- local openedfiles = { }
-
-local function fullname(name)
- return file.addsuffix(name,"pdf")
-end
-
-function pdfview.open(...)
- local opencall = opencalls[pdfview.method]
- if opencall then
- local t = { ... }
- for i=1,#t do
- local name = fullname(t[i])
- if io.exists(name) then
- runner(format('%s "%s"', opencall, name))
- -- openedfiles[name] = true
- end
- end
- end
-end
-
-function pdfview.close(...)
- local closecall = closecalls[pdfview.method]
- if closecall then
- local t = { ... }
- for i=1,#t do
- local name = fullname(t[i])
- -- if openedfiles[name] then
- runner(format('%s "%s"', closecall, name))
- -- openedfiles[name] = nil
- -- else
- -- pdfview.closeall()
- -- break
- -- end
- end
- end
-end
-
-function pdfview.closeall()
- local allcall = allcalls[pdfview.method]
- if allcall then
- runner(format('%s', allcall))
- end
- -- openedfiles = { }
-end
-
---~ pdfview.open("t:/document/show-exa.pdf")
---~ os.sleep(3)
---~ pdfview.close("t:/document/show-exa.pdf")
-
-return pdfview
+if not modules then modules = { } end modules ['l-pdfview'] = {
+ version = 1.001,
+ comment = "companion to mtx-context.lua",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Todo: figure out pdfopen/pdfclose on linux. Calling e.g. okular directly
+-- doesn't work in linux when issued from scite as it blocks the editor (no
+-- & possible or so). Unfortunately pdfopen keeps changing with not keeping
+-- downward compatibility (command line arguments and so).
+
+-- no 2>&1 any more, needs checking on windows
+
+local format, concat = string.format, table.concat
+
+pdfview = pdfview or { }
+
+local opencalls, closecalls, allcalls, runner
+
+if os.type == "windows" then
+
+ opencalls = {
+ ['default'] = "pdfopen --rxi --file",
+ ['acrobat'] = "pdfopen --rxi --file",
+ ['fullacrobat'] = "pdfopen --axi --file",
+ ['okular'] = 'start "test" "c:/data/system/kde/bin/okular.exe" --unique', -- todo!
+ ['sumatra'] = 'start "test" "c:/data/system/sumatrapdf/sumatrapdf.exe" -reuse-instance',
+ ['okular'] = 'start "test" "okular.exe" --unique',
+ ['sumatra'] = 'start "test" "sumatrapdf.exe" -reuse-instance -bg-color 0xCCCCCC',
+ }
+ closecalls= {
+ ['default'] = "pdfclose --file",
+ ['acrobat'] = "pdfclose --file",
+ ['okular'] = false,
+ ['sumatra'] = false,
+ }
+ allcalls = {
+ ['default'] = "pdfclose --all",
+ ['acrobat'] = "pdfclose --all",
+ ['okular'] = false,
+ ['sumatra'] = false,
+ }
+
+ pdfview.method = "acrobat"
+
+ runner = function(...)
+-- os.spawn(...)
+ os.execute(...)
+ end
+
+else
+
+ opencalls = {
+ ['default'] = "pdfopen", -- we could pass the default here
+ ['okular'] = 'okular --unique'
+ }
+ closecalls= {
+ ['default'] = "pdfclose --file",
+ ['okular'] = false,
+ }
+ allcalls = {
+ ['default'] = "pdfclose --all",
+ ['okular'] = false,
+ }
+
+ pdfview.method = "okular"
+
+ runner = function(...)
+ os.spawn(...)
+ end
+
+end
+
+directives.register("pdfview.method", function(v)
+ pdfview.method = (opencalls[v] and v) or 'default'
+end)
+
+function pdfview.setmethod(method)
+ if method and opencalls[method] then
+ pdfview.method = method
+ end
+end
+
+function pdfview.methods()
+ return concat(table.sortedkeys(opencalls), " ")
+end
+
+function pdfview.status()
+ return format("pdfview methods: %s, current method: %s (directives_pdfview_method)",pdfview.methods(),tostring(pdfview.method))
+end
+
+-- local openedfiles = { }
+
+local function fullname(name)
+ return file.addsuffix(name,"pdf")
+end
+
+function pdfview.open(...)
+ local opencall = opencalls[pdfview.method]
+ if opencall then
+ local t = { ... }
+ for i=1,#t do
+ local name = fullname(t[i])
+ if io.exists(name) then
+ runner(format('%s "%s"', opencall, name))
+ -- openedfiles[name] = true
+ end
+ end
+ end
+end
+
+function pdfview.close(...)
+ local closecall = closecalls[pdfview.method]
+ if closecall then
+ local t = { ... }
+ for i=1,#t do
+ local name = fullname(t[i])
+ -- if openedfiles[name] then
+ runner(format('%s "%s"', closecall, name))
+ -- openedfiles[name] = nil
+ -- else
+ -- pdfview.closeall()
+ -- break
+ -- end
+ end
+ end
+end
+
+function pdfview.closeall()
+ local allcall = allcalls[pdfview.method]
+ if allcall then
+ runner(format('%s', allcall))
+ end
+ -- openedfiles = { }
+end
+
+--~ pdfview.open("t:/document/show-exa.pdf")
+--~ os.sleep(3)
+--~ pdfview.close("t:/document/show-exa.pdf")
+
+return pdfview
diff --git a/tex/context/base/l-set.lua b/tex/context/base/l-set.lua
index dfaf89284..2370f0139 100644
--- a/tex/context/base/l-set.lua
+++ b/tex/context/base/l-set.lua
@@ -1,87 +1,87 @@
-if not modules then modules = { } end modules ['l-set'] = {
- 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"
-}
-
--- This will become obsolete when we have the bitset library embedded.
-
-set = set or { }
-
-local nums = { }
-local tabs = { }
-local concat = table.concat
-local next, type = next, type
-
-set.create = table.tohash
-
-function set.tonumber(t)
- if next(t) then
- local s = ""
- -- we could save mem by sorting, but it slows down
- for k, v in next, t do
- if v then
- -- why bother about the leading space
- s = s .. " " .. k
- end
- end
- local n = nums[s]
- if not n then
- n = #tabs + 1
- tabs[n] = t
- nums[s] = n
- end
- return n
- else
- return 0
- end
-end
-
-function set.totable(n)
- if n == 0 then
- return { }
- else
- return tabs[n] or { }
- end
-end
-
-function set.tolist(n)
- if n == 0 or not tabs[n] then
- return ""
- else
- local t, n = { }, 0
- for k, v in next, tabs[n] do
- if v then
- n = n + 1
- t[n] = k
- end
- end
- return concat(t," ")
- end
-end
-
-function set.contains(n,s)
- if type(n) == "table" then
- return n[s]
- elseif n == 0 then
- return false
- else
- local t = tabs[n]
- return t and t[s]
- end
-end
-
---~ local c = set.create{'aap','noot','mies'}
---~ local s = set.tonumber(c)
---~ local t = set.totable(s)
---~ print(t['aap'])
---~ local c = set.create{'zus','wim','jet'}
---~ local s = set.tonumber(c)
---~ local t = set.totable(s)
---~ print(t['aap'])
---~ print(t['jet'])
---~ print(set.contains(t,'jet'))
---~ print(set.contains(t,'aap'))
-
+if not modules then modules = { } end modules ['l-set'] = {
+ 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"
+}
+
+-- This will become obsolete when we have the bitset library embedded.
+
+set = set or { }
+
+local nums = { }
+local tabs = { }
+local concat = table.concat
+local next, type = next, type
+
+set.create = table.tohash
+
+function set.tonumber(t)
+ if next(t) then
+ local s = ""
+ -- we could save mem by sorting, but it slows down
+ for k, v in next, t do
+ if v then
+ -- why bother about the leading space
+ s = s .. " " .. k
+ end
+ end
+ local n = nums[s]
+ if not n then
+ n = #tabs + 1
+ tabs[n] = t
+ nums[s] = n
+ end
+ return n
+ else
+ return 0
+ end
+end
+
+function set.totable(n)
+ if n == 0 then
+ return { }
+ else
+ return tabs[n] or { }
+ end
+end
+
+function set.tolist(n)
+ if n == 0 or not tabs[n] then
+ return ""
+ else
+ local t, n = { }, 0
+ for k, v in next, tabs[n] do
+ if v then
+ n = n + 1
+ t[n] = k
+ end
+ end
+ return concat(t," ")
+ end
+end
+
+function set.contains(n,s)
+ if type(n) == "table" then
+ return n[s]
+ elseif n == 0 then
+ return false
+ else
+ local t = tabs[n]
+ return t and t[s]
+ end
+end
+
+--~ local c = set.create{'aap','noot','mies'}
+--~ local s = set.tonumber(c)
+--~ local t = set.totable(s)
+--~ print(t['aap'])
+--~ local c = set.create{'zus','wim','jet'}
+--~ local s = set.tonumber(c)
+--~ local t = set.totable(s)
+--~ print(t['aap'])
+--~ print(t['jet'])
+--~ print(set.contains(t,'jet'))
+--~ print(set.contains(t,'aap'))
+
diff --git a/tex/context/base/l-string.lua b/tex/context/base/l-string.lua
index c87c57521..77c076cc5 100644
--- a/tex/context/base/l-string.lua
+++ b/tex/context/base/l-string.lua
@@ -1,205 +1,205 @@
-if not modules then modules = { } end modules ['l-string'] = {
- 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 string = string
-local sub, gmatch, format, char, byte, rep, lower = string.sub, string.gmatch, string.format, string.char, string.byte, string.rep, string.lower
-local lpegmatch, patterns = lpeg.match, lpeg.patterns
-local P, S, C, Ct, Cc, Cs = lpeg.P, lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cs
-
--- Some functions are already defined in l-lpeg and maybe some from here will
--- move there (unless we also expose caches).
-
--- if not string.split then
---
--- function string.split(str,pattern)
--- local t = { }
--- if #str > 0 then
--- local n = 1
--- for s in gmatch(str..pattern,"(.-)"..pattern) do
--- t[n] = s
--- n = n + 1
--- end
--- end
--- return t
--- end
---
--- end
-
--- function string.unquoted(str)
--- return (gsub(str,"^([\"\'])(.*)%1$","%2")) -- interesting pattern
--- end
-
-local unquoted = patterns.squote * C(patterns.nosquote) * patterns.squote
- + patterns.dquote * C(patterns.nodquote) * patterns.dquote
-
-function string.unquoted(str)
- return lpegmatch(unquoted,str) or str
-end
-
--- print(string.unquoted("test"))
--- print(string.unquoted([["t\"est"]]))
--- print(string.unquoted([["t\"est"x]]))
--- print(string.unquoted("\'test\'"))
--- print(string.unquoted('"test"'))
--- print(string.unquoted('"test"'))
-
-function string.quoted(str)
- return format("%q",str) -- always double quote
-end
-
-function string.count(str,pattern) -- variant 3
- local n = 0
- for _ in gmatch(str,pattern) do -- not for utf
- n = n + 1
- end
- return n
-end
-
-function string.limit(str,n,sentinel) -- not utf proof
- if #str > n then
- sentinel = sentinel or "..."
- return sub(str,1,(n-#sentinel)) .. sentinel
- else
- return str
- end
-end
-
-local stripper = patterns.stripper
-local collapser = patterns.collapser
-local longtostring = patterns.longtostring
-
-function string.strip(str)
- return lpegmatch(stripper,str) or ""
-end
-
-function string.collapsespaces(str)
- return lpegmatch(collapser,str) or ""
-end
-
-function string.longtostring(str)
- return lpegmatch(longtostring,str) or ""
-end
-
--- function string.is_empty(str)
--- return not find(str,"%S")
--- end
-
-local pattern = P(" ")^0 * P(-1)
-
-function string.is_empty(str)
- if str == "" then
- return true
- else
- return lpegmatch(pattern,str) and true or false
- end
-end
-
--- if not string.escapedpattern then
---
--- local patterns_escapes = {
--- ["%"] = "%%",
--- ["."] = "%.",
--- ["+"] = "%+", ["-"] = "%-", ["*"] = "%*",
--- ["["] = "%[", ["]"] = "%]",
--- ["("] = "%(", [")"] = "%)",
--- -- ["{"] = "%{", ["}"] = "%}"
--- -- ["^"] = "%^", ["$"] = "%$",
--- }
---
--- local simple_escapes = {
--- ["-"] = "%-",
--- ["."] = "%.",
--- ["?"] = ".",
--- ["*"] = ".*",
--- }
---
--- function string.escapedpattern(str,simple)
--- return (gsub(str,".",simple and simple_escapes or patterns_escapes))
--- end
---
--- function string.topattern(str,lowercase,strict)
--- if str == "" then
--- return ".*"
--- else
--- str = gsub(str,".",simple_escapes)
--- if lowercase then
--- str = lower(str)
--- end
--- if strict then
--- return "^" .. str .. "$"
--- else
--- return str
--- end
--- end
--- end
---
--- end
-
---- needs checking
-
-local anything = patterns.anything
-local allescapes = Cc("%") * S(".-+%?()[]*") -- also {} and ^$ ?
-local someescapes = Cc("%") * S(".-+%()[]") -- also {} and ^$ ?
-local matchescapes = Cc(".") * S("*?") -- wildcard and single match
-
-local pattern_a = Cs ( ( allescapes + anything )^0 )
-local pattern_b = Cs ( ( someescapes + matchescapes + anything )^0 )
-local pattern_c = Cs ( Cc("^") * ( someescapes + matchescapes + anything )^0 * Cc("$") )
-
-function string.escapedpattern(str,simple)
- return lpegmatch(simple and pattern_b or pattern_a,str)
-end
-
-function string.topattern(str,lowercase,strict)
- if str=="" or type(str) ~= "string" then
- return ".*"
- elseif strict then
- str = lpegmatch(pattern_c,str)
- else
- str = lpegmatch(pattern_b,str)
- end
- if lowercase then
- return lower(str)
- else
- return str
- end
-end
-
--- print(string.escapedpattern("12+34*.tex",false))
--- print(string.escapedpattern("12+34*.tex",true))
--- print(string.topattern ("12+34*.tex",false,false))
--- print(string.topattern ("12+34*.tex",false,true))
-
-function string.valid(str,default)
- return (type(str) == "string" and str ~= "" and str) or default or nil
-end
-
--- handy fallback
-
-string.itself = function(s) return s end
-
--- also handy (see utf variant)
-
-local pattern = Ct(C(1)^0) -- string and not utf !
-
-function string.totable(str)
- return lpegmatch(pattern,str)
-end
-
--- handy from within tex:
-
-local replacer = lpeg.replacer("@","%%") -- Watch the escaped % in lpeg!
-
-function string.tformat(fmt,...)
- return format(lpegmatch(replacer,fmt),...)
-end
-
--- obsolete names:
-
-string.quote = string.quoted
-string.unquote = string.unquoted
+if not modules then modules = { } end modules ['l-string'] = {
+ 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 string = string
+local sub, gmatch, format, char, byte, rep, lower = string.sub, string.gmatch, string.format, string.char, string.byte, string.rep, string.lower
+local lpegmatch, patterns = lpeg.match, lpeg.patterns
+local P, S, C, Ct, Cc, Cs = lpeg.P, lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cs
+
+-- Some functions are already defined in l-lpeg and maybe some from here will
+-- move there (unless we also expose caches).
+
+-- if not string.split then
+--
+-- function string.split(str,pattern)
+-- local t = { }
+-- if #str > 0 then
+-- local n = 1
+-- for s in gmatch(str..pattern,"(.-)"..pattern) do
+-- t[n] = s
+-- n = n + 1
+-- end
+-- end
+-- return t
+-- end
+--
+-- end
+
+-- function string.unquoted(str)
+-- return (gsub(str,"^([\"\'])(.*)%1$","%2")) -- interesting pattern
+-- end
+
+local unquoted = patterns.squote * C(patterns.nosquote) * patterns.squote
+ + patterns.dquote * C(patterns.nodquote) * patterns.dquote
+
+function string.unquoted(str)
+ return lpegmatch(unquoted,str) or str
+end
+
+-- print(string.unquoted("test"))
+-- print(string.unquoted([["t\"est"]]))
+-- print(string.unquoted([["t\"est"x]]))
+-- print(string.unquoted("\'test\'"))
+-- print(string.unquoted('"test"'))
+-- print(string.unquoted('"test"'))
+
+function string.quoted(str)
+ return format("%q",str) -- always double quote
+end
+
+function string.count(str,pattern) -- variant 3
+ local n = 0
+ for _ in gmatch(str,pattern) do -- not for utf
+ n = n + 1
+ end
+ return n
+end
+
+function string.limit(str,n,sentinel) -- not utf proof
+ if #str > n then
+ sentinel = sentinel or "..."
+ return sub(str,1,(n-#sentinel)) .. sentinel
+ else
+ return str
+ end
+end
+
+local stripper = patterns.stripper
+local collapser = patterns.collapser
+local longtostring = patterns.longtostring
+
+function string.strip(str)
+ return lpegmatch(stripper,str) or ""
+end
+
+function string.collapsespaces(str)
+ return lpegmatch(collapser,str) or ""
+end
+
+function string.longtostring(str)
+ return lpegmatch(longtostring,str) or ""
+end
+
+-- function string.is_empty(str)
+-- return not find(str,"%S")
+-- end
+
+local pattern = P(" ")^0 * P(-1)
+
+function string.is_empty(str)
+ if str == "" then
+ return true
+ else
+ return lpegmatch(pattern,str) and true or false
+ end
+end
+
+-- if not string.escapedpattern then
+--
+-- local patterns_escapes = {
+-- ["%"] = "%%",
+-- ["."] = "%.",
+-- ["+"] = "%+", ["-"] = "%-", ["*"] = "%*",
+-- ["["] = "%[", ["]"] = "%]",
+-- ["("] = "%(", [")"] = "%)",
+-- -- ["{"] = "%{", ["}"] = "%}"
+-- -- ["^"] = "%^", ["$"] = "%$",
+-- }
+--
+-- local simple_escapes = {
+-- ["-"] = "%-",
+-- ["."] = "%.",
+-- ["?"] = ".",
+-- ["*"] = ".*",
+-- }
+--
+-- function string.escapedpattern(str,simple)
+-- return (gsub(str,".",simple and simple_escapes or patterns_escapes))
+-- end
+--
+-- function string.topattern(str,lowercase,strict)
+-- if str == "" then
+-- return ".*"
+-- else
+-- str = gsub(str,".",simple_escapes)
+-- if lowercase then
+-- str = lower(str)
+-- end
+-- if strict then
+-- return "^" .. str .. "$"
+-- else
+-- return str
+-- end
+-- end
+-- end
+--
+-- end
+
+--- needs checking
+
+local anything = patterns.anything
+local allescapes = Cc("%") * S(".-+%?()[]*") -- also {} and ^$ ?
+local someescapes = Cc("%") * S(".-+%()[]") -- also {} and ^$ ?
+local matchescapes = Cc(".") * S("*?") -- wildcard and single match
+
+local pattern_a = Cs ( ( allescapes + anything )^0 )
+local pattern_b = Cs ( ( someescapes + matchescapes + anything )^0 )
+local pattern_c = Cs ( Cc("^") * ( someescapes + matchescapes + anything )^0 * Cc("$") )
+
+function string.escapedpattern(str,simple)
+ return lpegmatch(simple and pattern_b or pattern_a,str)
+end
+
+function string.topattern(str,lowercase,strict)
+ if str=="" or type(str) ~= "string" then
+ return ".*"
+ elseif strict then
+ str = lpegmatch(pattern_c,str)
+ else
+ str = lpegmatch(pattern_b,str)
+ end
+ if lowercase then
+ return lower(str)
+ else
+ return str
+ end
+end
+
+-- print(string.escapedpattern("12+34*.tex",false))
+-- print(string.escapedpattern("12+34*.tex",true))
+-- print(string.topattern ("12+34*.tex",false,false))
+-- print(string.topattern ("12+34*.tex",false,true))
+
+function string.valid(str,default)
+ return (type(str) == "string" and str ~= "" and str) or default or nil
+end
+
+-- handy fallback
+
+string.itself = function(s) return s end
+
+-- also handy (see utf variant)
+
+local pattern = Ct(C(1)^0) -- string and not utf !
+
+function string.totable(str)
+ return lpegmatch(pattern,str)
+end
+
+-- handy from within tex:
+
+local replacer = lpeg.replacer("@","%%") -- Watch the escaped % in lpeg!
+
+function string.tformat(fmt,...)
+ return format(lpegmatch(replacer,fmt),...)
+end
+
+-- obsolete names:
+
+string.quote = string.quoted
+string.unquote = string.unquoted
diff --git a/tex/context/base/l-table.lua b/tex/context/base/l-table.lua
index 54c2b86e3..9a1b97fff 100644
--- a/tex/context/base/l-table.lua
+++ b/tex/context/base/l-table.lua
@@ -1,1362 +1,1362 @@
-if not modules then modules = { } end modules ['l-table'] = {
- 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 type, next, tostring, tonumber, ipairs, select = type, next, tostring, tonumber, ipairs, select
-local table, string = table, string
-local concat, sort, insert, remove = table.concat, table.sort, table.insert, table.remove
-local format, lower, dump = string.format, string.lower, string.dump
-local getmetatable, setmetatable = getmetatable, setmetatable
-local getinfo = debug.getinfo
-local lpegmatch, patterns = lpeg.match, lpeg.patterns
-local floor = math.floor
-
--- extra functions, some might go (when not used)
-
-local stripper = patterns.stripper
-
-function table.strip(tab)
- local lst, l = { }, 0
- for i=1,#tab do
- local s = lpegmatch(stripper,tab[i]) or ""
- if s == "" then
- -- skip this one
- else
- l = l + 1
- lst[l] = s
- end
- end
- return lst
-end
-
-function table.keys(t)
- if t then
- local keys, k = { }, 0
- for key, _ in next, t do
- k = k + 1
- keys[k] = key
- end
- return keys
- else
- return { }
- end
-end
-
-local function compare(a,b)
- local ta, tb = type(a), type(b) -- needed, else 11 < 2
- if ta == tb then
- return a < b
- else
- return tostring(a) < tostring(b)
- end
-end
-
-local function sortedkeys(tab)
- if tab then
- local srt, category, s = { }, 0, 0 -- 0=unknown 1=string, 2=number 3=mixed
- for key,_ in next, tab do
- s = s + 1
- srt[s] = key
- if category == 3 then
- -- no further check
- else
- local tkey = type(key)
- if tkey == "string" then
- category = (category == 2 and 3) or 1
- elseif tkey == "number" then
- category = (category == 1 and 3) or 2
- else
- category = 3
- end
- end
- end
- if category == 0 or category == 3 then
- sort(srt,compare)
- else
- sort(srt)
- end
- return srt
- else
- return { }
- end
-end
-
-local function sortedhashkeys(tab,cmp) -- fast one
- if tab then
- local srt, s = { }, 0
- for key,_ in next, tab do
- if key then
- s= s + 1
- srt[s] = key
- end
- end
- sort(srt,cmp)
- return srt
- else
- return { }
- end
-end
-
-function table.allkeys(t)
- local keys = { }
- for k, v in next, t do
- for k, v in next, v do
- keys[k] = true
- end
- end
- return sortedkeys(keys)
-end
-
-table.sortedkeys = sortedkeys
-table.sortedhashkeys = sortedhashkeys
-
-local function nothing() end
-
-local function sortedhash(t,cmp)
- if t then
- local s
- if cmp then
- -- it would be nice if the sort function would accept a third argument (or nicer, an optional first)
- s = sortedhashkeys(t,function(a,b) return cmp(t,a,b) end)
- else
- s = sortedkeys(t) -- the robust one
- end
- local n = 0
- local function kv(s)
- n = n + 1
- local k = s[n]
- return k, t[k]
- end
- return kv, s
- else
- return nothing
- end
-end
-
-table.sortedhash = sortedhash
-table.sortedpairs = sortedhash -- obsolete
-
-function table.append(t,list)
- local n = #t
- for i=1,#list do
- n = n + 1
- t[n] = list[i]
- end
- return t
-end
-
-function table.prepend(t, list)
- local nl = #list
- local nt = nl + #t
- for i=#t,1,-1 do
- t[nt] = t[i]
- nt = nt - 1
- end
- for i=1,#list do
- t[i] = list[i]
- end
- return t
-end
-
--- function table.merge(t, ...) -- first one is target
--- t = t or { }
--- local lst = { ... }
--- for i=1,#lst do
--- for k, v in next, lst[i] do
--- t[k] = v
--- end
--- end
--- return t
--- end
-
-function table.merge(t, ...) -- first one is target
- t = t or { }
- for i=1,select("#",...) do
- for k, v in next, (select(i,...)) do
- t[k] = v
- end
- end
- return t
-end
-
--- function table.merged(...)
--- local tmp, lst = { }, { ... }
--- for i=1,#lst do
--- for k, v in next, lst[i] do
--- tmp[k] = v
--- end
--- end
--- return tmp
--- end
-
-function table.merged(...)
- local t = { }
- for i=1,select("#",...) do
- for k, v in next, (select(i,...)) do
- t[k] = v
- end
- end
- return t
-end
-
--- function table.imerge(t, ...)
--- local lst, nt = { ... }, #t
--- for i=1,#lst do
--- local nst = lst[i]
--- for j=1,#nst do
--- nt = nt + 1
--- t[nt] = nst[j]
--- end
--- end
--- return t
--- end
-
-function table.imerge(t, ...)
- local nt = #t
- for i=1,select("#",...) do
- local nst = select(i,...)
- for j=1,#nst do
- nt = nt + 1
- t[nt] = nst[j]
- end
- end
- return t
-end
-
--- function table.imerged(...)
--- local tmp, ntmp, lst = { }, 0, {...}
--- for i=1,#lst do
--- local nst = lst[i]
--- for j=1,#nst do
--- ntmp = ntmp + 1
--- tmp[ntmp] = nst[j]
--- end
--- end
--- return tmp
--- end
-
-function table.imerged(...)
- local tmp, ntmp = { }, 0
- for i=1,select("#",...) do
- local nst = select(i,...)
- for j=1,#nst do
- ntmp = ntmp + 1
- tmp[ntmp] = nst[j]
- end
- end
- return tmp
-end
-
-local function fastcopy(old,metatabletoo) -- fast one
- if old then
- local new = { }
- for k, v in next, old do
- if type(v) == "table" then
- new[k] = fastcopy(v,metatabletoo) -- was just table.copy
- else
- new[k] = v
- end
- end
- if metatabletoo then
- -- optional second arg
- local mt = getmetatable(old)
- if mt then
- setmetatable(new,mt)
- end
- end
- return new
- else
- return { }
- end
-end
-
--- todo : copy without metatable
-
-local function copy(t, tables) -- taken from lua wiki, slightly adapted
- tables = tables or { }
- local tcopy = {}
- if not tables[t] then
- tables[t] = tcopy
- end
- for i,v in next, t do -- brrr, what happens with sparse indexed
- if type(i) == "table" then
- if tables[i] then
- i = tables[i]
- else
- i = copy(i, tables)
- end
- end
- if type(v) ~= "table" then
- tcopy[i] = v
- elseif tables[v] then
- tcopy[i] = tables[v]
- else
- tcopy[i] = copy(v, tables)
- end
- end
- local mt = getmetatable(t)
- if mt then
- setmetatable(tcopy,mt)
- end
- return tcopy
-end
-
-table.fastcopy = fastcopy
-table.copy = copy
-
-function table.derive(parent) -- for the moment not public
- local child = { }
- if parent then
- setmetatable(child,{ __index = parent })
- end
- return child
-end
-
-function table.tohash(t,value)
- local h = { }
- if t then
- if value == nil then value = true end
- for _, v in next, t do -- no ipairs here
- h[v] = value
- end
- end
- return h
-end
-
-function table.fromhash(t)
- local hsh, h = { }, 0
- for k, v in next, t do -- no ipairs here
- if v then
- h = h + 1
- hsh[h] = k
- end
- end
- return hsh
-end
-
-local noquotes, hexify, handle, reduce, compact, inline, functions
-
-local reserved = table.tohash { -- intercept a language inconvenience: no reserved words as key
- 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'if',
- 'in', 'local', 'nil', 'not', 'or', 'repeat', 'return', 'then', 'true', 'until', 'while',
-}
-
-local function simple_table(t)
- if #t > 0 then
- local n = 0
- for _,v in next, t do
- n = n + 1
- end
- if n == #t then
- local tt, nt = { }, 0
- for i=1,#t do
- local v = t[i]
- local tv = type(v)
- if tv == "number" then
- nt = nt + 1
- if hexify then
- tt[nt] = format("0x%04X",v)
- else
- tt[nt] = tostring(v) -- tostring not needed
- end
- elseif tv == "boolean" then
- nt = nt + 1
- tt[nt] = tostring(v)
- elseif tv == "string" then
- nt = nt + 1
- tt[nt] = format("%q",v)
- else
- tt = nil
- break
- end
- end
- return tt
- end
- end
- return nil
-end
-
--- Because this is a core function of mkiv I moved some function calls
--- inline.
---
--- twice as fast in a test:
---
--- local propername = lpeg.P(lpeg.R("AZ","az","__") * lpeg.R("09","AZ","az", "__")^0 * lpeg.P(-1) )
-
--- problem: there no good number_to_string converter with the best resolution
-
--- probably using .. is faster than format
--- maybe split in a few cases (yes/no hexify)
-
--- todo: %g faster on numbers than %s
-
--- we can speed this up with repeaters and formatters (is indeed faster)
-
-local propername = patterns.propername -- was find(name,"^%a[%w%_]*$")
-
-local function dummy() end
-
-local function do_serialize(root,name,depth,level,indexed)
- if level > 0 then
- depth = depth .. " "
- if indexed then
- handle(format("%s{",depth))
- else
- local tn = type(name)
- if tn == "number" then
- if hexify then
- handle(format("%s[0x%04X]={",depth,name))
- else
- handle(format("%s[%s]={",depth,name))
- end
- elseif tn == "string" then
- if noquotes and not reserved[name] and lpegmatch(propername,name) then
- handle(format("%s%s={",depth,name))
- else
- handle(format("%s[%q]={",depth,name))
- end
- elseif tn == "boolean" then
- handle(format("%s[%s]={",depth,tostring(name)))
- else
- handle(format("%s{",depth))
- end
- end
- end
- -- we could check for k (index) being number (cardinal)
- if root and next(root) then
- -- local first, last = nil, 0 -- #root cannot be trusted here (will be ok in 5.2 when ipairs is gone)
- -- if compact then
- -- -- NOT: for k=1,#root do (we need to quit at nil)
- -- for k,v in ipairs(root) do -- can we use next?
- -- if not first then first = k end
- -- last = last + 1
- -- end
- -- end
- local first, last = nil, 0
- if compact then
- last = #root
- for k=1,last do
- if root[k] == nil then
- last = k - 1
- break
- end
- end
- if last > 0 then
- first = 1
- end
- end
- local sk = sortedkeys(root)
- for i=1,#sk do
- local k = sk[i]
- local v = root[k]
- --~ if v == root then
- -- circular
- --~ else
- local t, tk = type(v), type(k)
- if compact and first and tk == "number" and k >= first and k <= last then
- if t == "number" then
- if hexify then
- handle(format("%s 0x%04X,",depth,v))
- else
- handle(format("%s %s,",depth,v)) -- %.99g
- end
- elseif t == "string" then
- if reduce and tonumber(v) then
- handle(format("%s %s,",depth,v))
- else
- handle(format("%s %q,",depth,v))
- end
- elseif t == "table" then
- if not next(v) then
- handle(format("%s {},",depth))
- elseif inline then -- and #t > 0
- local st = simple_table(v)
- if st then
- handle(format("%s { %s },",depth,concat(st,", ")))
- else
- do_serialize(v,k,depth,level+1,true)
- end
- else
- do_serialize(v,k,depth,level+1,true)
- end
- elseif t == "boolean" then
- handle(format("%s %s,",depth,tostring(v)))
- elseif t == "function" then
- if functions then
- handle(format('%s load(%q),',depth,dump(v)))
- else
- handle(format('%s "function",',depth))
- end
- else
- handle(format("%s %q,",depth,tostring(v)))
- end
- elseif k == "__p__" then -- parent
- if false then
- handle(format("%s __p__=nil,",depth))
- end
- elseif t == "number" then
- if tk == "number" then
- if hexify then
- handle(format("%s [0x%04X]=0x%04X,",depth,k,v))
- else
- handle(format("%s [%s]=%s,",depth,k,v)) -- %.99g
- end
- elseif tk == "boolean" then
- if hexify then
- handle(format("%s [%s]=0x%04X,",depth,tostring(k),v))
- else
- handle(format("%s [%s]=%s,",depth,tostring(k),v)) -- %.99g
- end
- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
- if hexify then
- handle(format("%s %s=0x%04X,",depth,k,v))
- else
- handle(format("%s %s=%s,",depth,k,v)) -- %.99g
- end
- else
- if hexify then
- handle(format("%s [%q]=0x%04X,",depth,k,v))
- else
- handle(format("%s [%q]=%s,",depth,k,v)) -- %.99g
- end
- end
- elseif t == "string" then
- if reduce and tonumber(v) then
- if tk == "number" then
- if hexify then
- handle(format("%s [0x%04X]=%s,",depth,k,v))
- else
- handle(format("%s [%s]=%s,",depth,k,v))
- end
- elseif tk == "boolean" then
- handle(format("%s [%s]=%s,",depth,tostring(k),v))
- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
- handle(format("%s %s=%s,",depth,k,v))
- else
- handle(format("%s [%q]=%s,",depth,k,v))
- end
- else
- if tk == "number" then
- if hexify then
- handle(format("%s [0x%04X]=%q,",depth,k,v))
- else
- handle(format("%s [%s]=%q,",depth,k,v))
- end
- elseif tk == "boolean" then
- handle(format("%s [%s]=%q,",depth,tostring(k),v))
- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
- handle(format("%s %s=%q,",depth,k,v))
- else
- handle(format("%s [%q]=%q,",depth,k,v))
- end
- end
- elseif t == "table" then
- if not next(v) then
- if tk == "number" then
- if hexify then
- handle(format("%s [0x%04X]={},",depth,k))
- else
- handle(format("%s [%s]={},",depth,k))
- end
- elseif tk == "boolean" then
- handle(format("%s [%s]={},",depth,tostring(k)))
- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
- handle(format("%s %s={},",depth,k))
- else
- handle(format("%s [%q]={},",depth,k))
- end
- elseif inline then
- local st = simple_table(v)
- if st then
- if tk == "number" then
- if hexify then
- handle(format("%s [0x%04X]={ %s },",depth,k,concat(st,", ")))
- else
- handle(format("%s [%s]={ %s },",depth,k,concat(st,", ")))
- end
- elseif tk == "boolean" then
- handle(format("%s [%s]={ %s },",depth,tostring(k),concat(st,", ")))
- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
- handle(format("%s %s={ %s },",depth,k,concat(st,", ")))
- else
- handle(format("%s [%q]={ %s },",depth,k,concat(st,", ")))
- end
- else
- do_serialize(v,k,depth,level+1)
- end
- else
- do_serialize(v,k,depth,level+1)
- end
- elseif t == "boolean" then
- if tk == "number" then
- if hexify then
- handle(format("%s [0x%04X]=%s,",depth,k,tostring(v)))
- else
- handle(format("%s [%s]=%s,",depth,k,tostring(v)))
- end
- elseif tk == "boolean" then
- handle(format("%s [%s]=%s,",depth,tostring(k),tostring(v)))
- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
- handle(format("%s %s=%s,",depth,k,tostring(v)))
- else
- handle(format("%s [%q]=%s,",depth,k,tostring(v)))
- end
- elseif t == "function" then
- if functions then
- local f = getinfo(v).what == "C" and dump(dummy) or dump(v)
- -- local f = getinfo(v).what == "C" and dump(function(...) return v(...) end) or dump(v)
- if tk == "number" then
- if hexify then
- handle(format("%s [0x%04X]=load(%q),",depth,k,f))
- else
- handle(format("%s [%s]=load(%q),",depth,k,f))
- end
- elseif tk == "boolean" then
- handle(format("%s [%s]=load(%q),",depth,tostring(k),f))
- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
- handle(format("%s %s=load(%q),",depth,k,f))
- else
- handle(format("%s [%q]=load(%q),",depth,k,f))
- end
- end
- else
- if tk == "number" then
- if hexify then
- handle(format("%s [0x%04X]=%q,",depth,k,tostring(v)))
- else
- handle(format("%s [%s]=%q,",depth,k,tostring(v)))
- end
- elseif tk == "boolean" then
- handle(format("%s [%s]=%q,",depth,tostring(k),tostring(v)))
- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
- handle(format("%s %s=%q,",depth,k,tostring(v)))
- else
- handle(format("%s [%q]=%q,",depth,k,tostring(v)))
- end
- end
- --~ end
- end
- end
- if level > 0 then
- handle(format("%s},",depth))
- end
-end
-
--- replacing handle by a direct t[#t+1] = ... (plus test) is not much
--- faster (0.03 on 1.00 for zapfino.tma)
-
-local function serialize(_handle,root,name,specification) -- handle wins
- local tname = type(name)
- if type(specification) == "table" then
- noquotes = specification.noquotes
- hexify = specification.hexify
- handle = _handle or specification.handle or print
- reduce = specification.reduce or false
- functions = specification.functions
- compact = specification.compact
- inline = specification.inline and compact
- if functions == nil then
- functions = true
- end
- if compact == nil then
- compact = true
- end
- if inline == nil then
- inline = compact
- end
- else
- noquotes = false
- hexify = false
- handle = _handle or print
- reduce = false
- compact = true
- inline = true
- functions = true
- end
- if tname == "string" then
- if name == "return" then
- handle("return {")
- else
- handle(name .. "={")
- end
- elseif tname == "number" then
- if hexify then
- handle(format("[0x%04X]={",name))
- else
- handle("[" .. name .. "]={")
- end
- elseif tname == "boolean" then
- if name then
- handle("return {")
- else
- handle("{")
- end
- else
- handle("t={")
- end
- if root then
- -- The dummy access will initialize a table that has a delayed initialization
- -- using a metatable. (maybe explicitly test for metatable)
- if getmetatable(root) then -- todo: make this an option, maybe even per subtable
- local dummy = root._w_h_a_t_e_v_e_r_
- root._w_h_a_t_e_v_e_r_ = nil
- end
- -- Let's forget about empty tables.
- if next(root) then
- do_serialize(root,name,"",0)
- end
- end
- handle("}")
-end
-
--- -- This is some 20% faster than using format (because formatters are much faster) but
--- -- of course, inlining the format using .. is then again faster .. anyway, as we do
--- -- some pretty printing as well there is not that much to gain unless we make a 'fast'
--- -- ugly variant as well. But, we would have to move the formatter to l-string then.
-
--- local formatters = string.formatters
-
--- local function do_serialize(root,name,level,indexed)
--- if level > 0 then
--- if indexed then
--- handle(formatters["%w{"](level))
--- else
--- local tn = type(name)
--- if tn == "number" then
--- if hexify then
--- handle(formatters["%w[%04H]={"](level,name))
--- else
--- handle(formatters["%w[%s]={"](level,name))
--- end
--- elseif tn == "string" then
--- if noquotes and not reserved[name] and lpegmatch(propername,name) then
--- handle(formatters["%w%s={"](level,name))
--- else
--- handle(formatters["%w[%q]={"](level,name))
--- end
--- elseif tn == "boolean" then
--- handle(formatters["%w[%S]={"](level,name))
--- else
--- handle(formatters["%w{"](level))
--- end
--- end
--- end
--- -- we could check for k (index) being number (cardinal)
--- if root and next(root) then
--- -- local first, last = nil, 0 -- #root cannot be trusted here (will be ok in 5.2 when ipairs is gone)
--- -- if compact then
--- -- -- NOT: for k=1,#root do (we need to quit at nil)
--- -- for k,v in ipairs(root) do -- can we use next?
--- -- if not first then first = k end
--- -- last = last + 1
--- -- end
--- -- end
--- local first, last = nil, 0
--- if compact then
--- last = #root
--- for k=1,last do
--- if root[k] == nil then
--- last = k - 1
--- break
--- end
--- end
--- if last > 0 then
--- first = 1
--- end
--- end
--- local sk = sortedkeys(root)
--- for i=1,#sk do
--- local k = sk[i]
--- local v = root[k]
--- --~ if v == root then
--- -- circular
--- --~ else
--- local t, tk = type(v), type(k)
--- if compact and first and tk == "number" and k >= first and k <= last then
--- if t == "number" then
--- if hexify then
--- handle(formatters["%w %04H,"](level,v))
--- else
--- handle(formatters["%w %s,"](level,v)) -- %.99g
--- end
--- elseif t == "string" then
--- if reduce and tonumber(v) then
--- handle(formatters["%w %s,"](level,v))
--- else
--- handle(formatters["%w %q,"](level,v))
--- end
--- elseif t == "table" then
--- if not next(v) then
--- handle(formatters["%w {},"](level))
--- elseif inline then -- and #t > 0
--- local st = simple_table(v)
--- if st then
--- handle(formatters["%w { %, t },"](level,st))
--- else
--- do_serialize(v,k,level+1,true)
--- end
--- else
--- do_serialize(v,k,level+1,true)
--- end
--- elseif t == "boolean" then
--- handle(formatters["%w %S,"](level,v))
--- elseif t == "function" then
--- if functions then
--- handle(formatters['%w load(%q),'](level,dump(v)))
--- else
--- handle(formatters['%w "function",'](level))
--- end
--- else
--- handle(formatters["%w %Q,"](level,v))
--- end
--- elseif k == "__p__" then -- parent
--- if false then
--- handle(formatters["%w __p__=nil,"](level))
--- end
--- elseif t == "number" then
--- if tk == "number" then
--- if hexify then
--- handle(formatters["%w [%04H]=%04H,"](level,k,v))
--- else
--- handle(formatters["%w [%s]=%s,"](level,k,v)) -- %.99g
--- end
--- elseif tk == "boolean" then
--- if hexify then
--- handle(formatters["%w [%S]=%04H,"](level,k,v))
--- else
--- handle(formatters["%w [%S]=%s,"](level,k,v)) -- %.99g
--- end
--- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
--- if hexify then
--- handle(formatters["%w %s=%04H,"](level,k,v))
--- else
--- handle(formatters["%w %s=%s,"](level,k,v)) -- %.99g
--- end
--- else
--- if hexify then
--- handle(formatters["%w [%q]=%04H,"](level,k,v))
--- else
--- handle(formatters["%w [%q]=%s,"](level,k,v)) -- %.99g
--- end
--- end
--- elseif t == "string" then
--- if reduce and tonumber(v) then
--- if tk == "number" then
--- if hexify then
--- handle(formatters["%w [%04H]=%s,"](level,k,v))
--- else
--- handle(formatters["%w [%s]=%s,"](level,k,v))
--- end
--- elseif tk == "boolean" then
--- handle(formatters["%w [%S]=%s,"](level,k,v))
--- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
--- handle(formatters["%w %s=%s,"](level,k,v))
--- else
--- handle(formatters["%w [%q]=%s,"](level,k,v))
--- end
--- else
--- if tk == "number" then
--- if hexify then
--- handle(formatters["%w [%04H]=%q,"](level,k,v))
--- else
--- handle(formatters["%w [%s]=%q,"](level,k,v))
--- end
--- elseif tk == "boolean" then
--- handle(formatters["%w [%S]=%q,"](level,k,v))
--- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
--- handle(formatters["%w %s=%q,"](level,k,v))
--- else
--- handle(formatters["%w [%q]=%q,"](level,k,v))
--- end
--- end
--- elseif t == "table" then
--- if not next(v) then
--- if tk == "number" then
--- if hexify then
--- handle(formatters["%w [%04H]={},"](level,k))
--- else
--- handle(formatters["%w [%s]={},"](level,k))
--- end
--- elseif tk == "boolean" then
--- handle(formatters["%w [%S]={},"](level,k))
--- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
--- handle(formatters["%w %s={},"](level,k))
--- else
--- handle(formatters["%w [%q]={},"](level,k))
--- end
--- elseif inline then
--- local st = simple_table(v)
--- if st then
--- if tk == "number" then
--- if hexify then
--- handle(formatters["%w [%04H]={ %, t },"](level,k,st))
--- else
--- handle(formatters["%w [%s]={ %, t },"](level,k,st))
--- end
--- elseif tk == "boolean" then
--- handle(formatters["%w [%S]={ %, t },"](level,k,st))
--- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
--- handle(formatters["%w %s={ %, t },"](level,k,st))
--- else
--- handle(formatters["%w [%q]={ %, t },"](level,k,st))
--- end
--- else
--- do_serialize(v,k,level+1)
--- end
--- else
--- do_serialize(v,k,level+1)
--- end
--- elseif t == "boolean" then
--- if tk == "number" then
--- if hexify then
--- handle(formatters["%w [%04H]=%S,"](level,k,v))
--- else
--- handle(formatters["%w [%s]=%S,"](level,k,v))
--- end
--- elseif tk == "boolean" then
--- handle(formatters["%w [%S]=%S,"](level,k,v))
--- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
--- handle(formatters["%w %s=%S,"](level,k,v))
--- else
--- handle(formatters["%w [%q]=%S,"](level,k,v))
--- end
--- elseif t == "function" then
--- if functions then
--- local f = getinfo(v).what == "C" and dump(dummy) or dump(v)
--- -- local f = getinfo(v).what == "C" and dump(function(...) return v(...) end) or dump(v)
--- if tk == "number" then
--- if hexify then
--- handle(formatters["%w [%04H]=load(%q),"](level,k,f))
--- else
--- handle(formatters["%w [%s]=load(%q),"](level,k,f))
--- end
--- elseif tk == "boolean" then
--- handle(formatters["%w [%S]=load(%q),"](level,k,f))
--- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
--- handle(formatters["%w %s=load(%q),"](level,k,f))
--- else
--- handle(formatters["%w [%q]=load(%q),"](level,k,f))
--- end
--- end
--- else
--- if tk == "number" then
--- if hexify then
--- handle(formatters["%w [%04H]=%Q,"](level,k,v))
--- else
--- handle(formatters["%w [%s]=%Q,"](level,k,v))
--- end
--- elseif tk == "boolean" then
--- handle(formatters["%w [%S]=%Q,"](level,k,v))
--- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
--- handle(formatters["%w %s=%Q,"](level,k,v))
--- else
--- handle(formatters["%w [%q]=%Q,"](level,k,v))
--- end
--- end
--- --~ end
--- end
--- end
--- if level > 0 then
--- handle(formatters["%w}"](level))
--- end
--- end
-
--- local function serialize(_handle,root,name,specification) -- handle wins
--- local tname = type(name)
--- if type(specification) == "table" then
--- noquotes = specification.noquotes
--- hexify = specification.hexify
--- handle = _handle or specification.handle or print
--- reduce = specification.reduce or false
--- functions = specification.functions
--- compact = specification.compact
--- inline = specification.inline and compact
--- if functions == nil then
--- functions = true
--- end
--- if compact == nil then
--- compact = true
--- end
--- if inline == nil then
--- inline = compact
--- end
--- else
--- noquotes = false
--- hexify = false
--- handle = _handle or print
--- reduce = false
--- compact = true
--- inline = true
--- functions = true
--- end
--- if tname == "string" then
--- if name == "return" then
--- handle("return {")
--- else
--- handle(name .. "={")
--- end
--- elseif tname == "number" then
--- if hexify then
--- handle(format("[0x%04X]={",name))
--- else
--- handle("[" .. name .. "]={")
--- end
--- elseif tname == "boolean" then
--- if name then
--- handle("return {")
--- else
--- handle("{")
--- end
--- else
--- handle("t={")
--- end
--- if root then
--- -- The dummy access will initialize a table that has a delayed initialization
--- -- using a metatable. (maybe explicitly test for metatable)
--- if getmetatable(root) then -- todo: make this an option, maybe even per subtable
--- local dummy = root._w_h_a_t_e_v_e_r_
--- root._w_h_a_t_e_v_e_r_ = nil
--- end
--- -- Let's forget about empty tables.
--- if next(root) then
--- do_serialize(root,name,0)
--- end
--- end
--- handle("}")
--- end
-
--- name:
---
--- true : return { }
--- false : { }
--- nil : t = { }
--- string : string = { }
--- "return" : return { }
--- number : [number] = { }
-
-function table.serialize(root,name,specification)
- local t, n = { }, 0
- local function flush(s)
- n = n + 1
- t[n] = s
- end
- serialize(flush,root,name,specification)
- return concat(t,"\n")
-end
-
--- local a = { e = { 1,2,3,4,5,6}, a = 1, b = 2, c = "ccc", d = { a = 1, b = 2, c = "ccc", d = { a = 1, b = 2, c = "ccc" } } }
--- local t = os.clock()
--- for i=1,10000 do
--- table.serialize(a)
--- end
--- print(os.clock()-t,table.serialize(a))
-
-table.tohandle = serialize
-
--- sometimes tables are real use (zapfino extra pro is some 85M) in which
--- case a stepwise serialization is nice; actually, we could consider:
---
--- for line in table.serializer(root,name,reduce,noquotes) do
--- ...(line)
--- end
---
--- so this is on the todo list
-
-local maxtab = 2*1024
-
-function table.tofile(filename,root,name,specification)
- local f = io.open(filename,'w')
- if f then
- if maxtab > 1 then
- local t, n = { }, 0
- local function flush(s)
- n = n + 1
- t[n] = s
- if n > maxtab then
- f:write(concat(t,"\n"),"\n") -- hm, write(sometable) should be nice
- t, n = { }, 0 -- we could recycle t if needed
- end
- end
- serialize(flush,root,name,specification)
- f:write(concat(t,"\n"),"\n")
- else
- local function flush(s)
- f:write(s,"\n")
- end
- serialize(flush,root,name,specification)
- end
- f:close()
- io.flush()
- end
-end
-
-local function flattened(t,f,depth) -- also handles { nil, 1, nil, 2 }
- if f == nil then
- f = { }
- depth = 0xFFFF
- elseif tonumber(f) then
- -- assume that only two arguments are given
- depth = f
- f = { }
- elseif not depth then
- depth = 0xFFFF
- end
- for k, v in next, t do
- if type(k) ~= "number" then
- if depth > 0 and type(v) == "table" then
- flattened(v,f,depth-1)
- else
- f[#f+1] = v
- end
- end
- end
- for k=1,#t do
- local v = t[k]
- if depth > 0 and type(v) == "table" then
- flattened(v,f,depth-1)
- else
- f[#f+1] = v
- end
- end
- return f
-end
-
-table.flattened = flattened
-
-local function unnest(t,f) -- only used in mk, for old times sake
- if not f then -- and only relevant for token lists
- f = { } -- this one can become obsolete
- end
- for i=1,#t do
- local v = t[i]
- if type(v) == "table" then
- if type(v[1]) == "table" then
- unnest(v,f)
- else
- f[#f+1] = v
- end
- else
- f[#f+1] = v
- end
- end
- return f
-end
-
-function table.unnest(t) -- bad name
- return unnest(t)
-end
-
-local function are_equal(a,b,n,m) -- indexed
- if a and b and #a == #b then
- n = n or 1
- m = m or #a
- for i=n,m do
- local ai, bi = a[i], b[i]
- if ai==bi then
- -- same
- elseif type(ai) == "table" and type(bi) == "table" then
- if not are_equal(ai,bi) then
- return false
- end
- else
- return false
- end
- end
- return true
- else
- return false
- end
-end
-
-local function identical(a,b) -- assumes same structure
- for ka, va in next, a do
- local vb = b[ka]
- if va == vb then
- -- same
- elseif type(va) == "table" and type(vb) == "table" then
- if not identical(va,vb) then
- return false
- end
- else
- return false
- end
- end
- return true
-end
-
-table.identical = identical
-table.are_equal = are_equal
-
--- maybe also make a combined one
-
-function table.compact(t) -- remove empty tables, assumes subtables
- if t then
- for k, v in next, t do
- if not next(v) then -- no type checking
- t[k] = nil
- end
- end
- end
-end
-
-function table.contains(t, v)
- if t then
- for i=1, #t do
- if t[i] == v then
- return i
- end
- end
- end
- return false
-end
-
-function table.count(t)
- local n = 0
- for k, v in next, t do
- n = n + 1
- end
- return n
-end
-
-function table.swapped(t,s) -- hash
- local n = { }
- if s then
- for k, v in next, s do
- n[k] = v
- end
- end
- for k, v in next, t do
- n[v] = k
- end
- return n
-end
-
-function table.mirrored(t) -- hash
- local n = { }
- for k, v in next, t do
- n[v] = k
- n[k] = v
- end
- return n
-end
-
-function table.reversed(t)
- if t then
- local tt, tn = { }, #t
- if tn > 0 then
- local ttn = 0
- for i=tn,1,-1 do
- ttn = ttn + 1
- tt[ttn] = t[i]
- end
- end
- return tt
- end
-end
-
-function table.reverse(t)
- if t then
- local n = #t
- for i=1,floor(n/2) do
- local j = n - i + 1
- t[i], t[j] = t[j], t[i]
- end
- return t
- end
-end
-
-function table.sequenced(t,sep,simple) -- hash only
- if not t then
- return ""
- end
- local n = #t
- local s = { }
- if n > 0 then
- -- indexed
- for i=1,n do
- s[i] = tostring(t[i])
- end
- else
- -- hashed
- n = 0
- for k, v in sortedhash(t) do
- if simple then
- if v == true then
- n = n + 1
- s[n] = k
- elseif v and v~= "" then
- n = n + 1
- s[n] = k .. "=" .. tostring(v)
- end
- else
- n = n + 1
- s[n] = k .. "=" .. tostring(v)
- end
- end
- end
- return concat(s,sep or " | ")
-end
-
-function table.print(t,...)
- if type(t) ~= "table" then
- print(tostring(t))
- else
- serialize(print,t,...)
- end
-end
-
-setinspector(function(v) if type(v) == "table" then serialize(print,v,"table") return true end end)
-
--- -- -- obsolete but we keep them for a while and might comment them later -- -- --
-
--- roughly: copy-loop : unpack : sub == 0.9 : 0.4 : 0.45 (so in critical apps, use unpack)
-
-function table.sub(t,i,j)
- return { unpack(t,i,j) }
-end
-
--- slower than #t on indexed tables (#t only returns the size of the numerically indexed slice)
-
-function table.is_empty(t)
- return not t or not next(t)
-end
-
-function table.has_one_entry(t)
- return t and not next(t,next(t))
-end
-
--- new
-
-function table.loweredkeys(t) -- maybe utf
- local l = { }
- for k, v in next, t do
- l[lower(k)] = v
- end
- return l
-end
-
--- new, might move (maybe duplicate)
-
-function table.unique(old)
- local hash = { }
- local new = { }
- local n = 0
- for i=1,#old do
- local oi = old[i]
- if not hash[oi] then
- n = n + 1
- new[n] = oi
- hash[oi] = true
- end
- end
- return new
-end
-
-function table.sorted(t,...)
- sort(t,...)
- return t -- still sorts in-place
-end
+if not modules then modules = { } end modules ['l-table'] = {
+ 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 type, next, tostring, tonumber, ipairs, select = type, next, tostring, tonumber, ipairs, select
+local table, string = table, string
+local concat, sort, insert, remove = table.concat, table.sort, table.insert, table.remove
+local format, lower, dump = string.format, string.lower, string.dump
+local getmetatable, setmetatable = getmetatable, setmetatable
+local getinfo = debug.getinfo
+local lpegmatch, patterns = lpeg.match, lpeg.patterns
+local floor = math.floor
+
+-- extra functions, some might go (when not used)
+
+local stripper = patterns.stripper
+
+function table.strip(tab)
+ local lst, l = { }, 0
+ for i=1,#tab do
+ local s = lpegmatch(stripper,tab[i]) or ""
+ if s == "" then
+ -- skip this one
+ else
+ l = l + 1
+ lst[l] = s
+ end
+ end
+ return lst
+end
+
+function table.keys(t)
+ if t then
+ local keys, k = { }, 0
+ for key, _ in next, t do
+ k = k + 1
+ keys[k] = key
+ end
+ return keys
+ else
+ return { }
+ end
+end
+
+local function compare(a,b)
+ local ta, tb = type(a), type(b) -- needed, else 11 < 2
+ if ta == tb then
+ return a < b
+ else
+ return tostring(a) < tostring(b)
+ end
+end
+
+local function sortedkeys(tab)
+ if tab then
+ local srt, category, s = { }, 0, 0 -- 0=unknown 1=string, 2=number 3=mixed
+ for key,_ in next, tab do
+ s = s + 1
+ srt[s] = key
+ if category == 3 then
+ -- no further check
+ else
+ local tkey = type(key)
+ if tkey == "string" then
+ category = (category == 2 and 3) or 1
+ elseif tkey == "number" then
+ category = (category == 1 and 3) or 2
+ else
+ category = 3
+ end
+ end
+ end
+ if category == 0 or category == 3 then
+ sort(srt,compare)
+ else
+ sort(srt)
+ end
+ return srt
+ else
+ return { }
+ end
+end
+
+local function sortedhashkeys(tab,cmp) -- fast one
+ if tab then
+ local srt, s = { }, 0
+ for key,_ in next, tab do
+ if key then
+ s= s + 1
+ srt[s] = key
+ end
+ end
+ sort(srt,cmp)
+ return srt
+ else
+ return { }
+ end
+end
+
+function table.allkeys(t)
+ local keys = { }
+ for k, v in next, t do
+ for k, v in next, v do
+ keys[k] = true
+ end
+ end
+ return sortedkeys(keys)
+end
+
+table.sortedkeys = sortedkeys
+table.sortedhashkeys = sortedhashkeys
+
+local function nothing() end
+
+local function sortedhash(t,cmp)
+ if t then
+ local s
+ if cmp then
+ -- it would be nice if the sort function would accept a third argument (or nicer, an optional first)
+ s = sortedhashkeys(t,function(a,b) return cmp(t,a,b) end)
+ else
+ s = sortedkeys(t) -- the robust one
+ end
+ local n = 0
+ local function kv(s)
+ n = n + 1
+ local k = s[n]
+ return k, t[k]
+ end
+ return kv, s
+ else
+ return nothing
+ end
+end
+
+table.sortedhash = sortedhash
+table.sortedpairs = sortedhash -- obsolete
+
+function table.append(t,list)
+ local n = #t
+ for i=1,#list do
+ n = n + 1
+ t[n] = list[i]
+ end
+ return t
+end
+
+function table.prepend(t, list)
+ local nl = #list
+ local nt = nl + #t
+ for i=#t,1,-1 do
+ t[nt] = t[i]
+ nt = nt - 1
+ end
+ for i=1,#list do
+ t[i] = list[i]
+ end
+ return t
+end
+
+-- function table.merge(t, ...) -- first one is target
+-- t = t or { }
+-- local lst = { ... }
+-- for i=1,#lst do
+-- for k, v in next, lst[i] do
+-- t[k] = v
+-- end
+-- end
+-- return t
+-- end
+
+function table.merge(t, ...) -- first one is target
+ t = t or { }
+ for i=1,select("#",...) do
+ for k, v in next, (select(i,...)) do
+ t[k] = v
+ end
+ end
+ return t
+end
+
+-- function table.merged(...)
+-- local tmp, lst = { }, { ... }
+-- for i=1,#lst do
+-- for k, v in next, lst[i] do
+-- tmp[k] = v
+-- end
+-- end
+-- return tmp
+-- end
+
+function table.merged(...)
+ local t = { }
+ for i=1,select("#",...) do
+ for k, v in next, (select(i,...)) do
+ t[k] = v
+ end
+ end
+ return t
+end
+
+-- function table.imerge(t, ...)
+-- local lst, nt = { ... }, #t
+-- for i=1,#lst do
+-- local nst = lst[i]
+-- for j=1,#nst do
+-- nt = nt + 1
+-- t[nt] = nst[j]
+-- end
+-- end
+-- return t
+-- end
+
+function table.imerge(t, ...)
+ local nt = #t
+ for i=1,select("#",...) do
+ local nst = select(i,...)
+ for j=1,#nst do
+ nt = nt + 1
+ t[nt] = nst[j]
+ end
+ end
+ return t
+end
+
+-- function table.imerged(...)
+-- local tmp, ntmp, lst = { }, 0, {...}
+-- for i=1,#lst do
+-- local nst = lst[i]
+-- for j=1,#nst do
+-- ntmp = ntmp + 1
+-- tmp[ntmp] = nst[j]
+-- end
+-- end
+-- return tmp
+-- end
+
+function table.imerged(...)
+ local tmp, ntmp = { }, 0
+ for i=1,select("#",...) do
+ local nst = select(i,...)
+ for j=1,#nst do
+ ntmp = ntmp + 1
+ tmp[ntmp] = nst[j]
+ end
+ end
+ return tmp
+end
+
+local function fastcopy(old,metatabletoo) -- fast one
+ if old then
+ local new = { }
+ for k, v in next, old do
+ if type(v) == "table" then
+ new[k] = fastcopy(v,metatabletoo) -- was just table.copy
+ else
+ new[k] = v
+ end
+ end
+ if metatabletoo then
+ -- optional second arg
+ local mt = getmetatable(old)
+ if mt then
+ setmetatable(new,mt)
+ end
+ end
+ return new
+ else
+ return { }
+ end
+end
+
+-- todo : copy without metatable
+
+local function copy(t, tables) -- taken from lua wiki, slightly adapted
+ tables = tables or { }
+ local tcopy = {}
+ if not tables[t] then
+ tables[t] = tcopy
+ end
+ for i,v in next, t do -- brrr, what happens with sparse indexed
+ if type(i) == "table" then
+ if tables[i] then
+ i = tables[i]
+ else
+ i = copy(i, tables)
+ end
+ end
+ if type(v) ~= "table" then
+ tcopy[i] = v
+ elseif tables[v] then
+ tcopy[i] = tables[v]
+ else
+ tcopy[i] = copy(v, tables)
+ end
+ end
+ local mt = getmetatable(t)
+ if mt then
+ setmetatable(tcopy,mt)
+ end
+ return tcopy
+end
+
+table.fastcopy = fastcopy
+table.copy = copy
+
+function table.derive(parent) -- for the moment not public
+ local child = { }
+ if parent then
+ setmetatable(child,{ __index = parent })
+ end
+ return child
+end
+
+function table.tohash(t,value)
+ local h = { }
+ if t then
+ if value == nil then value = true end
+ for _, v in next, t do -- no ipairs here
+ h[v] = value
+ end
+ end
+ return h
+end
+
+function table.fromhash(t)
+ local hsh, h = { }, 0
+ for k, v in next, t do -- no ipairs here
+ if v then
+ h = h + 1
+ hsh[h] = k
+ end
+ end
+ return hsh
+end
+
+local noquotes, hexify, handle, reduce, compact, inline, functions
+
+local reserved = table.tohash { -- intercept a language inconvenience: no reserved words as key
+ 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'if',
+ 'in', 'local', 'nil', 'not', 'or', 'repeat', 'return', 'then', 'true', 'until', 'while',
+}
+
+local function simple_table(t)
+ if #t > 0 then
+ local n = 0
+ for _,v in next, t do
+ n = n + 1
+ end
+ if n == #t then
+ local tt, nt = { }, 0
+ for i=1,#t do
+ local v = t[i]
+ local tv = type(v)
+ if tv == "number" then
+ nt = nt + 1
+ if hexify then
+ tt[nt] = format("0x%04X",v)
+ else
+ tt[nt] = tostring(v) -- tostring not needed
+ end
+ elseif tv == "boolean" then
+ nt = nt + 1
+ tt[nt] = tostring(v)
+ elseif tv == "string" then
+ nt = nt + 1
+ tt[nt] = format("%q",v)
+ else
+ tt = nil
+ break
+ end
+ end
+ return tt
+ end
+ end
+ return nil
+end
+
+-- Because this is a core function of mkiv I moved some function calls
+-- inline.
+--
+-- twice as fast in a test:
+--
+-- local propername = lpeg.P(lpeg.R("AZ","az","__") * lpeg.R("09","AZ","az", "__")^0 * lpeg.P(-1) )
+
+-- problem: there no good number_to_string converter with the best resolution
+
+-- probably using .. is faster than format
+-- maybe split in a few cases (yes/no hexify)
+
+-- todo: %g faster on numbers than %s
+
+-- we can speed this up with repeaters and formatters (is indeed faster)
+
+local propername = patterns.propername -- was find(name,"^%a[%w%_]*$")
+
+local function dummy() end
+
+local function do_serialize(root,name,depth,level,indexed)
+ if level > 0 then
+ depth = depth .. " "
+ if indexed then
+ handle(format("%s{",depth))
+ else
+ local tn = type(name)
+ if tn == "number" then
+ if hexify then
+ handle(format("%s[0x%04X]={",depth,name))
+ else
+ handle(format("%s[%s]={",depth,name))
+ end
+ elseif tn == "string" then
+ if noquotes and not reserved[name] and lpegmatch(propername,name) then
+ handle(format("%s%s={",depth,name))
+ else
+ handle(format("%s[%q]={",depth,name))
+ end
+ elseif tn == "boolean" then
+ handle(format("%s[%s]={",depth,tostring(name)))
+ else
+ handle(format("%s{",depth))
+ end
+ end
+ end
+ -- we could check for k (index) being number (cardinal)
+ if root and next(root) then
+ -- local first, last = nil, 0 -- #root cannot be trusted here (will be ok in 5.2 when ipairs is gone)
+ -- if compact then
+ -- -- NOT: for k=1,#root do (we need to quit at nil)
+ -- for k,v in ipairs(root) do -- can we use next?
+ -- if not first then first = k end
+ -- last = last + 1
+ -- end
+ -- end
+ local first, last = nil, 0
+ if compact then
+ last = #root
+ for k=1,last do
+ if root[k] == nil then
+ last = k - 1
+ break
+ end
+ end
+ if last > 0 then
+ first = 1
+ end
+ end
+ local sk = sortedkeys(root)
+ for i=1,#sk do
+ local k = sk[i]
+ local v = root[k]
+ --~ if v == root then
+ -- circular
+ --~ else
+ local t, tk = type(v), type(k)
+ if compact and first and tk == "number" and k >= first and k <= last then
+ if t == "number" then
+ if hexify then
+ handle(format("%s 0x%04X,",depth,v))
+ else
+ handle(format("%s %s,",depth,v)) -- %.99g
+ end
+ elseif t == "string" then
+ if reduce and tonumber(v) then
+ handle(format("%s %s,",depth,v))
+ else
+ handle(format("%s %q,",depth,v))
+ end
+ elseif t == "table" then
+ if not next(v) then
+ handle(format("%s {},",depth))
+ elseif inline then -- and #t > 0
+ local st = simple_table(v)
+ if st then
+ handle(format("%s { %s },",depth,concat(st,", ")))
+ else
+ do_serialize(v,k,depth,level+1,true)
+ end
+ else
+ do_serialize(v,k,depth,level+1,true)
+ end
+ elseif t == "boolean" then
+ handle(format("%s %s,",depth,tostring(v)))
+ elseif t == "function" then
+ if functions then
+ handle(format('%s load(%q),',depth,dump(v)))
+ else
+ handle(format('%s "function",',depth))
+ end
+ else
+ handle(format("%s %q,",depth,tostring(v)))
+ end
+ elseif k == "__p__" then -- parent
+ if false then
+ handle(format("%s __p__=nil,",depth))
+ end
+ elseif t == "number" then
+ if tk == "number" then
+ if hexify then
+ handle(format("%s [0x%04X]=0x%04X,",depth,k,v))
+ else
+ handle(format("%s [%s]=%s,",depth,k,v)) -- %.99g
+ end
+ elseif tk == "boolean" then
+ if hexify then
+ handle(format("%s [%s]=0x%04X,",depth,tostring(k),v))
+ else
+ handle(format("%s [%s]=%s,",depth,tostring(k),v)) -- %.99g
+ end
+ elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+ if hexify then
+ handle(format("%s %s=0x%04X,",depth,k,v))
+ else
+ handle(format("%s %s=%s,",depth,k,v)) -- %.99g
+ end
+ else
+ if hexify then
+ handle(format("%s [%q]=0x%04X,",depth,k,v))
+ else
+ handle(format("%s [%q]=%s,",depth,k,v)) -- %.99g
+ end
+ end
+ elseif t == "string" then
+ if reduce and tonumber(v) then
+ if tk == "number" then
+ if hexify then
+ handle(format("%s [0x%04X]=%s,",depth,k,v))
+ else
+ handle(format("%s [%s]=%s,",depth,k,v))
+ end
+ elseif tk == "boolean" then
+ handle(format("%s [%s]=%s,",depth,tostring(k),v))
+ elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+ handle(format("%s %s=%s,",depth,k,v))
+ else
+ handle(format("%s [%q]=%s,",depth,k,v))
+ end
+ else
+ if tk == "number" then
+ if hexify then
+ handle(format("%s [0x%04X]=%q,",depth,k,v))
+ else
+ handle(format("%s [%s]=%q,",depth,k,v))
+ end
+ elseif tk == "boolean" then
+ handle(format("%s [%s]=%q,",depth,tostring(k),v))
+ elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+ handle(format("%s %s=%q,",depth,k,v))
+ else
+ handle(format("%s [%q]=%q,",depth,k,v))
+ end
+ end
+ elseif t == "table" then
+ if not next(v) then
+ if tk == "number" then
+ if hexify then
+ handle(format("%s [0x%04X]={},",depth,k))
+ else
+ handle(format("%s [%s]={},",depth,k))
+ end
+ elseif tk == "boolean" then
+ handle(format("%s [%s]={},",depth,tostring(k)))
+ elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+ handle(format("%s %s={},",depth,k))
+ else
+ handle(format("%s [%q]={},",depth,k))
+ end
+ elseif inline then
+ local st = simple_table(v)
+ if st then
+ if tk == "number" then
+ if hexify then
+ handle(format("%s [0x%04X]={ %s },",depth,k,concat(st,", ")))
+ else
+ handle(format("%s [%s]={ %s },",depth,k,concat(st,", ")))
+ end
+ elseif tk == "boolean" then
+ handle(format("%s [%s]={ %s },",depth,tostring(k),concat(st,", ")))
+ elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+ handle(format("%s %s={ %s },",depth,k,concat(st,", ")))
+ else
+ handle(format("%s [%q]={ %s },",depth,k,concat(st,", ")))
+ end
+ else
+ do_serialize(v,k,depth,level+1)
+ end
+ else
+ do_serialize(v,k,depth,level+1)
+ end
+ elseif t == "boolean" then
+ if tk == "number" then
+ if hexify then
+ handle(format("%s [0x%04X]=%s,",depth,k,tostring(v)))
+ else
+ handle(format("%s [%s]=%s,",depth,k,tostring(v)))
+ end
+ elseif tk == "boolean" then
+ handle(format("%s [%s]=%s,",depth,tostring(k),tostring(v)))
+ elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+ handle(format("%s %s=%s,",depth,k,tostring(v)))
+ else
+ handle(format("%s [%q]=%s,",depth,k,tostring(v)))
+ end
+ elseif t == "function" then
+ if functions then
+ local f = getinfo(v).what == "C" and dump(dummy) or dump(v)
+ -- local f = getinfo(v).what == "C" and dump(function(...) return v(...) end) or dump(v)
+ if tk == "number" then
+ if hexify then
+ handle(format("%s [0x%04X]=load(%q),",depth,k,f))
+ else
+ handle(format("%s [%s]=load(%q),",depth,k,f))
+ end
+ elseif tk == "boolean" then
+ handle(format("%s [%s]=load(%q),",depth,tostring(k),f))
+ elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+ handle(format("%s %s=load(%q),",depth,k,f))
+ else
+ handle(format("%s [%q]=load(%q),",depth,k,f))
+ end
+ end
+ else
+ if tk == "number" then
+ if hexify then
+ handle(format("%s [0x%04X]=%q,",depth,k,tostring(v)))
+ else
+ handle(format("%s [%s]=%q,",depth,k,tostring(v)))
+ end
+ elseif tk == "boolean" then
+ handle(format("%s [%s]=%q,",depth,tostring(k),tostring(v)))
+ elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+ handle(format("%s %s=%q,",depth,k,tostring(v)))
+ else
+ handle(format("%s [%q]=%q,",depth,k,tostring(v)))
+ end
+ end
+ --~ end
+ end
+ end
+ if level > 0 then
+ handle(format("%s},",depth))
+ end
+end
+
+-- replacing handle by a direct t[#t+1] = ... (plus test) is not much
+-- faster (0.03 on 1.00 for zapfino.tma)
+
+local function serialize(_handle,root,name,specification) -- handle wins
+ local tname = type(name)
+ if type(specification) == "table" then
+ noquotes = specification.noquotes
+ hexify = specification.hexify
+ handle = _handle or specification.handle or print
+ reduce = specification.reduce or false
+ functions = specification.functions
+ compact = specification.compact
+ inline = specification.inline and compact
+ if functions == nil then
+ functions = true
+ end
+ if compact == nil then
+ compact = true
+ end
+ if inline == nil then
+ inline = compact
+ end
+ else
+ noquotes = false
+ hexify = false
+ handle = _handle or print
+ reduce = false
+ compact = true
+ inline = true
+ functions = true
+ end
+ if tname == "string" then
+ if name == "return" then
+ handle("return {")
+ else
+ handle(name .. "={")
+ end
+ elseif tname == "number" then
+ if hexify then
+ handle(format("[0x%04X]={",name))
+ else
+ handle("[" .. name .. "]={")
+ end
+ elseif tname == "boolean" then
+ if name then
+ handle("return {")
+ else
+ handle("{")
+ end
+ else
+ handle("t={")
+ end
+ if root then
+ -- The dummy access will initialize a table that has a delayed initialization
+ -- using a metatable. (maybe explicitly test for metatable)
+ if getmetatable(root) then -- todo: make this an option, maybe even per subtable
+ local dummy = root._w_h_a_t_e_v_e_r_
+ root._w_h_a_t_e_v_e_r_ = nil
+ end
+ -- Let's forget about empty tables.
+ if next(root) then
+ do_serialize(root,name,"",0)
+ end
+ end
+ handle("}")
+end
+
+-- -- This is some 20% faster than using format (because formatters are much faster) but
+-- -- of course, inlining the format using .. is then again faster .. anyway, as we do
+-- -- some pretty printing as well there is not that much to gain unless we make a 'fast'
+-- -- ugly variant as well. But, we would have to move the formatter to l-string then.
+
+-- local formatters = string.formatters
+
+-- local function do_serialize(root,name,level,indexed)
+-- if level > 0 then
+-- if indexed then
+-- handle(formatters["%w{"](level))
+-- else
+-- local tn = type(name)
+-- if tn == "number" then
+-- if hexify then
+-- handle(formatters["%w[%04H]={"](level,name))
+-- else
+-- handle(formatters["%w[%s]={"](level,name))
+-- end
+-- elseif tn == "string" then
+-- if noquotes and not reserved[name] and lpegmatch(propername,name) then
+-- handle(formatters["%w%s={"](level,name))
+-- else
+-- handle(formatters["%w[%q]={"](level,name))
+-- end
+-- elseif tn == "boolean" then
+-- handle(formatters["%w[%S]={"](level,name))
+-- else
+-- handle(formatters["%w{"](level))
+-- end
+-- end
+-- end
+-- -- we could check for k (index) being number (cardinal)
+-- if root and next(root) then
+-- -- local first, last = nil, 0 -- #root cannot be trusted here (will be ok in 5.2 when ipairs is gone)
+-- -- if compact then
+-- -- -- NOT: for k=1,#root do (we need to quit at nil)
+-- -- for k,v in ipairs(root) do -- can we use next?
+-- -- if not first then first = k end
+-- -- last = last + 1
+-- -- end
+-- -- end
+-- local first, last = nil, 0
+-- if compact then
+-- last = #root
+-- for k=1,last do
+-- if root[k] == nil then
+-- last = k - 1
+-- break
+-- end
+-- end
+-- if last > 0 then
+-- first = 1
+-- end
+-- end
+-- local sk = sortedkeys(root)
+-- for i=1,#sk do
+-- local k = sk[i]
+-- local v = root[k]
+-- --~ if v == root then
+-- -- circular
+-- --~ else
+-- local t, tk = type(v), type(k)
+-- if compact and first and tk == "number" and k >= first and k <= last then
+-- if t == "number" then
+-- if hexify then
+-- handle(formatters["%w %04H,"](level,v))
+-- else
+-- handle(formatters["%w %s,"](level,v)) -- %.99g
+-- end
+-- elseif t == "string" then
+-- if reduce and tonumber(v) then
+-- handle(formatters["%w %s,"](level,v))
+-- else
+-- handle(formatters["%w %q,"](level,v))
+-- end
+-- elseif t == "table" then
+-- if not next(v) then
+-- handle(formatters["%w {},"](level))
+-- elseif inline then -- and #t > 0
+-- local st = simple_table(v)
+-- if st then
+-- handle(formatters["%w { %, t },"](level,st))
+-- else
+-- do_serialize(v,k,level+1,true)
+-- end
+-- else
+-- do_serialize(v,k,level+1,true)
+-- end
+-- elseif t == "boolean" then
+-- handle(formatters["%w %S,"](level,v))
+-- elseif t == "function" then
+-- if functions then
+-- handle(formatters['%w load(%q),'](level,dump(v)))
+-- else
+-- handle(formatters['%w "function",'](level))
+-- end
+-- else
+-- handle(formatters["%w %Q,"](level,v))
+-- end
+-- elseif k == "__p__" then -- parent
+-- if false then
+-- handle(formatters["%w __p__=nil,"](level))
+-- end
+-- elseif t == "number" then
+-- if tk == "number" then
+-- if hexify then
+-- handle(formatters["%w [%04H]=%04H,"](level,k,v))
+-- else
+-- handle(formatters["%w [%s]=%s,"](level,k,v)) -- %.99g
+-- end
+-- elseif tk == "boolean" then
+-- if hexify then
+-- handle(formatters["%w [%S]=%04H,"](level,k,v))
+-- else
+-- handle(formatters["%w [%S]=%s,"](level,k,v)) -- %.99g
+-- end
+-- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+-- if hexify then
+-- handle(formatters["%w %s=%04H,"](level,k,v))
+-- else
+-- handle(formatters["%w %s=%s,"](level,k,v)) -- %.99g
+-- end
+-- else
+-- if hexify then
+-- handle(formatters["%w [%q]=%04H,"](level,k,v))
+-- else
+-- handle(formatters["%w [%q]=%s,"](level,k,v)) -- %.99g
+-- end
+-- end
+-- elseif t == "string" then
+-- if reduce and tonumber(v) then
+-- if tk == "number" then
+-- if hexify then
+-- handle(formatters["%w [%04H]=%s,"](level,k,v))
+-- else
+-- handle(formatters["%w [%s]=%s,"](level,k,v))
+-- end
+-- elseif tk == "boolean" then
+-- handle(formatters["%w [%S]=%s,"](level,k,v))
+-- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+-- handle(formatters["%w %s=%s,"](level,k,v))
+-- else
+-- handle(formatters["%w [%q]=%s,"](level,k,v))
+-- end
+-- else
+-- if tk == "number" then
+-- if hexify then
+-- handle(formatters["%w [%04H]=%q,"](level,k,v))
+-- else
+-- handle(formatters["%w [%s]=%q,"](level,k,v))
+-- end
+-- elseif tk == "boolean" then
+-- handle(formatters["%w [%S]=%q,"](level,k,v))
+-- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+-- handle(formatters["%w %s=%q,"](level,k,v))
+-- else
+-- handle(formatters["%w [%q]=%q,"](level,k,v))
+-- end
+-- end
+-- elseif t == "table" then
+-- if not next(v) then
+-- if tk == "number" then
+-- if hexify then
+-- handle(formatters["%w [%04H]={},"](level,k))
+-- else
+-- handle(formatters["%w [%s]={},"](level,k))
+-- end
+-- elseif tk == "boolean" then
+-- handle(formatters["%w [%S]={},"](level,k))
+-- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+-- handle(formatters["%w %s={},"](level,k))
+-- else
+-- handle(formatters["%w [%q]={},"](level,k))
+-- end
+-- elseif inline then
+-- local st = simple_table(v)
+-- if st then
+-- if tk == "number" then
+-- if hexify then
+-- handle(formatters["%w [%04H]={ %, t },"](level,k,st))
+-- else
+-- handle(formatters["%w [%s]={ %, t },"](level,k,st))
+-- end
+-- elseif tk == "boolean" then
+-- handle(formatters["%w [%S]={ %, t },"](level,k,st))
+-- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+-- handle(formatters["%w %s={ %, t },"](level,k,st))
+-- else
+-- handle(formatters["%w [%q]={ %, t },"](level,k,st))
+-- end
+-- else
+-- do_serialize(v,k,level+1)
+-- end
+-- else
+-- do_serialize(v,k,level+1)
+-- end
+-- elseif t == "boolean" then
+-- if tk == "number" then
+-- if hexify then
+-- handle(formatters["%w [%04H]=%S,"](level,k,v))
+-- else
+-- handle(formatters["%w [%s]=%S,"](level,k,v))
+-- end
+-- elseif tk == "boolean" then
+-- handle(formatters["%w [%S]=%S,"](level,k,v))
+-- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+-- handle(formatters["%w %s=%S,"](level,k,v))
+-- else
+-- handle(formatters["%w [%q]=%S,"](level,k,v))
+-- end
+-- elseif t == "function" then
+-- if functions then
+-- local f = getinfo(v).what == "C" and dump(dummy) or dump(v)
+-- -- local f = getinfo(v).what == "C" and dump(function(...) return v(...) end) or dump(v)
+-- if tk == "number" then
+-- if hexify then
+-- handle(formatters["%w [%04H]=load(%q),"](level,k,f))
+-- else
+-- handle(formatters["%w [%s]=load(%q),"](level,k,f))
+-- end
+-- elseif tk == "boolean" then
+-- handle(formatters["%w [%S]=load(%q),"](level,k,f))
+-- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+-- handle(formatters["%w %s=load(%q),"](level,k,f))
+-- else
+-- handle(formatters["%w [%q]=load(%q),"](level,k,f))
+-- end
+-- end
+-- else
+-- if tk == "number" then
+-- if hexify then
+-- handle(formatters["%w [%04H]=%Q,"](level,k,v))
+-- else
+-- handle(formatters["%w [%s]=%Q,"](level,k,v))
+-- end
+-- elseif tk == "boolean" then
+-- handle(formatters["%w [%S]=%Q,"](level,k,v))
+-- elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+-- handle(formatters["%w %s=%Q,"](level,k,v))
+-- else
+-- handle(formatters["%w [%q]=%Q,"](level,k,v))
+-- end
+-- end
+-- --~ end
+-- end
+-- end
+-- if level > 0 then
+-- handle(formatters["%w}"](level))
+-- end
+-- end
+
+-- local function serialize(_handle,root,name,specification) -- handle wins
+-- local tname = type(name)
+-- if type(specification) == "table" then
+-- noquotes = specification.noquotes
+-- hexify = specification.hexify
+-- handle = _handle or specification.handle or print
+-- reduce = specification.reduce or false
+-- functions = specification.functions
+-- compact = specification.compact
+-- inline = specification.inline and compact
+-- if functions == nil then
+-- functions = true
+-- end
+-- if compact == nil then
+-- compact = true
+-- end
+-- if inline == nil then
+-- inline = compact
+-- end
+-- else
+-- noquotes = false
+-- hexify = false
+-- handle = _handle or print
+-- reduce = false
+-- compact = true
+-- inline = true
+-- functions = true
+-- end
+-- if tname == "string" then
+-- if name == "return" then
+-- handle("return {")
+-- else
+-- handle(name .. "={")
+-- end
+-- elseif tname == "number" then
+-- if hexify then
+-- handle(format("[0x%04X]={",name))
+-- else
+-- handle("[" .. name .. "]={")
+-- end
+-- elseif tname == "boolean" then
+-- if name then
+-- handle("return {")
+-- else
+-- handle("{")
+-- end
+-- else
+-- handle("t={")
+-- end
+-- if root then
+-- -- The dummy access will initialize a table that has a delayed initialization
+-- -- using a metatable. (maybe explicitly test for metatable)
+-- if getmetatable(root) then -- todo: make this an option, maybe even per subtable
+-- local dummy = root._w_h_a_t_e_v_e_r_
+-- root._w_h_a_t_e_v_e_r_ = nil
+-- end
+-- -- Let's forget about empty tables.
+-- if next(root) then
+-- do_serialize(root,name,0)
+-- end
+-- end
+-- handle("}")
+-- end
+
+-- name:
+--
+-- true : return { }
+-- false : { }
+-- nil : t = { }
+-- string : string = { }
+-- "return" : return { }
+-- number : [number] = { }
+
+function table.serialize(root,name,specification)
+ local t, n = { }, 0
+ local function flush(s)
+ n = n + 1
+ t[n] = s
+ end
+ serialize(flush,root,name,specification)
+ return concat(t,"\n")
+end
+
+-- local a = { e = { 1,2,3,4,5,6}, a = 1, b = 2, c = "ccc", d = { a = 1, b = 2, c = "ccc", d = { a = 1, b = 2, c = "ccc" } } }
+-- local t = os.clock()
+-- for i=1,10000 do
+-- table.serialize(a)
+-- end
+-- print(os.clock()-t,table.serialize(a))
+
+table.tohandle = serialize
+
+-- sometimes tables are real use (zapfino extra pro is some 85M) in which
+-- case a stepwise serialization is nice; actually, we could consider:
+--
+-- for line in table.serializer(root,name,reduce,noquotes) do
+-- ...(line)
+-- end
+--
+-- so this is on the todo list
+
+local maxtab = 2*1024
+
+function table.tofile(filename,root,name,specification)
+ local f = io.open(filename,'w')
+ if f then
+ if maxtab > 1 then
+ local t, n = { }, 0
+ local function flush(s)
+ n = n + 1
+ t[n] = s
+ if n > maxtab then
+ f:write(concat(t,"\n"),"\n") -- hm, write(sometable) should be nice
+ t, n = { }, 0 -- we could recycle t if needed
+ end
+ end
+ serialize(flush,root,name,specification)
+ f:write(concat(t,"\n"),"\n")
+ else
+ local function flush(s)
+ f:write(s,"\n")
+ end
+ serialize(flush,root,name,specification)
+ end
+ f:close()
+ io.flush()
+ end
+end
+
+local function flattened(t,f,depth) -- also handles { nil, 1, nil, 2 }
+ if f == nil then
+ f = { }
+ depth = 0xFFFF
+ elseif tonumber(f) then
+ -- assume that only two arguments are given
+ depth = f
+ f = { }
+ elseif not depth then
+ depth = 0xFFFF
+ end
+ for k, v in next, t do
+ if type(k) ~= "number" then
+ if depth > 0 and type(v) == "table" then
+ flattened(v,f,depth-1)
+ else
+ f[#f+1] = v
+ end
+ end
+ end
+ for k=1,#t do
+ local v = t[k]
+ if depth > 0 and type(v) == "table" then
+ flattened(v,f,depth-1)
+ else
+ f[#f+1] = v
+ end
+ end
+ return f
+end
+
+table.flattened = flattened
+
+local function unnest(t,f) -- only used in mk, for old times sake
+ if not f then -- and only relevant for token lists
+ f = { } -- this one can become obsolete
+ end
+ for i=1,#t do
+ local v = t[i]
+ if type(v) == "table" then
+ if type(v[1]) == "table" then
+ unnest(v,f)
+ else
+ f[#f+1] = v
+ end
+ else
+ f[#f+1] = v
+ end
+ end
+ return f
+end
+
+function table.unnest(t) -- bad name
+ return unnest(t)
+end
+
+local function are_equal(a,b,n,m) -- indexed
+ if a and b and #a == #b then
+ n = n or 1
+ m = m or #a
+ for i=n,m do
+ local ai, bi = a[i], b[i]
+ if ai==bi then
+ -- same
+ elseif type(ai) == "table" and type(bi) == "table" then
+ if not are_equal(ai,bi) then
+ return false
+ end
+ else
+ return false
+ end
+ end
+ return true
+ else
+ return false
+ end
+end
+
+local function identical(a,b) -- assumes same structure
+ for ka, va in next, a do
+ local vb = b[ka]
+ if va == vb then
+ -- same
+ elseif type(va) == "table" and type(vb) == "table" then
+ if not identical(va,vb) then
+ return false
+ end
+ else
+ return false
+ end
+ end
+ return true
+end
+
+table.identical = identical
+table.are_equal = are_equal
+
+-- maybe also make a combined one
+
+function table.compact(t) -- remove empty tables, assumes subtables
+ if t then
+ for k, v in next, t do
+ if not next(v) then -- no type checking
+ t[k] = nil
+ end
+ end
+ end
+end
+
+function table.contains(t, v)
+ if t then
+ for i=1, #t do
+ if t[i] == v then
+ return i
+ end
+ end
+ end
+ return false
+end
+
+function table.count(t)
+ local n = 0
+ for k, v in next, t do
+ n = n + 1
+ end
+ return n
+end
+
+function table.swapped(t,s) -- hash
+ local n = { }
+ if s then
+ for k, v in next, s do
+ n[k] = v
+ end
+ end
+ for k, v in next, t do
+ n[v] = k
+ end
+ return n
+end
+
+function table.mirrored(t) -- hash
+ local n = { }
+ for k, v in next, t do
+ n[v] = k
+ n[k] = v
+ end
+ return n
+end
+
+function table.reversed(t)
+ if t then
+ local tt, tn = { }, #t
+ if tn > 0 then
+ local ttn = 0
+ for i=tn,1,-1 do
+ ttn = ttn + 1
+ tt[ttn] = t[i]
+ end
+ end
+ return tt
+ end
+end
+
+function table.reverse(t)
+ if t then
+ local n = #t
+ for i=1,floor(n/2) do
+ local j = n - i + 1
+ t[i], t[j] = t[j], t[i]
+ end
+ return t
+ end
+end
+
+function table.sequenced(t,sep,simple) -- hash only
+ if not t then
+ return ""
+ end
+ local n = #t
+ local s = { }
+ if n > 0 then
+ -- indexed
+ for i=1,n do
+ s[i] = tostring(t[i])
+ end
+ else
+ -- hashed
+ n = 0
+ for k, v in sortedhash(t) do
+ if simple then
+ if v == true then
+ n = n + 1
+ s[n] = k
+ elseif v and v~= "" then
+ n = n + 1
+ s[n] = k .. "=" .. tostring(v)
+ end
+ else
+ n = n + 1
+ s[n] = k .. "=" .. tostring(v)
+ end
+ end
+ end
+ return concat(s,sep or " | ")
+end
+
+function table.print(t,...)
+ if type(t) ~= "table" then
+ print(tostring(t))
+ else
+ serialize(print,t,...)
+ end
+end
+
+setinspector(function(v) if type(v) == "table" then serialize(print,v,"table") return true end end)
+
+-- -- -- obsolete but we keep them for a while and might comment them later -- -- --
+
+-- roughly: copy-loop : unpack : sub == 0.9 : 0.4 : 0.45 (so in critical apps, use unpack)
+
+function table.sub(t,i,j)
+ return { unpack(t,i,j) }
+end
+
+-- slower than #t on indexed tables (#t only returns the size of the numerically indexed slice)
+
+function table.is_empty(t)
+ return not t or not next(t)
+end
+
+function table.has_one_entry(t)
+ return t and not next(t,next(t))
+end
+
+-- new
+
+function table.loweredkeys(t) -- maybe utf
+ local l = { }
+ for k, v in next, t do
+ l[lower(k)] = v
+ end
+ return l
+end
+
+-- new, might move (maybe duplicate)
+
+function table.unique(old)
+ local hash = { }
+ local new = { }
+ local n = 0
+ for i=1,#old do
+ local oi = old[i]
+ if not hash[oi] then
+ n = n + 1
+ new[n] = oi
+ hash[oi] = true
+ end
+ end
+ return new
+end
+
+function table.sorted(t,...)
+ sort(t,...)
+ return t -- still sorts in-place
+end
diff --git a/tex/context/base/l-unicode.lua b/tex/context/base/l-unicode.lua
index d38d4cbd1..813ffd54b 100644
--- a/tex/context/base/l-unicode.lua
+++ b/tex/context/base/l-unicode.lua
@@ -1,942 +1,942 @@
-if not modules then modules = { } end modules ['l-unicode'] = {
- 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"
-}
-
--- this module will be reorganized
-
--- todo: utf.sub replacement (used in syst-aux)
-
--- we put these in the utf namespace:
-
-utf = utf or (unicode and unicode.utf8) or { }
-
-utf.characters = utf.characters or string.utfcharacters
-utf.values = utf.values or string.utfvalues
-
--- string.utfvalues
--- string.utfcharacters
--- string.characters
--- string.characterpairs
--- string.bytes
--- string.bytepairs
-
-local type = type
-local char, byte, format, sub = string.char, string.byte, string.format, string.sub
-local concat = table.concat
-local P, C, R, Cs, Ct, Cmt, Cc, Carg, Cp = lpeg.P, lpeg.C, lpeg.R, lpeg.Cs, lpeg.Ct, lpeg.Cmt, lpeg.Cc, lpeg.Carg, lpeg.Cp
-local lpegmatch, patterns = lpeg.match, lpeg.patterns
-
-local bytepairs = string.bytepairs
-
-local finder = lpeg.finder
-local replacer = lpeg.replacer
-
-local utfvalues = utf.values
-local utfgmatch = utf.gmatch -- not always present
-
-local p_utftype = patterns.utftype
-local p_utfoffset = patterns.utfoffset
-local p_utf8char = patterns.utf8char
-local p_utf8byte = patterns.utf8byte
-local p_utfbom = patterns.utfbom
-local p_newline = patterns.newline
-local p_whitespace = patterns.whitespace
-
-if not unicode then
-
- unicode = { utf = utf } -- for a while
-
-end
-
-if not utf.char then
-
- local floor, char = math.floor, string.char
-
- function utf.char(n)
- if n < 0x80 then
- -- 0aaaaaaa : 0x80
- return char(n)
- elseif n < 0x800 then
- -- 110bbbaa : 0xC0 : n >> 6
- -- 10aaaaaa : 0x80 : n & 0x3F
- return char(
- 0xC0 + floor(n/0x40),
- 0x80 + (n % 0x40)
- )
- elseif n < 0x10000 then
- -- 1110bbbb : 0xE0 : n >> 12
- -- 10bbbbaa : 0x80 : (n >> 6) & 0x3F
- -- 10aaaaaa : 0x80 : n & 0x3F
- return char(
- 0xE0 + floor(n/0x1000),
- 0x80 + (floor(n/0x40) % 0x40),
- 0x80 + (n % 0x40)
- )
- elseif n < 0x200000 then
- -- 11110ccc : 0xF0 : n >> 18
- -- 10ccbbbb : 0x80 : (n >> 12) & 0x3F
- -- 10bbbbaa : 0x80 : (n >> 6) & 0x3F
- -- 10aaaaaa : 0x80 : n & 0x3F
- -- dddd : ccccc - 1
- return char(
- 0xF0 + floor(n/0x40000),
- 0x80 + (floor(n/0x1000) % 0x40),
- 0x80 + (floor(n/0x40) % 0x40),
- 0x80 + (n % 0x40)
- )
- else
- return ""
- end
- end
-
-end
-
-if not utf.byte then
-
- local utf8byte = patterns.utf8byte
-
- function utf.byte(c)
- return lpegmatch(utf8byte,c)
- end
-
-end
-
-local utfchar, utfbyte = utf.char, utf.byte
-
--- As we want to get rid of the (unmaintained) utf library we implement our own
--- variants (in due time an independent module):
-
-function utf.filetype(data)
- return data and lpegmatch(p_utftype,data) or "unknown"
-end
-
-local toentities = Cs (
- (
- patterns.utf8one
- + (
- patterns.utf8two
- + patterns.utf8three
- + patterns.utf8four
- ) / function(s) local b = utfbyte(s) if b < 127 then return s else return format("&#%X;",b) end end
- )^0
-)
-
-patterns.toentities = toentities
-
-function utf.toentities(str)
- return lpegmatch(toentities,str)
-end
-
--- local utfchr = { } -- 60K -> 2.638 M extra mem but currently not called that often (on latin)
---
--- setmetatable(utfchr, { __index = function(t,k) local v = utfchar(k) t[k] = v return v end } )
---
--- collectgarbage("collect")
--- local u = collectgarbage("count")*1024
--- local t = os.clock()
--- for i=1,1000 do
--- for i=1,600 do
--- local a = utfchr[i]
--- end
--- end
--- print(os.clock()-t,collectgarbage("count")*1024-u)
-
--- collectgarbage("collect")
--- local t = os.clock()
--- for i=1,1000 do
--- for i=1,600 do
--- local a = utfchar(i)
--- end
--- end
--- print(os.clock()-t,collectgarbage("count")*1024-u)
-
--- local byte = string.byte
--- local utfchar = utf.char
-
-local one = P(1)
-local two = C(1) * C(1)
-local four = C(R(utfchar(0xD8),utfchar(0xFF))) * C(1) * C(1) * C(1)
-
--- actually one of them is already utf ... sort of useless this one
-
--- function utf.char(n)
--- if n < 0x80 then
--- return char(n)
--- elseif n < 0x800 then
--- return char(
--- 0xC0 + floor(n/0x40),
--- 0x80 + (n % 0x40)
--- )
--- elseif n < 0x10000 then
--- return char(
--- 0xE0 + floor(n/0x1000),
--- 0x80 + (floor(n/0x40) % 0x40),
--- 0x80 + (n % 0x40)
--- )
--- elseif n < 0x40000 then
--- return char(
--- 0xF0 + floor(n/0x40000),
--- 0x80 + floor(n/0x1000),
--- 0x80 + (floor(n/0x40) % 0x40),
--- 0x80 + (n % 0x40)
--- )
--- else
--- -- return char(
--- -- 0xF1 + floor(n/0x1000000),
--- -- 0x80 + floor(n/0x40000),
--- -- 0x80 + floor(n/0x1000),
--- -- 0x80 + (floor(n/0x40) % 0x40),
--- -- 0x80 + (n % 0x40)
--- -- )
--- return "?"
--- end
--- end
---
--- merge into:
-
-local pattern = P("\254\255") * Cs( (
- four / function(a,b,c,d)
- local ab = 0xFF * byte(a) + byte(b)
- local cd = 0xFF * byte(c) + byte(d)
- return utfchar((ab-0xD800)*0x400 + (cd-0xDC00) + 0x10000)
- end
- + two / function(a,b)
- return utfchar(byte(a)*256 + byte(b))
- end
- + one
- )^1 )
- + P("\255\254") * Cs( (
- four / function(b,a,d,c)
- local ab = 0xFF * byte(a) + byte(b)
- local cd = 0xFF * byte(c) + byte(d)
- return utfchar((ab-0xD800)*0x400 + (cd-0xDC00) + 0x10000)
- end
- + two / function(b,a)
- return utfchar(byte(a)*256 + byte(b))
- end
- + one
- )^1 )
-
-function string.toutf(s) -- in string namespace
- return lpegmatch(pattern,s) or s -- todo: utf32
-end
-
-local validatedutf = Cs (
- (
- patterns.utf8one
- + patterns.utf8two
- + patterns.utf8three
- + patterns.utf8four
- + P(1) / "�"
- )^0
-)
-
-patterns.validatedutf = validatedutf
-
-function utf.is_valid(str)
- return type(str) == "string" and lpegmatch(validatedutf,str) or false
-end
-
-if not utf.len then
-
- -- -- alternative 1: 0.77
- --
- -- local utfcharcounter = utfbom^-1 * Cs((p_utf8char/'!')^0)
- --
- -- function utf.len(str)
- -- return #lpegmatch(utfcharcounter,str or "")
- -- end
- --
- -- -- alternative 2: 1.70
- --
- -- local n = 0
- --
- -- local utfcharcounter = utfbom^-1 * (p_utf8char/function() n = n + 1 end)^0 -- slow
- --
- -- function utf.length(str)
- -- n = 0
- -- lpegmatch(utfcharcounter,str or "")
- -- return n
- -- end
- --
- -- -- alternative 3: 0.24 (native unicode.utf8.len: 0.047)
-
- -- local n = 0
- --
- -- -- local utfcharcounter = lpeg.patterns.utfbom^-1 * P ( ( Cp() * (
- -- -- patterns.utf8one ^1 * Cc(1)
- -- -- + patterns.utf8two ^1 * Cc(2)
- -- -- + patterns.utf8three^1 * Cc(3)
- -- -- + patterns.utf8four ^1 * Cc(4) ) * Cp() / function(f,d,t) n = n + (t - f)/d end
- -- -- )^0 ) -- just as many captures as below
- --
- -- -- local utfcharcounter = lpeg.patterns.utfbom^-1 * P ( (
- -- -- (Cmt(patterns.utf8one ^1,function(_,_,s) n = n + #s return true end))
- -- -- + (Cmt(patterns.utf8two ^1,function(_,_,s) n = n + #s/2 return true end))
- -- -- + (Cmt(patterns.utf8three^1,function(_,_,s) n = n + #s/3 return true end))
- -- -- + (Cmt(patterns.utf8four ^1,function(_,_,s) n = n + #s/4 return true end))
- -- -- )^0 ) -- not interesting as it creates strings but sometimes faster
- --
- -- -- The best so far:
- --
- -- local utfcharcounter = utfbom^-1 * P ( (
- -- Cp() * (patterns.utf8one )^1 * Cp() / function(f,t) n = n + t - f end
- -- + Cp() * (patterns.utf8two )^1 * Cp() / function(f,t) n = n + (t - f)/2 end
- -- + Cp() * (patterns.utf8three)^1 * Cp() / function(f,t) n = n + (t - f)/3 end
- -- + Cp() * (patterns.utf8four )^1 * Cp() / function(f,t) n = n + (t - f)/4 end
- -- )^0 )
-
- -- function utf.len(str)
- -- n = 0
- -- lpegmatch(utfcharcounter,str or "")
- -- return n
- -- end
-
- local n, f = 0, 1
-
- local utfcharcounter = patterns.utfbom^-1 * Cmt (
- Cc(1) * patterns.utf8one ^1
- + Cc(2) * patterns.utf8two ^1
- + Cc(3) * patterns.utf8three^1
- + Cc(4) * patterns.utf8four ^1,
- function(_,t,d) -- due to Cc no string captures, so faster
- n = n + (t - f)/d
- f = t
- return true
- end
- )^0
-
- function utf.len(str)
- n, f = 0, 1
- lpegmatch(utfcharcounter,str or "")
- return n
- end
-
- -- -- these are quite a bit slower:
-
- -- utfcharcounter = utfbom^-1 * (Cmt(P(1) * R("\128\191")^0, function() n = n + 1 return true end))^0 -- 50+ times slower
- -- utfcharcounter = utfbom^-1 * (Cmt(P(1), function() n = n + 1 return true end) * R("\128\191")^0)^0 -- 50- times slower
-
-end
-
-utf.length = utf.len
-
-if not utf.sub then
-
- -- inefficient as lpeg just copies ^n
-
- -- local function sub(str,start,stop)
- -- local pattern = p_utf8char^-(start-1) * C(p_utf8char^-(stop-start+1))
- -- inspect(pattern)
- -- return lpegmatch(pattern,str) or ""
- -- end
-
- -- local b, e, n, first, last = 0, 0, 0, 0, 0
- --
- -- local function slide(s,p)
- -- n = n + 1
- -- if n == first then
- -- b = p
- -- if not last then
- -- return nil
- -- end
- -- end
- -- if n == last then
- -- e = p
- -- return nil
- -- else
- -- return p
- -- end
- -- end
- --
- -- local pattern = Cmt(p_utf8char,slide)^0
- --
- -- function utf.sub(str,start,stop) -- todo: from the end
- -- if not start then
- -- return str
- -- end
- -- b, e, n, first, last = 0, 0, 0, start, stop
- -- lpegmatch(pattern,str)
- -- if not stop then
- -- return sub(str,b)
- -- else
- -- return sub(str,b,e-1)
- -- end
- -- end
-
- -- print(utf.sub("Hans Hagen is my name"))
- -- print(utf.sub("Hans Hagen is my name",5))
- -- print(utf.sub("Hans Hagen is my name",5,10))
-
- local utflength = utf.length
-
- -- also negative indices, upto 10 times slower than a c variant
-
- local b, e, n, first, last = 0, 0, 0, 0, 0
-
- local function slide_zero(s,p)
- n = n + 1
- if n >= last then
- e = p - 1
- else
- return p
- end
- end
-
- local function slide_one(s,p)
- n = n + 1
- if n == first then
- b = p
- end
- if n >= last then
- e = p - 1
- else
- return p
- end
- end
-
- local function slide_two(s,p)
- n = n + 1
- if n == first then
- b = p
- else
- return true
- end
- end
-
- local pattern_zero = Cmt(p_utf8char,slide_zero)^0
- local pattern_one = Cmt(p_utf8char,slide_one )^0
- local pattern_two = Cmt(p_utf8char,slide_two )^0
-
- function utf.sub(str,start,stop)
- if not start then
- return str
- end
- if start == 0 then
- start = 1
- end
- if not stop then
- if start < 0 then
- local l = utflength(str) -- we can inline this function if needed
- start = l + start
- else
- start = start - 1
- end
- b, n, first = 0, 0, start
- lpegmatch(pattern_two,str)
- if n >= first then
- return sub(str,b)
- else
- return ""
- end
- end
- if start < 0 or stop < 0 then
- local l = utf.length(str)
- if start < 0 then
- start = l + start
- if start <= 0 then
- start = 1
- else
- start = start + 1
- end
- end
- if stop < 0 then
- stop = l + stop
- if stop == 0 then
- stop = 1
- else
- stop = stop + 1
- end
- end
- end
- if start > stop then
- return ""
- elseif start > 1 then
- b, e, n, first, last = 0, 0, 0, start - 1, stop
- lpegmatch(pattern_one,str)
- if n >= first and e == 0 then
- e = #str
- end
- return sub(str,b,e)
- else
- b, e, n, last = 1, 0, 0, stop
- lpegmatch(pattern_zero,str)
- if e == 0 then
- e = #str
- end
- return sub(str,b,e)
- end
- end
-
- -- local n = 100000
- -- local str = string.rep("123456àáâãäå",100)
- --
- -- for i=-15,15,1 do
- -- for j=-15,15,1 do
- -- if utf.xsub(str,i,j) ~= utf.sub(str,i,j) then
- -- print("error",i,j,"l>"..utf.xsub(str,i,j),"s>"..utf.sub(str,i,j))
- -- end
- -- end
- -- if utf.xsub(str,i) ~= utf.sub(str,i) then
- -- print("error",i,"l>"..utf.xsub(str,i),"s>"..utf.sub(str,i))
- -- end
- -- end
-
- -- print(" 1, 7",utf.xsub(str, 1, 7),utf.sub(str, 1, 7))
- -- print(" 0, 7",utf.xsub(str, 0, 7),utf.sub(str, 0, 7))
- -- print(" 0, 9",utf.xsub(str, 0, 9),utf.sub(str, 0, 9))
- -- print(" 4 ",utf.xsub(str, 4 ),utf.sub(str, 4 ))
- -- print(" 0 ",utf.xsub(str, 0 ),utf.sub(str, 0 ))
- -- print(" 0, 0",utf.xsub(str, 0, 0),utf.sub(str, 0, 0))
- -- print(" 4, 4",utf.xsub(str, 4, 4),utf.sub(str, 4, 4))
- -- print(" 4, 0",utf.xsub(str, 4, 0),utf.sub(str, 4, 0))
- -- print("-3, 0",utf.xsub(str,-3, 0),utf.sub(str,-3, 0))
- -- print(" 0,-3",utf.xsub(str, 0,-3),utf.sub(str, 0,-3))
- -- print(" 5,-3",utf.xsub(str,-5,-3),utf.sub(str,-5,-3))
- -- print("-3 ",utf.xsub(str,-3 ),utf.sub(str,-3 ))
-
-end
-
--- a replacement for simple gsubs:
-
-function utf.remapper(mapping)
- local pattern = Cs((p_utf8char/mapping)^0)
- return function(str)
- if not str or str == "" then
- return ""
- else
- return lpegmatch(pattern,str)
- end
- end, pattern
-end
-
--- local remap = utf.remapper { a = 'd', b = "c", c = "b", d = "a" }
--- print(remap("abcd 1234 abcd"))
-
---
-
-function utf.replacer(t) -- no precheck, always string builder
- local r = replacer(t,false,false,true)
- return function(str)
- return lpegmatch(r,str)
- end
-end
-
-function utf.subtituter(t) -- with precheck and no building if no match
- local f = finder (t)
- local r = replacer(t,false,false,true)
- return function(str)
- local i = lpegmatch(f,str)
- if not i then
- return str
- elseif i > #str then
- return str
- else
- -- return sub(str,1,i-2) .. lpegmatch(r,str,i-1) -- slower
- return lpegmatch(r,str)
- end
- end
-end
-
--- inspect(utf.split("a b c d"))
--- inspect(utf.split("a b c d",true))
-
-local utflinesplitter = p_utfbom^-1 * lpeg.tsplitat(p_newline)
-local utfcharsplitter_ows = p_utfbom^-1 * Ct(C(p_utf8char)^0)
-local utfcharsplitter_iws = p_utfbom^-1 * Ct((p_whitespace^1 + C(p_utf8char))^0)
-local utfcharsplitter_raw = Ct(C(p_utf8char)^0)
-
-patterns.utflinesplitter = utflinesplitter
-
-function utf.splitlines(str)
- return lpegmatch(utflinesplitter,str or "")
-end
-
-function utf.split(str,ignorewhitespace) -- new
- if ignorewhitespace then
- return lpegmatch(utfcharsplitter_iws,str or "")
- else
- return lpegmatch(utfcharsplitter_ows,str or "")
- end
-end
-
-function utf.totable(str) -- keeps bom
- return lpegmatch(utfcharsplitter_raw,str)
-end
-
--- 0 EF BB BF UTF-8
--- 1 FF FE UTF-16-little-endian
--- 2 FE FF UTF-16-big-endian
--- 3 FF FE 00 00 UTF-32-little-endian
--- 4 00 00 FE FF UTF-32-big-endian
---
--- \000 fails in <= 5.0 but is valid in >=5.1 where %z is depricated
-
--- utf.name = {
--- [0] = 'utf-8',
--- [1] = 'utf-16-le',
--- [2] = 'utf-16-be',
--- [3] = 'utf-32-le',
--- [4] = 'utf-32-be'
--- }
---
--- function utf.magic(f)
--- local str = f:read(4)
--- if not str then
--- f:seek('set')
--- return 0
--- -- elseif find(str,"^%z%z\254\255") then -- depricated
--- -- elseif find(str,"^\000\000\254\255") then -- not permitted and bugged
--- elseif find(str,"\000\000\254\255",1,true) then -- seems to work okay (TH)
--- return 4
--- -- elseif find(str,"^\255\254%z%z") then -- depricated
--- -- elseif find(str,"^\255\254\000\000") then -- not permitted and bugged
--- elseif find(str,"\255\254\000\000",1,true) then -- seems to work okay (TH)
--- return 3
--- elseif find(str,"^\254\255") then
--- f:seek('set',2)
--- return 2
--- elseif find(str,"^\255\254") then
--- f:seek('set',2)
--- return 1
--- elseif find(str,"^\239\187\191") then
--- f:seek('set',3)
--- return 0
--- else
--- f:seek('set')
--- return 0
--- end
--- end
-
-function utf.magic(f) -- not used
- local str = f:read(4) or ""
- local off = lpegmatch(p_utfoffset,str)
- if off < 4 then
- f:seek('set',off)
- end
- return lpegmatch(p_utftype,str)
-end
-
-local function utf16_to_utf8_be(t)
- if type(t) == "string" then
- t = lpegmatch(utflinesplitter,t)
- end
- local result = { } -- we reuse result
- for i=1,#t do
- local r, more = 0, 0
- for left, right in bytepairs(t[i]) do
- if right then
- local now = 256*left + right
- if more > 0 then
- now = (more-0xD800)*0x400 + (now-0xDC00) + 0x10000 -- the 0x10000 smells wrong
- more = 0
- r = r + 1
- result[r] = utfchar(now)
- elseif now >= 0xD800 and now <= 0xDBFF then
- more = now
- else
- r = r + 1
- result[r] = utfchar(now)
- end
- end
- end
- t[i] = concat(result,"",1,r) -- we reused tmp, hence t
- end
- return t
-end
-
-local function utf16_to_utf8_le(t)
- if type(t) == "string" then
- t = lpegmatch(utflinesplitter,t)
- end
- local result = { } -- we reuse result
- for i=1,#t do
- local r, more = 0, 0
- for left, right in bytepairs(t[i]) do
- if right then
- local now = 256*right + left
- if more > 0 then
- now = (more-0xD800)*0x400 + (now-0xDC00) + 0x10000 -- the 0x10000 smells wrong
- more = 0
- r = r + 1
- result[r] = utfchar(now)
- elseif now >= 0xD800 and now <= 0xDBFF then
- more = now
- else
- r = r + 1
- result[r] = utfchar(now)
- end
- end
- end
- t[i] = concat(result,"",1,r) -- we reused tmp, hence t
- end
- return t
-end
-
-local function utf32_to_utf8_be(t)
- if type(t) == "string" then
- t = lpegmatch(utflinesplitter,t)
- end
- local result = { } -- we reuse result
- for i=1,#t do
- local r, more = 0, -1
- for a,b in bytepairs(t[i]) do
- if a and b then
- if more < 0 then
- more = 256*256*256*a + 256*256*b
- else
- r = r + 1
- result[t] = utfchar(more + 256*a + b)
- more = -1
- end
- else
- break
- end
- end
- t[i] = concat(result,"",1,r)
- end
- return t
-end
-
-local function utf32_to_utf8_le(t)
- if type(t) == "string" then
- t = lpegmatch(utflinesplitter,t)
- end
- local result = { } -- we reuse result
- for i=1,#t do
- local r, more = 0, -1
- for a,b in bytepairs(t[i]) do
- if a and b then
- if more < 0 then
- more = 256*b + a
- else
- r = r + 1
- result[t] = utfchar(more + 256*256*256*b + 256*256*a)
- more = -1
- end
- else
- break
- end
- end
- t[i] = concat(result,"",1,r)
- end
- return t
-end
-
-utf.utf32_to_utf8_be = utf32_to_utf8_be
-utf.utf32_to_utf8_le = utf32_to_utf8_le
-utf.utf16_to_utf8_be = utf16_to_utf8_be
-utf.utf16_to_utf8_le = utf16_to_utf8_le
-
-function utf.utf8_to_utf8(t)
- return type(t) == "string" and lpegmatch(utflinesplitter,t) or t
-end
-
-function utf.utf16_to_utf8(t,endian)
- return endian and utf16_to_utf8_be(t) or utf16_to_utf8_le(t) or t
-end
-
-function utf.utf32_to_utf8(t,endian)
- return endian and utf32_to_utf8_be(t) or utf32_to_utf8_le(t) or t
-end
-
-local function little(c)
- local b = byte(c)
- if b < 0x10000 then
- return char(b%256,b/256)
- else
- b = b - 0x10000
- local b1, b2 = b/1024 + 0xD800, b%1024 + 0xDC00
- return char(b1%256,b1/256,b2%256,b2/256)
- end
-end
-
-local function big(c)
- local b = byte(c)
- if b < 0x10000 then
- return char(b/256,b%256)
- else
- b = b - 0x10000
- local b1, b2 = b/1024 + 0xD800, b%1024 + 0xDC00
- return char(b1/256,b1%256,b2/256,b2%256)
- end
-end
-
--- function utf.utf8_to_utf16(str,littleendian)
--- if littleendian then
--- return char(255,254) .. utfgsub(str,".",little)
--- else
--- return char(254,255) .. utfgsub(str,".",big)
--- end
--- end
-
-local _, l_remap = utf.remapper(little)
-local _, b_remap = utf.remapper(big)
-
-function utf.utf8_to_utf16(str,littleendian)
- if littleendian then
- return char(255,254) .. lpegmatch(l_remap,str)
- else
- return char(254,255) .. lpegmatch(b_remap,str)
- end
-end
-
--- function utf.tocodes(str,separator) -- can be sped up with an lpeg
--- local t, n = { }, 0
--- for u in utfvalues(str) do
--- n = n + 1
--- t[n] = format("0x%04X",u)
--- end
--- return concat(t,separator or " ")
--- end
-
-local pattern = Cs (
- (p_utf8byte / function(unicode ) return format( "0x%04X", unicode) end) *
- (p_utf8byte * Carg(1) / function(unicode,separator) return format("%s0x%04X",separator,unicode) end)^0
-)
-
-function utf.tocodes(str,separator)
- return lpegmatch(pattern,str,1,separator or " ")
-end
-
-function utf.ustring(s)
- return format("U+%05X",type(s) == "number" and s or utfbyte(s))
-end
-
-function utf.xstring(s)
- return format("0x%05X",type(s) == "number" and s or utfbyte(s))
-end
-
---
-
-local p_nany = p_utf8char / ""
-
-if utfgmatch then
-
- function utf.count(str,what)
- if type(what) == "string" then
- local n = 0
- for _ in utfgmatch(str,what) do
- n = n + 1
- end
- return n
- else -- 4 times slower but still faster than / function
- return #lpegmatch(Cs((P(what)/" " + p_nany)^0),str)
- end
- end
-
-else
-
- local cache = { }
-
- function utf.count(str,what)
- if type(what) == "string" then
- local p = cache[what]
- if not p then
- p = Cs((P(what)/" " + p_nany)^0)
- cache[p] = p
- end
- return #lpegmatch(p,str)
- else -- 4 times slower but still faster than / function
- return #lpegmatch(Cs((P(what)/" " + p_nany)^0),str)
- end
- end
-
-end
-
--- maybe also register as string.utf*
-
-
-if not utf.characters then
-
- -- New: this gmatch hack is taken from the Lua 5.2 book. It's about two times slower
- -- than the built-in string.utfcharacters.
-
- function utf.characters(str)
- return gmatch(str,".[\128-\191]*")
- end
-
- string.utfcharacters = utf.characters
-
-end
-
-if not utf.values then
-
- -- So, a logical next step is to check for the values variant. It over five times
- -- slower than the built-in string.utfvalues. I optimized it a bit for n=0,1.
-
- ----- wrap, yield, gmatch = coroutine.wrap, coroutine.yield, string.gmatch
- local find = string.find
-
- local dummy = function()
- -- we share this one
- end
-
- -- function utf.values(str)
- -- local n = #str
- -- if n == 0 then
- -- return wrap(dummy)
- -- elseif n == 1 then
- -- return wrap(function() yield(utfbyte(str)) end)
- -- else
- -- return wrap(function() for s in gmatch(str,".[\128-\191]*") do
- -- yield(utfbyte(s))
- -- end end)
- -- end
- -- end
- --
- -- faster:
-
- function utf.values(str)
- local n = #str
- if n == 0 then
- return dummy
- elseif n == 1 then
- return function() return utfbyte(str) end
- else
- local p = 1
- -- local n = #str
- return function()
- -- if p <= n then -- slower than the last find
- local b, e = find(str,".[\128-\191]*",p)
- if b then
- p = e + 1
- return utfbyte(sub(str,b,e))
- end
- -- end
- end
- end
- end
-
- -- slower:
- --
- -- local pattern = C(patterns.utf8character) * Cp()
- -- ----- pattern = patterns.utf8character/utfbyte * Cp()
- -- ----- pattern = patterns.utf8byte * Cp()
- --
- -- function utf.values(str) -- one of the cases where a find is faster than an lpeg
- -- local n = #str
- -- if n == 0 then
- -- return dummy
- -- elseif n == 1 then
- -- return function() return utfbyte(str) end
- -- else
- -- local p = 1
- -- return function()
- -- local s, e = lpegmatch(pattern,str,p)
- -- if e then
- -- p = e
- -- return utfbyte(s)
- -- -- return s
- -- end
- -- end
- -- end
- -- end
-
- string.utfvalues = utf.values
-
-end
+if not modules then modules = { } end modules ['l-unicode'] = {
+ 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"
+}
+
+-- this module will be reorganized
+
+-- todo: utf.sub replacement (used in syst-aux)
+
+-- we put these in the utf namespace:
+
+utf = utf or (unicode and unicode.utf8) or { }
+
+utf.characters = utf.characters or string.utfcharacters
+utf.values = utf.values or string.utfvalues
+
+-- string.utfvalues
+-- string.utfcharacters
+-- string.characters
+-- string.characterpairs
+-- string.bytes
+-- string.bytepairs
+
+local type = type
+local char, byte, format, sub = string.char, string.byte, string.format, string.sub
+local concat = table.concat
+local P, C, R, Cs, Ct, Cmt, Cc, Carg, Cp = lpeg.P, lpeg.C, lpeg.R, lpeg.Cs, lpeg.Ct, lpeg.Cmt, lpeg.Cc, lpeg.Carg, lpeg.Cp
+local lpegmatch, patterns = lpeg.match, lpeg.patterns
+
+local bytepairs = string.bytepairs
+
+local finder = lpeg.finder
+local replacer = lpeg.replacer
+
+local utfvalues = utf.values
+local utfgmatch = utf.gmatch -- not always present
+
+local p_utftype = patterns.utftype
+local p_utfoffset = patterns.utfoffset
+local p_utf8char = patterns.utf8char
+local p_utf8byte = patterns.utf8byte
+local p_utfbom = patterns.utfbom
+local p_newline = patterns.newline
+local p_whitespace = patterns.whitespace
+
+if not unicode then
+
+ unicode = { utf = utf } -- for a while
+
+end
+
+if not utf.char then
+
+ local floor, char = math.floor, string.char
+
+ function utf.char(n)
+ if n < 0x80 then
+ -- 0aaaaaaa : 0x80
+ return char(n)
+ elseif n < 0x800 then
+ -- 110bbbaa : 0xC0 : n >> 6
+ -- 10aaaaaa : 0x80 : n & 0x3F
+ return char(
+ 0xC0 + floor(n/0x40),
+ 0x80 + (n % 0x40)
+ )
+ elseif n < 0x10000 then
+ -- 1110bbbb : 0xE0 : n >> 12
+ -- 10bbbbaa : 0x80 : (n >> 6) & 0x3F
+ -- 10aaaaaa : 0x80 : n & 0x3F
+ return char(
+ 0xE0 + floor(n/0x1000),
+ 0x80 + (floor(n/0x40) % 0x40),
+ 0x80 + (n % 0x40)
+ )
+ elseif n < 0x200000 then
+ -- 11110ccc : 0xF0 : n >> 18
+ -- 10ccbbbb : 0x80 : (n >> 12) & 0x3F
+ -- 10bbbbaa : 0x80 : (n >> 6) & 0x3F
+ -- 10aaaaaa : 0x80 : n & 0x3F
+ -- dddd : ccccc - 1
+ return char(
+ 0xF0 + floor(n/0x40000),
+ 0x80 + (floor(n/0x1000) % 0x40),
+ 0x80 + (floor(n/0x40) % 0x40),
+ 0x80 + (n % 0x40)
+ )
+ else
+ return ""
+ end
+ end
+
+end
+
+if not utf.byte then
+
+ local utf8byte = patterns.utf8byte
+
+ function utf.byte(c)
+ return lpegmatch(utf8byte,c)
+ end
+
+end
+
+local utfchar, utfbyte = utf.char, utf.byte
+
+-- As we want to get rid of the (unmaintained) utf library we implement our own
+-- variants (in due time an independent module):
+
+function utf.filetype(data)
+ return data and lpegmatch(p_utftype,data) or "unknown"
+end
+
+local toentities = Cs (
+ (
+ patterns.utf8one
+ + (
+ patterns.utf8two
+ + patterns.utf8three
+ + patterns.utf8four
+ ) / function(s) local b = utfbyte(s) if b < 127 then return s else return format("&#%X;",b) end end
+ )^0
+)
+
+patterns.toentities = toentities
+
+function utf.toentities(str)
+ return lpegmatch(toentities,str)
+end
+
+-- local utfchr = { } -- 60K -> 2.638 M extra mem but currently not called that often (on latin)
+--
+-- setmetatable(utfchr, { __index = function(t,k) local v = utfchar(k) t[k] = v return v end } )
+--
+-- collectgarbage("collect")
+-- local u = collectgarbage("count")*1024
+-- local t = os.clock()
+-- for i=1,1000 do
+-- for i=1,600 do
+-- local a = utfchr[i]
+-- end
+-- end
+-- print(os.clock()-t,collectgarbage("count")*1024-u)
+
+-- collectgarbage("collect")
+-- local t = os.clock()
+-- for i=1,1000 do
+-- for i=1,600 do
+-- local a = utfchar(i)
+-- end
+-- end
+-- print(os.clock()-t,collectgarbage("count")*1024-u)
+
+-- local byte = string.byte
+-- local utfchar = utf.char
+
+local one = P(1)
+local two = C(1) * C(1)
+local four = C(R(utfchar(0xD8),utfchar(0xFF))) * C(1) * C(1) * C(1)
+
+-- actually one of them is already utf ... sort of useless this one
+
+-- function utf.char(n)
+-- if n < 0x80 then
+-- return char(n)
+-- elseif n < 0x800 then
+-- return char(
+-- 0xC0 + floor(n/0x40),
+-- 0x80 + (n % 0x40)
+-- )
+-- elseif n < 0x10000 then
+-- return char(
+-- 0xE0 + floor(n/0x1000),
+-- 0x80 + (floor(n/0x40) % 0x40),
+-- 0x80 + (n % 0x40)
+-- )
+-- elseif n < 0x40000 then
+-- return char(
+-- 0xF0 + floor(n/0x40000),
+-- 0x80 + floor(n/0x1000),
+-- 0x80 + (floor(n/0x40) % 0x40),
+-- 0x80 + (n % 0x40)
+-- )
+-- else
+-- -- return char(
+-- -- 0xF1 + floor(n/0x1000000),
+-- -- 0x80 + floor(n/0x40000),
+-- -- 0x80 + floor(n/0x1000),
+-- -- 0x80 + (floor(n/0x40) % 0x40),
+-- -- 0x80 + (n % 0x40)
+-- -- )
+-- return "?"
+-- end
+-- end
+--
+-- merge into:
+
+local pattern = P("\254\255") * Cs( (
+ four / function(a,b,c,d)
+ local ab = 0xFF * byte(a) + byte(b)
+ local cd = 0xFF * byte(c) + byte(d)
+ return utfchar((ab-0xD800)*0x400 + (cd-0xDC00) + 0x10000)
+ end
+ + two / function(a,b)
+ return utfchar(byte(a)*256 + byte(b))
+ end
+ + one
+ )^1 )
+ + P("\255\254") * Cs( (
+ four / function(b,a,d,c)
+ local ab = 0xFF * byte(a) + byte(b)
+ local cd = 0xFF * byte(c) + byte(d)
+ return utfchar((ab-0xD800)*0x400 + (cd-0xDC00) + 0x10000)
+ end
+ + two / function(b,a)
+ return utfchar(byte(a)*256 + byte(b))
+ end
+ + one
+ )^1 )
+
+function string.toutf(s) -- in string namespace
+ return lpegmatch(pattern,s) or s -- todo: utf32
+end
+
+local validatedutf = Cs (
+ (
+ patterns.utf8one
+ + patterns.utf8two
+ + patterns.utf8three
+ + patterns.utf8four
+ + P(1) / "�"
+ )^0
+)
+
+patterns.validatedutf = validatedutf
+
+function utf.is_valid(str)
+ return type(str) == "string" and lpegmatch(validatedutf,str) or false
+end
+
+if not utf.len then
+
+ -- -- alternative 1: 0.77
+ --
+ -- local utfcharcounter = utfbom^-1 * Cs((p_utf8char/'!')^0)
+ --
+ -- function utf.len(str)
+ -- return #lpegmatch(utfcharcounter,str or "")
+ -- end
+ --
+ -- -- alternative 2: 1.70
+ --
+ -- local n = 0
+ --
+ -- local utfcharcounter = utfbom^-1 * (p_utf8char/function() n = n + 1 end)^0 -- slow
+ --
+ -- function utf.length(str)
+ -- n = 0
+ -- lpegmatch(utfcharcounter,str or "")
+ -- return n
+ -- end
+ --
+ -- -- alternative 3: 0.24 (native unicode.utf8.len: 0.047)
+
+ -- local n = 0
+ --
+ -- -- local utfcharcounter = lpeg.patterns.utfbom^-1 * P ( ( Cp() * (
+ -- -- patterns.utf8one ^1 * Cc(1)
+ -- -- + patterns.utf8two ^1 * Cc(2)
+ -- -- + patterns.utf8three^1 * Cc(3)
+ -- -- + patterns.utf8four ^1 * Cc(4) ) * Cp() / function(f,d,t) n = n + (t - f)/d end
+ -- -- )^0 ) -- just as many captures as below
+ --
+ -- -- local utfcharcounter = lpeg.patterns.utfbom^-1 * P ( (
+ -- -- (Cmt(patterns.utf8one ^1,function(_,_,s) n = n + #s return true end))
+ -- -- + (Cmt(patterns.utf8two ^1,function(_,_,s) n = n + #s/2 return true end))
+ -- -- + (Cmt(patterns.utf8three^1,function(_,_,s) n = n + #s/3 return true end))
+ -- -- + (Cmt(patterns.utf8four ^1,function(_,_,s) n = n + #s/4 return true end))
+ -- -- )^0 ) -- not interesting as it creates strings but sometimes faster
+ --
+ -- -- The best so far:
+ --
+ -- local utfcharcounter = utfbom^-1 * P ( (
+ -- Cp() * (patterns.utf8one )^1 * Cp() / function(f,t) n = n + t - f end
+ -- + Cp() * (patterns.utf8two )^1 * Cp() / function(f,t) n = n + (t - f)/2 end
+ -- + Cp() * (patterns.utf8three)^1 * Cp() / function(f,t) n = n + (t - f)/3 end
+ -- + Cp() * (patterns.utf8four )^1 * Cp() / function(f,t) n = n + (t - f)/4 end
+ -- )^0 )
+
+ -- function utf.len(str)
+ -- n = 0
+ -- lpegmatch(utfcharcounter,str or "")
+ -- return n
+ -- end
+
+ local n, f = 0, 1
+
+ local utfcharcounter = patterns.utfbom^-1 * Cmt (
+ Cc(1) * patterns.utf8one ^1
+ + Cc(2) * patterns.utf8two ^1
+ + Cc(3) * patterns.utf8three^1
+ + Cc(4) * patterns.utf8four ^1,
+ function(_,t,d) -- due to Cc no string captures, so faster
+ n = n + (t - f)/d
+ f = t
+ return true
+ end
+ )^0
+
+ function utf.len(str)
+ n, f = 0, 1
+ lpegmatch(utfcharcounter,str or "")
+ return n
+ end
+
+ -- -- these are quite a bit slower:
+
+ -- utfcharcounter = utfbom^-1 * (Cmt(P(1) * R("\128\191")^0, function() n = n + 1 return true end))^0 -- 50+ times slower
+ -- utfcharcounter = utfbom^-1 * (Cmt(P(1), function() n = n + 1 return true end) * R("\128\191")^0)^0 -- 50- times slower
+
+end
+
+utf.length = utf.len
+
+if not utf.sub then
+
+ -- inefficient as lpeg just copies ^n
+
+ -- local function sub(str,start,stop)
+ -- local pattern = p_utf8char^-(start-1) * C(p_utf8char^-(stop-start+1))
+ -- inspect(pattern)
+ -- return lpegmatch(pattern,str) or ""
+ -- end
+
+ -- local b, e, n, first, last = 0, 0, 0, 0, 0
+ --
+ -- local function slide(s,p)
+ -- n = n + 1
+ -- if n == first then
+ -- b = p
+ -- if not last then
+ -- return nil
+ -- end
+ -- end
+ -- if n == last then
+ -- e = p
+ -- return nil
+ -- else
+ -- return p
+ -- end
+ -- end
+ --
+ -- local pattern = Cmt(p_utf8char,slide)^0
+ --
+ -- function utf.sub(str,start,stop) -- todo: from the end
+ -- if not start then
+ -- return str
+ -- end
+ -- b, e, n, first, last = 0, 0, 0, start, stop
+ -- lpegmatch(pattern,str)
+ -- if not stop then
+ -- return sub(str,b)
+ -- else
+ -- return sub(str,b,e-1)
+ -- end
+ -- end
+
+ -- print(utf.sub("Hans Hagen is my name"))
+ -- print(utf.sub("Hans Hagen is my name",5))
+ -- print(utf.sub("Hans Hagen is my name",5,10))
+
+ local utflength = utf.length
+
+ -- also negative indices, upto 10 times slower than a c variant
+
+ local b, e, n, first, last = 0, 0, 0, 0, 0
+
+ local function slide_zero(s,p)
+ n = n + 1
+ if n >= last then
+ e = p - 1
+ else
+ return p
+ end
+ end
+
+ local function slide_one(s,p)
+ n = n + 1
+ if n == first then
+ b = p
+ end
+ if n >= last then
+ e = p - 1
+ else
+ return p
+ end
+ end
+
+ local function slide_two(s,p)
+ n = n + 1
+ if n == first then
+ b = p
+ else
+ return true
+ end
+ end
+
+ local pattern_zero = Cmt(p_utf8char,slide_zero)^0
+ local pattern_one = Cmt(p_utf8char,slide_one )^0
+ local pattern_two = Cmt(p_utf8char,slide_two )^0
+
+ function utf.sub(str,start,stop)
+ if not start then
+ return str
+ end
+ if start == 0 then
+ start = 1
+ end
+ if not stop then
+ if start < 0 then
+ local l = utflength(str) -- we can inline this function if needed
+ start = l + start
+ else
+ start = start - 1
+ end
+ b, n, first = 0, 0, start
+ lpegmatch(pattern_two,str)
+ if n >= first then
+ return sub(str,b)
+ else
+ return ""
+ end
+ end
+ if start < 0 or stop < 0 then
+ local l = utf.length(str)
+ if start < 0 then
+ start = l + start
+ if start <= 0 then
+ start = 1
+ else
+ start = start + 1
+ end
+ end
+ if stop < 0 then
+ stop = l + stop
+ if stop == 0 then
+ stop = 1
+ else
+ stop = stop + 1
+ end
+ end
+ end
+ if start > stop then
+ return ""
+ elseif start > 1 then
+ b, e, n, first, last = 0, 0, 0, start - 1, stop
+ lpegmatch(pattern_one,str)
+ if n >= first and e == 0 then
+ e = #str
+ end
+ return sub(str,b,e)
+ else
+ b, e, n, last = 1, 0, 0, stop
+ lpegmatch(pattern_zero,str)
+ if e == 0 then
+ e = #str
+ end
+ return sub(str,b,e)
+ end
+ end
+
+ -- local n = 100000
+ -- local str = string.rep("123456àáâãäå",100)
+ --
+ -- for i=-15,15,1 do
+ -- for j=-15,15,1 do
+ -- if utf.xsub(str,i,j) ~= utf.sub(str,i,j) then
+ -- print("error",i,j,"l>"..utf.xsub(str,i,j),"s>"..utf.sub(str,i,j))
+ -- end
+ -- end
+ -- if utf.xsub(str,i) ~= utf.sub(str,i) then
+ -- print("error",i,"l>"..utf.xsub(str,i),"s>"..utf.sub(str,i))
+ -- end
+ -- end
+
+ -- print(" 1, 7",utf.xsub(str, 1, 7),utf.sub(str, 1, 7))
+ -- print(" 0, 7",utf.xsub(str, 0, 7),utf.sub(str, 0, 7))
+ -- print(" 0, 9",utf.xsub(str, 0, 9),utf.sub(str, 0, 9))
+ -- print(" 4 ",utf.xsub(str, 4 ),utf.sub(str, 4 ))
+ -- print(" 0 ",utf.xsub(str, 0 ),utf.sub(str, 0 ))
+ -- print(" 0, 0",utf.xsub(str, 0, 0),utf.sub(str, 0, 0))
+ -- print(" 4, 4",utf.xsub(str, 4, 4),utf.sub(str, 4, 4))
+ -- print(" 4, 0",utf.xsub(str, 4, 0),utf.sub(str, 4, 0))
+ -- print("-3, 0",utf.xsub(str,-3, 0),utf.sub(str,-3, 0))
+ -- print(" 0,-3",utf.xsub(str, 0,-3),utf.sub(str, 0,-3))
+ -- print(" 5,-3",utf.xsub(str,-5,-3),utf.sub(str,-5,-3))
+ -- print("-3 ",utf.xsub(str,-3 ),utf.sub(str,-3 ))
+
+end
+
+-- a replacement for simple gsubs:
+
+function utf.remapper(mapping)
+ local pattern = Cs((p_utf8char/mapping)^0)
+ return function(str)
+ if not str or str == "" then
+ return ""
+ else
+ return lpegmatch(pattern,str)
+ end
+ end, pattern
+end
+
+-- local remap = utf.remapper { a = 'd', b = "c", c = "b", d = "a" }
+-- print(remap("abcd 1234 abcd"))
+
+--
+
+function utf.replacer(t) -- no precheck, always string builder
+ local r = replacer(t,false,false,true)
+ return function(str)
+ return lpegmatch(r,str)
+ end
+end
+
+function utf.subtituter(t) -- with precheck and no building if no match
+ local f = finder (t)
+ local r = replacer(t,false,false,true)
+ return function(str)
+ local i = lpegmatch(f,str)
+ if not i then
+ return str
+ elseif i > #str then
+ return str
+ else
+ -- return sub(str,1,i-2) .. lpegmatch(r,str,i-1) -- slower
+ return lpegmatch(r,str)
+ end
+ end
+end
+
+-- inspect(utf.split("a b c d"))
+-- inspect(utf.split("a b c d",true))
+
+local utflinesplitter = p_utfbom^-1 * lpeg.tsplitat(p_newline)
+local utfcharsplitter_ows = p_utfbom^-1 * Ct(C(p_utf8char)^0)
+local utfcharsplitter_iws = p_utfbom^-1 * Ct((p_whitespace^1 + C(p_utf8char))^0)
+local utfcharsplitter_raw = Ct(C(p_utf8char)^0)
+
+patterns.utflinesplitter = utflinesplitter
+
+function utf.splitlines(str)
+ return lpegmatch(utflinesplitter,str or "")
+end
+
+function utf.split(str,ignorewhitespace) -- new
+ if ignorewhitespace then
+ return lpegmatch(utfcharsplitter_iws,str or "")
+ else
+ return lpegmatch(utfcharsplitter_ows,str or "")
+ end
+end
+
+function utf.totable(str) -- keeps bom
+ return lpegmatch(utfcharsplitter_raw,str)
+end
+
+-- 0 EF BB BF UTF-8
+-- 1 FF FE UTF-16-little-endian
+-- 2 FE FF UTF-16-big-endian
+-- 3 FF FE 00 00 UTF-32-little-endian
+-- 4 00 00 FE FF UTF-32-big-endian
+--
+-- \000 fails in <= 5.0 but is valid in >=5.1 where %z is depricated
+
+-- utf.name = {
+-- [0] = 'utf-8',
+-- [1] = 'utf-16-le',
+-- [2] = 'utf-16-be',
+-- [3] = 'utf-32-le',
+-- [4] = 'utf-32-be'
+-- }
+--
+-- function utf.magic(f)
+-- local str = f:read(4)
+-- if not str then
+-- f:seek('set')
+-- return 0
+-- -- elseif find(str,"^%z%z\254\255") then -- depricated
+-- -- elseif find(str,"^\000\000\254\255") then -- not permitted and bugged
+-- elseif find(str,"\000\000\254\255",1,true) then -- seems to work okay (TH)
+-- return 4
+-- -- elseif find(str,"^\255\254%z%z") then -- depricated
+-- -- elseif find(str,"^\255\254\000\000") then -- not permitted and bugged
+-- elseif find(str,"\255\254\000\000",1,true) then -- seems to work okay (TH)
+-- return 3
+-- elseif find(str,"^\254\255") then
+-- f:seek('set',2)
+-- return 2
+-- elseif find(str,"^\255\254") then
+-- f:seek('set',2)
+-- return 1
+-- elseif find(str,"^\239\187\191") then
+-- f:seek('set',3)
+-- return 0
+-- else
+-- f:seek('set')
+-- return 0
+-- end
+-- end
+
+function utf.magic(f) -- not used
+ local str = f:read(4) or ""
+ local off = lpegmatch(p_utfoffset,str)
+ if off < 4 then
+ f:seek('set',off)
+ end
+ return lpegmatch(p_utftype,str)
+end
+
+local function utf16_to_utf8_be(t)
+ if type(t) == "string" then
+ t = lpegmatch(utflinesplitter,t)
+ end
+ local result = { } -- we reuse result
+ for i=1,#t do
+ local r, more = 0, 0
+ for left, right in bytepairs(t[i]) do
+ if right then
+ local now = 256*left + right
+ if more > 0 then
+ now = (more-0xD800)*0x400 + (now-0xDC00) + 0x10000 -- the 0x10000 smells wrong
+ more = 0
+ r = r + 1
+ result[r] = utfchar(now)
+ elseif now >= 0xD800 and now <= 0xDBFF then
+ more = now
+ else
+ r = r + 1
+ result[r] = utfchar(now)
+ end
+ end
+ end
+ t[i] = concat(result,"",1,r) -- we reused tmp, hence t
+ end
+ return t
+end
+
+local function utf16_to_utf8_le(t)
+ if type(t) == "string" then
+ t = lpegmatch(utflinesplitter,t)
+ end
+ local result = { } -- we reuse result
+ for i=1,#t do
+ local r, more = 0, 0
+ for left, right in bytepairs(t[i]) do
+ if right then
+ local now = 256*right + left
+ if more > 0 then
+ now = (more-0xD800)*0x400 + (now-0xDC00) + 0x10000 -- the 0x10000 smells wrong
+ more = 0
+ r = r + 1
+ result[r] = utfchar(now)
+ elseif now >= 0xD800 and now <= 0xDBFF then
+ more = now
+ else
+ r = r + 1
+ result[r] = utfchar(now)
+ end
+ end
+ end
+ t[i] = concat(result,"",1,r) -- we reused tmp, hence t
+ end
+ return t
+end
+
+local function utf32_to_utf8_be(t)
+ if type(t) == "string" then
+ t = lpegmatch(utflinesplitter,t)
+ end
+ local result = { } -- we reuse result
+ for i=1,#t do
+ local r, more = 0, -1
+ for a,b in bytepairs(t[i]) do
+ if a and b then
+ if more < 0 then
+ more = 256*256*256*a + 256*256*b
+ else
+ r = r + 1
+ result[t] = utfchar(more + 256*a + b)
+ more = -1
+ end
+ else
+ break
+ end
+ end
+ t[i] = concat(result,"",1,r)
+ end
+ return t
+end
+
+local function utf32_to_utf8_le(t)
+ if type(t) == "string" then
+ t = lpegmatch(utflinesplitter,t)
+ end
+ local result = { } -- we reuse result
+ for i=1,#t do
+ local r, more = 0, -1
+ for a,b in bytepairs(t[i]) do
+ if a and b then
+ if more < 0 then
+ more = 256*b + a
+ else
+ r = r + 1
+ result[t] = utfchar(more + 256*256*256*b + 256*256*a)
+ more = -1
+ end
+ else
+ break
+ end
+ end
+ t[i] = concat(result,"",1,r)
+ end
+ return t
+end
+
+utf.utf32_to_utf8_be = utf32_to_utf8_be
+utf.utf32_to_utf8_le = utf32_to_utf8_le
+utf.utf16_to_utf8_be = utf16_to_utf8_be
+utf.utf16_to_utf8_le = utf16_to_utf8_le
+
+function utf.utf8_to_utf8(t)
+ return type(t) == "string" and lpegmatch(utflinesplitter,t) or t
+end
+
+function utf.utf16_to_utf8(t,endian)
+ return endian and utf16_to_utf8_be(t) or utf16_to_utf8_le(t) or t
+end
+
+function utf.utf32_to_utf8(t,endian)
+ return endian and utf32_to_utf8_be(t) or utf32_to_utf8_le(t) or t
+end
+
+local function little(c)
+ local b = byte(c)
+ if b < 0x10000 then
+ return char(b%256,b/256)
+ else
+ b = b - 0x10000
+ local b1, b2 = b/1024 + 0xD800, b%1024 + 0xDC00
+ return char(b1%256,b1/256,b2%256,b2/256)
+ end
+end
+
+local function big(c)
+ local b = byte(c)
+ if b < 0x10000 then
+ return char(b/256,b%256)
+ else
+ b = b - 0x10000
+ local b1, b2 = b/1024 + 0xD800, b%1024 + 0xDC00
+ return char(b1/256,b1%256,b2/256,b2%256)
+ end
+end
+
+-- function utf.utf8_to_utf16(str,littleendian)
+-- if littleendian then
+-- return char(255,254) .. utfgsub(str,".",little)
+-- else
+-- return char(254,255) .. utfgsub(str,".",big)
+-- end
+-- end
+
+local _, l_remap = utf.remapper(little)
+local _, b_remap = utf.remapper(big)
+
+function utf.utf8_to_utf16(str,littleendian)
+ if littleendian then
+ return char(255,254) .. lpegmatch(l_remap,str)
+ else
+ return char(254,255) .. lpegmatch(b_remap,str)
+ end
+end
+
+-- function utf.tocodes(str,separator) -- can be sped up with an lpeg
+-- local t, n = { }, 0
+-- for u in utfvalues(str) do
+-- n = n + 1
+-- t[n] = format("0x%04X",u)
+-- end
+-- return concat(t,separator or " ")
+-- end
+
+local pattern = Cs (
+ (p_utf8byte / function(unicode ) return format( "0x%04X", unicode) end) *
+ (p_utf8byte * Carg(1) / function(unicode,separator) return format("%s0x%04X",separator,unicode) end)^0
+)
+
+function utf.tocodes(str,separator)
+ return lpegmatch(pattern,str,1,separator or " ")
+end
+
+function utf.ustring(s)
+ return format("U+%05X",type(s) == "number" and s or utfbyte(s))
+end
+
+function utf.xstring(s)
+ return format("0x%05X",type(s) == "number" and s or utfbyte(s))
+end
+
+--
+
+local p_nany = p_utf8char / ""
+
+if utfgmatch then
+
+ function utf.count(str,what)
+ if type(what) == "string" then
+ local n = 0
+ for _ in utfgmatch(str,what) do
+ n = n + 1
+ end
+ return n
+ else -- 4 times slower but still faster than / function
+ return #lpegmatch(Cs((P(what)/" " + p_nany)^0),str)
+ end
+ end
+
+else
+
+ local cache = { }
+
+ function utf.count(str,what)
+ if type(what) == "string" then
+ local p = cache[what]
+ if not p then
+ p = Cs((P(what)/" " + p_nany)^0)
+ cache[p] = p
+ end
+ return #lpegmatch(p,str)
+ else -- 4 times slower but still faster than / function
+ return #lpegmatch(Cs((P(what)/" " + p_nany)^0),str)
+ end
+ end
+
+end
+
+-- maybe also register as string.utf*
+
+
+if not utf.characters then
+
+ -- New: this gmatch hack is taken from the Lua 5.2 book. It's about two times slower
+ -- than the built-in string.utfcharacters.
+
+ function utf.characters(str)
+ return gmatch(str,".[\128-\191]*")
+ end
+
+ string.utfcharacters = utf.characters
+
+end
+
+if not utf.values then
+
+ -- So, a logical next step is to check for the values variant. It over five times
+ -- slower than the built-in string.utfvalues. I optimized it a bit for n=0,1.
+
+ ----- wrap, yield, gmatch = coroutine.wrap, coroutine.yield, string.gmatch
+ local find = string.find
+
+ local dummy = function()
+ -- we share this one
+ end
+
+ -- function utf.values(str)
+ -- local n = #str
+ -- if n == 0 then
+ -- return wrap(dummy)
+ -- elseif n == 1 then
+ -- return wrap(function() yield(utfbyte(str)) end)
+ -- else
+ -- return wrap(function() for s in gmatch(str,".[\128-\191]*") do
+ -- yield(utfbyte(s))
+ -- end end)
+ -- end
+ -- end
+ --
+ -- faster:
+
+ function utf.values(str)
+ local n = #str
+ if n == 0 then
+ return dummy
+ elseif n == 1 then
+ return function() return utfbyte(str) end
+ else
+ local p = 1
+ -- local n = #str
+ return function()
+ -- if p <= n then -- slower than the last find
+ local b, e = find(str,".[\128-\191]*",p)
+ if b then
+ p = e + 1
+ return utfbyte(sub(str,b,e))
+ end
+ -- end
+ end
+ end
+ end
+
+ -- slower:
+ --
+ -- local pattern = C(patterns.utf8character) * Cp()
+ -- ----- pattern = patterns.utf8character/utfbyte * Cp()
+ -- ----- pattern = patterns.utf8byte * Cp()
+ --
+ -- function utf.values(str) -- one of the cases where a find is faster than an lpeg
+ -- local n = #str
+ -- if n == 0 then
+ -- return dummy
+ -- elseif n == 1 then
+ -- return function() return utfbyte(str) end
+ -- else
+ -- local p = 1
+ -- return function()
+ -- local s, e = lpegmatch(pattern,str,p)
+ -- if e then
+ -- p = e
+ -- return utfbyte(s)
+ -- -- return s
+ -- end
+ -- end
+ -- end
+ -- end
+
+ string.utfvalues = utf.values
+
+end
diff --git a/tex/context/base/l-url.lua b/tex/context/base/l-url.lua
index 5cfeb252c..4624a0507 100644
--- a/tex/context/base/l-url.lua
+++ b/tex/context/base/l-url.lua
@@ -1,344 +1,344 @@
-if not modules then modules = { } end modules ['l-url'] = {
- 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 char, format, byte = string.char, string.format, string.byte
-local concat = table.concat
-local tonumber, type = tonumber, type
-local P, C, R, S, Cs, Cc, Ct, Cf, Cg, V = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cs, lpeg.Cc, lpeg.Ct, lpeg.Cf, lpeg.Cg, lpeg.V
-local lpegmatch, lpegpatterns, replacer = lpeg.match, lpeg.patterns, lpeg.replacer
-
--- from wikipedia:
---
--- foo://username:password@example.com:8042/over/there/index.dtb?type=animal;name=narwhal#nose
--- \_/ \_______________/ \_________/ \__/ \___/ \_/ \______________________/ \__/
--- | | | | | | | |
--- | userinfo hostname port | | query fragment
--- | \________________________________/\_____________|____|/
--- scheme | | | |
--- | authority path | |
--- | | |
--- | path interpretable as filename
--- | ___________|____________ |
--- / \ / \ |
--- urn:example:animal:ferret:nose interpretable as extension
-
-url = url or { }
-local url = url
-
-local tochar = function(s) return char(tonumber(s,16)) end
-
-local colon = P(":")
-local qmark = P("?")
-local hash = P("#")
-local slash = P("/")
-local percent = P("%")
-local endofstring = P(-1)
-
-local hexdigit = R("09","AF","af")
-local plus = P("+")
-local nothing = Cc("")
-local escapedchar = (percent * C(hexdigit * hexdigit)) / tochar
-local escaped = (plus / " ") + escapedchar
-
-local noslash = P("/") / ""
-
--- we assume schemes with more than 1 character (in order to avoid problems with windows disks)
--- we also assume that when we have a scheme, we also have an authority
---
--- maybe we should already split the query (better for unescaping as = & can be part of a value
-
-local schemestr = Cs((escaped+(1-colon-slash-qmark-hash))^2)
-local authoritystr = Cs((escaped+(1- slash-qmark-hash))^0)
-local pathstr = Cs((escaped+(1- qmark-hash))^0)
------ querystr = Cs((escaped+(1- hash))^0)
-local querystr = Cs(( (1- hash))^0)
-local fragmentstr = Cs((escaped+(1- endofstring))^0)
-
-local scheme = schemestr * colon + nothing
-local authority = slash * slash * authoritystr + nothing
-local path = slash * pathstr + nothing
-local query = qmark * querystr + nothing
-local fragment = hash * fragmentstr + nothing
-
-local validurl = scheme * authority * path * query * fragment
-local parser = Ct(validurl)
-
-lpegpatterns.url = validurl
-lpegpatterns.urlsplitter = parser
-
-local escapes = { }
-
-setmetatable(escapes, { __index = function(t,k)
- local v = format("%%%02X",byte(k))
- t[k] = v
- return v
-end })
-
-local escaper = Cs((R("09","AZ","az")^1 + P(" ")/"%%20" + S("-./_")^1 + P(1) / escapes)^0) -- space happens most
-local unescaper = Cs((escapedchar + 1)^0)
-
-lpegpatterns.urlunescaped = escapedchar
-lpegpatterns.urlescaper = escaper
-lpegpatterns.urlunescaper = unescaper
-
--- todo: reconsider Ct as we can as well have five return values (saves a table)
--- so we can have two parsers, one with and one without
-
-local function split(str)
- return (type(str) == "string" and lpegmatch(parser,str)) or str
-end
-
-local isscheme = schemestr * colon * slash * slash -- this test also assumes authority
-
-local function hasscheme(str)
- if str then
- local scheme = lpegmatch(isscheme,str) -- at least one character
- return scheme ~= "" and scheme or false
- else
- return false
- end
-end
-
---~ print(hasscheme("home:"))
---~ print(hasscheme("home://"))
-
--- todo: cache them
-
-local rootletter = R("az","AZ")
- + S("_-+")
-local separator = P("://")
-local qualified = P(".")^0 * P("/")
- + rootletter * P(":")
- + rootletter^1 * separator
- + rootletter^1 * P("/")
-local rootbased = P("/")
- + rootletter * P(":")
-
-local barswapper = replacer("|",":")
-local backslashswapper = replacer("\\","/")
-
--- queries:
-
-local equal = P("=")
-local amp = P("&")
-local key = Cs(((escapedchar+1)-equal )^0)
-local value = Cs(((escapedchar+1)-amp -endofstring)^0)
-
-local splitquery = Cf ( Ct("") * P { "sequence",
- sequence = V("pair") * (amp * V("pair"))^0,
- pair = Cg(key * equal * value),
-}, rawset)
-
--- hasher
-
-local function hashed(str) -- not yet ok (/test?test)
- if str == "" then
- return {
- scheme = "invalid",
- original = str,
- }
- end
- local s = split(str)
- local rawscheme = s[1]
- local rawquery = s[4]
- local somescheme = rawscheme ~= ""
- local somequery = rawquery ~= ""
- if not somescheme and not somequery then
- s = {
- scheme = "file",
- authority = "",
- path = str,
- query = "",
- fragment = "",
- original = str,
- noscheme = true,
- filename = str,
- }
- else -- not always a filename but handy anyway
- local authority, path, filename = s[2], s[3]
- if authority == "" then
- filename = path
- elseif path == "" then
- filename = ""
- else
- filename = authority .. "/" .. path
- end
- s = {
- scheme = rawscheme,
- authority = authority,
- path = path,
- query = lpegmatch(unescaper,rawquery), -- unescaped, but possible conflict with & and =
- queries = lpegmatch(splitquery,rawquery), -- split first and then unescaped
- fragment = s[5],
- original = str,
- noscheme = false,
- filename = filename,
- }
- end
- return s
-end
-
--- inspect(hashed("template://test"))
-
--- Here we assume:
---
--- files: /// = relative
--- files: //// = absolute (!)
-
---~ table.print(hashed("file://c:/opt/tex/texmf-local")) -- c:/opt/tex/texmf-local
---~ table.print(hashed("file://opt/tex/texmf-local" )) -- opt/tex/texmf-local
---~ table.print(hashed("file:///opt/tex/texmf-local" )) -- opt/tex/texmf-local
---~ table.print(hashed("file:////opt/tex/texmf-local" )) -- /opt/tex/texmf-local
---~ table.print(hashed("file:///./opt/tex/texmf-local" )) -- ./opt/tex/texmf-local
-
---~ table.print(hashed("c:/opt/tex/texmf-local" )) -- c:/opt/tex/texmf-local
---~ table.print(hashed("opt/tex/texmf-local" )) -- opt/tex/texmf-local
---~ table.print(hashed("/opt/tex/texmf-local" )) -- /opt/tex/texmf-local
-
-url.split = split
-url.hasscheme = hasscheme
-url.hashed = hashed
-
-function url.addscheme(str,scheme) -- no authority
- if hasscheme(str) then
- return str
- elseif not scheme then
- return "file:///" .. str
- else
- return scheme .. ":///" .. str
- end
-end
-
-function url.construct(hash) -- dodo: we need to escape !
- local fullurl, f = { }, 0
- local scheme, authority, path, query, fragment = hash.scheme, hash.authority, hash.path, hash.query, hash.fragment
- if scheme and scheme ~= "" then
- f = f + 1 ; fullurl[f] = scheme .. "://"
- end
- if authority and authority ~= "" then
- f = f + 1 ; fullurl[f] = authority
- end
- if path and path ~= "" then
- f = f + 1 ; fullurl[f] = "/" .. path
- end
- if query and query ~= "" then
- f = f + 1 ; fullurl[f] = "?".. query
- end
- if fragment and fragment ~= "" then
- f = f + 1 ; fullurl[f] = "#".. fragment
- end
- return lpegmatch(escaper,concat(fullurl))
-end
-
-local pattern = Cs(noslash * R("az","AZ") * (S(":|")/":") * noslash * P(1)^0)
-
-function url.filename(filename)
- local spec = hashed(filename)
- local path = spec.path
- return (spec.scheme == "file" and path and lpegmatch(pattern,path)) or filename
-end
-
--- print(url.filename("/c|/test"))
--- print(url.filename("/c/test"))
-
-local function escapestring(str)
- return lpegmatch(escaper,str)
-end
-
-url.escape = escapestring
-
-function url.query(str)
- if type(str) == "string" then
- return lpegmatch(splitquery,str) or ""
- else
- return str
- end
-end
-
-function url.toquery(data)
- local td = type(data)
- if td == "string" then
- return #str and escape(data) or nil -- beware of double escaping
- elseif td == "table" then
- if next(data) then
- local t = { }
- for k, v in next, data do
- t[#t+1] = format("%s=%s",k,escapestring(v))
- end
- return concat(t,"&")
- end
- else
- -- nil is a signal that no query
- end
-end
-
--- /test/ | /test | test/ | test => test
-
-local pattern = Cs(noslash^0 * (1 - noslash * P(-1))^0)
-
-function url.barepath(path)
- if not path or path == "" then
- return ""
- else
- return lpegmatch(pattern,path)
- end
-end
-
--- print(url.barepath("/test"),url.barepath("test/"),url.barepath("/test/"),url.barepath("test"))
--- print(url.barepath("/x/yz"),url.barepath("x/yz/"),url.barepath("/x/yz/"),url.barepath("x/yz"))
-
---~ print(url.filename("file:///c:/oeps.txt"))
---~ print(url.filename("c:/oeps.txt"))
---~ print(url.filename("file:///oeps.txt"))
---~ print(url.filename("file:///etc/test.txt"))
---~ print(url.filename("/oeps.txt"))
-
---~ from the spec on the web (sort of):
-
---~ local function test(str)
---~ local t = url.hashed(str)
---~ t.constructed = url.construct(t)
---~ print(table.serialize(t))
---~ end
-
---~ inspect(url.hashed("http://www.pragma-ade.com/test%20test?test=test%20test&x=123%3d45"))
---~ inspect(url.hashed("http://www.pragma-ade.com/test%20test?test=test%20test&x=123%3d45"))
-
---~ test("sys:///./colo-rgb")
-
---~ test("/data/site/output/q2p-develop/resources/ecaboperception4_res/topicresources/58313733/figuur-cow.jpg")
---~ test("file:///M:/q2p/develop/output/q2p-develop/resources/ecaboperception4_res/topicresources/58313733")
---~ test("M:/q2p/develop/output/q2p-develop/resources/ecaboperception4_res/topicresources/58313733")
---~ test("file:///q2p/develop/output/q2p-develop/resources/ecaboperception4_res/topicresources/58313733")
---~ test("/q2p/develop/output/q2p-develop/resources/ecaboperception4_res/topicresources/58313733")
-
---~ test("file:///cow%20with%20spaces")
---~ test("file:///cow%20with%20spaces.pdf")
---~ test("cow%20with%20spaces.pdf")
---~ test("some%20file")
---~ test("/etc/passwords")
---~ test("http://www.myself.com/some%20words.html")
---~ test("file:///c:/oeps.txt")
---~ test("file:///c|/oeps.txt")
---~ test("file:///etc/oeps.txt")
---~ test("file://./etc/oeps.txt")
---~ test("file:////etc/oeps.txt")
---~ test("ftp://ftp.is.co.za/rfc/rfc1808.txt")
---~ test("http://www.ietf.org/rfc/rfc2396.txt")
---~ test("ldap://[2001:db8::7]/c=GB?objectClass?one#what")
---~ test("mailto:John.Doe@example.com")
---~ test("news:comp.infosystems.www.servers.unix")
---~ test("tel:+1-816-555-1212")
---~ test("telnet://192.0.2.16:80/")
---~ test("urn:oasis:names:specification:docbook:dtd:xml:4.1.2")
---~ test("http://www.pragma-ade.com/spaced%20name")
-
---~ test("zip:///oeps/oeps.zip#bla/bla.tex")
---~ test("zip:///oeps/oeps.zip?bla/bla.tex")
-
---~ table.print(url.hashed("/test?test"))
+if not modules then modules = { } end modules ['l-url'] = {
+ 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 char, format, byte = string.char, string.format, string.byte
+local concat = table.concat
+local tonumber, type = tonumber, type
+local P, C, R, S, Cs, Cc, Ct, Cf, Cg, V = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cs, lpeg.Cc, lpeg.Ct, lpeg.Cf, lpeg.Cg, lpeg.V
+local lpegmatch, lpegpatterns, replacer = lpeg.match, lpeg.patterns, lpeg.replacer
+
+-- from wikipedia:
+--
+-- foo://username:password@example.com:8042/over/there/index.dtb?type=animal;name=narwhal#nose
+-- \_/ \_______________/ \_________/ \__/ \___/ \_/ \______________________/ \__/
+-- | | | | | | | |
+-- | userinfo hostname port | | query fragment
+-- | \________________________________/\_____________|____|/
+-- scheme | | | |
+-- | authority path | |
+-- | | |
+-- | path interpretable as filename
+-- | ___________|____________ |
+-- / \ / \ |
+-- urn:example:animal:ferret:nose interpretable as extension
+
+url = url or { }
+local url = url
+
+local tochar = function(s) return char(tonumber(s,16)) end
+
+local colon = P(":")
+local qmark = P("?")
+local hash = P("#")
+local slash = P("/")
+local percent = P("%")
+local endofstring = P(-1)
+
+local hexdigit = R("09","AF","af")
+local plus = P("+")
+local nothing = Cc("")
+local escapedchar = (percent * C(hexdigit * hexdigit)) / tochar
+local escaped = (plus / " ") + escapedchar
+
+local noslash = P("/") / ""
+
+-- we assume schemes with more than 1 character (in order to avoid problems with windows disks)
+-- we also assume that when we have a scheme, we also have an authority
+--
+-- maybe we should already split the query (better for unescaping as = & can be part of a value
+
+local schemestr = Cs((escaped+(1-colon-slash-qmark-hash))^2)
+local authoritystr = Cs((escaped+(1- slash-qmark-hash))^0)
+local pathstr = Cs((escaped+(1- qmark-hash))^0)
+----- querystr = Cs((escaped+(1- hash))^0)
+local querystr = Cs(( (1- hash))^0)
+local fragmentstr = Cs((escaped+(1- endofstring))^0)
+
+local scheme = schemestr * colon + nothing
+local authority = slash * slash * authoritystr + nothing
+local path = slash * pathstr + nothing
+local query = qmark * querystr + nothing
+local fragment = hash * fragmentstr + nothing
+
+local validurl = scheme * authority * path * query * fragment
+local parser = Ct(validurl)
+
+lpegpatterns.url = validurl
+lpegpatterns.urlsplitter = parser
+
+local escapes = { }
+
+setmetatable(escapes, { __index = function(t,k)
+ local v = format("%%%02X",byte(k))
+ t[k] = v
+ return v
+end })
+
+local escaper = Cs((R("09","AZ","az")^1 + P(" ")/"%%20" + S("-./_")^1 + P(1) / escapes)^0) -- space happens most
+local unescaper = Cs((escapedchar + 1)^0)
+
+lpegpatterns.urlunescaped = escapedchar
+lpegpatterns.urlescaper = escaper
+lpegpatterns.urlunescaper = unescaper
+
+-- todo: reconsider Ct as we can as well have five return values (saves a table)
+-- so we can have two parsers, one with and one without
+
+local function split(str)
+ return (type(str) == "string" and lpegmatch(parser,str)) or str
+end
+
+local isscheme = schemestr * colon * slash * slash -- this test also assumes authority
+
+local function hasscheme(str)
+ if str then
+ local scheme = lpegmatch(isscheme,str) -- at least one character
+ return scheme ~= "" and scheme or false
+ else
+ return false
+ end
+end
+
+--~ print(hasscheme("home:"))
+--~ print(hasscheme("home://"))
+
+-- todo: cache them
+
+local rootletter = R("az","AZ")
+ + S("_-+")
+local separator = P("://")
+local qualified = P(".")^0 * P("/")
+ + rootletter * P(":")
+ + rootletter^1 * separator
+ + rootletter^1 * P("/")
+local rootbased = P("/")
+ + rootletter * P(":")
+
+local barswapper = replacer("|",":")
+local backslashswapper = replacer("\\","/")
+
+-- queries:
+
+local equal = P("=")
+local amp = P("&")
+local key = Cs(((escapedchar+1)-equal )^0)
+local value = Cs(((escapedchar+1)-amp -endofstring)^0)
+
+local splitquery = Cf ( Ct("") * P { "sequence",
+ sequence = V("pair") * (amp * V("pair"))^0,
+ pair = Cg(key * equal * value),
+}, rawset)
+
+-- hasher
+
+local function hashed(str) -- not yet ok (/test?test)
+ if str == "" then
+ return {
+ scheme = "invalid",
+ original = str,
+ }
+ end
+ local s = split(str)
+ local rawscheme = s[1]
+ local rawquery = s[4]
+ local somescheme = rawscheme ~= ""
+ local somequery = rawquery ~= ""
+ if not somescheme and not somequery then
+ s = {
+ scheme = "file",
+ authority = "",
+ path = str,
+ query = "",
+ fragment = "",
+ original = str,
+ noscheme = true,
+ filename = str,
+ }
+ else -- not always a filename but handy anyway
+ local authority, path, filename = s[2], s[3]
+ if authority == "" then
+ filename = path
+ elseif path == "" then
+ filename = ""
+ else
+ filename = authority .. "/" .. path
+ end
+ s = {
+ scheme = rawscheme,
+ authority = authority,
+ path = path,
+ query = lpegmatch(unescaper,rawquery), -- unescaped, but possible conflict with & and =
+ queries = lpegmatch(splitquery,rawquery), -- split first and then unescaped
+ fragment = s[5],
+ original = str,
+ noscheme = false,
+ filename = filename,
+ }
+ end
+ return s
+end
+
+-- inspect(hashed("template://test"))
+
+-- Here we assume:
+--
+-- files: /// = relative
+-- files: //// = absolute (!)
+
+--~ table.print(hashed("file://c:/opt/tex/texmf-local")) -- c:/opt/tex/texmf-local
+--~ table.print(hashed("file://opt/tex/texmf-local" )) -- opt/tex/texmf-local
+--~ table.print(hashed("file:///opt/tex/texmf-local" )) -- opt/tex/texmf-local
+--~ table.print(hashed("file:////opt/tex/texmf-local" )) -- /opt/tex/texmf-local
+--~ table.print(hashed("file:///./opt/tex/texmf-local" )) -- ./opt/tex/texmf-local
+
+--~ table.print(hashed("c:/opt/tex/texmf-local" )) -- c:/opt/tex/texmf-local
+--~ table.print(hashed("opt/tex/texmf-local" )) -- opt/tex/texmf-local
+--~ table.print(hashed("/opt/tex/texmf-local" )) -- /opt/tex/texmf-local
+
+url.split = split
+url.hasscheme = hasscheme
+url.hashed = hashed
+
+function url.addscheme(str,scheme) -- no authority
+ if hasscheme(str) then
+ return str
+ elseif not scheme then
+ return "file:///" .. str
+ else
+ return scheme .. ":///" .. str
+ end
+end
+
+function url.construct(hash) -- dodo: we need to escape !
+ local fullurl, f = { }, 0
+ local scheme, authority, path, query, fragment = hash.scheme, hash.authority, hash.path, hash.query, hash.fragment
+ if scheme and scheme ~= "" then
+ f = f + 1 ; fullurl[f] = scheme .. "://"
+ end
+ if authority and authority ~= "" then
+ f = f + 1 ; fullurl[f] = authority
+ end
+ if path and path ~= "" then
+ f = f + 1 ; fullurl[f] = "/" .. path
+ end
+ if query and query ~= "" then
+ f = f + 1 ; fullurl[f] = "?".. query
+ end
+ if fragment and fragment ~= "" then
+ f = f + 1 ; fullurl[f] = "#".. fragment
+ end
+ return lpegmatch(escaper,concat(fullurl))
+end
+
+local pattern = Cs(noslash * R("az","AZ") * (S(":|")/":") * noslash * P(1)^0)
+
+function url.filename(filename)
+ local spec = hashed(filename)
+ local path = spec.path
+ return (spec.scheme == "file" and path and lpegmatch(pattern,path)) or filename
+end
+
+-- print(url.filename("/c|/test"))
+-- print(url.filename("/c/test"))
+
+local function escapestring(str)
+ return lpegmatch(escaper,str)
+end
+
+url.escape = escapestring
+
+function url.query(str)
+ if type(str) == "string" then
+ return lpegmatch(splitquery,str) or ""
+ else
+ return str
+ end
+end
+
+function url.toquery(data)
+ local td = type(data)
+ if td == "string" then
+ return #str and escape(data) or nil -- beware of double escaping
+ elseif td == "table" then
+ if next(data) then
+ local t = { }
+ for k, v in next, data do
+ t[#t+1] = format("%s=%s",k,escapestring(v))
+ end
+ return concat(t,"&")
+ end
+ else
+ -- nil is a signal that no query
+ end
+end
+
+-- /test/ | /test | test/ | test => test
+
+local pattern = Cs(noslash^0 * (1 - noslash * P(-1))^0)
+
+function url.barepath(path)
+ if not path or path == "" then
+ return ""
+ else
+ return lpegmatch(pattern,path)
+ end
+end
+
+-- print(url.barepath("/test"),url.barepath("test/"),url.barepath("/test/"),url.barepath("test"))
+-- print(url.barepath("/x/yz"),url.barepath("x/yz/"),url.barepath("/x/yz/"),url.barepath("x/yz"))
+
+--~ print(url.filename("file:///c:/oeps.txt"))
+--~ print(url.filename("c:/oeps.txt"))
+--~ print(url.filename("file:///oeps.txt"))
+--~ print(url.filename("file:///etc/test.txt"))
+--~ print(url.filename("/oeps.txt"))
+
+--~ from the spec on the web (sort of):
+
+--~ local function test(str)
+--~ local t = url.hashed(str)
+--~ t.constructed = url.construct(t)
+--~ print(table.serialize(t))
+--~ end
+
+--~ inspect(url.hashed("http://www.pragma-ade.com/test%20test?test=test%20test&x=123%3d45"))
+--~ inspect(url.hashed("http://www.pragma-ade.com/test%20test?test=test%20test&x=123%3d45"))
+
+--~ test("sys:///./colo-rgb")
+
+--~ test("/data/site/output/q2p-develop/resources/ecaboperception4_res/topicresources/58313733/figuur-cow.jpg")
+--~ test("file:///M:/q2p/develop/output/q2p-develop/resources/ecaboperception4_res/topicresources/58313733")
+--~ test("M:/q2p/develop/output/q2p-develop/resources/ecaboperception4_res/topicresources/58313733")
+--~ test("file:///q2p/develop/output/q2p-develop/resources/ecaboperception4_res/topicresources/58313733")
+--~ test("/q2p/develop/output/q2p-develop/resources/ecaboperception4_res/topicresources/58313733")
+
+--~ test("file:///cow%20with%20spaces")
+--~ test("file:///cow%20with%20spaces.pdf")
+--~ test("cow%20with%20spaces.pdf")
+--~ test("some%20file")
+--~ test("/etc/passwords")
+--~ test("http://www.myself.com/some%20words.html")
+--~ test("file:///c:/oeps.txt")
+--~ test("file:///c|/oeps.txt")
+--~ test("file:///etc/oeps.txt")
+--~ test("file://./etc/oeps.txt")
+--~ test("file:////etc/oeps.txt")
+--~ test("ftp://ftp.is.co.za/rfc/rfc1808.txt")
+--~ test("http://www.ietf.org/rfc/rfc2396.txt")
+--~ test("ldap://[2001:db8::7]/c=GB?objectClass?one#what")
+--~ test("mailto:John.Doe@example.com")
+--~ test("news:comp.infosystems.www.servers.unix")
+--~ test("tel:+1-816-555-1212")
+--~ test("telnet://192.0.2.16:80/")
+--~ test("urn:oasis:names:specification:docbook:dtd:xml:4.1.2")
+--~ test("http://www.pragma-ade.com/spaced%20name")
+
+--~ test("zip:///oeps/oeps.zip#bla/bla.tex")
+--~ test("zip:///oeps/oeps.zip?bla/bla.tex")
+
+--~ table.print(url.hashed("/test?test"))
diff --git a/tex/context/base/l-xml.lua b/tex/context/base/l-xml.lua
index d8cc4a984..14e97337b 100644
--- a/tex/context/base/l-xml.lua
+++ b/tex/context/base/l-xml.lua
@@ -1,23 +1,23 @@
-if not modules then modules = { } end modules ['l-xml'] = {
- version = 1.001,
- comment = "this module is replaced by the lxml-* ones",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- We asume that the helper modules l-*.lua are loaded
--- already. But anyway if you use mtxrun to run your script
--- all is taken care of.
-
-if not trackers then
- require('trac-tra')
-end
-
-if not xml then
- require('lxml-tab')
- require('lxml-lpt')
- require('lxml-mis')
- require('lxml-aux')
- require('lxml-xml')
-end
+if not modules then modules = { } end modules ['l-xml'] = {
+ version = 1.001,
+ comment = "this module is replaced by the lxml-* ones",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- We asume that the helper modules l-*.lua are loaded
+-- already. But anyway if you use mtxrun to run your script
+-- all is taken care of.
+
+if not trackers then
+ require('trac-tra')
+end
+
+if not xml then
+ require('lxml-tab')
+ require('lxml-lpt')
+ require('lxml-mis')
+ require('lxml-aux')
+ require('lxml-xml')
+end
diff --git a/tex/context/base/lang-def.lua b/tex/context/base/lang-def.lua
index 274bb8090..c0c3981f7 100644
--- a/tex/context/base/lang-def.lua
+++ b/tex/context/base/lang-def.lua
@@ -1,466 +1,466 @@
-if not modules then modules = { } end modules ['lang-def'] = {
- 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"
- -- dataonly = true, -- saves 10K
-}
-
-local rawget = rawget
-local lower = string.lower
-
-languages = languages or { }
-local languages = languages
-languages.data = languages.data or { }
-local data = languages.data
-
-local allocate = utilities.storage.allocate
-local setmetatableindex = table.setmetatableindex
-
--- The specifications are based on an analysis done by Arthur. The
--- names of tags were changed by Hans. The data is not yet used but
--- will be some day.
---
--- description
---
--- The description is only meant as an indication; for example 'no' is
--- "Norwegian, undetermined" because that's really what it is.
---
--- script
---
--- This is the 4-letter script tag according to ISO 15924, the
--- official standard.
---
--- bibliographical and terminological
---
--- Then we have *two* ISO-639 3-letter tags: one is supposed to be used
--- for "bibliographical" purposes, the other for "terminological". The
--- first one is quite special (and mostly used in American libraries),
--- and the more interesting one is the other (apparently it's that one
--- we find everywhere).
---
--- context
---
--- These are the ones used in ConteXt. Kind of numberplate ones.
---
--- opentype
---
--- This is the 3-letter OpenType language tag, obviously.
---
--- variant
---
--- This is actually the rfc4646: an extension of ISO-639 that also defines
--- codes for variants like de-1901 for "German, 1901 orthography" or zh-Hans for
--- "Chinese, simplified characters" ('Hans' is the ISO-15924 tag for
--- "HAN ideographs, Simplified" :-) As I said yesterday, I think this
--- should be the reference since it's exactly what we want: it's really
--- standard (it's a RFC) and it's more than simply languages. To my
--- knowledge this is the only system that addresses this issue.
---
--- Warning: it's not unique! Because we have two "German" languages
--- (and could, potentially, have two Chinese, etc.)
---
--- Beware: the abbreviations are lowercased, which makes it more
--- convenient to use them.
---
--- todo: add default features
-
-local specifications = allocate {
- {
- ["description"] = "Dutch",
- ["script"] = "latn",
- -- ["bibliographical"] = "nld",
- -- ["terminological"] = "nld",
- ["context"] = "nl",
- ["opentype"] = "nld",
- ["variant"] = "nl",
- },
- {
- ["description"] = "Basque",
- ["script"] = "latn",
- ["bibliographical"] = "baq",
- ["terminological"] = "eus",
- ["context"] = "ba",
- ["opentype"] = "euq",
- ["variant"] = "eu",
- },
- {
- ["description"] = "Welsh",
- ["script"] = "latn",
- ["bibliographical"] = "wel",
- ["terminological"] = "cym",
- ["context"] = "cy",
- ["opentype"] = "wel",
- ["variant"] = "cy",
- },
- {
- ["description"] = "Icelandic",
- ["script"] = "latn",
- ["bibliographical"] = "ice",
- ["terminological"] = "isl",
- ["context"] = "is",
- ["opentype"] = "isl",
- ["variant"] = "is",
- },
- {
- ["description"] = "Norwegian, undetermined",
- ["script"] = "latn",
- ["bibliographical"] = "nor",
- ["terminological"] = "nor",
- ["context"] = "no",
- ["variant"] = "no",
- },
- {
- ["description"] = "Norwegian bokmal",
- ["script"] = "latn",
- ["bibliographical"] = "nob",
- ["terminological"] = "nob",
- ["opentype"] = "nor", -- not sure!
- ["variant"] = "nb",
- },
- {
- ["description"] = "Norwegian nynorsk",
- ["script"] = "latn",
- ["bibliographical"] = "nno",
- ["terminological"] = "nno",
- ["opentype"] = "nny",
- ["variant"] = "nn",
- },
- {
- ["description"] = "Ancient Greek",
- ["script"] = "grek",
- ["bibliographical"] = "grc",
- ["terminological"] = "grc",
- ["context"] = "agr",
- ["variant"] = "grc",
- },
- {
- ["description"] = "German, 1901 orthography",
- ["script"] = "latn",
- ["terminological"] = "deu",
- ["context"] = "deo",
- ["opentype"] = "deu",
- ["variant"] = "de-1901",
- },
- {
- ["description"] = "German, 1996 orthography",
- ["script"] = "latn",
- ["bibliographical"] = "ger",
- ["terminological"] = "deu",
- ["context"] = "de",
- ["opentype"] = "deu",
- ["variant"] = "de-1996",
- },
- {
- ["description"] = "Afrikaans",
- ["script"] = "latn",
- ["bibliographical"] = "afr",
- ["terminological"] = "afr",
- ["context"] = "af",
- ["opentype"] = "afk",
- ["variant"] = "af",
- },
- {
- ["description"] = "Catalan",
- ["script"] = "latn",
- ["bibliographical"] = "cat",
- ["terminological"] = "cat",
- ["context"] = "ca",
- ["opentype"] = "cat",
- ["variant"] = "ca",
- },
- {
- ["description"] = "Czech",
- ["script"] = "latn",
- ["bibliographical"] = "cze",
- ["terminological"] = "ces",
- ["context"] = "cz",
- ["opentype"] = "csy",
- ["variant"] = "cs",
- },
- {
- ["description"] = "Greek",
- ["script"] = "grek",
- ["bibliographical"] = "gre",
- ["terminological"] = "ell",
- ["context"] = "gr",
- ["opentype"] = "ell",
- ["variant"] = "el",
- },
- {
- ["description"] = "American English",
- ["script"] = "latn",
- ["bibliographical"] = "eng",
- ["terminological"] = "eng",
- ["context"] = "us",
- ["opentype"] = "eng",
- ["variant"] = "en-US",
- },
- {
- ["description"] = "British English",
- ["script"] = "latn",
- ["bibliographical"] = "eng",
- ["terminological"] = "eng",
- ["context"] = "uk",
- ["opentype"] = "eng",
- ["variant"] = "en-UK", -- Could be en-GB as well ...
- },
- {
- ["description"] = "Spanish",
- ["script"] = "latn",
- ["bibliographical"] = "spa",
- ["terminological"] = "spa",
- ["context"] = "es",
- ["opentype"] = "esp",
- ["variant"] = "es",
- },
- {
- ["description"] = "Finnish",
- ["script"] = "latn",
- ["bibliographical"] = "fin",
- ["terminological"] = "fin",
- ["context"] = "fi",
- ["opentype"] = "fin",
- ["variant"] = "fi",
- },
- {
- ["description"] = "French",
- ["script"] = "latn",
- ["bibliographical"] = "fre",
- ["terminological"] = "fra",
- ["context"] = "fr",
- ["opentype"] = "fra",
- ["variant"] = "fr",
- },
- {
- ["description"] = "Croatian",
- ["script"] = "latn",
- ["bibliographical"] = "scr",
- ["terminological"] = "hrv",
- ["context"] = "hr",
- ["opentype"] = "hrv",
- ["variant"] = "hr",
- },
- {
- ["description"] = "Hungarian",
- ["script"] = "latn",
- ["bibliographical"] = "hun",
- ["terminological"] = "hun",
- ["context"] = "hu",
- ["opentype"] = "hun",
- ["variant"] = "hu",
- },
- {
- ["description"] = "Italian",
- ["script"] = "latn",
- ["bibliographical"] = "ita",
- ["terminological"] = "ita",
- ["context"] = "it",
- ["opentype"] = "ita",
- ["variant"] = "it",
- },
- {
- ["description"] = "Japanese",
- ["script"] = "jpan",
- ["bibliographical"] = "jpn",
- ["terminological"] = "jpn",
- ["context"] = "ja",
- ["opentype"] = "jan",
- ["variant"] = "ja",
- },
- {
- ["description"] = "Latin",
- ["script"] = "latn",
- ["bibliographical"] = "lat",
- ["terminological"] = "lat",
- ["context"] = "la",
- ["opentype"] = "lat",
- ["variant"] = "la",
- },
- {
- ["description"] = "Portuguese",
- ["script"] = "latn",
- ["bibliographical"] = "por",
- ["terminological"] = "por",
- ["context"] = "pt",
- ["opentype"] = "ptg",
- ["variant"] = "pt",
- },
- {
- ["description"] = "Polish",
- ["script"] = "latn",
- ["bibliographical"] = "pol",
- ["terminological"] = "pol",
- ["context"] = "pl",
- ["opentype"] = "plk",
- ["variant"] = "pl",
- },
- {
- ["description"] = "Romanian",
- ["script"] = "latn",
- ["bibliographical"] = "rum",
- ["terminological"] = "ron",
- ["context"] = "ro",
- ["opentype"] = "rom",
- ["variant"] = "ro",
- },
- {
- ["description"] = "Russian",
- ["script"] = "cyrl",
- ["bibliographical"] = "rus",
- ["terminological"] = "rus",
- ["context"] = "ru",
- ["opentype"] = "rus",
- ["variant"] = "ru",
- },
- {
- ["description"] = "Slovak",
- ["script"] = "latn",
- ["bibliographical"] = "slo",
- ["terminological"] = "slk",
- ["context"] = "sk",
- ["opentype"] = "sky",
- ["variant"] = "sk",
- },
- {
- ["description"] = "Slovenian",
- ["script"] = "latn",
- ["bibliographical"] = "slv",
- ["terminological"] = "slv",
- ["context"] = "sl",
- ["opentype"] = "slv",
- ["variant"] = "sl",
- },
- {
- ["description"] = "Swedish",
- ["script"] = "latn",
- ["bibliographical"] = "swe",
- ["terminological"] = "swe",
- ["context"] = "sv",
- ["opentype"] = "sve",
- ["variant"] = "sv",
- },
- {
- ["description"] = "Thai",
- ["script"] = "thai",
- -- ["bibliographical"] = "",
- -- ["terminological"] = "",
- ["context"] = "th",
- ["opentype"] = "tha",
- -- ["variant"] = "",
- },
- {
- ["description"] = "Turkish",
- ["script"] = "latn",
- ["bibliographical"] = "tur",
- ["terminological"] = "tur",
- ["context"] = "tr",
- ["opentype"] = "trk",
- ["variant"] = "tr",
- },
- {
- ["description"] = "Vietnamese",
- ["script"] = "latn",
- ["bibliographical"] = "vie",
- ["terminological"] = "vie",
- ["context"] = "vn",
- ["opentype"] = "vit",
- ["variant"] = "vi",
- },
- {
- ["description"] = "Chinese, simplified",
- ["script"] = "hans",
- ["opentypescript"] = "hani",
- ["bibliographical"] = "chi",
- ["terminological"] = "zho",
- ["context"] = "cn",
- ["opentype"] = "zhs",
- ["variant"] = "zh-hans",
- },
-}
-
-data.specifications = specifications
-
-local variants = { } data.variants = variants
-local contexts = { } data.contexts = contexts
-local records = { } data.records = records
-local scripts = { } data.scripts = scripts
-local opentypes = { } data.opentypes = opentypes
-local opentypescripts = { } data.opentypescripts = opentypescripts
-
-for k=1,#specifications do
- local specification = specifications[k]
- local variant = specification.variant
- if variant then
- variants[lower(variant)] = specification
- end
- local opentype = specification.opentype
- if opentype then
- opentypes[lower(opentype)] = specification
- end
- local script = specification.script
- if script then
- scripts[lower(script)] = specification
- end
- local opentypescript = specification.opentypescript
- if opentypescript then
- opentypescripts[lower(opentypescript)] = specification
- end
- local context = context
- if context then
- if type(context) == "table" then
- for k=1,#context do
- contexts[context[k]] = specification
- end
- else
- contexts[context] = specification
- end
- end
-end
-
-local defaultvariant = variants["en-us"]
-
-local function get(k,key)
- local v = rawget(variants,k) or rawget(opentypes,k) or rawget(contexts,k)
- return v and v[key]
-end
-
-setmetatableindex(variants, function(t,k)
- k = lower(k)
- local v = get(k,"language") or defaultvariant.language
- t[k] = v
- return v
-end)
-
-setmetatableindex(opentypes, function(t,k)
- k = lower(k)
- local v = get(k,"opentype") or "dflt"
- t[k] = v
- return v
-end)
-
-setmetatableindex(opentypescripts, function(t,k)
- k = lower(k)
- local v = get(k,"opentypescript") or get(k,"script") or defaultvariant.opentypescript or defaultvariant.script
- t[k] = v
- return v
-end)
-
-setmetatableindex(contexts, function(t,k)
- k = lower(str)
- local v = get(k,"context") or defaultvariant.context
- v = type(v) == "table" and v[1] or v
- t[k] = v
- return v
-end)
-
-setmetatableindex(records, function(t,k) -- how useful is this one?
- k = lower(k)
- local v = get(k) or defaultvariant
- t[k] = v
- return v
-end)
-
--- print(opentypes.nl,opentypescripts.nl)
--- print(opentypes.de,opentypescripts.de)
+if not modules then modules = { } end modules ['lang-def'] = {
+ 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"
+ -- dataonly = true, -- saves 10K
+}
+
+local rawget = rawget
+local lower = string.lower
+
+languages = languages or { }
+local languages = languages
+languages.data = languages.data or { }
+local data = languages.data
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+
+-- The specifications are based on an analysis done by Arthur. The
+-- names of tags were changed by Hans. The data is not yet used but
+-- will be some day.
+--
+-- description
+--
+-- The description is only meant as an indication; for example 'no' is
+-- "Norwegian, undetermined" because that's really what it is.
+--
+-- script
+--
+-- This is the 4-letter script tag according to ISO 15924, the
+-- official standard.
+--
+-- bibliographical and terminological
+--
+-- Then we have *two* ISO-639 3-letter tags: one is supposed to be used
+-- for "bibliographical" purposes, the other for "terminological". The
+-- first one is quite special (and mostly used in American libraries),
+-- and the more interesting one is the other (apparently it's that one
+-- we find everywhere).
+--
+-- context
+--
+-- These are the ones used in ConteXt. Kind of numberplate ones.
+--
+-- opentype
+--
+-- This is the 3-letter OpenType language tag, obviously.
+--
+-- variant
+--
+-- This is actually the rfc4646: an extension of ISO-639 that also defines
+-- codes for variants like de-1901 for "German, 1901 orthography" or zh-Hans for
+-- "Chinese, simplified characters" ('Hans' is the ISO-15924 tag for
+-- "HAN ideographs, Simplified" :-) As I said yesterday, I think this
+-- should be the reference since it's exactly what we want: it's really
+-- standard (it's a RFC) and it's more than simply languages. To my
+-- knowledge this is the only system that addresses this issue.
+--
+-- Warning: it's not unique! Because we have two "German" languages
+-- (and could, potentially, have two Chinese, etc.)
+--
+-- Beware: the abbreviations are lowercased, which makes it more
+-- convenient to use them.
+--
+-- todo: add default features
+
+local specifications = allocate {
+ {
+ ["description"] = "Dutch",
+ ["script"] = "latn",
+ -- ["bibliographical"] = "nld",
+ -- ["terminological"] = "nld",
+ ["context"] = "nl",
+ ["opentype"] = "nld",
+ ["variant"] = "nl",
+ },
+ {
+ ["description"] = "Basque",
+ ["script"] = "latn",
+ ["bibliographical"] = "baq",
+ ["terminological"] = "eus",
+ ["context"] = "ba",
+ ["opentype"] = "euq",
+ ["variant"] = "eu",
+ },
+ {
+ ["description"] = "Welsh",
+ ["script"] = "latn",
+ ["bibliographical"] = "wel",
+ ["terminological"] = "cym",
+ ["context"] = "cy",
+ ["opentype"] = "wel",
+ ["variant"] = "cy",
+ },
+ {
+ ["description"] = "Icelandic",
+ ["script"] = "latn",
+ ["bibliographical"] = "ice",
+ ["terminological"] = "isl",
+ ["context"] = "is",
+ ["opentype"] = "isl",
+ ["variant"] = "is",
+ },
+ {
+ ["description"] = "Norwegian, undetermined",
+ ["script"] = "latn",
+ ["bibliographical"] = "nor",
+ ["terminological"] = "nor",
+ ["context"] = "no",
+ ["variant"] = "no",
+ },
+ {
+ ["description"] = "Norwegian bokmal",
+ ["script"] = "latn",
+ ["bibliographical"] = "nob",
+ ["terminological"] = "nob",
+ ["opentype"] = "nor", -- not sure!
+ ["variant"] = "nb",
+ },
+ {
+ ["description"] = "Norwegian nynorsk",
+ ["script"] = "latn",
+ ["bibliographical"] = "nno",
+ ["terminological"] = "nno",
+ ["opentype"] = "nny",
+ ["variant"] = "nn",
+ },
+ {
+ ["description"] = "Ancient Greek",
+ ["script"] = "grek",
+ ["bibliographical"] = "grc",
+ ["terminological"] = "grc",
+ ["context"] = "agr",
+ ["variant"] = "grc",
+ },
+ {
+ ["description"] = "German, 1901 orthography",
+ ["script"] = "latn",
+ ["terminological"] = "deu",
+ ["context"] = "deo",
+ ["opentype"] = "deu",
+ ["variant"] = "de-1901",
+ },
+ {
+ ["description"] = "German, 1996 orthography",
+ ["script"] = "latn",
+ ["bibliographical"] = "ger",
+ ["terminological"] = "deu",
+ ["context"] = "de",
+ ["opentype"] = "deu",
+ ["variant"] = "de-1996",
+ },
+ {
+ ["description"] = "Afrikaans",
+ ["script"] = "latn",
+ ["bibliographical"] = "afr",
+ ["terminological"] = "afr",
+ ["context"] = "af",
+ ["opentype"] = "afk",
+ ["variant"] = "af",
+ },
+ {
+ ["description"] = "Catalan",
+ ["script"] = "latn",
+ ["bibliographical"] = "cat",
+ ["terminological"] = "cat",
+ ["context"] = "ca",
+ ["opentype"] = "cat",
+ ["variant"] = "ca",
+ },
+ {
+ ["description"] = "Czech",
+ ["script"] = "latn",
+ ["bibliographical"] = "cze",
+ ["terminological"] = "ces",
+ ["context"] = "cz",
+ ["opentype"] = "csy",
+ ["variant"] = "cs",
+ },
+ {
+ ["description"] = "Greek",
+ ["script"] = "grek",
+ ["bibliographical"] = "gre",
+ ["terminological"] = "ell",
+ ["context"] = "gr",
+ ["opentype"] = "ell",
+ ["variant"] = "el",
+ },
+ {
+ ["description"] = "American English",
+ ["script"] = "latn",
+ ["bibliographical"] = "eng",
+ ["terminological"] = "eng",
+ ["context"] = "us",
+ ["opentype"] = "eng",
+ ["variant"] = "en-US",
+ },
+ {
+ ["description"] = "British English",
+ ["script"] = "latn",
+ ["bibliographical"] = "eng",
+ ["terminological"] = "eng",
+ ["context"] = "uk",
+ ["opentype"] = "eng",
+ ["variant"] = "en-UK", -- Could be en-GB as well ...
+ },
+ {
+ ["description"] = "Spanish",
+ ["script"] = "latn",
+ ["bibliographical"] = "spa",
+ ["terminological"] = "spa",
+ ["context"] = "es",
+ ["opentype"] = "esp",
+ ["variant"] = "es",
+ },
+ {
+ ["description"] = "Finnish",
+ ["script"] = "latn",
+ ["bibliographical"] = "fin",
+ ["terminological"] = "fin",
+ ["context"] = "fi",
+ ["opentype"] = "fin",
+ ["variant"] = "fi",
+ },
+ {
+ ["description"] = "French",
+ ["script"] = "latn",
+ ["bibliographical"] = "fre",
+ ["terminological"] = "fra",
+ ["context"] = "fr",
+ ["opentype"] = "fra",
+ ["variant"] = "fr",
+ },
+ {
+ ["description"] = "Croatian",
+ ["script"] = "latn",
+ ["bibliographical"] = "scr",
+ ["terminological"] = "hrv",
+ ["context"] = "hr",
+ ["opentype"] = "hrv",
+ ["variant"] = "hr",
+ },
+ {
+ ["description"] = "Hungarian",
+ ["script"] = "latn",
+ ["bibliographical"] = "hun",
+ ["terminological"] = "hun",
+ ["context"] = "hu",
+ ["opentype"] = "hun",
+ ["variant"] = "hu",
+ },
+ {
+ ["description"] = "Italian",
+ ["script"] = "latn",
+ ["bibliographical"] = "ita",
+ ["terminological"] = "ita",
+ ["context"] = "it",
+ ["opentype"] = "ita",
+ ["variant"] = "it",
+ },
+ {
+ ["description"] = "Japanese",
+ ["script"] = "jpan",
+ ["bibliographical"] = "jpn",
+ ["terminological"] = "jpn",
+ ["context"] = "ja",
+ ["opentype"] = "jan",
+ ["variant"] = "ja",
+ },
+ {
+ ["description"] = "Latin",
+ ["script"] = "latn",
+ ["bibliographical"] = "lat",
+ ["terminological"] = "lat",
+ ["context"] = "la",
+ ["opentype"] = "lat",
+ ["variant"] = "la",
+ },
+ {
+ ["description"] = "Portuguese",
+ ["script"] = "latn",
+ ["bibliographical"] = "por",
+ ["terminological"] = "por",
+ ["context"] = "pt",
+ ["opentype"] = "ptg",
+ ["variant"] = "pt",
+ },
+ {
+ ["description"] = "Polish",
+ ["script"] = "latn",
+ ["bibliographical"] = "pol",
+ ["terminological"] = "pol",
+ ["context"] = "pl",
+ ["opentype"] = "plk",
+ ["variant"] = "pl",
+ },
+ {
+ ["description"] = "Romanian",
+ ["script"] = "latn",
+ ["bibliographical"] = "rum",
+ ["terminological"] = "ron",
+ ["context"] = "ro",
+ ["opentype"] = "rom",
+ ["variant"] = "ro",
+ },
+ {
+ ["description"] = "Russian",
+ ["script"] = "cyrl",
+ ["bibliographical"] = "rus",
+ ["terminological"] = "rus",
+ ["context"] = "ru",
+ ["opentype"] = "rus",
+ ["variant"] = "ru",
+ },
+ {
+ ["description"] = "Slovak",
+ ["script"] = "latn",
+ ["bibliographical"] = "slo",
+ ["terminological"] = "slk",
+ ["context"] = "sk",
+ ["opentype"] = "sky",
+ ["variant"] = "sk",
+ },
+ {
+ ["description"] = "Slovenian",
+ ["script"] = "latn",
+ ["bibliographical"] = "slv",
+ ["terminological"] = "slv",
+ ["context"] = "sl",
+ ["opentype"] = "slv",
+ ["variant"] = "sl",
+ },
+ {
+ ["description"] = "Swedish",
+ ["script"] = "latn",
+ ["bibliographical"] = "swe",
+ ["terminological"] = "swe",
+ ["context"] = "sv",
+ ["opentype"] = "sve",
+ ["variant"] = "sv",
+ },
+ {
+ ["description"] = "Thai",
+ ["script"] = "thai",
+ -- ["bibliographical"] = "",
+ -- ["terminological"] = "",
+ ["context"] = "th",
+ ["opentype"] = "tha",
+ -- ["variant"] = "",
+ },
+ {
+ ["description"] = "Turkish",
+ ["script"] = "latn",
+ ["bibliographical"] = "tur",
+ ["terminological"] = "tur",
+ ["context"] = "tr",
+ ["opentype"] = "trk",
+ ["variant"] = "tr",
+ },
+ {
+ ["description"] = "Vietnamese",
+ ["script"] = "latn",
+ ["bibliographical"] = "vie",
+ ["terminological"] = "vie",
+ ["context"] = "vn",
+ ["opentype"] = "vit",
+ ["variant"] = "vi",
+ },
+ {
+ ["description"] = "Chinese, simplified",
+ ["script"] = "hans",
+ ["opentypescript"] = "hani",
+ ["bibliographical"] = "chi",
+ ["terminological"] = "zho",
+ ["context"] = "cn",
+ ["opentype"] = "zhs",
+ ["variant"] = "zh-hans",
+ },
+}
+
+data.specifications = specifications
+
+local variants = { } data.variants = variants
+local contexts = { } data.contexts = contexts
+local records = { } data.records = records
+local scripts = { } data.scripts = scripts
+local opentypes = { } data.opentypes = opentypes
+local opentypescripts = { } data.opentypescripts = opentypescripts
+
+for k=1,#specifications do
+ local specification = specifications[k]
+ local variant = specification.variant
+ if variant then
+ variants[lower(variant)] = specification
+ end
+ local opentype = specification.opentype
+ if opentype then
+ opentypes[lower(opentype)] = specification
+ end
+ local script = specification.script
+ if script then
+ scripts[lower(script)] = specification
+ end
+ local opentypescript = specification.opentypescript
+ if opentypescript then
+ opentypescripts[lower(opentypescript)] = specification
+ end
+ local context = context
+ if context then
+ if type(context) == "table" then
+ for k=1,#context do
+ contexts[context[k]] = specification
+ end
+ else
+ contexts[context] = specification
+ end
+ end
+end
+
+local defaultvariant = variants["en-us"]
+
+local function get(k,key)
+ local v = rawget(variants,k) or rawget(opentypes,k) or rawget(contexts,k)
+ return v and v[key]
+end
+
+setmetatableindex(variants, function(t,k)
+ k = lower(k)
+ local v = get(k,"language") or defaultvariant.language
+ t[k] = v
+ return v
+end)
+
+setmetatableindex(opentypes, function(t,k)
+ k = lower(k)
+ local v = get(k,"opentype") or "dflt"
+ t[k] = v
+ return v
+end)
+
+setmetatableindex(opentypescripts, function(t,k)
+ k = lower(k)
+ local v = get(k,"opentypescript") or get(k,"script") or defaultvariant.opentypescript or defaultvariant.script
+ t[k] = v
+ return v
+end)
+
+setmetatableindex(contexts, function(t,k)
+ k = lower(str)
+ local v = get(k,"context") or defaultvariant.context
+ v = type(v) == "table" and v[1] or v
+ t[k] = v
+ return v
+end)
+
+setmetatableindex(records, function(t,k) -- how useful is this one?
+ k = lower(k)
+ local v = get(k) or defaultvariant
+ t[k] = v
+ return v
+end)
+
+-- print(opentypes.nl,opentypescripts.nl)
+-- print(opentypes.de,opentypescripts.de)
diff --git a/tex/context/base/lang-frq-de.lua b/tex/context/base/lang-frq-de.lua
index 4e54db2c8..3733f39f9 100644
--- a/tex/context/base/lang-frq-de.lua
+++ b/tex/context/base/lang-frq-de.lua
@@ -1,12 +1,12 @@
-return {
- language = "de",
- source = "http://www.blankenburg.de/gat/pages/fach/info/analyse2.htm",
- frequencies = {
- [0x0061] = 6.47, [0x0062] = 1.93, [0x0063] = 2.68, [0x0064] = 4.83, [0x0065] = 17.48,
- [0x0066] = 1.65, [0x0067] = 3.06, [0x0068] = 4.23, [0x0069] = 7.73, [0x006A] = 0.27,
- [0x006B] = 1.46, [0x006C] = 3.49, [0x006D] = 2.58, [0x006E] = 9.84, [0x006F] = 2.98,
- [0x0070] = 0.96, [0x0071] = 0.02, [0x0072] = 7.54, [0x0073] = 6.83, [0x0074] = 6.13,
- [0x0075] = 4.17, [0x0076] = 0.94, [0x0077] = 1.48, [0x0078] = 0.04, [0x0079] = 0.08,
- [0x007A] = 1.14,
- }
-}
+return {
+ language = "de",
+ source = "http://www.blankenburg.de/gat/pages/fach/info/analyse2.htm",
+ frequencies = {
+ [0x0061] = 6.47, [0x0062] = 1.93, [0x0063] = 2.68, [0x0064] = 4.83, [0x0065] = 17.48,
+ [0x0066] = 1.65, [0x0067] = 3.06, [0x0068] = 4.23, [0x0069] = 7.73, [0x006A] = 0.27,
+ [0x006B] = 1.46, [0x006C] = 3.49, [0x006D] = 2.58, [0x006E] = 9.84, [0x006F] = 2.98,
+ [0x0070] = 0.96, [0x0071] = 0.02, [0x0072] = 7.54, [0x0073] = 6.83, [0x0074] = 6.13,
+ [0x0075] = 4.17, [0x0076] = 0.94, [0x0077] = 1.48, [0x0078] = 0.04, [0x0079] = 0.08,
+ [0x007A] = 1.14,
+ }
+}
diff --git a/tex/context/base/lang-frq-en.lua b/tex/context/base/lang-frq-en.lua
index ee122c9da..9e18d7166 100644
--- a/tex/context/base/lang-frq-en.lua
+++ b/tex/context/base/lang-frq-en.lua
@@ -1,26 +1,26 @@
--- return {
--- language = "en",
--- source = "http://caislab.icu.ac.kr/course/2001/spring/ice605/down/010306.pdf",
--- frequencies = {
--- [0x0061] = 8.2, [0x0062] = 1.5, [0x0063] = 2.8, [0x0064] = 4.3, [0x0065] = 12.7,
--- [0x0066] = 2.2, [0x0067] = 2.0, [0x0068] = 6.1, [0x0069] = 7.0, [0x006A] = 0.2,
--- [0x006B] = 0.8, [0x006C] = 4.0, [0x006D] = 2.4, [0x006E] = 6.7, [0x006F] = 7.5,
--- [0x0070] = 1.9, [0x0071] = 0.1, [0x0072] = 6.0, [0x0073] = 6.3, [0x0074] = 9.1,
--- [0x0075] = 2.8, [0x0076] = 1.0, [0x0077] = 2.3, [0x0078] = 0.1, [0x0079] = 2.0,
--- [0x007A] = 0.1,
--- }
--- }
-
-return {
- language = "en",
- source = "http://www.blankenburg.de/gat/pages/fach/info/analyse2.htm",
- frequencies = {
- [0x0061] = 8.04, [0x0062] = 1.54, [0x0063] = 3.06, [0x0064] = 3.99, [0x0065] = 12.51,
- [0x0066] = 2.30, [0x0067] = 1.96, [0x0068] = 5.49, [0x0069] = 7.26, [0x006A] = 0.16,
- [0x006B] = 0.67, [0x006C] = 4.14, [0x006D] = 2.53, [0x006E] = 7.09, [0x006F] = 7.60,
- [0x0070] = 2.00, [0x0071] = 0.11, [0x0072] = 6.12, [0x0073] = 6.54, [0x0074] = 9.25,
- [0x0075] = 2.71, [0x0076] = 0.99, [0x0077] = 1.92, [0x0078] = 0.19, [0x0079] = 1.73,
- [0x007A] = 0.09,
- }
-}
-
+-- return {
+-- language = "en",
+-- source = "http://caislab.icu.ac.kr/course/2001/spring/ice605/down/010306.pdf",
+-- frequencies = {
+-- [0x0061] = 8.2, [0x0062] = 1.5, [0x0063] = 2.8, [0x0064] = 4.3, [0x0065] = 12.7,
+-- [0x0066] = 2.2, [0x0067] = 2.0, [0x0068] = 6.1, [0x0069] = 7.0, [0x006A] = 0.2,
+-- [0x006B] = 0.8, [0x006C] = 4.0, [0x006D] = 2.4, [0x006E] = 6.7, [0x006F] = 7.5,
+-- [0x0070] = 1.9, [0x0071] = 0.1, [0x0072] = 6.0, [0x0073] = 6.3, [0x0074] = 9.1,
+-- [0x0075] = 2.8, [0x0076] = 1.0, [0x0077] = 2.3, [0x0078] = 0.1, [0x0079] = 2.0,
+-- [0x007A] = 0.1,
+-- }
+-- }
+
+return {
+ language = "en",
+ source = "http://www.blankenburg.de/gat/pages/fach/info/analyse2.htm",
+ frequencies = {
+ [0x0061] = 8.04, [0x0062] = 1.54, [0x0063] = 3.06, [0x0064] = 3.99, [0x0065] = 12.51,
+ [0x0066] = 2.30, [0x0067] = 1.96, [0x0068] = 5.49, [0x0069] = 7.26, [0x006A] = 0.16,
+ [0x006B] = 0.67, [0x006C] = 4.14, [0x006D] = 2.53, [0x006E] = 7.09, [0x006F] = 7.60,
+ [0x0070] = 2.00, [0x0071] = 0.11, [0x0072] = 6.12, [0x0073] = 6.54, [0x0074] = 9.25,
+ [0x0075] = 2.71, [0x0076] = 0.99, [0x0077] = 1.92, [0x0078] = 0.19, [0x0079] = 1.73,
+ [0x007A] = 0.09,
+ }
+}
+
diff --git a/tex/context/base/lang-frq-nl.lua b/tex/context/base/lang-frq-nl.lua
index fa4851e63..7b640b779 100644
--- a/tex/context/base/lang-frq-nl.lua
+++ b/tex/context/base/lang-frq-nl.lua
@@ -1,12 +1,12 @@
-return {
- language = "nl",
- source = "http://www.onzetaal.nl/advies/letterfreq.html",
- frequencies = {
- [0x0061] = 7.47, [0x0062] = 1.58, [0x0063] = 1.24, [0x0064] = 5.93, [0x0065] = 18.91,
- [0x0066] = 0.81, [0x0067] = 3.40, [0x0068] = 2.38, [0x0069] = 6.50, [0x006A] = 1.46,
- [0x006B] = 2.25, [0x006C] = 3.57, [0x006D] = 2.21, [0x006E] = 10.03, [0x006F] = 6.06,
- [0x0070] = 1.57, [0x0071] = 0.009, [0x0072] = 6.41, [0x0073] = 3.73, [0x0074] = 6.79,
- [0x0075] = 1.99, [0x0076] = 2.85, [0x0077] = 1.52, [0x0078] = 0.04, [0x0079] = 0.035,
- [0x007A] = 1.39,
- }
-}
+return {
+ language = "nl",
+ source = "http://www.onzetaal.nl/advies/letterfreq.html",
+ frequencies = {
+ [0x0061] = 7.47, [0x0062] = 1.58, [0x0063] = 1.24, [0x0064] = 5.93, [0x0065] = 18.91,
+ [0x0066] = 0.81, [0x0067] = 3.40, [0x0068] = 2.38, [0x0069] = 6.50, [0x006A] = 1.46,
+ [0x006B] = 2.25, [0x006C] = 3.57, [0x006D] = 2.21, [0x006E] = 10.03, [0x006F] = 6.06,
+ [0x0070] = 1.57, [0x0071] = 0.009, [0x0072] = 6.41, [0x0073] = 3.73, [0x0074] = 6.79,
+ [0x0075] = 1.99, [0x0076] = 2.85, [0x0077] = 1.52, [0x0078] = 0.04, [0x0079] = 0.035,
+ [0x007A] = 1.39,
+ }
+}
diff --git a/tex/context/base/lang-ini.lua b/tex/context/base/lang-ini.lua
index b5bdfd894..b79bd0661 100644
--- a/tex/context/base/lang-ini.lua
+++ b/tex/context/base/lang-ini.lua
@@ -1,376 +1,376 @@
-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)
-
---~ lang:hyphenation(string) string = lang:hyphenation() lang:clear_hyphenation()
-
-local type, tonumber = type, tonumber
-local utfbyte = utf.byte
-local format, gsub = string.format, string.gsub
-local concat, sortedkeys, sortedpairs = table.concat, table.sortedkeys, table.sortedpairs
-local lpegmatch = lpeg.match
-
-local settings_to_array = utilities.parsers.settings_to_array
-
-local trace_patterns = false trackers.register("languages.patterns", function(v) trace_patterns = v end)
-
-local report_initialization = logs.reporter("languages","initialization")
-
-local prehyphenchar = lang.prehyphenchar -- global per language
-local posthyphenchar = lang.posthyphenchar -- global per language
-local lefthyphenmin = lang.lefthyphenmin
-local righthyphenmin = lang.righthyphenmin
-
-local lang = lang
-lang.exceptions = lang.hyphenation
-local new_langage = lang.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/numbers", numbers, "languages.numbers")
-storage.register("languages/registered",registered,"languages.registered")
-storage.register("languages/associated",associated,"languages.associated")
-storage.register("languages/data", data, "languages.data")
-
-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_langage(data.number)
- data.instance = instance
- end
- end
- return data, instance
-end
-
-local function tolang(what) -- returns lang object
- local tag = numbers[what]
- local data = tag and registered[tag] or registered[what]
- if data then
- local instance = data.lang
- if not instance then
- instance = new_langage(data.number)
- data.instance = instance
- end
- return instance
- end
-end
-
--- languages.tolang = tolang
-
--- patterns=en
--- patterns=en,de
-
-local function validdata(dataset,what,tag)
- if dataset then
- local data = dataset.data
- if not data or data == "" then
- return nil
- 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
-
-local function loaddefinitions(tag,specification)
- statistics.starttiming(languages)
- local data, instance = resolve(tag)
- local definitions = settings_to_array(specification.patterns or "")
- if #definitions > 0 then
- if trace_patterns then
- report_initialization("pattern specification for language %a: %s",tag,specification.patterns)
- end
- local dataused, ok = data.used, false
- for i=1,#definitions do
- local definition = definitions[i]
- if definition == "" then
- -- error
- elseif definition == "reset" then -- interfaces.variables.reset
- if trace_patterns then
- report_initialization("clearing patterns for language %a",tag)
- end
- instance:clear_patterns()
- elseif not dataused[definition] then
- dataused[definition] = definition
- local filename = "lang-" .. definition .. ".lua"
- local fullname = resolvers.findfile(filename) or ""
- if fullname ~= "" then
- if trace_patterns then
- report_initialization("loading definition %a for language %a from %a",definition,tag,fullname)
- end
- local defs = dofile(fullname) -- use regular loader instead
- if defs then -- todo: version test
- ok, nofloaded = true, nofloaded + 1
- -- instance:patterns (defs.patterns and defs.patterns .data or "")
- -- instance:hyphenation(defs.exceptions and defs.exceptions.data or "")
- instance:patterns (validdata(defs.patterns, "patterns", tag) or "")
- instance:hyphenation(validdata(defs.exceptions,"exceptions",tag) or "")
- 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
- 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 tex.language]
-end
-
-function languages.associate(tag,script,language) -- not yet used
- associated[tag] = { script, language }
-end
-
-function languages.association(tag) -- not yet used
- if 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)
- local l = registered[tag]
- if l then
- if l.dirty then
- 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
-
-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.lefthyphenmin (what) return lefthyphenmin (tolang(what)) end
-function 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'
-
-function languages.loadwords(tag,filename)
- local data, instance = resolve(tag)
- if data then
- statistics.starttiming(languages)
- instance:hyphenation(io.loaddata(filename) or "")
- statistics.stoptiming(languages)
- end
-end
-
-function languages.setexceptions(tag,str)
- local data, instance = resolve(tag)
- if data then
- instance:hyphenation(string.strip(str)) -- we need to strip leading spaces
- 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
-
-local getnumber = languages.getnumber
-
-function commands.languagenumber(tag,default,patterns)
- context(getnumber(tag,default,patterns))
-end
-
-function commands.installedlanguages(separator)
- context(languages.installed(separator))
-end
-
-commands.definelanguage = languages.define
-commands.setlanguagesynonym = languages.setsynonym
-commands.unloadlanguage = languages.unload
-commands.setlanguageexceptions = languages.setexceptions
+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)
+
+--~ lang:hyphenation(string) string = lang:hyphenation() lang:clear_hyphenation()
+
+local type, tonumber = type, tonumber
+local utfbyte = utf.byte
+local format, gsub = string.format, string.gsub
+local concat, sortedkeys, sortedpairs = table.concat, table.sortedkeys, table.sortedpairs
+local lpegmatch = lpeg.match
+
+local settings_to_array = utilities.parsers.settings_to_array
+
+local trace_patterns = false trackers.register("languages.patterns", function(v) trace_patterns = v end)
+
+local report_initialization = logs.reporter("languages","initialization")
+
+local prehyphenchar = lang.prehyphenchar -- global per language
+local posthyphenchar = lang.posthyphenchar -- global per language
+local lefthyphenmin = lang.lefthyphenmin
+local righthyphenmin = lang.righthyphenmin
+
+local lang = lang
+lang.exceptions = lang.hyphenation
+local new_langage = lang.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/numbers", numbers, "languages.numbers")
+storage.register("languages/registered",registered,"languages.registered")
+storage.register("languages/associated",associated,"languages.associated")
+storage.register("languages/data", data, "languages.data")
+
+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_langage(data.number)
+ data.instance = instance
+ end
+ end
+ return data, instance
+end
+
+local function tolang(what) -- returns lang object
+ local tag = numbers[what]
+ local data = tag and registered[tag] or registered[what]
+ if data then
+ local instance = data.lang
+ if not instance then
+ instance = new_langage(data.number)
+ data.instance = instance
+ end
+ return instance
+ end
+end
+
+-- languages.tolang = tolang
+
+-- patterns=en
+-- patterns=en,de
+
+local function validdata(dataset,what,tag)
+ if dataset then
+ local data = dataset.data
+ if not data or data == "" then
+ return nil
+ 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
+
+local function loaddefinitions(tag,specification)
+ statistics.starttiming(languages)
+ local data, instance = resolve(tag)
+ local definitions = settings_to_array(specification.patterns or "")
+ if #definitions > 0 then
+ if trace_patterns then
+ report_initialization("pattern specification for language %a: %s",tag,specification.patterns)
+ end
+ local dataused, ok = data.used, false
+ for i=1,#definitions do
+ local definition = definitions[i]
+ if definition == "" then
+ -- error
+ elseif definition == "reset" then -- interfaces.variables.reset
+ if trace_patterns then
+ report_initialization("clearing patterns for language %a",tag)
+ end
+ instance:clear_patterns()
+ elseif not dataused[definition] then
+ dataused[definition] = definition
+ local filename = "lang-" .. definition .. ".lua"
+ local fullname = resolvers.findfile(filename) or ""
+ if fullname ~= "" then
+ if trace_patterns then
+ report_initialization("loading definition %a for language %a from %a",definition,tag,fullname)
+ end
+ local defs = dofile(fullname) -- use regular loader instead
+ if defs then -- todo: version test
+ ok, nofloaded = true, nofloaded + 1
+ -- instance:patterns (defs.patterns and defs.patterns .data or "")
+ -- instance:hyphenation(defs.exceptions and defs.exceptions.data or "")
+ instance:patterns (validdata(defs.patterns, "patterns", tag) or "")
+ instance:hyphenation(validdata(defs.exceptions,"exceptions",tag) or "")
+ 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
+ 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 tex.language]
+end
+
+function languages.associate(tag,script,language) -- not yet used
+ associated[tag] = { script, language }
+end
+
+function languages.association(tag) -- not yet used
+ if 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)
+ local l = registered[tag]
+ if l then
+ if l.dirty then
+ 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
+
+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.lefthyphenmin (what) return lefthyphenmin (tolang(what)) end
+function 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'
+
+function languages.loadwords(tag,filename)
+ local data, instance = resolve(tag)
+ if data then
+ statistics.starttiming(languages)
+ instance:hyphenation(io.loaddata(filename) or "")
+ statistics.stoptiming(languages)
+ end
+end
+
+function languages.setexceptions(tag,str)
+ local data, instance = resolve(tag)
+ if data then
+ instance:hyphenation(string.strip(str)) -- we need to strip leading spaces
+ 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
+
+local getnumber = languages.getnumber
+
+function commands.languagenumber(tag,default,patterns)
+ context(getnumber(tag,default,patterns))
+end
+
+function commands.installedlanguages(separator)
+ context(languages.installed(separator))
+end
+
+commands.definelanguage = languages.define
+commands.setlanguagesynonym = languages.setsynonym
+commands.unloadlanguage = languages.unload
+commands.setlanguageexceptions = languages.setexceptions
diff --git a/tex/context/base/lang-lab.lua b/tex/context/base/lang-lab.lua
index c83cd8bc8..91c258418 100644
--- a/tex/context/base/lang-lab.lua
+++ b/tex/context/base/lang-lab.lua
@@ -1,142 +1,142 @@
-if not modules then modules = { } end modules ['lang-lab'] = {
- version = 1.001,
- comment = "companion to lang-lab.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local format, find = string.format, string.find
-local next, rawget, type = next, rawget, type
-local lpegmatch = lpeg.match
-local formatters = string.formatters
-
-local prtcatcodes = catcodes.numbers.prtcatcodes -- todo: use different method
-
-local trace_labels = false trackers.register("languages.labels", function(v) trace_labels = v end)
-local report_labels = logs.reporter("languages","labels")
-
-languages.labels = languages.labels or { }
-local labels = languages.labels
-
-local variables = interfaces.variables
-local settings_to_array = utilities.parsers.settings_to_array
-
-local splitter = lpeg.splitat(":")
-
-local function split(tag)
- return lpegmatch(splitter,tag)
-end
-
-labels.split = split
-
-local contextsprint = context.sprint
-
-local function definelanguagelabels(data,class,tag,rawtag)
- for language, text in next, data.labels do
- if text == "" then
- -- skip
- elseif type(text) == "table" then
- contextsprint(prtcatcodes,"\\setlabeltextpair{",class,"}{",language,"}{",tag,"}{",text[1],"}{",text[2],"}")
- if trace_labels then
- report_labels("language %a, defining label %a as %a and %a",language,rawtag,text[1],text[2])
- end
- else
- contextsprint(prtcatcodes,"\\setlabeltextpair{",class,"}{",language,"}{",tag,"}{",text,"}{}")
- if trace_labels then
- report_labels("language %a, defining label %a as %a",language,rawtag,text)
- end
- end
- end
-end
-
-function labels.define(class,name,prefixed)
- local list = languages.data.labels[name]
- if list then
- report_labels("defining label set %a",name)
- for tag, data in next, list do
- if data.hidden then
- -- skip
- elseif prefixed then
- local first, second = lpegmatch(splitter,tag)
- if second then
- if rawget(variables,first) then
- if rawget(variables,second) then
- definelanguagelabels(data,class,formatters["\\v!%s:\\v!%s"](first,second),tag)
- else
- definelanguagelabels(data,class,formatters["\\v!%s:%s"](first,second),tag)
- end
- elseif rawget(variables,second) then
- definelanguagelabels(data,class,formatters["%s:\\v!%s"](first,second),tag)
- else
- definelanguagelabels(data,class,formatters["%s:%s"](first,second),tag)
- end
- elseif rawget(variables,rawtag) then
- definelanguagelabels(data,class,formatters["\\v!%s"](tag),tag)
- else
- definelanguagelabels(data,class,tag,tag)
- end
- else
- definelanguagelabels(data,class,tag,tag)
- end
- end
- else
- report_labels("unknown label set %a",name)
- end
-end
-
--- function labels.check()
--- for category, list in next, languages.data.labels do
--- for tag, specification in next, list do
--- for language, text in next, specification.labels do
--- if type(text) == "string" and find(text,",") then
--- report_labels("warning: label with comma found, category %a, language %a, tag %a, text %a",
--- category, language, tag, text)
--- end
--- end
--- end
--- end
--- end
---
--- labels.check()
-
--- interface
-
-commands.definelabels = labels.define
-
--- function commands.setstrippedtextprefix(str)
--- context(string.strip(str))
--- end
-
--- list : { "a", "b", "c" }
--- separator : ", "
--- last : " and "
-
--- text : "a,b,c"
--- separators : "{, },{ and }"
-
-function commands.concatcommalist(settings) -- it's too easy to forget that this one is there
- local list = settings.list or settings_to_array(settings.text or "")
- local size = #list
- local command = settings.command and context[settings.command] or context
- if size > 1 then
- local separator, last = " ", " "
- if settings.separators then
- local set = settings_to_array(settings.separators)
- separator = set[1] or settings.separator or separator
- last = set[2] or settings.last or last
- else
- separator = settings.separator or separator
- last = settings.last or last
- end
- command(list[1])
- for i=2,size-1 do
- context(separator)
- command(list[i])
- end
- context(last)
- end
- if size > 0 then
- command(list[size])
- end
-end
+if not modules then modules = { } end modules ['lang-lab'] = {
+ version = 1.001,
+ comment = "companion to lang-lab.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local format, find = string.format, string.find
+local next, rawget, type = next, rawget, type
+local lpegmatch = lpeg.match
+local formatters = string.formatters
+
+local prtcatcodes = catcodes.numbers.prtcatcodes -- todo: use different method
+
+local trace_labels = false trackers.register("languages.labels", function(v) trace_labels = v end)
+local report_labels = logs.reporter("languages","labels")
+
+languages.labels = languages.labels or { }
+local labels = languages.labels
+
+local variables = interfaces.variables
+local settings_to_array = utilities.parsers.settings_to_array
+
+local splitter = lpeg.splitat(":")
+
+local function split(tag)
+ return lpegmatch(splitter,tag)
+end
+
+labels.split = split
+
+local contextsprint = context.sprint
+
+local function definelanguagelabels(data,class,tag,rawtag)
+ for language, text in next, data.labels do
+ if text == "" then
+ -- skip
+ elseif type(text) == "table" then
+ contextsprint(prtcatcodes,"\\setlabeltextpair{",class,"}{",language,"}{",tag,"}{",text[1],"}{",text[2],"}")
+ if trace_labels then
+ report_labels("language %a, defining label %a as %a and %a",language,rawtag,text[1],text[2])
+ end
+ else
+ contextsprint(prtcatcodes,"\\setlabeltextpair{",class,"}{",language,"}{",tag,"}{",text,"}{}")
+ if trace_labels then
+ report_labels("language %a, defining label %a as %a",language,rawtag,text)
+ end
+ end
+ end
+end
+
+function labels.define(class,name,prefixed)
+ local list = languages.data.labels[name]
+ if list then
+ report_labels("defining label set %a",name)
+ for tag, data in next, list do
+ if data.hidden then
+ -- skip
+ elseif prefixed then
+ local first, second = lpegmatch(splitter,tag)
+ if second then
+ if rawget(variables,first) then
+ if rawget(variables,second) then
+ definelanguagelabels(data,class,formatters["\\v!%s:\\v!%s"](first,second),tag)
+ else
+ definelanguagelabels(data,class,formatters["\\v!%s:%s"](first,second),tag)
+ end
+ elseif rawget(variables,second) then
+ definelanguagelabels(data,class,formatters["%s:\\v!%s"](first,second),tag)
+ else
+ definelanguagelabels(data,class,formatters["%s:%s"](first,second),tag)
+ end
+ elseif rawget(variables,rawtag) then
+ definelanguagelabels(data,class,formatters["\\v!%s"](tag),tag)
+ else
+ definelanguagelabels(data,class,tag,tag)
+ end
+ else
+ definelanguagelabels(data,class,tag,tag)
+ end
+ end
+ else
+ report_labels("unknown label set %a",name)
+ end
+end
+
+-- function labels.check()
+-- for category, list in next, languages.data.labels do
+-- for tag, specification in next, list do
+-- for language, text in next, specification.labels do
+-- if type(text) == "string" and find(text,",") then
+-- report_labels("warning: label with comma found, category %a, language %a, tag %a, text %a",
+-- category, language, tag, text)
+-- end
+-- end
+-- end
+-- end
+-- end
+--
+-- labels.check()
+
+-- interface
+
+commands.definelabels = labels.define
+
+-- function commands.setstrippedtextprefix(str)
+-- context(string.strip(str))
+-- end
+
+-- list : { "a", "b", "c" }
+-- separator : ", "
+-- last : " and "
+
+-- text : "a,b,c"
+-- separators : "{, },{ and }"
+
+function commands.concatcommalist(settings) -- it's too easy to forget that this one is there
+ local list = settings.list or settings_to_array(settings.text or "")
+ local size = #list
+ local command = settings.command and context[settings.command] or context
+ if size > 1 then
+ local separator, last = " ", " "
+ if settings.separators then
+ local set = settings_to_array(settings.separators)
+ separator = set[1] or settings.separator or separator
+ last = set[2] or settings.last or last
+ else
+ separator = settings.separator or separator
+ last = settings.last or last
+ end
+ command(list[1])
+ for i=2,size-1 do
+ context(separator)
+ command(list[i])
+ end
+ context(last)
+ end
+ if size > 0 then
+ command(list[size])
+ end
+end
diff --git a/tex/context/base/lang-url.lua b/tex/context/base/lang-url.lua
index 86733c876..35381e672 100644
--- a/tex/context/base/lang-url.lua
+++ b/tex/context/base/lang-url.lua
@@ -1,113 +1,113 @@
-if not modules then modules = { } end modules ['lang-url'] = {
- version = 1.001,
- comment = "companion to lang-url.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local utfcharacters, utfvalues, utfbyte, utfchar = utf.characters, utf.values, utf.byte, utf.char
-
-context = context
-
-commands = commands or { }
-local commands = commands
-
---[[
-<p>Hyphenating <l n='url'/>'s is somewhat tricky and a matter of taste. I did
-consider using a dedicated hyphenation pattern or dealing with it by node
-parsing, but the following solution suits as well. After all, we're mostly
-dealing with <l n='ascii'/> characters.</p>
-]]--
-
-commands.hyphenatedurl = commands.hyphenatedurl or { }
-local hyphenatedurl = commands.hyphenatedurl
-
-local characters = utilities.storage.allocate {
- ["!"] = 1,
- ["\""] = 1,
- ["#"] = 1,
- ["$"] = 1,
- ["%"] = 1,
- ["&"] = 1,
- ["("] = 1,
- ["*"] = 1,
- ["+"] = 1,
- [","] = 1,
- ["-"] = 1,
- ["."] = 1,
- ["/"] = 1,
- [":"] = 1,
- [";"] = 1,
- ["<"] = 1,
- ["="] = 1,
- [">"] = 1,
- ["?"] = 1,
- ["@"] = 1,
- ["["] = 1,
- ["\\"] = 1,
- ["^"] = 1,
- ["_"] = 1,
- ["`"] = 1,
- ["{"] = 1,
- ["|"] = 1,
- ["~"] = 1,
-
- ["'"] = 2,
- [")"] = 2,
- ["]"] = 2,
- ["}"] = 2,
-}
-
-local mapping = utilities.storage.allocate {
- -- [utfchar(0xA0)] = "~", -- nbsp (catch)
-}
-
-hyphenatedurl.characters = characters
-hyphenatedurl.mapping = mapping
-hyphenatedurl.lefthyphenmin = 2
-hyphenatedurl.righthyphenmin = 3
-hyphenatedurl.discretionary = nil
-
--- more fun is to write nodes .. maybe it's nicer to do this
--- in an attribute handler anyway
-
-local function action(hyphenatedurl,str,left,right,disc)
- local n = 0
- local b = math.max( left or hyphenatedurl.lefthyphenmin, 2)
- local e = math.min(#str-(right or hyphenatedurl.righthyphenmin)+2,#str)
- local d = disc or hyphenatedurl.discretionary
- for s in utfcharacters(str) do
- n = n + 1
- s = mapping[s] or s
- if n > 1 then
- context.s() -- can be option
- end
- if s == d then
- context.d(utfbyte(s))
- else
- local c = characters[s]
- if not c or n<=b or n>=e then
- context.n(utfbyte(s))
- elseif c == 1 then
- context.b(utfbyte(s))
- elseif c == 2 then
- context.a(utfbyte(s))
- end
- end
- end
-end
-
--- hyphenatedurl.action = function(_,...) action(...) end -- sort of obsolete
-
-table.setmetatablecall(hyphenatedurl,action) -- watch out: a caller
-
--- todo, no interface in mkiv yet
-
-function hyphenatedurl.setcharacters(str,value) -- 1, 2 == before, after
- for s in utfcharacters(str) do
- characters[s] = value or 1
- end
-end
-
--- .hyphenatedurl.setcharacters("')]}",2)
+if not modules then modules = { } end modules ['lang-url'] = {
+ version = 1.001,
+ comment = "companion to lang-url.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local utfcharacters, utfvalues, utfbyte, utfchar = utf.characters, utf.values, utf.byte, utf.char
+
+context = context
+
+commands = commands or { }
+local commands = commands
+
+--[[
+<p>Hyphenating <l n='url'/>'s is somewhat tricky and a matter of taste. I did
+consider using a dedicated hyphenation pattern or dealing with it by node
+parsing, but the following solution suits as well. After all, we're mostly
+dealing with <l n='ascii'/> characters.</p>
+]]--
+
+commands.hyphenatedurl = commands.hyphenatedurl or { }
+local hyphenatedurl = commands.hyphenatedurl
+
+local characters = utilities.storage.allocate {
+ ["!"] = 1,
+ ["\""] = 1,
+ ["#"] = 1,
+ ["$"] = 1,
+ ["%"] = 1,
+ ["&"] = 1,
+ ["("] = 1,
+ ["*"] = 1,
+ ["+"] = 1,
+ [","] = 1,
+ ["-"] = 1,
+ ["."] = 1,
+ ["/"] = 1,
+ [":"] = 1,
+ [";"] = 1,
+ ["<"] = 1,
+ ["="] = 1,
+ [">"] = 1,
+ ["?"] = 1,
+ ["@"] = 1,
+ ["["] = 1,
+ ["\\"] = 1,
+ ["^"] = 1,
+ ["_"] = 1,
+ ["`"] = 1,
+ ["{"] = 1,
+ ["|"] = 1,
+ ["~"] = 1,
+
+ ["'"] = 2,
+ [")"] = 2,
+ ["]"] = 2,
+ ["}"] = 2,
+}
+
+local mapping = utilities.storage.allocate {
+ -- [utfchar(0xA0)] = "~", -- nbsp (catch)
+}
+
+hyphenatedurl.characters = characters
+hyphenatedurl.mapping = mapping
+hyphenatedurl.lefthyphenmin = 2
+hyphenatedurl.righthyphenmin = 3
+hyphenatedurl.discretionary = nil
+
+-- more fun is to write nodes .. maybe it's nicer to do this
+-- in an attribute handler anyway
+
+local function action(hyphenatedurl,str,left,right,disc)
+ local n = 0
+ local b = math.max( left or hyphenatedurl.lefthyphenmin, 2)
+ local e = math.min(#str-(right or hyphenatedurl.righthyphenmin)+2,#str)
+ local d = disc or hyphenatedurl.discretionary
+ for s in utfcharacters(str) do
+ n = n + 1
+ s = mapping[s] or s
+ if n > 1 then
+ context.s() -- can be option
+ end
+ if s == d then
+ context.d(utfbyte(s))
+ else
+ local c = characters[s]
+ if not c or n<=b or n>=e then
+ context.n(utfbyte(s))
+ elseif c == 1 then
+ context.b(utfbyte(s))
+ elseif c == 2 then
+ context.a(utfbyte(s))
+ end
+ end
+ end
+end
+
+-- hyphenatedurl.action = function(_,...) action(...) end -- sort of obsolete
+
+table.setmetatablecall(hyphenatedurl,action) -- watch out: a caller
+
+-- todo, no interface in mkiv yet
+
+function hyphenatedurl.setcharacters(str,value) -- 1, 2 == before, after
+ for s in utfcharacters(str) do
+ characters[s] = value or 1
+ end
+end
+
+-- .hyphenatedurl.setcharacters("')]}",2)
diff --git a/tex/context/base/lang-wrd.lua b/tex/context/base/lang-wrd.lua
index 6a9b39fdf..06a2311a6 100644
--- a/tex/context/base/lang-wrd.lua
+++ b/tex/context/base/lang-wrd.lua
@@ -1,353 +1,353 @@
-if not modules then modules = { } end modules ['lang-wrd'] = {
- 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 lower = string.lower
-local utfchar = utf.char
-local concat = table.concat
-local lpegmatch = lpeg.match
-local P, S, Cs = lpeg.P, lpeg.S, lpeg.Cs
-
-local report_words = logs.reporter("languages","words")
-
-local nodes, node, languages = nodes, node, languages
-
-languages.words = languages.words or { }
-local words = languages.words
-
-words.data = words.data or { }
-words.enables = false
-words.threshold = 4
-
-local numbers = languages.numbers
-local registered = languages.registered
-
-local traverse_nodes = node.traverse
-local wordsdata = words.data
-local chardata = characters.data
-local tasks = nodes.tasks
-
-local unsetvalue = attributes.unsetvalue
-
-local nodecodes = nodes.nodecodes
-local kerncodes = nodes.kerncodes
-
-local glyph_code = nodecodes.glyph
-local disc_code = nodecodes.disc
-local kern_code = nodecodes.kern
-
-local kerning_code = kerncodes.kerning
-local lowerchar = characters.lower
-
-local a_color = attributes.private('color')
-local colist = attributes.list[a_color]
-
-local is_letter = characters.is_letter -- maybe is_character as variant
-
-local spacing = S(" \n\r\t")
-local markup = S("-=")
-local lbrace = P("{")
-local rbrace = P("}")
-local disc = (lbrace * (1-rbrace)^0 * rbrace)^1 -- or just 3 times, time this
-local word = Cs((markup/"" + disc/"" + (1-spacing))^1)
-
-local loaded = { } -- we share lists
-
-function words.load(tag,filename)
- local fullname = resolvers.findfile(filename,'other text file') or ""
- if fullname ~= "" then
- report_words("loading word file %a",fullname)
- statistics.starttiming(languages)
- local list = loaded[fullname]
- if not list then
- list = wordsdata[tag] or { }
- local parser = (spacing + word/function(s) list[s] = true end)^0
- lpegmatch(parser,io.loaddata(fullname) or "")
- loaded[fullname] = list
- end
- wordsdata[tag] = list
- statistics.stoptiming(languages)
- else
- report_words("missing word file %a",filename)
- end
-end
-
-function words.found(id, str)
- local tag = languages.numbers[id]
- if tag then
- local data = wordsdata[tag]
- if data then
- if data[str] then
- return 1
- elseif data[lower(str)] then
- return 2
- end
- end
- end
-end
-
--- The following code is an adaption of experimental code for hyphenating and
--- spell checking.
-
--- there is an n=1 problem somewhere in nested boxes
-
-local function mark_words(head,whenfound) -- can be optimized and shared
- local current, language, done = head, nil, nil, 0, false
- local str, s, nds, n = { }, 0, { }, 0 -- n could also be a table, saves calls
- local function action()
- if s > 0 then
- local word = concat(str,"",1,s)
- local mark = whenfound(language,word)
- if mark then
- done = true
- for i=1,n do
- mark(nds[i])
- end
- end
- end
- n, s = 0, 0
- end
- while current do
- local id = current.id
- if id == glyph_code then
- local a = current.lang
- if a then
- if a ~= language then
- if s > 0 then
- action()
- end
- language = a
- end
- elseif s > 0 then
- action()
- language = a
- end
- local components = current.components
- if components then
- n = n + 1
- nds[n] = current
- for g in traverse_nodes(components) do
- s = s + 1
- str[s] = utfchar(g.char)
- end
- else
- local code = current.char
- local data = chardata[code]
- if is_letter[data.category] then
- n = n + 1
- nds[n] = current
- s = s + 1
- str[s] = utfchar(code)
- elseif s > 0 then
- action()
- end
- end
- elseif id == disc_code then -- take the replace
- if n > 0 then
- n = n + 1
- nds[n] = current
- end
- elseif id == kern_code and current.subtype == kerning_code and s > 0 then
- -- ok
- elseif s > 0 then
- action()
- end
- current = current.next
- end
- if s > 0 then
- action()
- end
- return head, done
-end
-
-local methods = { }
-words.methods = methods
-
-local enablers = { }
-words.enablers = enablers
-
-local wordmethod = 1
-local enabled = false
-
-function words.check(head)
- if enabled then
- return methods[wordmethod](head)
- else
- return head, false
- end
-end
-
-function words.enable(settings)
- local method = settings.method
- wordmethod = method and tonumber(method) or wordmethod or 1
- local e = enablers[wordmethod]
- if e then e(settings) end
- tasks.enableaction("processors","languages.words.check")
- enabled = true
-end
-
-function words.disable()
- enabled = false
-end
-
--- colors
-
-local cache = { } -- can also be done with method 1 -- frozen colors once used
-
-table.setmetatableindex(cache, function(t,k) -- k == language, numbers[k] == tag
- local c
- if type(k) == "string" then
- c = colist[k]
- elseif k < 0 then
- c = colist["word:unset"]
- else
- c = colist["word:" .. (numbers[k] or "unset")] or colist["word:unknown"]
- end
- local v = c and function(n) n[a_color] = c end or false
- t[k] = v
- return v
-end)
-
--- method 1
-
-local function sweep(language,str)
- if #str < words.threshold then
- return false
- elseif words.found(language,str) then -- can become a local wordsfound
- return cache["word:yes"] -- maybe variables.yes
- else
- return cache["word:no"]
- end
-end
-
-methods[1] = function(head)
- for n in traverse_nodes(head) do
- n[a_color] = unsetvalue -- hm, not that selective (reset color)
- end
- return mark_words(head,sweep)
-end
-
--- method 2
-
-local dumpname = nil
-local dumpthem = false
-local listname = "document"
-
-local category = { }
-local categories = { }
-
-setmetatable(categories, {
- __index = function(t,k)
- local languages = { }
- setmetatable(languages, {
- __index = function(t,k)
- local r = registered[k]
- local v = {
- number = language,
- parent = r and r.parent or nil,
- patterns = r and r.patterns or nil,
- tag = r and r.tag or nil,
- list = { },
- total = 0,
- unique = 0,
- }
- t[k] = v
- return v
- end
- } )
- local v = {
- languages = languages,
- total = 0,
- }
- t[k] = v
- return v
- end
-} )
-
-local collected = {
- total = 0,
- version = 1.000,
- categories = categories,
-}
-
-enablers[2] = function(settings)
- local name = settings.list
- listname = name and name ~= "" and name or "document"
- category = collected.categories[listname]
-end
-
-local function sweep(language,str)
- if #str >= words.threshold then
- str = lowerchar(str)
- local words = category.languages[numbers[language] or "unset"]
- local list = words.list
- local ls = list[str]
- if ls then
- list[str] = ls + 1
- else
- list[str] = 1
- words.unique = words.unique + 1
- end
- collected.total = collected.total + 1
- category.total = category.total + 1
- words.total = words.total + 1
- end
-end
-
-methods[2] = function(head)
- dumpthem = true
- return mark_words(head,sweep)
-end
-
-local function dumpusedwords()
- if dumpthem then
- collected.threshold = words.threshold
- dumpname = dumpname or file.addsuffix(tex.jobname,"words")
- report_words("saving list of used words in %a",dumpname)
- io.savedata(dumpname,table.serialize(collected,true))
- -- table.tofile(dumpname,list,true)
- end
-end
-
-directives.register("languages.words.dump", function(v)
- dumpname = type(v) == "string" and v ~= "" and v
-end)
-
-luatex.registerstopactions(dumpusedwords)
-
--- method 3
-
-local function sweep(language,str)
- return cache[language]
-end
-
-methods[3] = function(head)
- for n in traverse_nodes(head) do
- n[a_color] = unsetvalue
- end
- return mark_words(head,sweep)
-end
-
--- for the moment we hook it into the attribute handler
-
---~ languagehacks = { }
-
---~ function languagehacks.process(namespace,attribute,head)
---~ return languages.check(head)
---~ end
-
---~ chars.plugins[chars.plugins+1] = {
---~ name = "language",
---~ namespace = languagehacks,
---~ processor = languagehacks.process
---~ }
-
--- interface
-
-commands.enablespellchecking = words.enable
-commands.disablespellchecking = words.disable
-commands.loadspellchecklist = words.load
+if not modules then modules = { } end modules ['lang-wrd'] = {
+ 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 lower = string.lower
+local utfchar = utf.char
+local concat = table.concat
+local lpegmatch = lpeg.match
+local P, S, Cs = lpeg.P, lpeg.S, lpeg.Cs
+
+local report_words = logs.reporter("languages","words")
+
+local nodes, node, languages = nodes, node, languages
+
+languages.words = languages.words or { }
+local words = languages.words
+
+words.data = words.data or { }
+words.enables = false
+words.threshold = 4
+
+local numbers = languages.numbers
+local registered = languages.registered
+
+local traverse_nodes = node.traverse
+local wordsdata = words.data
+local chardata = characters.data
+local tasks = nodes.tasks
+
+local unsetvalue = attributes.unsetvalue
+
+local nodecodes = nodes.nodecodes
+local kerncodes = nodes.kerncodes
+
+local glyph_code = nodecodes.glyph
+local disc_code = nodecodes.disc
+local kern_code = nodecodes.kern
+
+local kerning_code = kerncodes.kerning
+local lowerchar = characters.lower
+
+local a_color = attributes.private('color')
+local colist = attributes.list[a_color]
+
+local is_letter = characters.is_letter -- maybe is_character as variant
+
+local spacing = S(" \n\r\t")
+local markup = S("-=")
+local lbrace = P("{")
+local rbrace = P("}")
+local disc = (lbrace * (1-rbrace)^0 * rbrace)^1 -- or just 3 times, time this
+local word = Cs((markup/"" + disc/"" + (1-spacing))^1)
+
+local loaded = { } -- we share lists
+
+function words.load(tag,filename)
+ local fullname = resolvers.findfile(filename,'other text file') or ""
+ if fullname ~= "" then
+ report_words("loading word file %a",fullname)
+ statistics.starttiming(languages)
+ local list = loaded[fullname]
+ if not list then
+ list = wordsdata[tag] or { }
+ local parser = (spacing + word/function(s) list[s] = true end)^0
+ lpegmatch(parser,io.loaddata(fullname) or "")
+ loaded[fullname] = list
+ end
+ wordsdata[tag] = list
+ statistics.stoptiming(languages)
+ else
+ report_words("missing word file %a",filename)
+ end
+end
+
+function words.found(id, str)
+ local tag = languages.numbers[id]
+ if tag then
+ local data = wordsdata[tag]
+ if data then
+ if data[str] then
+ return 1
+ elseif data[lower(str)] then
+ return 2
+ end
+ end
+ end
+end
+
+-- The following code is an adaption of experimental code for hyphenating and
+-- spell checking.
+
+-- there is an n=1 problem somewhere in nested boxes
+
+local function mark_words(head,whenfound) -- can be optimized and shared
+ local current, language, done = head, nil, nil, 0, false
+ local str, s, nds, n = { }, 0, { }, 0 -- n could also be a table, saves calls
+ local function action()
+ if s > 0 then
+ local word = concat(str,"",1,s)
+ local mark = whenfound(language,word)
+ if mark then
+ done = true
+ for i=1,n do
+ mark(nds[i])
+ end
+ end
+ end
+ n, s = 0, 0
+ end
+ while current do
+ local id = current.id
+ if id == glyph_code then
+ local a = current.lang
+ if a then
+ if a ~= language then
+ if s > 0 then
+ action()
+ end
+ language = a
+ end
+ elseif s > 0 then
+ action()
+ language = a
+ end
+ local components = current.components
+ if components then
+ n = n + 1
+ nds[n] = current
+ for g in traverse_nodes(components) do
+ s = s + 1
+ str[s] = utfchar(g.char)
+ end
+ else
+ local code = current.char
+ local data = chardata[code]
+ if is_letter[data.category] then
+ n = n + 1
+ nds[n] = current
+ s = s + 1
+ str[s] = utfchar(code)
+ elseif s > 0 then
+ action()
+ end
+ end
+ elseif id == disc_code then -- take the replace
+ if n > 0 then
+ n = n + 1
+ nds[n] = current
+ end
+ elseif id == kern_code and current.subtype == kerning_code and s > 0 then
+ -- ok
+ elseif s > 0 then
+ action()
+ end
+ current = current.next
+ end
+ if s > 0 then
+ action()
+ end
+ return head, done
+end
+
+local methods = { }
+words.methods = methods
+
+local enablers = { }
+words.enablers = enablers
+
+local wordmethod = 1
+local enabled = false
+
+function words.check(head)
+ if enabled then
+ return methods[wordmethod](head)
+ else
+ return head, false
+ end
+end
+
+function words.enable(settings)
+ local method = settings.method
+ wordmethod = method and tonumber(method) or wordmethod or 1
+ local e = enablers[wordmethod]
+ if e then e(settings) end
+ tasks.enableaction("processors","languages.words.check")
+ enabled = true
+end
+
+function words.disable()
+ enabled = false
+end
+
+-- colors
+
+local cache = { } -- can also be done with method 1 -- frozen colors once used
+
+table.setmetatableindex(cache, function(t,k) -- k == language, numbers[k] == tag
+ local c
+ if type(k) == "string" then
+ c = colist[k]
+ elseif k < 0 then
+ c = colist["word:unset"]
+ else
+ c = colist["word:" .. (numbers[k] or "unset")] or colist["word:unknown"]
+ end
+ local v = c and function(n) n[a_color] = c end or false
+ t[k] = v
+ return v
+end)
+
+-- method 1
+
+local function sweep(language,str)
+ if #str < words.threshold then
+ return false
+ elseif words.found(language,str) then -- can become a local wordsfound
+ return cache["word:yes"] -- maybe variables.yes
+ else
+ return cache["word:no"]
+ end
+end
+
+methods[1] = function(head)
+ for n in traverse_nodes(head) do
+ n[a_color] = unsetvalue -- hm, not that selective (reset color)
+ end
+ return mark_words(head,sweep)
+end
+
+-- method 2
+
+local dumpname = nil
+local dumpthem = false
+local listname = "document"
+
+local category = { }
+local categories = { }
+
+setmetatable(categories, {
+ __index = function(t,k)
+ local languages = { }
+ setmetatable(languages, {
+ __index = function(t,k)
+ local r = registered[k]
+ local v = {
+ number = language,
+ parent = r and r.parent or nil,
+ patterns = r and r.patterns or nil,
+ tag = r and r.tag or nil,
+ list = { },
+ total = 0,
+ unique = 0,
+ }
+ t[k] = v
+ return v
+ end
+ } )
+ local v = {
+ languages = languages,
+ total = 0,
+ }
+ t[k] = v
+ return v
+ end
+} )
+
+local collected = {
+ total = 0,
+ version = 1.000,
+ categories = categories,
+}
+
+enablers[2] = function(settings)
+ local name = settings.list
+ listname = name and name ~= "" and name or "document"
+ category = collected.categories[listname]
+end
+
+local function sweep(language,str)
+ if #str >= words.threshold then
+ str = lowerchar(str)
+ local words = category.languages[numbers[language] or "unset"]
+ local list = words.list
+ local ls = list[str]
+ if ls then
+ list[str] = ls + 1
+ else
+ list[str] = 1
+ words.unique = words.unique + 1
+ end
+ collected.total = collected.total + 1
+ category.total = category.total + 1
+ words.total = words.total + 1
+ end
+end
+
+methods[2] = function(head)
+ dumpthem = true
+ return mark_words(head,sweep)
+end
+
+local function dumpusedwords()
+ if dumpthem then
+ collected.threshold = words.threshold
+ dumpname = dumpname or file.addsuffix(tex.jobname,"words")
+ report_words("saving list of used words in %a",dumpname)
+ io.savedata(dumpname,table.serialize(collected,true))
+ -- table.tofile(dumpname,list,true)
+ end
+end
+
+directives.register("languages.words.dump", function(v)
+ dumpname = type(v) == "string" and v ~= "" and v
+end)
+
+luatex.registerstopactions(dumpusedwords)
+
+-- method 3
+
+local function sweep(language,str)
+ return cache[language]
+end
+
+methods[3] = function(head)
+ for n in traverse_nodes(head) do
+ n[a_color] = unsetvalue
+ end
+ return mark_words(head,sweep)
+end
+
+-- for the moment we hook it into the attribute handler
+
+--~ languagehacks = { }
+
+--~ function languagehacks.process(namespace,attribute,head)
+--~ return languages.check(head)
+--~ end
+
+--~ chars.plugins[chars.plugins+1] = {
+--~ name = "language",
+--~ namespace = languagehacks,
+--~ processor = languagehacks.process
+--~ }
+
+-- interface
+
+commands.enablespellchecking = words.enable
+commands.disablespellchecking = words.disable
+commands.loadspellchecklist = words.load
diff --git a/tex/context/base/layo-ini.lua b/tex/context/base/layo-ini.lua
index cc483aa3b..56ced2c0b 100644
--- a/tex/context/base/layo-ini.lua
+++ b/tex/context/base/layo-ini.lua
@@ -1,61 +1,61 @@
-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.
---
--- Code might move.
-
--- conditionals.layoutisdoublesided
--- conditionals.layoutissinglesided
--- texcount.pagenoshift
--- texcount.realpageno
-
-local texcount = tex.count
-local conditionals = tex.conditionals
-
-layouts = {
- status = { },
-}
-
-local status = layouts.status
-
-function status.leftorrightpagection(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 texcount.pagenoshift % 2 == 0 then
- if texcount.realpageno % 2 == 0 then
- return right, left
- else
- return left, right
- end
- else
- if texcount.realpageno % 2 == 0 then
- return left, right
- else
- return right, left
- end
- end
-end
-
-function status.isleftpage()
- if not conditionals.layoutisdoublesided then
- return false
- elseif conditionals.layoutissinglesided then
- return false
- elseif texcount.pagenoshift % 2 == 0 then
- return texcount.realpageno % 2 == 0
- else
- return not texcount.realpageno % 2 == 0
- end
-end
+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.
+--
+-- Code might move.
+
+-- conditionals.layoutisdoublesided
+-- conditionals.layoutissinglesided
+-- texcount.pagenoshift
+-- texcount.realpageno
+
+local texcount = tex.count
+local conditionals = tex.conditionals
+
+layouts = {
+ status = { },
+}
+
+local status = layouts.status
+
+function status.leftorrightpagection(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 texcount.pagenoshift % 2 == 0 then
+ if texcount.realpageno % 2 == 0 then
+ return right, left
+ else
+ return left, right
+ end
+ else
+ if texcount.realpageno % 2 == 0 then
+ return left, right
+ else
+ return right, left
+ end
+ end
+end
+
+function status.isleftpage()
+ if not conditionals.layoutisdoublesided then
+ return false
+ elseif conditionals.layoutissinglesided then
+ return false
+ elseif texcount.pagenoshift % 2 == 0 then
+ return texcount.realpageno % 2 == 0
+ else
+ return not texcount.realpageno % 2 == 0
+ end
+end
diff --git a/tex/context/base/lpdf-ano.lua b/tex/context/base/lpdf-ano.lua
index ee9cb851b..adfea3812 100644
--- a/tex/context/base/lpdf-ano.lua
+++ b/tex/context/base/lpdf-ano.lua
@@ -1,753 +1,753 @@
-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
-
-local next, tostring = next, tostring
-local rep, format = string.rep, string.format
-local texcount = tex.count
-local lpegmatch = lpeg.match
-local formatters = string.formatters
-
-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 report_reference = logs.reporter("backend","references")
-local report_destination = logs.reporter("backend","destinations")
-local report_bookmark = logs.reporter("backend","bookmarks")
-
-local variables = interfaces.variables
-local constants = interfaces.constants
-
-local settings_to_array = utilities.parsers.settings_to_array
-
-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 runners = references.runners
-local specials = references.specials
-local handlers = references.handlers
-local executers = references.executers
-local getinnermethod = references.getinnermethod
-
-local nodepool = nodes.pool
-
-local pdfannotation_node = nodepool.pdfannotation
-local pdfdestination_node = nodepool.pdfdestination
-local latelua_node = nodepool.latelua
-
-local pdfdictionary = lpdf.dictionary
-local pdfarray = lpdf.array
-local pdfreference = lpdf.reference
-local pdfunicode = lpdf.unicode
-local pdfconstant = lpdf.constant
-local pdfflushobject = lpdf.flushobject
-local pdfshareobjectreference = lpdf.shareobjectreference
-local pdfreserveobject = lpdf.reserveobject
-local pdfpagereference = lpdf.pagereference
-local pdfdelayedobject = lpdf.delayedobject
-local pdfregisterannotation = lpdf.registerannotation
-
--- todo: 3dview
-
-local 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 pdf_border = pdfarray { 0, 0, 0 }
-
-local cache = { }
-
-local function pagedestination(n) -- only cache fit
- if n > 0 then
- local pd = cache[n]
- if not pd then
- local a = pdfarray {
- pdfreference(pdfpagereference(n)),
- pdf_fit,
- }
- pd = pdfshareobjectreference(a)
- cache[n] = pd
- end
- return pd
- end
-end
-
-lpdf.pagedestination = pagedestination
-
-local defaultdestination = pdfarray { 0, pdf_fit }
-
-local function link(url,filename,destination,page,actions)
- if filename and filename ~= "" then
- if file.basename(filename) == tex.jobname then
- return false
- else
- filename = file.addsuffix(filename,"pdf")
- end
- end
- if url and url ~= "" then
- if filename and filename ~= "" then
- if destination and destination ~= "" then
- url = file.join(url,filename).."#"..destination
- else
- url = file.join(url,filename)
- end
- end
- return pdfdictionary {
- S = pdf_uri,
- URI = url,
- }
- elseif filename and filename ~= "" then
- -- no page ?
- if destination == "" then
- destination = nil
- end
- if not destination and page then
- destination = pdfarray { page - 1, pdf_fit }
- end
- return pdfdictionary {
- S = pdf_gotor, -- can also be pdf_launch
- F = filename,
- D = destination or defaultdestination, -- D is mandate
- NewWindow = (actions.newwindow and true) or nil,
- }
- elseif destination and destination ~= "" then
- return pdfdictionary { -- can be cached
- S = pdf_goto,
- D = destination,
- }
- else
- local p = tonumber(page)
- if p and p > 0 then
- return pdfdictionary { -- can be cached
- S = pdf_goto,
- D = pdfarray {
- pdfreference(pdfpagereference(p)),
- pdf_fit,
- }
- }
- elseif trace_references then
- report_reference("invalid page reference %a",page)
- end
- end
- return false
-end
-
-lpdf.link = link
-
-function lpdf.launch(program,parameters)
- if program and program ~= "" then
- local d = pdfdictionary {
- S = pdf_launch,
- F = program,
- D = ".",
- }
- if parameters and parameters ~= "" then
- d.P = parameters
- end
- return d
- end
-end
-
-function lpdf.javascript(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 what then
- action.Next = what
- action = what
- else
- -- error
- return nil
- end
- end
- return first, actions.n
- 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
- main = pdfdictionary {
- Subtype = pdf_link,
- Border = pdf_border,
- 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_reference("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, nofunique, nofused = { }, 0, 0
-
-local f_annot = formatters["<< /Type /Annot %s /Rect [%0.3f %0.3f %0.3f %0.3f] >>"]
-local f_bpnf = formatters["_bpnf_(%s,%s,%s,'%s')"]
-
-local function use_shared_annotations()
-
- local factor = number.dimenfactors.bp
-
- local function finishreference(width,height,depth,prerolled) -- %0.2f looks okay enough (no scaling anyway)
- local h, v = pdf.h, pdf.v
- local llx, lly = h*factor, (v - depth)*factor
- local urx, ury = (h + width)*factor, (v + height)*factor
- local annot = f_annot(prerolled,llx,lly,urx,ury)
- local n = hashed[annot]
- if not n then
- n = pdfdelayedobject(annot)
- hashed[annot] = n
- nofunique = nofunique + 1
- end
- nofused = nofused + 1
- pdfregisterannotation(n)
- end
-
- _bpnf_ = finishreference
-
- local function reference(width,height,depth,prerolled)
- if prerolled then
- if trace_references then
- report_reference("width %p, height %p, depth %p, prerolled %a",width,height,depth,prerolled)
- end
- local luacode = f_bpnf(width,height,depth,prerolled)
- return latelua_node(luacode)
- end
- end
-
- statistics.register("pdf annotations", function()
- if nofused > 0 then
- return format("%s embedded, %s unique",nofused,nofunique)
- else
- return nil
- end
- end)
-
-
- return reference, finishreference
-
-end
-
-local lln = latelua_node() if node.has_field(lln,'string') then
-
- directives.register("refences.sharelinks", function(v)
- if v then
- nodeinjections.reference, codeinjections.finishreference = use_shared_annotations()
- else
- nodeinjections.reference, codeinjections.finishreference = use_normal_annotations()
- end
- end)
-
- nodeinjections.reference, codeinjections.finishreference = use_shared_annotations()
-
-else
-
- nodeinjections.reference, codeinjections.finishreference = use_normal_annotations()
-
-end node.free(lln)
-
--- -- -- --
--- -- -- --
-
-local done = { } -- prevent messages
-
-function nodeinjections.destination(width,height,depth,name,view)
- if not done[name] then
- done[name] = true
- if trace_destinations then
- report_destination("width %p, height %p, depth %p, name %a, view %a",width,height,depth,name,view)
- end
- return pdfdestination_node(width,height,depth,name,view) -- can be begin/end node
- end
-end
-
--- runners and specials
-
-runners["inner"] = function(var,actions)
- if getinnermethod() == "names" then
- local vi = var.i
- if vi then
- local vir = vi.references
- if vir then
- local internal = vir.internal
- if internal then
- var.inner = "aut:" .. internal
- end
- end
- end
- else
- var.inner = nil
- end
- local prefix = var.p
- local inner = var.inner
- if inner and prefix and prefix ~= "" then
- inner = prefix .. ":" .. inner -- might not always be ok
- end
- return link(nil,nil,inner,var.r,actions)
-end
-
-runners["inner with arguments"] = function(var,actions)
- report_reference("todo: inner with arguments")
- return false
-end
-
-runners["outer"] = function(var,actions)
- local file, url = references.checkedfileorurl(var.outer,var.outer)
- return link(url,file,var.arguments,nil,actions)
-end
-
-runners["outer with inner"] = function(var,actions)
- local file = references.checkedfile(var.outer) -- was var.f but fails ... why
- return link(nil,file,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_reference("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_reference("todo: outer with inner with arguments")
- return false
-end
-
-runners["outer with special and operation and arguments"] = function(var,actions)
- report_reference("todo: outer with special and operation and arguments")
- return false
-end
-
-runners["outer with special"] = function(var,actions)
- report_reference("todo: outer with special")
- return false
-end
-
-runners["outer with special and operation"] = function(var,actions)
- report_reference("todo: outer with special and operation")
- return false
-end
-
-runners["special operation"] = runners["special"]
-runners["special operation with arguments"] = runners["special"]
-
-function specials.internal(var,actions) -- better resolve in strc-ref
- local i = tonumber(var.operation)
- local v = i and references.internals[i]
- if not v then
- -- error
- report_reference("no internal reference %a",i)
- elseif getinnermethod() == "names" then
- -- named
- return link(nil,nil,"aut:"..i,v.references.realpage,actions)
- else
- -- page
- return link(nil,nil,nil,v.references.realpage,actions)
- 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
- file = references.checkedfile(file)
- return link(nil,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
- -- if p then
- -- var.r = p
- -- end
- end
- return link(nil,nil,nil,p or var.operation,actions)
- end
-end
-
-function specials.realpage(var,actions)
- local file = var.f
- if file then
- file = references.checkedfile(file)
- return link(nil,file,nil,var.operation,actions)
- else
- return link(nil,nil,nil,var.operation,actions)
- end
-end
-
-function specials.userpage(var,actions)
- local file = var.f
- if file then
- file = references.checkedfile(file)
- return link(nil,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 link(nil,nil,nil,p or var.operation,actions)
- end
-end
-
-function specials.deltapage(var,actions)
- local p = tonumber(var.operation)
- if p then
- p = references.checkedrealpage(p + texcount.realpageno)
- return link(nil,nil,nil,p,actions)
- end
-end
-
--- sections
-
--- function specials.section(var,actions)
--- local sectionname = var.operation
--- local destination = var.arguments
--- local internal = structures.sections.internalreference(sectionname,destination)
--- if internal then
--- var.special = "internal"
--- var.operation = internal
--- var.arguments = nil
--- specials.internal(var,actions)
--- end
--- end
-
-specials.section = specials.internal -- specials.section just need to have a value as it's checked
-
--- 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)
- local url = references.checkedurl(var.operation)
- return link(url,nil,var.arguments,nil,actions)
-end
-
-function specials.file(var,actions)
- local file = references.checkedfile(var.operation)
- return link(nil,file,var.arguments,nil,actions)
-end
-
-function specials.fileorurl(var,actions)
- local file, url = references.checkedfileorurl(var.operation,var.operation)
- return link(url,file,var.arguments,nil,actions)
-end
-
-function specials.program(var,content)
- local program = references.checkedprogram(var.operation)
- return lpdf.launch(program,var.arguments)
-end
-
-function specials.javascript(var)
- return lpdf.javascript(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
-
---~ entry.A = pdfdictionary {
---~ S = pdf_goto,
---~ D = ....
---~ }
-
-local function build(levels,start,parent,method)
- local startlevel = levels[start][1]
- local i, n = start, 0
- local child, entry, m, prev, first, last, f, l
- while i and i <= #levels do
- local li = levels[i]
- local level, title, reference, open = li[1], li[2], li[3], li[4]
- if level < startlevel then
- pdfflushobject(child,entry)
- return i, n, first, last
- elseif level == startlevel then
- if trace_bookmarks then
- report_bookmark("%3i %w%s %s",reference.realpage,(level-1)*2,(open and "+") or "-",title)
- end
- local prev = child
- child = pdfreserveobject()
- if entry then
- entry.Next = child and pdfreference(child)
- pdfflushobject(prev,entry)
- end
- entry = pdfdictionary {
- Title = pdfunicode(title),
- Parent = parent,
- Prev = prev and pdfreference(prev),
- }
- if method == "internal" then
- entry.Dest = "aut:" .. reference.internal
- else -- if method == "page" then
- entry.Dest = pagedestination(reference.realpage)
- end
- if not first then first, last = child, child end
- prev = child
- last = prev
- n = n + 1
- i = i + 1
- elseif i < #levels and level > startlevel then
- i, m, f, l = build(levels,i,pdfreference(child),method)
- entry.Count = (open and m) or -m
- if m > 0 then
- entry.First, entry.Last = pdfreference(f), pdfreference(l)
- end
- else
- -- missing intermediate level but ok
- i, m, f, l = build(levels,i,pdfreference(child),method)
- entry.Count = (open and m) or -m
- if m > 0 then
- entry.First, entry.Last = pdfreference(f), pdfreference(l)
- end
- pdfflushobject(child,entry)
- return i, n, first, last
- end
- end
- pdfflushobject(child,entry)
- return nil, n, first, last
-end
-
-function codeinjections.addbookmarks(levels,method)
- if #levels > 0 then
- structures.bookmarks.flatten(levels) -- dirty trick for lack of structure
- local parent = pdfreserveobject()
- local _, m, first, last = build(levels,1,pdfreference(parent),method or "internal")
- local dict = pdfdictionary {
- Type = pdfconstant("Outlines"),
- First = pdfreference(first),
- Last = pdfreference(last),
- Count = m,
- }
- pdfflushobject(parent,dict)
- lpdf.addtocatalog("Outlines",lpdf.reference(parent))
- end
-end
-
--- this could also be hooked into the frontend finalizer
-
-lpdf.registerdocumentfinalizer(function() bookmarks.place() end,1,"bookmarks")
+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
+
+local next, tostring = next, tostring
+local rep, format = string.rep, string.format
+local texcount = tex.count
+local lpegmatch = lpeg.match
+local formatters = string.formatters
+
+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 report_reference = logs.reporter("backend","references")
+local report_destination = logs.reporter("backend","destinations")
+local report_bookmark = logs.reporter("backend","bookmarks")
+
+local variables = interfaces.variables
+local constants = interfaces.constants
+
+local settings_to_array = utilities.parsers.settings_to_array
+
+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 runners = references.runners
+local specials = references.specials
+local handlers = references.handlers
+local executers = references.executers
+local getinnermethod = references.getinnermethod
+
+local nodepool = nodes.pool
+
+local pdfannotation_node = nodepool.pdfannotation
+local pdfdestination_node = nodepool.pdfdestination
+local latelua_node = nodepool.latelua
+
+local pdfdictionary = lpdf.dictionary
+local pdfarray = lpdf.array
+local pdfreference = lpdf.reference
+local pdfunicode = lpdf.unicode
+local pdfconstant = lpdf.constant
+local pdfflushobject = lpdf.flushobject
+local pdfshareobjectreference = lpdf.shareobjectreference
+local pdfreserveobject = lpdf.reserveobject
+local pdfpagereference = lpdf.pagereference
+local pdfdelayedobject = lpdf.delayedobject
+local pdfregisterannotation = lpdf.registerannotation
+
+-- todo: 3dview
+
+local 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 pdf_border = pdfarray { 0, 0, 0 }
+
+local cache = { }
+
+local function pagedestination(n) -- only cache fit
+ if n > 0 then
+ local pd = cache[n]
+ if not pd then
+ local a = pdfarray {
+ pdfreference(pdfpagereference(n)),
+ pdf_fit,
+ }
+ pd = pdfshareobjectreference(a)
+ cache[n] = pd
+ end
+ return pd
+ end
+end
+
+lpdf.pagedestination = pagedestination
+
+local defaultdestination = pdfarray { 0, pdf_fit }
+
+local function link(url,filename,destination,page,actions)
+ if filename and filename ~= "" then
+ if file.basename(filename) == tex.jobname then
+ return false
+ else
+ filename = file.addsuffix(filename,"pdf")
+ end
+ end
+ if url and url ~= "" then
+ if filename and filename ~= "" then
+ if destination and destination ~= "" then
+ url = file.join(url,filename).."#"..destination
+ else
+ url = file.join(url,filename)
+ end
+ end
+ return pdfdictionary {
+ S = pdf_uri,
+ URI = url,
+ }
+ elseif filename and filename ~= "" then
+ -- no page ?
+ if destination == "" then
+ destination = nil
+ end
+ if not destination and page then
+ destination = pdfarray { page - 1, pdf_fit }
+ end
+ return pdfdictionary {
+ S = pdf_gotor, -- can also be pdf_launch
+ F = filename,
+ D = destination or defaultdestination, -- D is mandate
+ NewWindow = (actions.newwindow and true) or nil,
+ }
+ elseif destination and destination ~= "" then
+ return pdfdictionary { -- can be cached
+ S = pdf_goto,
+ D = destination,
+ }
+ else
+ local p = tonumber(page)
+ if p and p > 0 then
+ return pdfdictionary { -- can be cached
+ S = pdf_goto,
+ D = pdfarray {
+ pdfreference(pdfpagereference(p)),
+ pdf_fit,
+ }
+ }
+ elseif trace_references then
+ report_reference("invalid page reference %a",page)
+ end
+ end
+ return false
+end
+
+lpdf.link = link
+
+function lpdf.launch(program,parameters)
+ if program and program ~= "" then
+ local d = pdfdictionary {
+ S = pdf_launch,
+ F = program,
+ D = ".",
+ }
+ if parameters and parameters ~= "" then
+ d.P = parameters
+ end
+ return d
+ end
+end
+
+function lpdf.javascript(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 what then
+ action.Next = what
+ action = what
+ else
+ -- error
+ return nil
+ end
+ end
+ return first, actions.n
+ 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
+ main = pdfdictionary {
+ Subtype = pdf_link,
+ Border = pdf_border,
+ 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_reference("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, nofunique, nofused = { }, 0, 0
+
+local f_annot = formatters["<< /Type /Annot %s /Rect [%0.3f %0.3f %0.3f %0.3f] >>"]
+local f_bpnf = formatters["_bpnf_(%s,%s,%s,'%s')"]
+
+local function use_shared_annotations()
+
+ local factor = number.dimenfactors.bp
+
+ local function finishreference(width,height,depth,prerolled) -- %0.2f looks okay enough (no scaling anyway)
+ local h, v = pdf.h, pdf.v
+ local llx, lly = h*factor, (v - depth)*factor
+ local urx, ury = (h + width)*factor, (v + height)*factor
+ local annot = f_annot(prerolled,llx,lly,urx,ury)
+ local n = hashed[annot]
+ if not n then
+ n = pdfdelayedobject(annot)
+ hashed[annot] = n
+ nofunique = nofunique + 1
+ end
+ nofused = nofused + 1
+ pdfregisterannotation(n)
+ end
+
+ _bpnf_ = finishreference
+
+ local function reference(width,height,depth,prerolled)
+ if prerolled then
+ if trace_references then
+ report_reference("width %p, height %p, depth %p, prerolled %a",width,height,depth,prerolled)
+ end
+ local luacode = f_bpnf(width,height,depth,prerolled)
+ return latelua_node(luacode)
+ end
+ end
+
+ statistics.register("pdf annotations", function()
+ if nofused > 0 then
+ return format("%s embedded, %s unique",nofused,nofunique)
+ else
+ return nil
+ end
+ end)
+
+
+ return reference, finishreference
+
+end
+
+local lln = latelua_node() if node.has_field(lln,'string') then
+
+ directives.register("refences.sharelinks", function(v)
+ if v then
+ nodeinjections.reference, codeinjections.finishreference = use_shared_annotations()
+ else
+ nodeinjections.reference, codeinjections.finishreference = use_normal_annotations()
+ end
+ end)
+
+ nodeinjections.reference, codeinjections.finishreference = use_shared_annotations()
+
+else
+
+ nodeinjections.reference, codeinjections.finishreference = use_normal_annotations()
+
+end node.free(lln)
+
+-- -- -- --
+-- -- -- --
+
+local done = { } -- prevent messages
+
+function nodeinjections.destination(width,height,depth,name,view)
+ if not done[name] then
+ done[name] = true
+ if trace_destinations then
+ report_destination("width %p, height %p, depth %p, name %a, view %a",width,height,depth,name,view)
+ end
+ return pdfdestination_node(width,height,depth,name,view) -- can be begin/end node
+ end
+end
+
+-- runners and specials
+
+runners["inner"] = function(var,actions)
+ if getinnermethod() == "names" then
+ local vi = var.i
+ if vi then
+ local vir = vi.references
+ if vir then
+ local internal = vir.internal
+ if internal then
+ var.inner = "aut:" .. internal
+ end
+ end
+ end
+ else
+ var.inner = nil
+ end
+ local prefix = var.p
+ local inner = var.inner
+ if inner and prefix and prefix ~= "" then
+ inner = prefix .. ":" .. inner -- might not always be ok
+ end
+ return link(nil,nil,inner,var.r,actions)
+end
+
+runners["inner with arguments"] = function(var,actions)
+ report_reference("todo: inner with arguments")
+ return false
+end
+
+runners["outer"] = function(var,actions)
+ local file, url = references.checkedfileorurl(var.outer,var.outer)
+ return link(url,file,var.arguments,nil,actions)
+end
+
+runners["outer with inner"] = function(var,actions)
+ local file = references.checkedfile(var.outer) -- was var.f but fails ... why
+ return link(nil,file,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_reference("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_reference("todo: outer with inner with arguments")
+ return false
+end
+
+runners["outer with special and operation and arguments"] = function(var,actions)
+ report_reference("todo: outer with special and operation and arguments")
+ return false
+end
+
+runners["outer with special"] = function(var,actions)
+ report_reference("todo: outer with special")
+ return false
+end
+
+runners["outer with special and operation"] = function(var,actions)
+ report_reference("todo: outer with special and operation")
+ return false
+end
+
+runners["special operation"] = runners["special"]
+runners["special operation with arguments"] = runners["special"]
+
+function specials.internal(var,actions) -- better resolve in strc-ref
+ local i = tonumber(var.operation)
+ local v = i and references.internals[i]
+ if not v then
+ -- error
+ report_reference("no internal reference %a",i)
+ elseif getinnermethod() == "names" then
+ -- named
+ return link(nil,nil,"aut:"..i,v.references.realpage,actions)
+ else
+ -- page
+ return link(nil,nil,nil,v.references.realpage,actions)
+ 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
+ file = references.checkedfile(file)
+ return link(nil,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
+ -- if p then
+ -- var.r = p
+ -- end
+ end
+ return link(nil,nil,nil,p or var.operation,actions)
+ end
+end
+
+function specials.realpage(var,actions)
+ local file = var.f
+ if file then
+ file = references.checkedfile(file)
+ return link(nil,file,nil,var.operation,actions)
+ else
+ return link(nil,nil,nil,var.operation,actions)
+ end
+end
+
+function specials.userpage(var,actions)
+ local file = var.f
+ if file then
+ file = references.checkedfile(file)
+ return link(nil,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 link(nil,nil,nil,p or var.operation,actions)
+ end
+end
+
+function specials.deltapage(var,actions)
+ local p = tonumber(var.operation)
+ if p then
+ p = references.checkedrealpage(p + texcount.realpageno)
+ return link(nil,nil,nil,p,actions)
+ end
+end
+
+-- sections
+
+-- function specials.section(var,actions)
+-- local sectionname = var.operation
+-- local destination = var.arguments
+-- local internal = structures.sections.internalreference(sectionname,destination)
+-- if internal then
+-- var.special = "internal"
+-- var.operation = internal
+-- var.arguments = nil
+-- specials.internal(var,actions)
+-- end
+-- end
+
+specials.section = specials.internal -- specials.section just need to have a value as it's checked
+
+-- 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)
+ local url = references.checkedurl(var.operation)
+ return link(url,nil,var.arguments,nil,actions)
+end
+
+function specials.file(var,actions)
+ local file = references.checkedfile(var.operation)
+ return link(nil,file,var.arguments,nil,actions)
+end
+
+function specials.fileorurl(var,actions)
+ local file, url = references.checkedfileorurl(var.operation,var.operation)
+ return link(url,file,var.arguments,nil,actions)
+end
+
+function specials.program(var,content)
+ local program = references.checkedprogram(var.operation)
+ return lpdf.launch(program,var.arguments)
+end
+
+function specials.javascript(var)
+ return lpdf.javascript(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
+
+--~ entry.A = pdfdictionary {
+--~ S = pdf_goto,
+--~ D = ....
+--~ }
+
+local function build(levels,start,parent,method)
+ local startlevel = levels[start][1]
+ local i, n = start, 0
+ local child, entry, m, prev, first, last, f, l
+ while i and i <= #levels do
+ local li = levels[i]
+ local level, title, reference, open = li[1], li[2], li[3], li[4]
+ if level < startlevel then
+ pdfflushobject(child,entry)
+ return i, n, first, last
+ elseif level == startlevel then
+ if trace_bookmarks then
+ report_bookmark("%3i %w%s %s",reference.realpage,(level-1)*2,(open and "+") or "-",title)
+ end
+ local prev = child
+ child = pdfreserveobject()
+ if entry then
+ entry.Next = child and pdfreference(child)
+ pdfflushobject(prev,entry)
+ end
+ entry = pdfdictionary {
+ Title = pdfunicode(title),
+ Parent = parent,
+ Prev = prev and pdfreference(prev),
+ }
+ if method == "internal" then
+ entry.Dest = "aut:" .. reference.internal
+ else -- if method == "page" then
+ entry.Dest = pagedestination(reference.realpage)
+ end
+ if not first then first, last = child, child end
+ prev = child
+ last = prev
+ n = n + 1
+ i = i + 1
+ elseif i < #levels and level > startlevel then
+ i, m, f, l = build(levels,i,pdfreference(child),method)
+ entry.Count = (open and m) or -m
+ if m > 0 then
+ entry.First, entry.Last = pdfreference(f), pdfreference(l)
+ end
+ else
+ -- missing intermediate level but ok
+ i, m, f, l = build(levels,i,pdfreference(child),method)
+ entry.Count = (open and m) or -m
+ if m > 0 then
+ entry.First, entry.Last = pdfreference(f), pdfreference(l)
+ end
+ pdfflushobject(child,entry)
+ return i, n, first, last
+ end
+ end
+ pdfflushobject(child,entry)
+ return nil, n, first, last
+end
+
+function codeinjections.addbookmarks(levels,method)
+ if #levels > 0 then
+ structures.bookmarks.flatten(levels) -- dirty trick for lack of structure
+ local parent = pdfreserveobject()
+ local _, m, first, last = build(levels,1,pdfreference(parent),method or "internal")
+ local dict = pdfdictionary {
+ Type = pdfconstant("Outlines"),
+ First = pdfreference(first),
+ Last = pdfreference(last),
+ Count = m,
+ }
+ pdfflushobject(parent,dict)
+ lpdf.addtocatalog("Outlines",lpdf.reference(parent))
+ end
+end
+
+-- this could also be hooked into the frontend finalizer
+
+lpdf.registerdocumentfinalizer(function() bookmarks.place() end,1,"bookmarks")
diff --git a/tex/context/base/lpdf-enc.lua b/tex/context/base/lpdf-enc.lua
index 6dd286191..090fb15cd 100644
--- a/tex/context/base/lpdf-enc.lua
+++ b/tex/context/base/lpdf-enc.lua
@@ -1,157 +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"),
- },
-}
+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/tex/context/base/lpdf-epa.lua b/tex/context/base/lpdf-epa.lua
index 8d00c8c26..034e6d7e2 100644
--- a/tex/context/base/lpdf-epa.lua
+++ b/tex/context/base/lpdf-epa.lua
@@ -1,226 +1,226 @@
-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"
-}
-
--- This is a rather experimental feature and the code will probably
--- change.
-
-local type, tonumber = type, tonumber
-local format, gsub = string.format, string.gsub
-local formatters = string.formatters
-
------ lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
-
-local trace_links = false trackers.register("figures.links", function(v) trace_links = v end)
-
-local report_link = logs.reporter("backend","merging")
-
-local backends, lpdf = backends, lpdf
-
-local variables = interfaces.variables
-local codeinjections = backends.pdf.codeinjections
------ urlescaper = lpegpatterns.urlescaper
------ utftohigh = lpegpatterns.utftohigh
-local escapetex = characters.filters.utf.private.escape
-
-local layerspec = { -- predefining saves time
- "epdflinks"
-}
-
-local function makenamespace(filename)
- return format("lpdf-epa-%s-",file.removesuffix(file.basename(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
- 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
-
-local function link_file(x,y,w,h,document,annotation)
- local a = annotation.A
- if a then
- local filename = a.F
- 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
- destination = destination[1] -- array
- if tonumber(destination) then
- add_link(x,y,w,h,formatters["%s::page(%s)"](filename,destination),"file (page)")
- else
- add_link(x,y,w,h,formatters["file(%s)"](filename),"file")
- end
- end
- end
- end
-end
-
-function codeinjections.mergereferences(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 document = lpdf.epdf.load(fullname)
- if document then
- local pagenumber = specification.page or 1
- local xscale = specification.yscale or 1
- local yscale = specification.yscale or 1
- local size = specification.size or "crop" -- todo
- local pagedata = document.pages[pagenumber]
- local annotations = pagedata and pagedata.Annots
- if annotations and annotations.n > 0 then
- local namespace = format("lpdf-epa-%s-",file.removesuffix(file.basename(fullname)))
- local reference = namespace .. pagenumber
- local mediabox = pagedata.MediaBox
- local llx, lly, urx, ury = mediabox[1], mediabox[2], mediabox[3], mediabox[4]
- local width, height = xscale * (urx - llx), yscale * (ury - lly) -- \\overlaywidth, \\overlayheight
- context.definelayer( { "epdflinks" }, { height = height.."bp" , width = width.."bp" })
- for i=1,annotations.n do
- local annotation = annotations[i]
- if annotation then
- local subtype = annotation.Subtype
- local rectangle = annotation.Rect
- local a_llx, a_lly, a_urx, a_ury = rectangle[1], rectangle[2], rectangle[3], rectangle[4]
- local x, y = xscale * (a_llx - llx), yscale * (a_lly - lly)
- local w, h = xscale * (a_urx - a_llx), yscale * (a_ury - a_lly)
- if subtype == "Link" then
- local a = annotation.A
- if a 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
- else
- report_link("mising link annotation")
- end
- elseif trace_links then
- report_link("unsupported annotation %a",subtype)
- end
- elseif trace_links then
- report_link("broken annotation, index %a",i)
- end
- end
- context.flushlayer { "epdflinks" }
- -- context("\\gdef\\figurereference{%s}",reference) -- global
- context.setgvalue("figurereference",reference) -- global
- if trace_links then
- report_link("setting figure reference to %a",reference)
- end
- specification.reference = reference
- return namespace
- end
- end
- end
- return ""-- no namespace, empty, not nil
-end
-
-function codeinjections.mergeviewerlayers(specification)
- -- todo: parse included page for layers
- if true then
- return
- end
- if not specification then
- specification = figures and figures.current()
- specification = specification and specification.status
- end
- if specification then
- local fullname = specification.fullname
- local document = lpdf.epdf.load(fullname)
- if document then
- local namespace = format("lpdf:epa:%s:",file.removesuffix(file.basename(fullname)))
- local layers = document.layers
- if layers then
- for i=1,layers.n 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
- end
-end
-
+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"
+}
+
+-- This is a rather experimental feature and the code will probably
+-- change.
+
+local type, tonumber = type, tonumber
+local format, gsub = string.format, string.gsub
+local formatters = string.formatters
+
+----- lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+
+local trace_links = false trackers.register("figures.links", function(v) trace_links = v end)
+
+local report_link = logs.reporter("backend","merging")
+
+local backends, lpdf = backends, lpdf
+
+local variables = interfaces.variables
+local codeinjections = backends.pdf.codeinjections
+----- urlescaper = lpegpatterns.urlescaper
+----- utftohigh = lpegpatterns.utftohigh
+local escapetex = characters.filters.utf.private.escape
+
+local layerspec = { -- predefining saves time
+ "epdflinks"
+}
+
+local function makenamespace(filename)
+ return format("lpdf-epa-%s-",file.removesuffix(file.basename(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
+ 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
+
+local function link_file(x,y,w,h,document,annotation)
+ local a = annotation.A
+ if a then
+ local filename = a.F
+ 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
+ destination = destination[1] -- array
+ if tonumber(destination) then
+ add_link(x,y,w,h,formatters["%s::page(%s)"](filename,destination),"file (page)")
+ else
+ add_link(x,y,w,h,formatters["file(%s)"](filename),"file")
+ end
+ end
+ end
+ end
+end
+
+function codeinjections.mergereferences(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 document = lpdf.epdf.load(fullname)
+ if document then
+ local pagenumber = specification.page or 1
+ local xscale = specification.yscale or 1
+ local yscale = specification.yscale or 1
+ local size = specification.size or "crop" -- todo
+ local pagedata = document.pages[pagenumber]
+ local annotations = pagedata and pagedata.Annots
+ if annotations and annotations.n > 0 then
+ local namespace = format("lpdf-epa-%s-",file.removesuffix(file.basename(fullname)))
+ local reference = namespace .. pagenumber
+ local mediabox = pagedata.MediaBox
+ local llx, lly, urx, ury = mediabox[1], mediabox[2], mediabox[3], mediabox[4]
+ local width, height = xscale * (urx - llx), yscale * (ury - lly) -- \\overlaywidth, \\overlayheight
+ context.definelayer( { "epdflinks" }, { height = height.."bp" , width = width.."bp" })
+ for i=1,annotations.n do
+ local annotation = annotations[i]
+ if annotation then
+ local subtype = annotation.Subtype
+ local rectangle = annotation.Rect
+ local a_llx, a_lly, a_urx, a_ury = rectangle[1], rectangle[2], rectangle[3], rectangle[4]
+ local x, y = xscale * (a_llx - llx), yscale * (a_lly - lly)
+ local w, h = xscale * (a_urx - a_llx), yscale * (a_ury - a_lly)
+ if subtype == "Link" then
+ local a = annotation.A
+ if a 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
+ else
+ report_link("mising link annotation")
+ end
+ elseif trace_links then
+ report_link("unsupported annotation %a",subtype)
+ end
+ elseif trace_links then
+ report_link("broken annotation, index %a",i)
+ end
+ end
+ context.flushlayer { "epdflinks" }
+ -- context("\\gdef\\figurereference{%s}",reference) -- global
+ context.setgvalue("figurereference",reference) -- global
+ if trace_links then
+ report_link("setting figure reference to %a",reference)
+ end
+ specification.reference = reference
+ return namespace
+ end
+ end
+ end
+ return ""-- no namespace, empty, not nil
+end
+
+function codeinjections.mergeviewerlayers(specification)
+ -- todo: parse included page for layers
+ if true then
+ return
+ end
+ if not specification then
+ specification = figures and figures.current()
+ specification = specification and specification.status
+ end
+ if specification then
+ local fullname = specification.fullname
+ local document = lpdf.epdf.load(fullname)
+ if document then
+ local namespace = format("lpdf:epa:%s:",file.removesuffix(file.basename(fullname)))
+ local layers = document.layers
+ if layers then
+ for i=1,layers.n 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
+ end
+end
+
diff --git a/tex/context/base/lpdf-epd.lua b/tex/context/base/lpdf-epd.lua
index 4bf98edcc..b9f8cfc7c 100644
--- a/tex/context/base/lpdf-epd.lua
+++ b/tex/context/base/lpdf-epd.lua
@@ -1,351 +1,351 @@
-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"
-}
-
--- This is an experimental layer around the epdf library. The reason for
--- this layer is that I want to be independent of the library (which
--- implements a selection of what a file provides) and also because I
--- want an interface closer to Lua's table model while the API stays
--- close to the original xpdf library. Of course, after prototyping a
--- solution, we can optimize it using the low level epdf accessors.
-
--- It will be handy when we have a __length and __next that can trigger
--- the resolve till then we will provide .n as #.
-
--- As there can be references to the parent we cannot expand a tree. I
--- played with some expansion variants but it does to pay off.
-
--- Maybe we need a close().
--- We cannot access all destinations in one run.
-
-local setmetatable, rawset, rawget, tostring, tonumber = setmetatable, rawset, rawget, tostring, tonumber
-local lower, match, char, find, sub = string.lower, string.match, string.char, string.find, string.sub
-local concat = table.concat
-local toutf = string.toutf
-
-local report_epdf = logs.reporter("epdf")
-
--- a bit of protection
-
-local limited = false
-
-directives.register("system.inputmode", function(v)
- if not limited then
- local i_limiter = io.i_limiter(v)
- if i_limiter then
- epdf.open = i_limiter.protect(epdf.open)
- limited = true
- end
- end
-end)
-
---
-
-function epdf.type(o)
- local t = lower(match(tostring(o),"[^ :]+"))
- return t or "?"
-end
-
-lpdf = lpdf or { }
-local lpdf = lpdf
-
-lpdf.epdf = { }
-
-local checked_access
-
-local function prepare(document,d,t,n,k)
- for i=1,n do
- local v = d:getVal(i)
- local r = d:getValNF(i)
- if r:getTypeName() == "ref" then
- r = r:getRef().num
- local c = document.cache[r]
- if c then
- --
- else
- c = checked_access[v:getTypeName()](v,document,r)
- if c then
- document.cache[r] = c
- document.xrefs[c] = r
- end
- end
- t[d:getKey(i)] = c
- else
- t[d:getKey(i)] = checked_access[v:getTypeName()](v,document)
- end
- end
- getmetatable(t).__index = nil
- return t[k]
-end
-
-local function some_dictionary(d,document,r)
- local n = d and d:getLength() or 0
- if n > 0 then
- local t = { }
- setmetatable(t, { __index = function(t,k) return prepare(document,d,t,n,k) end } )
- return t
- end
-end
-
-local done = { }
-
-local function prepare(document,a,t,n,k)
- for i=1,n do
- local v = a:get(i)
- local r = a:getNF(i)
- if v:getTypeName() == "null" then
- -- TH: weird, but appears possible
- elseif r:getTypeName() == "ref" then
- r = r:getRef().num
- local c = document.cache[r]
- if c then
- --
- else
- c = checked_access[v:getTypeName()](v,document,r)
- document.cache[r] = c
- document.xrefs[c] = r
- end
- t[i] = c
- else
- t[i] = checked_access[v:getTypeName()](v,document)
- end
- end
- getmetatable(t).__index = nil
- return t[k]
-end
-
-local function some_array(a,document,r)
- local n = a and a:getLength() or 0
- if n > 0 then
- local t = { n = n }
- setmetatable(t, { __index = function(t,k) return prepare(document,a,t,n,k) end } )
- return t
- end
-end
-
-local function streamaccess(s,_,what)
- if not what or what == "all" or what == "*all" then
- local t, n = { }, 0
- s:streamReset()
- while true do
- local c = s:streamGetChar()
- if c < 0 then
- break
- else
- n = n + 1
- t[n] = char(c)
- end
- end
- return concat(t)
- end
-end
-
-local function some_stream(d,document,r)
- if d then
- d:streamReset()
- local s = some_dictionary(d:streamGetDict(),document,r)
- getmetatable(s).__call = function(...) return streamaccess(d,...) end
- return s
- end
-end
-
--- we need epdf.getBool
-
-checked_access = {
- dictionary = function(d,document,r)
- return some_dictionary(d:getDict(),document,r)
- end,
- array = function(a,document,r)
- return some_array(a:getArray(),document,r)
- end,
- stream = function(v,document,r)
- return some_stream(v,document,r)
- end,
- real = function(v)
- return v:getReal()
- end,
- integer = function(v)
- return v:getNum()
- end,
- string = function(v)
- return toutf(v:getString())
- end,
- boolean = function(v)
- return v:getBool()
- end,
- name = function(v)
- return v:getName()
- end,
- ref = function(v)
- return v:getRef()
- end,
- null = function()
- return nil
- end,
-}
-
--- checked_access.real = epdf.real
--- checked_access.integer = epdf.integer
--- checked_access.string = epdf.string
--- checked_access.boolean = epdf.boolean
--- checked_access.name = epdf.name
--- checked_access.ref = epdf.ref
-
-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.n,2 do
- target[Names[i]] = Names[i+1]
- end
- else
- local Kids = n.Kids
- if Kids then
- for i=1,Kids.n 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.n do
- target = getkids(document,Kids[i],target)
- end
- elseif target then
- target[#target+1] = n
- else
- target = { n }
- end
- return target
- end
-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 [ ]
--- >>
--- >>
-
-local function getlayers(document)
- local properties = document.Catalog.OCProperties
- if properties then
- local layers = properties.OCGs
- if layers then
- local t = { }
- local n = layers.n
- for i=1,n do
- local layer = layers[i]
---~ print(document.xrefs[layer])
- t[i] = layer.Name
- end
- t.n = n
- return t
- end
- end
-end
-
-local function getpages(document)
- local data = document.data
- local xrefs = document.xrefs
- local cache = document.cache
- local cata = data:getCatalog()
- local xref = data:getXRef()
- local pages = { }
- local nofpages = cata:getNumPages()
- for pagenumber=1,nofpages do
- local pagereference = cata:getPageRef(pagenumber).num
- local pagedata = some_dictionary(xref:fetch(pagereference,0):getDict(),document,pagereference)
- if pagedata then
- pagedata.number = pagenumber
- pages[pagenumber] = pagedata
- xrefs[pagedata] = pagereference
- cache[pagereference] = pagedata
- else
- report_epdf("missing pagedata at slot %i",i)
- end
- end
- pages.n = nofpages
- return pages
-end
-
--- loader
-
-local function delayed(document,tag,f)
- local t = { }
- setmetatable(t, { __index = function(t,k)
- local result = f()
- if result then
- document[tag] = result
- return result[k]
- end
- end } )
- return t
-end
-
-local loaded = { }
-
-function lpdf.epdf.load(filename)
- local document = loaded[filename]
- if not document then
- statistics.starttiming(lpdf.epdf)
- local data = epdf.open(filename) -- maybe resolvers.find_file
- if data then
- document = {
- filename = filename,
- cache = { },
- xrefs = { },
- data = data,
- }
- local Catalog = some_dictionary(data:getXRef():getCatalog():getDict(),document)
- local Info = some_dictionary(data:getXRef():getDocInfo():getDict(),document)
- document.Catalog = Catalog
- document.Info = Info
- -- document.catalog = Catalog
- -- a few handy helper tables
- document.pages = delayed(document,"pages", function() return getpages(document) end)
- document.destinations = delayed(document,"destinations", function() return getnames(document,Catalog.Names and Catalog.Names.Dests) end)
- document.javascripts = delayed(document,"javascripts", function() return getnames(document,Catalog.Names and Catalog.Names.JS) end)
- document.widgets = delayed(document,"widgets", function() return getnames(document,Catalog.Names and Catalog.Names.AcroForm) end)
- document.embeddedfiles = delayed(document,"embeddedfiles",function() return getnames(document,Catalog.Names and Catalog.Names.EmbeddedFiles) end)
- document.layers = delayed(document,"layers", function() return getlayers(document) end)
- else
- document = false
- end
- loaded[filename] = document
- statistics.stoptiming(lpdf.epdf)
- -- print(statistics.elapsedtime(lpdf.epdf))
- end
- return document
-end
-
--- for k, v in next, expand(t) do
-
-function lpdf.epdf.expand(t)
- if type(t) == "table" then
- local dummy = t.dummy
- end
- return t
-end
-
--- helpers
-
--- function lpdf.epdf.getdestinationpage(document,name)
--- local destination = document.data:findDest(name)
--- return destination and destination.number
--- end
+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"
+}
+
+-- This is an experimental layer around the epdf library. The reason for
+-- this layer is that I want to be independent of the library (which
+-- implements a selection of what a file provides) and also because I
+-- want an interface closer to Lua's table model while the API stays
+-- close to the original xpdf library. Of course, after prototyping a
+-- solution, we can optimize it using the low level epdf accessors.
+
+-- It will be handy when we have a __length and __next that can trigger
+-- the resolve till then we will provide .n as #.
+
+-- As there can be references to the parent we cannot expand a tree. I
+-- played with some expansion variants but it does to pay off.
+
+-- Maybe we need a close().
+-- We cannot access all destinations in one run.
+
+local setmetatable, rawset, rawget, tostring, tonumber = setmetatable, rawset, rawget, tostring, tonumber
+local lower, match, char, find, sub = string.lower, string.match, string.char, string.find, string.sub
+local concat = table.concat
+local toutf = string.toutf
+
+local report_epdf = logs.reporter("epdf")
+
+-- a bit of protection
+
+local limited = false
+
+directives.register("system.inputmode", function(v)
+ if not limited then
+ local i_limiter = io.i_limiter(v)
+ if i_limiter then
+ epdf.open = i_limiter.protect(epdf.open)
+ limited = true
+ end
+ end
+end)
+
+--
+
+function epdf.type(o)
+ local t = lower(match(tostring(o),"[^ :]+"))
+ return t or "?"
+end
+
+lpdf = lpdf or { }
+local lpdf = lpdf
+
+lpdf.epdf = { }
+
+local checked_access
+
+local function prepare(document,d,t,n,k)
+ for i=1,n do
+ local v = d:getVal(i)
+ local r = d:getValNF(i)
+ if r:getTypeName() == "ref" then
+ r = r:getRef().num
+ local c = document.cache[r]
+ if c then
+ --
+ else
+ c = checked_access[v:getTypeName()](v,document,r)
+ if c then
+ document.cache[r] = c
+ document.xrefs[c] = r
+ end
+ end
+ t[d:getKey(i)] = c
+ else
+ t[d:getKey(i)] = checked_access[v:getTypeName()](v,document)
+ end
+ end
+ getmetatable(t).__index = nil
+ return t[k]
+end
+
+local function some_dictionary(d,document,r)
+ local n = d and d:getLength() or 0
+ if n > 0 then
+ local t = { }
+ setmetatable(t, { __index = function(t,k) return prepare(document,d,t,n,k) end } )
+ return t
+ end
+end
+
+local done = { }
+
+local function prepare(document,a,t,n,k)
+ for i=1,n do
+ local v = a:get(i)
+ local r = a:getNF(i)
+ if v:getTypeName() == "null" then
+ -- TH: weird, but appears possible
+ elseif r:getTypeName() == "ref" then
+ r = r:getRef().num
+ local c = document.cache[r]
+ if c then
+ --
+ else
+ c = checked_access[v:getTypeName()](v,document,r)
+ document.cache[r] = c
+ document.xrefs[c] = r
+ end
+ t[i] = c
+ else
+ t[i] = checked_access[v:getTypeName()](v,document)
+ end
+ end
+ getmetatable(t).__index = nil
+ return t[k]
+end
+
+local function some_array(a,document,r)
+ local n = a and a:getLength() or 0
+ if n > 0 then
+ local t = { n = n }
+ setmetatable(t, { __index = function(t,k) return prepare(document,a,t,n,k) end } )
+ return t
+ end
+end
+
+local function streamaccess(s,_,what)
+ if not what or what == "all" or what == "*all" then
+ local t, n = { }, 0
+ s:streamReset()
+ while true do
+ local c = s:streamGetChar()
+ if c < 0 then
+ break
+ else
+ n = n + 1
+ t[n] = char(c)
+ end
+ end
+ return concat(t)
+ end
+end
+
+local function some_stream(d,document,r)
+ if d then
+ d:streamReset()
+ local s = some_dictionary(d:streamGetDict(),document,r)
+ getmetatable(s).__call = function(...) return streamaccess(d,...) end
+ return s
+ end
+end
+
+-- we need epdf.getBool
+
+checked_access = {
+ dictionary = function(d,document,r)
+ return some_dictionary(d:getDict(),document,r)
+ end,
+ array = function(a,document,r)
+ return some_array(a:getArray(),document,r)
+ end,
+ stream = function(v,document,r)
+ return some_stream(v,document,r)
+ end,
+ real = function(v)
+ return v:getReal()
+ end,
+ integer = function(v)
+ return v:getNum()
+ end,
+ string = function(v)
+ return toutf(v:getString())
+ end,
+ boolean = function(v)
+ return v:getBool()
+ end,
+ name = function(v)
+ return v:getName()
+ end,
+ ref = function(v)
+ return v:getRef()
+ end,
+ null = function()
+ return nil
+ end,
+}
+
+-- checked_access.real = epdf.real
+-- checked_access.integer = epdf.integer
+-- checked_access.string = epdf.string
+-- checked_access.boolean = epdf.boolean
+-- checked_access.name = epdf.name
+-- checked_access.ref = epdf.ref
+
+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.n,2 do
+ target[Names[i]] = Names[i+1]
+ end
+ else
+ local Kids = n.Kids
+ if Kids then
+ for i=1,Kids.n 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.n do
+ target = getkids(document,Kids[i],target)
+ end
+ elseif target then
+ target[#target+1] = n
+ else
+ target = { n }
+ end
+ return target
+ end
+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 [ ]
+-- >>
+-- >>
+
+local function getlayers(document)
+ local properties = document.Catalog.OCProperties
+ if properties then
+ local layers = properties.OCGs
+ if layers then
+ local t = { }
+ local n = layers.n
+ for i=1,n do
+ local layer = layers[i]
+--~ print(document.xrefs[layer])
+ t[i] = layer.Name
+ end
+ t.n = n
+ return t
+ end
+ end
+end
+
+local function getpages(document)
+ local data = document.data
+ local xrefs = document.xrefs
+ local cache = document.cache
+ local cata = data:getCatalog()
+ local xref = data:getXRef()
+ local pages = { }
+ local nofpages = cata:getNumPages()
+ for pagenumber=1,nofpages do
+ local pagereference = cata:getPageRef(pagenumber).num
+ local pagedata = some_dictionary(xref:fetch(pagereference,0):getDict(),document,pagereference)
+ if pagedata then
+ pagedata.number = pagenumber
+ pages[pagenumber] = pagedata
+ xrefs[pagedata] = pagereference
+ cache[pagereference] = pagedata
+ else
+ report_epdf("missing pagedata at slot %i",i)
+ end
+ end
+ pages.n = nofpages
+ return pages
+end
+
+-- loader
+
+local function delayed(document,tag,f)
+ local t = { }
+ setmetatable(t, { __index = function(t,k)
+ local result = f()
+ if result then
+ document[tag] = result
+ return result[k]
+ end
+ end } )
+ return t
+end
+
+local loaded = { }
+
+function lpdf.epdf.load(filename)
+ local document = loaded[filename]
+ if not document then
+ statistics.starttiming(lpdf.epdf)
+ local data = epdf.open(filename) -- maybe resolvers.find_file
+ if data then
+ document = {
+ filename = filename,
+ cache = { },
+ xrefs = { },
+ data = data,
+ }
+ local Catalog = some_dictionary(data:getXRef():getCatalog():getDict(),document)
+ local Info = some_dictionary(data:getXRef():getDocInfo():getDict(),document)
+ document.Catalog = Catalog
+ document.Info = Info
+ -- document.catalog = Catalog
+ -- a few handy helper tables
+ document.pages = delayed(document,"pages", function() return getpages(document) end)
+ document.destinations = delayed(document,"destinations", function() return getnames(document,Catalog.Names and Catalog.Names.Dests) end)
+ document.javascripts = delayed(document,"javascripts", function() return getnames(document,Catalog.Names and Catalog.Names.JS) end)
+ document.widgets = delayed(document,"widgets", function() return getnames(document,Catalog.Names and Catalog.Names.AcroForm) end)
+ document.embeddedfiles = delayed(document,"embeddedfiles",function() return getnames(document,Catalog.Names and Catalog.Names.EmbeddedFiles) end)
+ document.layers = delayed(document,"layers", function() return getlayers(document) end)
+ else
+ document = false
+ end
+ loaded[filename] = document
+ statistics.stoptiming(lpdf.epdf)
+ -- print(statistics.elapsedtime(lpdf.epdf))
+ end
+ return document
+end
+
+-- for k, v in next, expand(t) do
+
+function lpdf.epdf.expand(t)
+ if type(t) == "table" then
+ local dummy = t.dummy
+ end
+ return t
+end
+
+-- helpers
+
+-- function lpdf.epdf.getdestinationpage(document,name)
+-- local destination = document.data:findDest(name)
+-- return destination and destination.number
+-- end
diff --git a/tex/context/base/lpdf-fld.lua b/tex/context/base/lpdf-fld.lua
index 0a15bb850..a9b9fd72d 100644
--- a/tex/context/base/lpdf-fld.lua
+++ b/tex/context/base/lpdf-fld.lua
@@ -1,1305 +1,1305 @@
-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"
-}
-
--- 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 gmatch, lower, format = string.gmatch, string.lower, string.format
-local lpegmatch = lpeg.match
-local utfchar = utf.char
-local bpfactor, todimen = number.dimenfactors.bp, string.todimen
-
-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 pdftoeight = lpdf.toeight
-local pdfflushobject = lpdf.flushobject
-local pdfshareobjectreference = lpdf.shareobjectreference
-local pdfshareobject = lpdf.shareobject
-local pdfreserveobject = lpdf.reserveobject
-local pdfreserveannotation = lpdf.reserveannotation
-local pdfaction = lpdf.action
-
-local hpack_node = node.hpack
-
-local nodepool = nodes.pool
-
-local pdfannotation_node = nodepool.pdfannotation
-
-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_ch = pdfconstant("Ch")
-local pdf_btn = pdfconstant("Btn")
------ 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 = 1, -- 1
- Required = 2, -- 2
- NoExport = 4, -- 3
- MultiLine = 4096, -- 13
- Password = 8192, -- 14
- NoToggleToOff = 16384, -- 15
- Radio = 32768, -- 16
- PushButton = 65536, -- 17
- PopUp = 131072, -- 18
- Edit = 262144, -- 19
- Sort = 524288, -- 20
- FileSelect = 1048576, -- 21
- DoNotSpellCheck = 4194304, -- 23
- DoNotScroll = 8388608, -- 24
- Comb = 16777216, -- 25
- RichText = 33554432, -- 26
- RadiosInUnison = 33554432, -- 26
- CommitOnSelChange = 67108864, -- 27
-}
-
-local plus = { -- /F
- Invisible = 1, -- 1
- Hidden = 2, -- 2
- Printable = 4, -- 3
- Print = 4, -- 3
- NoZoom = 8, -- 4
- NoRotate = 16, -- 5
- NoView = 32, -- 6
- ReadOnly = 64, -- 7
- Locked = 128, -- 8
- ToggleNoView = 256, -- 9
- LockedContents = 512, -- 10,
- AutoView = 256, -- 288 (6+9)
-}
-
--- 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
-
--- 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
-
-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
-
--- 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.lua")) -- 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 = 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 = format("%0.4f Tf %0.4f Ts",fontsize,fontraise)
- -- we could test for colorvalue being 1 (black) and omit it then
- local colorcode = lpdf.color(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),
- format("%s %s",fontcode,colorcode)
- }
- usedfonts[tag] = a -- the name
- -- move up with "x.y Ts"
- return tostring(stream)
-end
-
-local function registerfonts()
- if next(usedfonts) then
- checkpdfdocencoding() -- already done
- local d = pdfdictionary()
- local pdffonttype, pdffontsubtype = pdfconstant("Font"), pdfconstant("Type1")
- for tag, name in next, usedfonts do
- local f = pdfdictionary {
- Type = pdffonttype,
- Subtype = pdffontsubtype,
- Name = pdfconstant(tag),
- BaseFont = pdfconstant(name),
- Encoding = pdfdocencodingvector,
- }
- d[tag] = pdfreference(pdfflushobject(f))
- end
- return d
- 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
-
-local YesorOn = "Yes" -- somehow On is not always working out well any longer (why o why this change)
-
--- beware ... maybe we should have unique /Yes1 ... we will probably
--- change this one too.
---
--- TODO: the same as radio .. play safe and use different names.
-
-local function fieldstates_check(specification,forceyes,values,default,yesdefault)
- -- we don't use Opt here (too messy for radio buttons)
- local values, default = values or specification.values, default or 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 forceyes == true then
- forceyes = YesorOn -- spec likes Yes more but we've used On for ages now
- else
- -- false or string
- end
- if default == yesn then
- default = pdfconstant(forceyes or yesn)
- else
- default = pdf_off
- end
- local appearance
- if false then -- needs testing
- appearance = pdfdictionary { -- maybe also cache components
- N = pdfshareobjectreference(pdfdictionary { [forceyes or yesn] = registeredsymbol(yesn), Off = registeredsymbol(offn) }),
- R = pdfshareobjectreference(pdfdictionary { [forceyes or yesr] = registeredsymbol(yesr), Off = registeredsymbol(offr) }),
- D = pdfshareobjectreference(pdfdictionary { [forceyes or yesd] = registeredsymbol(yesd), Off = registeredsymbol(offd) }),
- }
- else
- appearance = pdfdictionary { -- maybe also cache components
- N = pdfdictionary { [forceyes or yesn] = registeredsymbol(yesn), Off = registeredsymbol(offn) },
- R = pdfdictionary { [forceyes or yesr] = registeredsymbol(yesr), Off = registeredsymbol(offr) },
- D = pdfdictionary { [forceyes or yesd] = 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)
- 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 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 zero_bc = pdfarray { 0, 0, 0 }
--- local zero_bg = pdfarray { 1, 1, 1 }
-
-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 { lpdf.colorvalues(3,bvalue) } or nil, -- or zero_bg,
- BC = fvalue and pdfarray { lpdf.colorvalues(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 lpdf.layerreference(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 table.sortedhash(fields) do
- result[#result+1] = format(" <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
-
-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, c, v = specification.parent, specification.children, 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
-
-local collected = pdfarray()
-local forceencoding = false
-
-local function finishfields()
- local sometext = forceencoding
- for name, field in next, 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 next, 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 = true,
- Fields = pdfreference(pdfflushobject(collected)),
- CO = fieldsetlist(calculationset),
- }
- if sometext then
- checkpdfdocencoding()
- usedfonts.tttf = fontnames.tt.tf
- acroform.DA = "/tttf 12 Tf 0 g"
- acroform.DR = pdfdictionary {
- Font = registerfonts(),
- Encoding = pdfdocencodingcapsule,
- }
- 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,hasopt)
- local kidsnum = pdfreserveobject()
- d.Kids = pdfreference(kidsnum)
- field.kidsnum = kidsnum
- field.kids = pdfarray()
- if hasopt 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 = pdfreserveannotation()
- 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, height, depth = specification.width or 0, specification.height or 0, specification.depth
- local box = hpack_node(pdfannotation_node(width,height,depth,d(),kn))
- box.width, box.height, box.depth = width, height, depth -- redundant
- 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, parent = clones[name], 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
- 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
-
-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, parent = clones[name], 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 d = pdfdictionary {
- T = pdfunicode(specification.title), -- todo: when tracing use a string
- F = fieldplus(specification),
- Ff = fieldflag(specification),
- OC = fieldlayer(specification),
- AA = fieldactions(specification),
- FT = pdf_btn,
- V = fielddefault(field),
- }
- save_parent(field,specification,d,true)
-end
-
-local function makecheckchild(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 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),
- 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,true)
- d.AS = default
- d.AP = appearance
- return save_kid(parent,specification,d,value)
- 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),
- 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),
- 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")
- specification = enhance(specification,"Radio,RadiosInUnison,Print,NoToggleToOff")
--- specification = enhance(specification,"Radio,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,true)
-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="star"
--- 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
--- local appearance, default, value = fieldstates_radio(field,true,false,false,name) -- false is also ok
--- specification.fontsymbol = todingbat(fontsymbol)
--- specification.fontstyle = "symbol"
--- specification.fontalternative = "dingbats"
--- d.DA = fieldsurrounding(specification)
--- d.MK = fieldrendering(specification)
--- d.AS = pdfconstant(value) -- default -- mandate when AP but confuses viewers
--- d.AP = appearance
--- return save_kid(parent,specification,d,value)
--- -- return save_kid(parent,specification,d,name)
--- else
--- -- local appearance, default, value = fieldstates_radio(field,true) -- false is also ok
--- local appearance, default, value = fieldstates_radio(field,true,false,false,name) -- false is also ok
--- d.AS = default -- mandate when AP but confuses viewers
--- d.AP = appearance
--- 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
- return save_kid(parent,specification,d,value)
-end
-
-function methods.sub(name,specification)
- return makeradiochild(name,enhance(specification,"Radio,RadiosInUnison"))
-end
+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"
+}
+
+-- 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 gmatch, lower, format = string.gmatch, string.lower, string.format
+local lpegmatch = lpeg.match
+local utfchar = utf.char
+local bpfactor, todimen = number.dimenfactors.bp, string.todimen
+
+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 pdftoeight = lpdf.toeight
+local pdfflushobject = lpdf.flushobject
+local pdfshareobjectreference = lpdf.shareobjectreference
+local pdfshareobject = lpdf.shareobject
+local pdfreserveobject = lpdf.reserveobject
+local pdfreserveannotation = lpdf.reserveannotation
+local pdfaction = lpdf.action
+
+local hpack_node = node.hpack
+
+local nodepool = nodes.pool
+
+local pdfannotation_node = nodepool.pdfannotation
+
+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_ch = pdfconstant("Ch")
+local pdf_btn = pdfconstant("Btn")
+----- 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 = 1, -- 1
+ Required = 2, -- 2
+ NoExport = 4, -- 3
+ MultiLine = 4096, -- 13
+ Password = 8192, -- 14
+ NoToggleToOff = 16384, -- 15
+ Radio = 32768, -- 16
+ PushButton = 65536, -- 17
+ PopUp = 131072, -- 18
+ Edit = 262144, -- 19
+ Sort = 524288, -- 20
+ FileSelect = 1048576, -- 21
+ DoNotSpellCheck = 4194304, -- 23
+ DoNotScroll = 8388608, -- 24
+ Comb = 16777216, -- 25
+ RichText = 33554432, -- 26
+ RadiosInUnison = 33554432, -- 26
+ CommitOnSelChange = 67108864, -- 27
+}
+
+local plus = { -- /F
+ Invisible = 1, -- 1
+ Hidden = 2, -- 2
+ Printable = 4, -- 3
+ Print = 4, -- 3
+ NoZoom = 8, -- 4
+ NoRotate = 16, -- 5
+ NoView = 32, -- 6
+ ReadOnly = 64, -- 7
+ Locked = 128, -- 8
+ ToggleNoView = 256, -- 9
+ LockedContents = 512, -- 10,
+ AutoView = 256, -- 288 (6+9)
+}
+
+-- 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
+
+-- 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
+
+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
+
+-- 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.lua")) -- 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 = 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 = format("%0.4f Tf %0.4f Ts",fontsize,fontraise)
+ -- we could test for colorvalue being 1 (black) and omit it then
+ local colorcode = lpdf.color(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),
+ format("%s %s",fontcode,colorcode)
+ }
+ usedfonts[tag] = a -- the name
+ -- move up with "x.y Ts"
+ return tostring(stream)
+end
+
+local function registerfonts()
+ if next(usedfonts) then
+ checkpdfdocencoding() -- already done
+ local d = pdfdictionary()
+ local pdffonttype, pdffontsubtype = pdfconstant("Font"), pdfconstant("Type1")
+ for tag, name in next, usedfonts do
+ local f = pdfdictionary {
+ Type = pdffonttype,
+ Subtype = pdffontsubtype,
+ Name = pdfconstant(tag),
+ BaseFont = pdfconstant(name),
+ Encoding = pdfdocencodingvector,
+ }
+ d[tag] = pdfreference(pdfflushobject(f))
+ end
+ return d
+ 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
+
+local YesorOn = "Yes" -- somehow On is not always working out well any longer (why o why this change)
+
+-- beware ... maybe we should have unique /Yes1 ... we will probably
+-- change this one too.
+--
+-- TODO: the same as radio .. play safe and use different names.
+
+local function fieldstates_check(specification,forceyes,values,default,yesdefault)
+ -- we don't use Opt here (too messy for radio buttons)
+ local values, default = values or specification.values, default or 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 forceyes == true then
+ forceyes = YesorOn -- spec likes Yes more but we've used On for ages now
+ else
+ -- false or string
+ end
+ if default == yesn then
+ default = pdfconstant(forceyes or yesn)
+ else
+ default = pdf_off
+ end
+ local appearance
+ if false then -- needs testing
+ appearance = pdfdictionary { -- maybe also cache components
+ N = pdfshareobjectreference(pdfdictionary { [forceyes or yesn] = registeredsymbol(yesn), Off = registeredsymbol(offn) }),
+ R = pdfshareobjectreference(pdfdictionary { [forceyes or yesr] = registeredsymbol(yesr), Off = registeredsymbol(offr) }),
+ D = pdfshareobjectreference(pdfdictionary { [forceyes or yesd] = registeredsymbol(yesd), Off = registeredsymbol(offd) }),
+ }
+ else
+ appearance = pdfdictionary { -- maybe also cache components
+ N = pdfdictionary { [forceyes or yesn] = registeredsymbol(yesn), Off = registeredsymbol(offn) },
+ R = pdfdictionary { [forceyes or yesr] = registeredsymbol(yesr), Off = registeredsymbol(offr) },
+ D = pdfdictionary { [forceyes or yesd] = 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)
+ 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 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 zero_bc = pdfarray { 0, 0, 0 }
+-- local zero_bg = pdfarray { 1, 1, 1 }
+
+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 { lpdf.colorvalues(3,bvalue) } or nil, -- or zero_bg,
+ BC = fvalue and pdfarray { lpdf.colorvalues(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 lpdf.layerreference(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 table.sortedhash(fields) do
+ result[#result+1] = format(" <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
+
+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, c, v = specification.parent, specification.children, 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
+
+local collected = pdfarray()
+local forceencoding = false
+
+local function finishfields()
+ local sometext = forceencoding
+ for name, field in next, 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 next, 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 = true,
+ Fields = pdfreference(pdfflushobject(collected)),
+ CO = fieldsetlist(calculationset),
+ }
+ if sometext then
+ checkpdfdocencoding()
+ usedfonts.tttf = fontnames.tt.tf
+ acroform.DA = "/tttf 12 Tf 0 g"
+ acroform.DR = pdfdictionary {
+ Font = registerfonts(),
+ Encoding = pdfdocencodingcapsule,
+ }
+ 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,hasopt)
+ local kidsnum = pdfreserveobject()
+ d.Kids = pdfreference(kidsnum)
+ field.kidsnum = kidsnum
+ field.kids = pdfarray()
+ if hasopt 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 = pdfreserveannotation()
+ 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, height, depth = specification.width or 0, specification.height or 0, specification.depth
+ local box = hpack_node(pdfannotation_node(width,height,depth,d(),kn))
+ box.width, box.height, box.depth = width, height, depth -- redundant
+ 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, parent = clones[name], 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
+ 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
+
+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, parent = clones[name], 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 d = pdfdictionary {
+ T = pdfunicode(specification.title), -- todo: when tracing use a string
+ F = fieldplus(specification),
+ Ff = fieldflag(specification),
+ OC = fieldlayer(specification),
+ AA = fieldactions(specification),
+ FT = pdf_btn,
+ V = fielddefault(field),
+ }
+ save_parent(field,specification,d,true)
+end
+
+local function makecheckchild(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 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),
+ 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,true)
+ d.AS = default
+ d.AP = appearance
+ return save_kid(parent,specification,d,value)
+ 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),
+ 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),
+ 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")
+ specification = enhance(specification,"Radio,RadiosInUnison,Print,NoToggleToOff")
+-- specification = enhance(specification,"Radio,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,true)
+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="star"
+-- 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
+-- local appearance, default, value = fieldstates_radio(field,true,false,false,name) -- false is also ok
+-- specification.fontsymbol = todingbat(fontsymbol)
+-- specification.fontstyle = "symbol"
+-- specification.fontalternative = "dingbats"
+-- d.DA = fieldsurrounding(specification)
+-- d.MK = fieldrendering(specification)
+-- d.AS = pdfconstant(value) -- default -- mandate when AP but confuses viewers
+-- d.AP = appearance
+-- return save_kid(parent,specification,d,value)
+-- -- return save_kid(parent,specification,d,name)
+-- else
+-- -- local appearance, default, value = fieldstates_radio(field,true) -- false is also ok
+-- local appearance, default, value = fieldstates_radio(field,true,false,false,name) -- false is also ok
+-- d.AS = default -- mandate when AP but confuses viewers
+-- d.AP = appearance
+-- 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
+ return save_kid(parent,specification,d,value)
+end
+
+function methods.sub(name,specification)
+ return makeradiochild(name,enhance(specification,"Radio,RadiosInUnison"))
+end
diff --git a/tex/context/base/lpdf-grp.lua b/tex/context/base/lpdf-grp.lua
index a255658ed..fed5e6a46 100644
--- a/tex/context/base/lpdf-grp.lua
+++ b/tex/context/base/lpdf-grp.lua
@@ -1,244 +1,244 @@
-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 format, gsub = string.format, 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 inches = number.dimenfactors["in"]
-
-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 = lpdf.flushobject
-
--- 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)
- local f = pdfdictionary {
- FunctionType = 2,
- Domain = pdfarray(domain), -- domain is actually a string
- C0 = pdfarray(color_a),
- C1 = pdfarray(color_b),
- N = tonumber(n),
- }
- separation = separation and registrations.getspotcolorreference(separation)
- local s = pdfdictionary {
- ShadingType = stype,
- ColorSpace = separation and pdfreference(separation) or pdfconstant(colorspace),
- Function = pdfreference(pdfflushobject(f)),
- 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)
- shade(3,name,domain,color_a,color_b,n,colorspace,coordinates,separation)
-end
-
-function lpdf.linearshade(name,domain,color_a,color_b,n,colorspace,coordinates,separation)
- shade(2,name,domain,color_a,color_b,n,colorspace,coordinates,separation)
-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 image = img.new {
- stream = format(template,d(),t.data),
- width = width,
- height = height,
- bbox = { 0, 0, urx, ury },
- }
- return img.node(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
- img.immediatewrite(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
- img.immediatewrite(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,
- }
- figures.identify()
- local maskfigure = figures.check()
- if maskfigure then
- local image = maskfigure.status.private
- if image then
- img.immediatewrite(image)
- local d = pdfdictionary {
- Interpolate = false,
- SMask = pdfreference(image.objnum),
- }
- figure.attr = d()
- end
- end
- figures.pop()
- end
-end
-
--- temp hack
-
-local factor = number.dimenfactors.bp
-
-function img.package(image) -- see lpdf-u3d **
- local boundingbox = image.bbox
- local imagetag = "Im" .. image.index
- local resources = pdfdictionary {
- ProcSet = pdfarray {
- pdfconstant("PDF"),
- pdfconstant("ImageC")
- },
- Resources = pdfdictionary {
- XObject = pdfdictionary {
- [imagetag] = pdfreference(image.objnum)
- }
- }
- }
- local width = boundingbox[3]
- local height = boundingbox[4]
- local xform = img.scan {
- attr = resources(),
- stream = format("%f 0 0 %f 0 0 cm /%s Do",width,height,imagetag),
- bbox = { 0, 0, width/factor, height/factor },
- }
- img.immediatewrite(xform)
- return xform
-end
+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 format, gsub = string.format, 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 inches = number.dimenfactors["in"]
+
+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 = lpdf.flushobject
+
+-- 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)
+ local f = pdfdictionary {
+ FunctionType = 2,
+ Domain = pdfarray(domain), -- domain is actually a string
+ C0 = pdfarray(color_a),
+ C1 = pdfarray(color_b),
+ N = tonumber(n),
+ }
+ separation = separation and registrations.getspotcolorreference(separation)
+ local s = pdfdictionary {
+ ShadingType = stype,
+ ColorSpace = separation and pdfreference(separation) or pdfconstant(colorspace),
+ Function = pdfreference(pdfflushobject(f)),
+ 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)
+ shade(3,name,domain,color_a,color_b,n,colorspace,coordinates,separation)
+end
+
+function lpdf.linearshade(name,domain,color_a,color_b,n,colorspace,coordinates,separation)
+ shade(2,name,domain,color_a,color_b,n,colorspace,coordinates,separation)
+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 image = img.new {
+ stream = format(template,d(),t.data),
+ width = width,
+ height = height,
+ bbox = { 0, 0, urx, ury },
+ }
+ return img.node(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
+ img.immediatewrite(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
+ img.immediatewrite(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,
+ }
+ figures.identify()
+ local maskfigure = figures.check()
+ if maskfigure then
+ local image = maskfigure.status.private
+ if image then
+ img.immediatewrite(image)
+ local d = pdfdictionary {
+ Interpolate = false,
+ SMask = pdfreference(image.objnum),
+ }
+ figure.attr = d()
+ end
+ end
+ figures.pop()
+ end
+end
+
+-- temp hack
+
+local factor = number.dimenfactors.bp
+
+function img.package(image) -- see lpdf-u3d **
+ local boundingbox = image.bbox
+ local imagetag = "Im" .. image.index
+ local resources = pdfdictionary {
+ ProcSet = pdfarray {
+ pdfconstant("PDF"),
+ pdfconstant("ImageC")
+ },
+ Resources = pdfdictionary {
+ XObject = pdfdictionary {
+ [imagetag] = pdfreference(image.objnum)
+ }
+ }
+ }
+ local width = boundingbox[3]
+ local height = boundingbox[4]
+ local xform = img.scan {
+ attr = resources(),
+ stream = format("%f 0 0 %f 0 0 cm /%s Do",width,height,imagetag),
+ bbox = { 0, 0, width/factor, height/factor },
+ }
+ img.immediatewrite(xform)
+ return xform
+end
diff --git a/tex/context/base/lpdf-ini.lua b/tex/context/base/lpdf-ini.lua
index 77ccd85fc..cd601f21f 100644
--- a/tex/context/base/lpdf-ini.lua
+++ b/tex/context/base/lpdf-ini.lua
@@ -1,822 +1,822 @@
-if not modules then modules = { } end modules ['lpdf-ini'] = {
- 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 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, utfvalues = utf.char, utf.values
-local sind, cosd = math.sind, math.cosd
-local lpegmatch, P, C, R, S, Cc, Cs = lpeg.match, lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cc, lpeg.Cs
-local formatters = string.formatters
-
-local pdfreserveobject = pdf.reserveobj
-local pdfimmediateobject = pdf.immediateobj
-local pdfdeferredobject = pdf.obj
-local pdfreferenceobject = pdf.refobj
-
-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 trace_objects = false trackers.register("backend.objects", function(v) trace_objects = v end)
-local trace_detail = false trackers.register("backend.detail", function(v) trace_detail = v end)
-
-local report_objects = logs.reporter("backend","objects")
-local report_finalizing = logs.reporter("backend","finalizing")
-
-local backends = backends
-
-backends.pdf = backends.pdf or {
- comment = "backend for directly generating pdf output",
- nodeinjections = { },
- codeinjections = { },
- registrations = { },
- tables = { },
-}
-
-lpdf = lpdf or { }
-local lpdf = lpdf
-
-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",b/1024+0xD800,b%1024+0xDC00)
- end
- end
- n = n + 1
- r[n] = ">"
- return concat(r)
- end
-end
-
-lpdf.tosixteen = tosixteen
-
--- lpeg is some 5 times faster than gsub (in test) on escaping
-
--- local escapes = {
--- ["\\"] = "\\\\",
--- ["/"] = "\\/", ["#"] = "\\#",
--- ["<"] = "\\<", [">"] = "\\>",
--- ["["] = "\\[", ["]"] = "\\]",
--- ["("] = "\\(", [")"] = "\\)",
--- }
---
--- local escaped = Cs(Cc("(") * (S("\\/#<>[]()")/escapes + P(1))^0 * Cc(")"))
---
--- local function toeight(str)
--- if not str or str == "" then
--- return "()"
--- else
--- return lpegmatch(escaped,str)
--- end
--- end
---
--- -- no need for escaping .. just use unicode instead
-
--- \0 \t \n \r \f <space> ( ) [ ] { } / %
-
-local function toeight(str)
- return "(" .. str .. ")"
-end
-
-lpdf.toeight = toeight
-
---~ local escaped = lpeg.Cs((lpeg.S("\0\t\n\r\f ()[]{}/%")/function(s) return format("#%02X",byte(s)) end + lpeg.P(1))^0)
-
---~ local function cleaned(str)
---~ return (str and str ~= "" and lpegmatch(escaped,str)) or ""
---~ end
-
---~ lpdf.cleaned = cleaned -- not public yet
-
-local function merge_t(a,b)
- local t = { }
- for k,v in next, a do t[k] = v end
- for k,v in next, b do t[k] = v end
- return setmetatable(t,getmetatable(a))
-end
-
-local f_key_value = formatters["/%s %s"]
-local f_key_dictionary = formatters["/%s << % t >>"]
-local f_dictionary = formatters["<< % t >>"]
-local f_key_array = formatters["/%s [ % t ]"]
-local f_array = formatters["[ % t ]"]
-
-local tostring_a, tostring_d
-
-tostring_d = function(t,contentonly,key)
- if not next(t) then
- if contentonly then
- return ""
- else
- return "<< >>"
- end
- else
- local r, rn = { }, 0
- for k, v in next, t do
- rn = rn + 1
- local tv = type(v)
- if tv == "string" then
- r[rn] = f_key_value(k,toeight(v))
- elseif tv == "unicode" then
- r[rn] = f_key_value(k,tosixteen(v))
- elseif tv == "table" then
- local mv = getmetatable(v)
- if mv and mv.__lpdftype then
- r[rn] = f_key_value(k,tostring(v))
- elseif v[1] then
- r[rn] = f_key_value(k,tostring_a(v))
- else
- r[rn] = f_key_value(k,tostring_d(v))
- end
- else
- r[rn] = f_key_value(k,tostring(v))
- end
- end
- if contentonly then
- return concat(r," ")
- elseif key then
- return f_key_dictionary(key,r)
- else
- return f_dictionary(r)
- end
- end
-end
-
-tostring_a = function(t,contentonly,key)
- local tn = #t
- if tn == 0 then
- if contentonly then
- return ""
- else
- return "[ ]"
- end
- else
- local r = { }
- for k=1,tn do
- local v = t[k]
- local tv = type(v)
- if tv == "string" then
- r[k] = toeight(v)
- elseif tv == "unicode" then
- r[k] = tosixteen(v)
- elseif tv == "table" then
- local mv = getmetatable(v)
- local mt = mv and mv.__lpdftype
- if mt then
- r[k] = tostring(v)
- elseif v[1] then
- r[k] = tostring_a(v)
- else
- r[k] = tostring_d(v)
- end
- else
- r[k] = tostring(v)
- end
- end
- if contentonly then
- return concat(r, " ")
- elseif key then
- return f_key_array(key,r)
- else
- return f_array(r)
- end
- end
-end
-
-local tostring_x = function(t) return concat(t, " ") end
-local tostring_s = function(t) return toeight(t[1]) end
-local tostring_u = function(t) return tosixteen(t[1]) end
-local tostring_n = function(t) return tostring(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 function value_x(t) return t end -- the call is experimental
-local function value_s(t,key) return t[1] end -- the call is experimental
-local function value_u(t,key) return t[1] end -- the call is experimental
-local function value_n(t,key) return t[1] end -- the call is experimental
-local function value_c(t) return sub(t[1],2) end -- the call is experimental
-local function value_d(t) return tostring_d(t,true) end -- the call is experimental
-local function value_a(t) return tostring_a(t,true) end -- the call is experimental
-local function value_z() return nil end -- the call is experimental
-local function value_t(t) return t.value or true end -- the call is experimental
-local function value_f(t) return t.value or false end -- the call is experimental
-local function value_r() return t[1] or 0 end -- the call is experimental -- NULL
-local function value_v() return t[1] end -- the call is experimental
-
-local function add_x(t,k,v) rawset(t,k,tostring(v)) end
-
-local mt_x = { __lpdftype = "stream", __tostring = tostring_x, __call = value_x, __newindex = add_x }
-local mt_d = { __lpdftype = "dictionary", __tostring = tostring_d, __call = value_d }
-local mt_a = { __lpdftype = "array", __tostring = tostring_a, __call = value_a }
-local mt_u = { __lpdftype = "unicode", __tostring = tostring_u, __call = value_u }
-local mt_s = { __lpdftype = "string", __tostring = tostring_s, __call = value_s }
-local mt_n = { __lpdftype = "number", __tostring = tostring_n, __call = value_n }
-local mt_c = { __lpdftype = "constant", __tostring = tostring_c, __call = value_c }
-local mt_z = { __lpdftype = "null", __tostring = tostring_z, __call = value_z }
-local mt_t = { __lpdftype = "true", __tostring = tostring_t, __call = value_t }
-local mt_f = { __lpdftype = "false", __tostring = tostring_f, __call = value_f }
-local mt_r = { __lpdftype = "reference", __tostring = tostring_r, __call = value_r }
-local mt_v = { __lpdftype = "verbose", __tostring = tostring_v, __call = value_v }
-
-local function pdfstream(t) -- we need to add attributes
- if t then
- for i=1,#t do
- t[i] = tostring(t[i])
- 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 pdfunicode(str,default)
- return setmetatable({ str or default or "" },mt_u)
-end
-
-local cache = { } -- can be weak
-
-local function pdfnumber(n,default) -- 0-10
- n = n or default
- 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 cache = { } -- can be weak
-
-local forbidden, replacements = "\0\t\n\r\f ()[]{}/%%#\\", { } -- table faster than function
-
-for s in gmatch(forbidden,".") do
- replacements[s] = format("#%02x",byte(s))
-end
-
-local escaped = Cs(Cc("/") * (S(forbidden)/replacements + P(1))^0)
-
-local function pdfconstant(str,default)
- str = str or default or ""
- local c = cache[str]
- if not c then
- -- c = setmetatable({ "/" .. str },mt_c)
- c = setmetatable({ lpegmatch(escaped,str) },mt_c)
- cache[str] = c
- end
- return c
-end
-
-local p_null = { } setmetatable(p_null, mt_z)
-local p_true = { } setmetatable(p_true, mt_t)
-local p_false = { } setmetatable(p_false,mt_f)
-
-local function pdfnull()
- return p_null
-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 function pdfboolean(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
-
-local function pdfreference(r)
- return setmetatable({ r or 0 },mt_r)
-end
-
-local function pdfverbose(t) -- maybe check for type
- return setmetatable({ t or "" },mt_v)
-end
-
-lpdf.stream = pdfstream -- THIS WILL PROBABLY CHANGE
-lpdf.dictionary = pdfdictionary
-lpdf.array = pdfarray
-lpdf.string = pdfstring
-lpdf.unicode = pdfunicode
-lpdf.number = pdfnumber
-lpdf.constant = pdfconstant
-lpdf.null = pdfnull
-lpdf.boolean = pdfboolean
-lpdf.reference = pdfreference
-lpdf.verbose = pdfverbose
-
--- n = pdf.obj(n, str)
--- n = pdf.obj(n, "file", filename)
--- n = pdf.obj(n, "stream", streamtext, attrtext)
--- n = pdf.obj(n, "streamfile", filename, attrtext)
-
--- we only use immediate objects
-
--- todo: tracing
-
-local names, cache = { }, { }
-
-function lpdf.reserveobject(name)
- if name == "annot" then
- -- catch misuse
- return pdfreserveobject("annot")
- else
- local r = pdfreserveobject()
- if name then
- names[name] = r
- if trace_objects then
- report_objects("reserving number %a under name %a",r,name)
- end
- elseif trace_objects then
- report_objects("reserving number %a",r)
- end
- return r
- end
-end
-
-function lpdf.reserveannotation()
- return pdfreserveobject("annot")
-end
-
--- lpdf.immediateobject = pdfimmediateobject
--- lpdf.deferredobject = pdfdeferredobject
--- lpdf.object = pdfdeferredobject
--- lpdf.referenceobject = pdfreferenceobject
-
-lpdf.pagereference = pdf.pageref or tex.pdfpageref
-lpdf.registerannotation = pdf.registerannot
-
-function lpdf.delayedobject(data) -- we will get rid of this one
- local n = pdfdeferredobject(data)
- 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_detail 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_detail 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_detail then
- report_objects("flushing data: %S",name)
- end
- return pdfimmediateobject(tostring(name))
- end
-end
-
-
-function lpdf.flushstreamobject(data,dict,compressed) -- default compressed
- if trace_objects then
- report_objects("flushing stream object of %s bytes",#data)
- end
- local dtype = type(dict)
- return pdfdeferredobject {
- immediate = true,
- 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) -- default compressed
- if trace_objects then
- report_objects("flushing stream file object %a",filename)
- end
- local dtype = type(dict)
- return pdfdeferredobject {
- 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
-
---~ local d = lpdf.dictionary()
---~ local e = lpdf.dictionary { ["e"] = "abc", x = lpdf.dictionary { ["f"] = "ABC" } }
---~ local f = lpdf.dictionary { ["f"] = "ABC" }
---~ local a = lpdf.array { lpdf.array { lpdf.string("xxx") } }
-
---~ print(a)
---~ os.exit()
-
---~ d["test"] = lpdf.string ("test")
---~ d["more"] = "more"
---~ d["bool"] = true
---~ d["numb"] = 1234
---~ d["oeps"] = lpdf.dictionary { ["hans"] = "ton" }
---~ d["whow"] = lpdf.array { lpdf.string("ton") }
-
---~ a[#a+1] = lpdf.string("xxx")
---~ a[#a+1] = lpdf.string("yyy")
-
---~ d.what = a
-
---~ print(e)
-
---~ local d = lpdf.dictionary()
---~ d["abcd"] = { 1, 2, 3, "test" }
---~ print(d)
---~ print(d())
-
---~ local d = lpdf.array()
---~ d[#d+1] = 1
---~ d[#d+1] = 2
---~ d[#d+1] = 3
---~ d[#d+1] = "test"
---~ print(d)
-
---~ local d = lpdf.array()
---~ d[#d+1] = { 1, 2, 3, "test" }
---~ print(d)
-
---~ local d = lpdf.array()
---~ d[#d+1] = { a=1, b=2, c=3, d="test" }
---~ print(d)
-
---~ local s = lpdf.constant("xx")
---~ print(s) -- fails somehow
---~ print(s()) -- fails somehow
-
---~ local s = lpdf.boolean(false)
---~ s.value = true
---~ print(s)
---~ print(s())
-
--- three priority levels, default=2
-
-local pagefinalizers, documentfinalizers = { { }, { }, { } }, { { }, { }, { } }
-
-local pageresources, pageattributes, pagesattributes
-
-local function resetpageproperties()
- pageresources = pdfdictionary()
- pageattributes = pdfdictionary()
- pagesattributes = pdfdictionary()
-end
-
-resetpageproperties()
-
-local function setpageproperties()
- pdf.pageresources = pageresources ()
- pdf.pageattributes = pageattributes ()
- pdf.pagesattributes = pagesattributes()
-end
-
-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()
- if not environment.initex then
- -- resetpageproperties() -- maybe better before
- run(pagefinalizers,"page")
- setpageproperties()
- 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
-
-backends.pdf.codeinjections.finalizepage = lpdf.finalizepage -- will go when we have hook
-
---~ callbacks.register("finish_pdfpage", lpdf.finalizepage)
-callbacks.register("finish_pdffile", lpdf.finalizedocument)
-
--- 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
-local names = pdfdictionary { Type = pdfconstant("Names") } -- nicer, but when we assign we nil the Type
-
-local function flushcatalog() if not environment.initex then trace_flush("catalog") catalog.Type = nil pdf.catalog = catalog() end end
-local function flushinfo () if not environment.initex then trace_flush("info") info .Type = nil pdf.info = info () end end
-local function flushnames () if not environment.initex then trace_flush("names") names .Type = nil pdf.names = names () 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
-function lpdf.addtonames (k,v) if not (lpdf.protectresources and names [k]) then trace_set("names", k) names [k] = v end end
-
-local dummy = pdfreserveobject() -- else bug in hvmd due so some internal luatex conflict
-
--- Some day I will implement a proper minimalized resource management.
-
-local r_extgstates, d_extgstates = pdfreserveobject(), pdfdictionary() local p_extgstates = pdfreference(r_extgstates)
-local r_colorspaces, d_colorspaces = pdfreserveobject(), pdfdictionary() local p_colorspaces = pdfreference(r_colorspaces)
-local r_patterns, d_patterns = pdfreserveobject(), pdfdictionary() local p_patterns = pdfreference(r_patterns)
-local r_shades, d_shades = pdfreserveobject(), pdfdictionary() local p_shades = pdfreference(r_shades)
-
-local function checkextgstates () if next(d_extgstates ) then addtopageresources("ExtGState", p_extgstates ) end end
-local function checkcolorspaces() if next(d_colorspaces) then addtopageresources("ColorSpace",p_colorspaces) end end
-local function checkpatterns () if next(d_patterns ) then addtopageresources("Pattern", p_patterns ) end end
-local function checkshades () if next(d_shades ) then addtopageresources("Shading", p_shades ) end end
-
-local function flushextgstates () if next(d_extgstates ) then trace_flush("extgstates") pdfimmediateobject(r_extgstates, tostring(d_extgstates )) end end
-local function flushcolorspaces() if next(d_colorspaces) then trace_flush("colorspaces") pdfimmediateobject(r_colorspaces,tostring(d_colorspaces)) end end
-local function flushpatterns () if next(d_patterns ) then trace_flush("patterns") pdfimmediateobject(r_patterns, tostring(d_patterns )) end end
-local function flushshades () if next(d_shades ) then trace_flush("shades") pdfimmediateobject(r_shades, tostring(d_shades )) end end
-
-function lpdf.collectedresources()
- local ExtGState = next(d_extgstates ) and p_extgstates
- local ColorSpace = next(d_colorspaces) and p_colorspaces
- local Pattern = next(d_patterns ) and p_patterns
- local Shading = next(d_shades ) and p_shades
- if ExtGState or ColorSpace or Pattern or Shading then
- local collected = pdfdictionary {
- ExtGState = ExtGState,
- ColorSpace = ColorSpace,
- Pattern = Pattern,
- Shading = Shading,
- -- ProcSet = pdfarray { pdfconstant("PDF") },
- }
- return collected()
- else
- return ""
- end
-end
-
-function lpdf.adddocumentextgstate (k,v) d_extgstates [k] = v end
-function lpdf.adddocumentcolorspace(k,v) d_colorspaces[k] = v end
-function lpdf.adddocumentpattern (k,v) d_patterns [k] = v end
-function lpdf.adddocumentshade (k,v) 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(flushcatalog,3,"catalog")
-registerdocumentfinalizer(flushinfo,3,"info")
-registerdocumentfinalizer(flushnames,3,"names") -- before catalog
-
-registerpagefinalizer(checkextgstates,3,"extended graphic states")
-registerpagefinalizer(checkcolorspaces,3,"color spaces")
-registerpagefinalizer(checkpatterns,3,"patterns")
-registerpagefinalizer(checkshades,3,"shades")
-
--- in strc-bkm: lpdf.registerdocumentfinalizer(function() structures.bookmarks.place() end,1)
-
-function lpdf.rotationcm(a)
- local s, c = sind(a), cosd(a)
- return format("%0.6f %0.6f %0.6f %0.6f 0 0 cm",c,s,-s,c)
-end
-
--- ! -> universaltime
-
-local timestamp = os.date("%Y-%m-%dT%X") .. os.timezone(true)
-
-function lpdf.timestamp()
- return timestamp
-end
-
-function lpdf.pdftimestamp(str)
- 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)
-end
-
-function lpdf.id()
- return format("%s.%s",tex.jobname,timestamp)
-end
-
-function lpdf.checkedkey(t,key,variant)
- local pn = t and t[key]
- if pn then
- local tn = type(pn)
- if tn == variant then
- if variant == "string" then
- return pn ~= "" and pn or nil
- elseif variant == "table" then
- return next(pn) and pn or nil
- else
- return pn
- end
- elseif tn == "string" and variant == "number" then
- return tonumber(pn)
- end
- end
-end
-
-function lpdf.checkedvalue(value,variant) -- code not shared
- if value then
- local tv = type(value)
- if tv == variant then
- if variant == "string" then
- return value ~= "" and value
- elseif variant == "table" then
- return next(value) and value
- else
- return value
- end
- elseif tv == "string" and variant == "number" then
- return tonumber(value)
- 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
-
--- lpdf.addtoinfo("ConTeXt.Version", tex.contextversiontoks)
--- lpdf.addtoinfo("ConTeXt.Time", os.date("%Y.%m.%d %H:%M")) -- :%S
--- lpdf.addtoinfo("ConTeXt.Jobname", environment.jobname)
--- lpdf.addtoinfo("ConTeXt.Url", "www.pragma-ade.com")
-
-if not pdfreferenceobject then
-
- local delayed = { }
-
- local function flush()
- local n = 0
- for k,v in next, delayed do
- pdfimmediateobject(k,v)
- n = n + 1
- end
- if trace_objects then
- report_objects("%s objects flushed",n)
- end
- delayed = { }
- end
-
- lpdf.registerdocumentfinalizer(flush,3,"objects") -- so we need a final flush too
- lpdf.registerpagefinalizer (flush,3,"objects") -- somehow this lags behind .. I need to look into that some day
-
- function lpdf.delayedobject(data)
- local n = pdfreserveobject()
- delayed[n] = data
- return n
- end
-
-end
+if not modules then modules = { } end modules ['lpdf-ini'] = {
+ 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 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, utfvalues = utf.char, utf.values
+local sind, cosd = math.sind, math.cosd
+local lpegmatch, P, C, R, S, Cc, Cs = lpeg.match, lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cc, lpeg.Cs
+local formatters = string.formatters
+
+local pdfreserveobject = pdf.reserveobj
+local pdfimmediateobject = pdf.immediateobj
+local pdfdeferredobject = pdf.obj
+local pdfreferenceobject = pdf.refobj
+
+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 trace_objects = false trackers.register("backend.objects", function(v) trace_objects = v end)
+local trace_detail = false trackers.register("backend.detail", function(v) trace_detail = v end)
+
+local report_objects = logs.reporter("backend","objects")
+local report_finalizing = logs.reporter("backend","finalizing")
+
+local backends = backends
+
+backends.pdf = backends.pdf or {
+ comment = "backend for directly generating pdf output",
+ nodeinjections = { },
+ codeinjections = { },
+ registrations = { },
+ tables = { },
+}
+
+lpdf = lpdf or { }
+local lpdf = lpdf
+
+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",b/1024+0xD800,b%1024+0xDC00)
+ end
+ end
+ n = n + 1
+ r[n] = ">"
+ return concat(r)
+ end
+end
+
+lpdf.tosixteen = tosixteen
+
+-- lpeg is some 5 times faster than gsub (in test) on escaping
+
+-- local escapes = {
+-- ["\\"] = "\\\\",
+-- ["/"] = "\\/", ["#"] = "\\#",
+-- ["<"] = "\\<", [">"] = "\\>",
+-- ["["] = "\\[", ["]"] = "\\]",
+-- ["("] = "\\(", [")"] = "\\)",
+-- }
+--
+-- local escaped = Cs(Cc("(") * (S("\\/#<>[]()")/escapes + P(1))^0 * Cc(")"))
+--
+-- local function toeight(str)
+-- if not str or str == "" then
+-- return "()"
+-- else
+-- return lpegmatch(escaped,str)
+-- end
+-- end
+--
+-- -- no need for escaping .. just use unicode instead
+
+-- \0 \t \n \r \f <space> ( ) [ ] { } / %
+
+local function toeight(str)
+ return "(" .. str .. ")"
+end
+
+lpdf.toeight = toeight
+
+--~ local escaped = lpeg.Cs((lpeg.S("\0\t\n\r\f ()[]{}/%")/function(s) return format("#%02X",byte(s)) end + lpeg.P(1))^0)
+
+--~ local function cleaned(str)
+--~ return (str and str ~= "" and lpegmatch(escaped,str)) or ""
+--~ end
+
+--~ lpdf.cleaned = cleaned -- not public yet
+
+local function merge_t(a,b)
+ local t = { }
+ for k,v in next, a do t[k] = v end
+ for k,v in next, b do t[k] = v end
+ return setmetatable(t,getmetatable(a))
+end
+
+local f_key_value = formatters["/%s %s"]
+local f_key_dictionary = formatters["/%s << % t >>"]
+local f_dictionary = formatters["<< % t >>"]
+local f_key_array = formatters["/%s [ % t ]"]
+local f_array = formatters["[ % t ]"]
+
+local tostring_a, tostring_d
+
+tostring_d = function(t,contentonly,key)
+ if not next(t) then
+ if contentonly then
+ return ""
+ else
+ return "<< >>"
+ end
+ else
+ local r, rn = { }, 0
+ for k, v in next, t do
+ rn = rn + 1
+ local tv = type(v)
+ if tv == "string" then
+ r[rn] = f_key_value(k,toeight(v))
+ elseif tv == "unicode" then
+ r[rn] = f_key_value(k,tosixteen(v))
+ elseif tv == "table" then
+ local mv = getmetatable(v)
+ if mv and mv.__lpdftype then
+ r[rn] = f_key_value(k,tostring(v))
+ elseif v[1] then
+ r[rn] = f_key_value(k,tostring_a(v))
+ else
+ r[rn] = f_key_value(k,tostring_d(v))
+ end
+ else
+ r[rn] = f_key_value(k,tostring(v))
+ end
+ end
+ if contentonly then
+ return concat(r," ")
+ elseif key then
+ return f_key_dictionary(key,r)
+ else
+ return f_dictionary(r)
+ end
+ end
+end
+
+tostring_a = function(t,contentonly,key)
+ local tn = #t
+ if tn == 0 then
+ if contentonly then
+ return ""
+ else
+ return "[ ]"
+ end
+ else
+ local r = { }
+ for k=1,tn do
+ local v = t[k]
+ local tv = type(v)
+ if tv == "string" then
+ r[k] = toeight(v)
+ elseif tv == "unicode" then
+ r[k] = tosixteen(v)
+ elseif tv == "table" then
+ local mv = getmetatable(v)
+ local mt = mv and mv.__lpdftype
+ if mt then
+ r[k] = tostring(v)
+ elseif v[1] then
+ r[k] = tostring_a(v)
+ else
+ r[k] = tostring_d(v)
+ end
+ else
+ r[k] = tostring(v)
+ end
+ end
+ if contentonly then
+ return concat(r, " ")
+ elseif key then
+ return f_key_array(key,r)
+ else
+ return f_array(r)
+ end
+ end
+end
+
+local tostring_x = function(t) return concat(t, " ") end
+local tostring_s = function(t) return toeight(t[1]) end
+local tostring_u = function(t) return tosixteen(t[1]) end
+local tostring_n = function(t) return tostring(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 function value_x(t) return t end -- the call is experimental
+local function value_s(t,key) return t[1] end -- the call is experimental
+local function value_u(t,key) return t[1] end -- the call is experimental
+local function value_n(t,key) return t[1] end -- the call is experimental
+local function value_c(t) return sub(t[1],2) end -- the call is experimental
+local function value_d(t) return tostring_d(t,true) end -- the call is experimental
+local function value_a(t) return tostring_a(t,true) end -- the call is experimental
+local function value_z() return nil end -- the call is experimental
+local function value_t(t) return t.value or true end -- the call is experimental
+local function value_f(t) return t.value or false end -- the call is experimental
+local function value_r() return t[1] or 0 end -- the call is experimental -- NULL
+local function value_v() return t[1] end -- the call is experimental
+
+local function add_x(t,k,v) rawset(t,k,tostring(v)) end
+
+local mt_x = { __lpdftype = "stream", __tostring = tostring_x, __call = value_x, __newindex = add_x }
+local mt_d = { __lpdftype = "dictionary", __tostring = tostring_d, __call = value_d }
+local mt_a = { __lpdftype = "array", __tostring = tostring_a, __call = value_a }
+local mt_u = { __lpdftype = "unicode", __tostring = tostring_u, __call = value_u }
+local mt_s = { __lpdftype = "string", __tostring = tostring_s, __call = value_s }
+local mt_n = { __lpdftype = "number", __tostring = tostring_n, __call = value_n }
+local mt_c = { __lpdftype = "constant", __tostring = tostring_c, __call = value_c }
+local mt_z = { __lpdftype = "null", __tostring = tostring_z, __call = value_z }
+local mt_t = { __lpdftype = "true", __tostring = tostring_t, __call = value_t }
+local mt_f = { __lpdftype = "false", __tostring = tostring_f, __call = value_f }
+local mt_r = { __lpdftype = "reference", __tostring = tostring_r, __call = value_r }
+local mt_v = { __lpdftype = "verbose", __tostring = tostring_v, __call = value_v }
+
+local function pdfstream(t) -- we need to add attributes
+ if t then
+ for i=1,#t do
+ t[i] = tostring(t[i])
+ 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 pdfunicode(str,default)
+ return setmetatable({ str or default or "" },mt_u)
+end
+
+local cache = { } -- can be weak
+
+local function pdfnumber(n,default) -- 0-10
+ n = n or default
+ 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 cache = { } -- can be weak
+
+local forbidden, replacements = "\0\t\n\r\f ()[]{}/%%#\\", { } -- table faster than function
+
+for s in gmatch(forbidden,".") do
+ replacements[s] = format("#%02x",byte(s))
+end
+
+local escaped = Cs(Cc("/") * (S(forbidden)/replacements + P(1))^0)
+
+local function pdfconstant(str,default)
+ str = str or default or ""
+ local c = cache[str]
+ if not c then
+ -- c = setmetatable({ "/" .. str },mt_c)
+ c = setmetatable({ lpegmatch(escaped,str) },mt_c)
+ cache[str] = c
+ end
+ return c
+end
+
+local p_null = { } setmetatable(p_null, mt_z)
+local p_true = { } setmetatable(p_true, mt_t)
+local p_false = { } setmetatable(p_false,mt_f)
+
+local function pdfnull()
+ return p_null
+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 function pdfboolean(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
+
+local function pdfreference(r)
+ return setmetatable({ r or 0 },mt_r)
+end
+
+local function pdfverbose(t) -- maybe check for type
+ return setmetatable({ t or "" },mt_v)
+end
+
+lpdf.stream = pdfstream -- THIS WILL PROBABLY CHANGE
+lpdf.dictionary = pdfdictionary
+lpdf.array = pdfarray
+lpdf.string = pdfstring
+lpdf.unicode = pdfunicode
+lpdf.number = pdfnumber
+lpdf.constant = pdfconstant
+lpdf.null = pdfnull
+lpdf.boolean = pdfboolean
+lpdf.reference = pdfreference
+lpdf.verbose = pdfverbose
+
+-- n = pdf.obj(n, str)
+-- n = pdf.obj(n, "file", filename)
+-- n = pdf.obj(n, "stream", streamtext, attrtext)
+-- n = pdf.obj(n, "streamfile", filename, attrtext)
+
+-- we only use immediate objects
+
+-- todo: tracing
+
+local names, cache = { }, { }
+
+function lpdf.reserveobject(name)
+ if name == "annot" then
+ -- catch misuse
+ return pdfreserveobject("annot")
+ else
+ local r = pdfreserveobject()
+ if name then
+ names[name] = r
+ if trace_objects then
+ report_objects("reserving number %a under name %a",r,name)
+ end
+ elseif trace_objects then
+ report_objects("reserving number %a",r)
+ end
+ return r
+ end
+end
+
+function lpdf.reserveannotation()
+ return pdfreserveobject("annot")
+end
+
+-- lpdf.immediateobject = pdfimmediateobject
+-- lpdf.deferredobject = pdfdeferredobject
+-- lpdf.object = pdfdeferredobject
+-- lpdf.referenceobject = pdfreferenceobject
+
+lpdf.pagereference = pdf.pageref or tex.pdfpageref
+lpdf.registerannotation = pdf.registerannot
+
+function lpdf.delayedobject(data) -- we will get rid of this one
+ local n = pdfdeferredobject(data)
+ 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_detail 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_detail 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_detail then
+ report_objects("flushing data: %S",name)
+ end
+ return pdfimmediateobject(tostring(name))
+ end
+end
+
+
+function lpdf.flushstreamobject(data,dict,compressed) -- default compressed
+ if trace_objects then
+ report_objects("flushing stream object of %s bytes",#data)
+ end
+ local dtype = type(dict)
+ return pdfdeferredobject {
+ immediate = true,
+ 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) -- default compressed
+ if trace_objects then
+ report_objects("flushing stream file object %a",filename)
+ end
+ local dtype = type(dict)
+ return pdfdeferredobject {
+ 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
+
+--~ local d = lpdf.dictionary()
+--~ local e = lpdf.dictionary { ["e"] = "abc", x = lpdf.dictionary { ["f"] = "ABC" } }
+--~ local f = lpdf.dictionary { ["f"] = "ABC" }
+--~ local a = lpdf.array { lpdf.array { lpdf.string("xxx") } }
+
+--~ print(a)
+--~ os.exit()
+
+--~ d["test"] = lpdf.string ("test")
+--~ d["more"] = "more"
+--~ d["bool"] = true
+--~ d["numb"] = 1234
+--~ d["oeps"] = lpdf.dictionary { ["hans"] = "ton" }
+--~ d["whow"] = lpdf.array { lpdf.string("ton") }
+
+--~ a[#a+1] = lpdf.string("xxx")
+--~ a[#a+1] = lpdf.string("yyy")
+
+--~ d.what = a
+
+--~ print(e)
+
+--~ local d = lpdf.dictionary()
+--~ d["abcd"] = { 1, 2, 3, "test" }
+--~ print(d)
+--~ print(d())
+
+--~ local d = lpdf.array()
+--~ d[#d+1] = 1
+--~ d[#d+1] = 2
+--~ d[#d+1] = 3
+--~ d[#d+1] = "test"
+--~ print(d)
+
+--~ local d = lpdf.array()
+--~ d[#d+1] = { 1, 2, 3, "test" }
+--~ print(d)
+
+--~ local d = lpdf.array()
+--~ d[#d+1] = { a=1, b=2, c=3, d="test" }
+--~ print(d)
+
+--~ local s = lpdf.constant("xx")
+--~ print(s) -- fails somehow
+--~ print(s()) -- fails somehow
+
+--~ local s = lpdf.boolean(false)
+--~ s.value = true
+--~ print(s)
+--~ print(s())
+
+-- three priority levels, default=2
+
+local pagefinalizers, documentfinalizers = { { }, { }, { } }, { { }, { }, { } }
+
+local pageresources, pageattributes, pagesattributes
+
+local function resetpageproperties()
+ pageresources = pdfdictionary()
+ pageattributes = pdfdictionary()
+ pagesattributes = pdfdictionary()
+end
+
+resetpageproperties()
+
+local function setpageproperties()
+ pdf.pageresources = pageresources ()
+ pdf.pageattributes = pageattributes ()
+ pdf.pagesattributes = pagesattributes()
+end
+
+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()
+ if not environment.initex then
+ -- resetpageproperties() -- maybe better before
+ run(pagefinalizers,"page")
+ setpageproperties()
+ 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
+
+backends.pdf.codeinjections.finalizepage = lpdf.finalizepage -- will go when we have hook
+
+--~ callbacks.register("finish_pdfpage", lpdf.finalizepage)
+callbacks.register("finish_pdffile", lpdf.finalizedocument)
+
+-- 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
+local names = pdfdictionary { Type = pdfconstant("Names") } -- nicer, but when we assign we nil the Type
+
+local function flushcatalog() if not environment.initex then trace_flush("catalog") catalog.Type = nil pdf.catalog = catalog() end end
+local function flushinfo () if not environment.initex then trace_flush("info") info .Type = nil pdf.info = info () end end
+local function flushnames () if not environment.initex then trace_flush("names") names .Type = nil pdf.names = names () 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
+function lpdf.addtonames (k,v) if not (lpdf.protectresources and names [k]) then trace_set("names", k) names [k] = v end end
+
+local dummy = pdfreserveobject() -- else bug in hvmd due so some internal luatex conflict
+
+-- Some day I will implement a proper minimalized resource management.
+
+local r_extgstates, d_extgstates = pdfreserveobject(), pdfdictionary() local p_extgstates = pdfreference(r_extgstates)
+local r_colorspaces, d_colorspaces = pdfreserveobject(), pdfdictionary() local p_colorspaces = pdfreference(r_colorspaces)
+local r_patterns, d_patterns = pdfreserveobject(), pdfdictionary() local p_patterns = pdfreference(r_patterns)
+local r_shades, d_shades = pdfreserveobject(), pdfdictionary() local p_shades = pdfreference(r_shades)
+
+local function checkextgstates () if next(d_extgstates ) then addtopageresources("ExtGState", p_extgstates ) end end
+local function checkcolorspaces() if next(d_colorspaces) then addtopageresources("ColorSpace",p_colorspaces) end end
+local function checkpatterns () if next(d_patterns ) then addtopageresources("Pattern", p_patterns ) end end
+local function checkshades () if next(d_shades ) then addtopageresources("Shading", p_shades ) end end
+
+local function flushextgstates () if next(d_extgstates ) then trace_flush("extgstates") pdfimmediateobject(r_extgstates, tostring(d_extgstates )) end end
+local function flushcolorspaces() if next(d_colorspaces) then trace_flush("colorspaces") pdfimmediateobject(r_colorspaces,tostring(d_colorspaces)) end end
+local function flushpatterns () if next(d_patterns ) then trace_flush("patterns") pdfimmediateobject(r_patterns, tostring(d_patterns )) end end
+local function flushshades () if next(d_shades ) then trace_flush("shades") pdfimmediateobject(r_shades, tostring(d_shades )) end end
+
+function lpdf.collectedresources()
+ local ExtGState = next(d_extgstates ) and p_extgstates
+ local ColorSpace = next(d_colorspaces) and p_colorspaces
+ local Pattern = next(d_patterns ) and p_patterns
+ local Shading = next(d_shades ) and p_shades
+ if ExtGState or ColorSpace or Pattern or Shading then
+ local collected = pdfdictionary {
+ ExtGState = ExtGState,
+ ColorSpace = ColorSpace,
+ Pattern = Pattern,
+ Shading = Shading,
+ -- ProcSet = pdfarray { pdfconstant("PDF") },
+ }
+ return collected()
+ else
+ return ""
+ end
+end
+
+function lpdf.adddocumentextgstate (k,v) d_extgstates [k] = v end
+function lpdf.adddocumentcolorspace(k,v) d_colorspaces[k] = v end
+function lpdf.adddocumentpattern (k,v) d_patterns [k] = v end
+function lpdf.adddocumentshade (k,v) 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(flushcatalog,3,"catalog")
+registerdocumentfinalizer(flushinfo,3,"info")
+registerdocumentfinalizer(flushnames,3,"names") -- before catalog
+
+registerpagefinalizer(checkextgstates,3,"extended graphic states")
+registerpagefinalizer(checkcolorspaces,3,"color spaces")
+registerpagefinalizer(checkpatterns,3,"patterns")
+registerpagefinalizer(checkshades,3,"shades")
+
+-- in strc-bkm: lpdf.registerdocumentfinalizer(function() structures.bookmarks.place() end,1)
+
+function lpdf.rotationcm(a)
+ local s, c = sind(a), cosd(a)
+ return format("%0.6f %0.6f %0.6f %0.6f 0 0 cm",c,s,-s,c)
+end
+
+-- ! -> universaltime
+
+local timestamp = os.date("%Y-%m-%dT%X") .. os.timezone(true)
+
+function lpdf.timestamp()
+ return timestamp
+end
+
+function lpdf.pdftimestamp(str)
+ 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)
+end
+
+function lpdf.id()
+ return format("%s.%s",tex.jobname,timestamp)
+end
+
+function lpdf.checkedkey(t,key,variant)
+ local pn = t and t[key]
+ if pn then
+ local tn = type(pn)
+ if tn == variant then
+ if variant == "string" then
+ return pn ~= "" and pn or nil
+ elseif variant == "table" then
+ return next(pn) and pn or nil
+ else
+ return pn
+ end
+ elseif tn == "string" and variant == "number" then
+ return tonumber(pn)
+ end
+ end
+end
+
+function lpdf.checkedvalue(value,variant) -- code not shared
+ if value then
+ local tv = type(value)
+ if tv == variant then
+ if variant == "string" then
+ return value ~= "" and value
+ elseif variant == "table" then
+ return next(value) and value
+ else
+ return value
+ end
+ elseif tv == "string" and variant == "number" then
+ return tonumber(value)
+ 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
+
+-- lpdf.addtoinfo("ConTeXt.Version", tex.contextversiontoks)
+-- lpdf.addtoinfo("ConTeXt.Time", os.date("%Y.%m.%d %H:%M")) -- :%S
+-- lpdf.addtoinfo("ConTeXt.Jobname", environment.jobname)
+-- lpdf.addtoinfo("ConTeXt.Url", "www.pragma-ade.com")
+
+if not pdfreferenceobject then
+
+ local delayed = { }
+
+ local function flush()
+ local n = 0
+ for k,v in next, delayed do
+ pdfimmediateobject(k,v)
+ n = n + 1
+ end
+ if trace_objects then
+ report_objects("%s objects flushed",n)
+ end
+ delayed = { }
+ end
+
+ lpdf.registerdocumentfinalizer(flush,3,"objects") -- so we need a final flush too
+ lpdf.registerpagefinalizer (flush,3,"objects") -- somehow this lags behind .. I need to look into that some day
+
+ function lpdf.delayedobject(data)
+ local n = pdfreserveobject()
+ delayed[n] = data
+ return n
+ end
+
+end
diff --git a/tex/context/base/lpdf-mov.lua b/tex/context/base/lpdf-mov.lua
index 2f0033d1a..41db97e0c 100644
--- a/tex/context/base/lpdf-mov.lua
+++ b/tex/context/base/lpdf-mov.lua
@@ -1,63 +1,63 @@
-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 nodeinjections = backends.pdf.nodeinjections
-local pdfannotation_node = nodes.pool.pdfannotation
-local pdfconstant = lpdf.constant
-local pdfdictionary = lpdf.dictionary
-local pdfarray = lpdf.array
-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 action = pdfdictionary {
- Subtype = pdfconstant("Movie"),
- Border = pdfarray { 0, 0, 0 },
- T = format("movie %s",specification.label),
- Movie = moviedict,
- A = controldict,
- }
- write_node(pdfannotation_node(width,height,0,action())) -- test: context(...)
-end
-
-function nodeinjections.insertsound(specification)
- -- rmanaged 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 action = pdfdictionary {
- Subtype = pdfconstant("Movie"),
- Border = pdfarray { 0, 0, 0 },
- T = format("sound %s",specification.label),
- Movie = sounddict,
- A = controldict,
- }
- write_node(pdfannotation_node(0,0,0,action())) -- test: context(...)
- end
-end
+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 nodeinjections = backends.pdf.nodeinjections
+local pdfannotation_node = nodes.pool.pdfannotation
+local pdfconstant = lpdf.constant
+local pdfdictionary = lpdf.dictionary
+local pdfarray = lpdf.array
+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 action = pdfdictionary {
+ Subtype = pdfconstant("Movie"),
+ Border = pdfarray { 0, 0, 0 },
+ T = format("movie %s",specification.label),
+ Movie = moviedict,
+ A = controldict,
+ }
+ write_node(pdfannotation_node(width,height,0,action())) -- test: context(...)
+end
+
+function nodeinjections.insertsound(specification)
+ -- rmanaged 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 action = pdfdictionary {
+ Subtype = pdfconstant("Movie"),
+ Border = pdfarray { 0, 0, 0 },
+ T = format("sound %s",specification.label),
+ Movie = sounddict,
+ A = controldict,
+ }
+ write_node(pdfannotation_node(0,0,0,action())) -- test: context(...)
+ end
+end
diff --git a/tex/context/base/lpdf-nod.lua b/tex/context/base/lpdf-nod.lua
index 9c57d6289..60d3fcd5b 100644
--- a/tex/context/base/lpdf-nod.lua
+++ b/tex/context/base/lpdf-nod.lua
@@ -1,136 +1,136 @@
-if not modules then modules = { } end modules ['lpdf-nod'] = {
- 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 copy_node = node.copy
-local new_node = node.new
-
-local nodepool = nodes.pool
-local register = nodepool.register
-local whatsitcodes = nodes.whatsitcodes
-local nodeinjections = backends.nodeinjections
-
-local pdfliteral = register(new_node("whatsit", whatsitcodes.pdfliteral)) pdfliteral.mode = 1
-local pdfsave = register(new_node("whatsit", whatsitcodes.pdfsave))
-local pdfrestore = register(new_node("whatsit", whatsitcodes.pdfrestore))
-local pdfsetmatrix = register(new_node("whatsit", whatsitcodes.pdfsetmatrix))
-local pdfdest = register(new_node("whatsit", whatsitcodes.pdfdest)) pdfdest.named_id = 1 -- xyz_zoom untouched
-local pdfannot = register(new_node("whatsit", whatsitcodes.pdfannot))
-
-local variables = interfaces.variables
-
-local views = { -- beware, we do support the pdf keys but this is *not* official
- xyz = 0, [variables.standard] = 0,
- fit = 1, [variables.fit] = 1,
- fith = 2, [variables.width] = 2,
- fitv = 3, [variables.height] = 3,
- fitb = 4,
- fitbh = 5, [variables.minwidth] = 5,
- fitbv = 6, [variables.minheight] = 6,
- fitr = 7,
-}
-
-function nodepool.pdfliteral(str)
- local t = copy_node(pdfliteral)
- t.data = str
- return t
-end
-
-function nodepool.pdfdirect(str)
- local t = copy_node(pdfliteral)
- t.data = str
- t.mode = 1
- return t
-end
-
-function nodepool.pdfsave()
- return copy_node(pdfsave)
-end
-
-function nodepool.pdfrestore()
- return copy_node(pdfrestore)
-end
-
-function nodepool.pdfsetmatrix(rx,sx,sy,ry,tx,ty)
- local t = copy_node(pdfsetmatrix)
- t.data = format("%s %s %s %s",rx or 0,sx or 0,sy or 0,ry or 0) -- todo: tx ty
- return t
-end
-
-nodeinjections.save = nodepool.pdfsave
-nodeinjections.restore = nodepool.pdfrestore
-nodeinjections.transform = nodepool.pdfsetmatrix
-
-function nodepool.pdfannotation(w,h,d,data,n)
- local t = copy_node(pdfannot)
- if w and w ~= 0 then
- t.width = w
- end
- if h and h ~= 0 then
- t.height = h
- end
- if d and d ~= 0 then
- t.depth = d
- end
- if n then
- t.objnum = n
- end
- if data and data ~= "" then
- t.data = data
- end
- return t
-end
-
--- (!) The next code in pdfdest.w is wrong:
---
--- case pdf_dest_xyz:
--- if (matrixused()) {
--- set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_dest_margin) ;
--- } else {
--- pdf_ann_left(p) = pos.h ;
--- pdf_ann_top (p) = pos.v ;
--- }
--- break ;
---
--- so we need to force a matrix.
-
-function nodepool.pdfdestination(w,h,d,name,view,n)
- local t = copy_node(pdfdest)
- local hasdimensions = false
- if w and w ~= 0 then
- t.width = w
- hasdimensions = true
- end
- if h and h ~= 0 then
- t.height = h
- hasdimensions = true
- end
- if d and d ~= 0 then
- t.depth = d
- hasdimensions = true
- end
- if n then
- t.objnum = n
- end
- view = views[view] or view or 1 -- fit is default
- t.dest_id = name
- t.dest_type = view
- if hasdimensions and view == 0 then -- xyz
- -- see (!) s -> m -> t -> r
- local s = copy_node(pdfsave)
- local m = copy_node(pdfsetmatrix)
- local r = copy_node(pdfrestore)
- m.data = "1 0 0 1"
- s.next = m m.next = t t.next = r
- m.prev = s t.prev = m r.prev = t
- return s -- a list
- else
- return t
- end
-end
+if not modules then modules = { } end modules ['lpdf-nod'] = {
+ 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 copy_node = node.copy
+local new_node = node.new
+
+local nodepool = nodes.pool
+local register = nodepool.register
+local whatsitcodes = nodes.whatsitcodes
+local nodeinjections = backends.nodeinjections
+
+local pdfliteral = register(new_node("whatsit", whatsitcodes.pdfliteral)) pdfliteral.mode = 1
+local pdfsave = register(new_node("whatsit", whatsitcodes.pdfsave))
+local pdfrestore = register(new_node("whatsit", whatsitcodes.pdfrestore))
+local pdfsetmatrix = register(new_node("whatsit", whatsitcodes.pdfsetmatrix))
+local pdfdest = register(new_node("whatsit", whatsitcodes.pdfdest)) pdfdest.named_id = 1 -- xyz_zoom untouched
+local pdfannot = register(new_node("whatsit", whatsitcodes.pdfannot))
+
+local variables = interfaces.variables
+
+local views = { -- beware, we do support the pdf keys but this is *not* official
+ xyz = 0, [variables.standard] = 0,
+ fit = 1, [variables.fit] = 1,
+ fith = 2, [variables.width] = 2,
+ fitv = 3, [variables.height] = 3,
+ fitb = 4,
+ fitbh = 5, [variables.minwidth] = 5,
+ fitbv = 6, [variables.minheight] = 6,
+ fitr = 7,
+}
+
+function nodepool.pdfliteral(str)
+ local t = copy_node(pdfliteral)
+ t.data = str
+ return t
+end
+
+function nodepool.pdfdirect(str)
+ local t = copy_node(pdfliteral)
+ t.data = str
+ t.mode = 1
+ return t
+end
+
+function nodepool.pdfsave()
+ return copy_node(pdfsave)
+end
+
+function nodepool.pdfrestore()
+ return copy_node(pdfrestore)
+end
+
+function nodepool.pdfsetmatrix(rx,sx,sy,ry,tx,ty)
+ local t = copy_node(pdfsetmatrix)
+ t.data = format("%s %s %s %s",rx or 0,sx or 0,sy or 0,ry or 0) -- todo: tx ty
+ return t
+end
+
+nodeinjections.save = nodepool.pdfsave
+nodeinjections.restore = nodepool.pdfrestore
+nodeinjections.transform = nodepool.pdfsetmatrix
+
+function nodepool.pdfannotation(w,h,d,data,n)
+ local t = copy_node(pdfannot)
+ if w and w ~= 0 then
+ t.width = w
+ end
+ if h and h ~= 0 then
+ t.height = h
+ end
+ if d and d ~= 0 then
+ t.depth = d
+ end
+ if n then
+ t.objnum = n
+ end
+ if data and data ~= "" then
+ t.data = data
+ end
+ return t
+end
+
+-- (!) The next code in pdfdest.w is wrong:
+--
+-- case pdf_dest_xyz:
+-- if (matrixused()) {
+-- set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_dest_margin) ;
+-- } else {
+-- pdf_ann_left(p) = pos.h ;
+-- pdf_ann_top (p) = pos.v ;
+-- }
+-- break ;
+--
+-- so we need to force a matrix.
+
+function nodepool.pdfdestination(w,h,d,name,view,n)
+ local t = copy_node(pdfdest)
+ local hasdimensions = false
+ if w and w ~= 0 then
+ t.width = w
+ hasdimensions = true
+ end
+ if h and h ~= 0 then
+ t.height = h
+ hasdimensions = true
+ end
+ if d and d ~= 0 then
+ t.depth = d
+ hasdimensions = true
+ end
+ if n then
+ t.objnum = n
+ end
+ view = views[view] or view or 1 -- fit is default
+ t.dest_id = name
+ t.dest_type = view
+ if hasdimensions and view == 0 then -- xyz
+ -- see (!) s -> m -> t -> r
+ local s = copy_node(pdfsave)
+ local m = copy_node(pdfsetmatrix)
+ local r = copy_node(pdfrestore)
+ m.data = "1 0 0 1"
+ s.next = m m.next = t t.next = r
+ m.prev = s t.prev = m r.prev = t
+ return s -- a list
+ else
+ return t
+ end
+end
diff --git a/tex/context/base/lpdf-ren.lua b/tex/context/base/lpdf-ren.lua
index 19582817d..6af65f9de 100644
--- a/tex/context/base/lpdf-ren.lua
+++ b/tex/context/base/lpdf-ren.lua
@@ -1,349 +1,349 @@
-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 format, rep = string.format, string.rep
-local concat = table.concat
-local settings_to_array = utilities.parsers.settings_to_array
-
-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 = lpdf.flushobject
-local pdfreserveobject = lpdf.reserveobject
-
-local nodepool = nodes.pool
-local register = nodepool.register
-local pdfliteral = nodepool.pdfliteral
-
-local pdf_ocg = pdfconstant("OCG")
-local pdf_ocmd = pdfconstant("OCMD")
-local pdf_off = pdfconstant("OFF")
-local pdf_on = pdfconstant("ON")
-local pdf_toggle = pdfconstant("Toggle")
-local pdf_setocgstate = pdfconstant("SetOCGState")
-
-local copy_node = node.copy
-
-local lpdf_usage = pdfdictionary { Print = pdfdictionary { PrintState = 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 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",
- Intent = ((specification.editable ~= v_no) and pdf_design) or nil, -- disable layer hiding by user
- Usage = ((specification.printable == v_no) and lpdf_usage) or nil, -- printable 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[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,
- },
- }
- lpdf.addtocatalog("OCProperties",d)
- textlayers = nil
- end
- end
-end
-
-local function flushpagelayers() -- we can share these
- if pagelayers then
- lpdf.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
-
-function codeinjections.startlayer(name) -- used in mp
- if not name then
- name = "unknown"
- end
- useviewerlayer(name)
- return format("/OC /%s BDC",name)
-end
-
-function codeinjections.stoplayer(name) -- used in mp
- return "EMC"
-end
-
-local cache = { }
-
-function nodeinjections.startlayer(name)
- local c = cache[name]
- if not c then
- useviewerlayer(name)
- c = register(pdfliteral(format("/OC /%s BDC",name)))
- cache[name] = c
- end
- return copy_node(c)
-end
-
-local stop = register(pdfliteral("EMC"))
-
-function nodeinjections.stoplayer()
- 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 pdfliteral(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 pdfliteral(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 pdfliteral(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 = math.random(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
- lpdf.addtopageattributes("Dur",delay)
- end
- lpdf.addtopageattributes("Trans",d)
- end
-end
+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 format, rep = string.format, string.rep
+local concat = table.concat
+local settings_to_array = utilities.parsers.settings_to_array
+
+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 = lpdf.flushobject
+local pdfreserveobject = lpdf.reserveobject
+
+local nodepool = nodes.pool
+local register = nodepool.register
+local pdfliteral = nodepool.pdfliteral
+
+local pdf_ocg = pdfconstant("OCG")
+local pdf_ocmd = pdfconstant("OCMD")
+local pdf_off = pdfconstant("OFF")
+local pdf_on = pdfconstant("ON")
+local pdf_toggle = pdfconstant("Toggle")
+local pdf_setocgstate = pdfconstant("SetOCGState")
+
+local copy_node = node.copy
+
+local lpdf_usage = pdfdictionary { Print = pdfdictionary { PrintState = 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 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",
+ Intent = ((specification.editable ~= v_no) and pdf_design) or nil, -- disable layer hiding by user
+ Usage = ((specification.printable == v_no) and lpdf_usage) or nil, -- printable 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[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,
+ },
+ }
+ lpdf.addtocatalog("OCProperties",d)
+ textlayers = nil
+ end
+ end
+end
+
+local function flushpagelayers() -- we can share these
+ if pagelayers then
+ lpdf.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
+
+function codeinjections.startlayer(name) -- used in mp
+ if not name then
+ name = "unknown"
+ end
+ useviewerlayer(name)
+ return format("/OC /%s BDC",name)
+end
+
+function codeinjections.stoplayer(name) -- used in mp
+ return "EMC"
+end
+
+local cache = { }
+
+function nodeinjections.startlayer(name)
+ local c = cache[name]
+ if not c then
+ useviewerlayer(name)
+ c = register(pdfliteral(format("/OC /%s BDC",name)))
+ cache[name] = c
+ end
+ return copy_node(c)
+end
+
+local stop = register(pdfliteral("EMC"))
+
+function nodeinjections.stoplayer()
+ 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 pdfliteral(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 pdfliteral(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 pdfliteral(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 = math.random(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
+ lpdf.addtopageattributes("Dur",delay)
+ end
+ lpdf.addtopageattributes("Trans",d)
+ end
+end
diff --git a/tex/context/base/lpdf-swf.lua b/tex/context/base/lpdf-swf.lua
index 0267e5255..12c80036f 100644
--- a/tex/context/base/lpdf-swf.lua
+++ b/tex/context/base/lpdf-swf.lua
@@ -1,306 +1,306 @@
-if not modules then modules = { } end modules ['lpdf-swf'] = {
- 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 tests by Luigi Scarso. His prototype
--- was using tex code. This is the official implementation.
-
-local format, gsub = string.format, string.gsub
-
-local backends, lpdf = backends, lpdf
-
-local pdfconstant = lpdf.constant
-local pdfboolean = lpdf.boolean
-local pdfstring = lpdf.string
-local pdfunicode = lpdf.unicode
-local pdfdictionary = lpdf.dictionary
-local pdfarray = lpdf.array
-local pdfnull = lpdf.null
-local pdfreference = lpdf.reference
-local pdfflushobject = lpdf.flushobject
-
-local checkedkey = lpdf.checkedkey
-
-local codeinjections = backends.pdf.codeinjections
-local nodeinjections = backends.pdf.nodeinjections
-
-local pdfannotation_node = nodes.pool.pdfannotation
-
-local trace_swf = false trackers.register("backend.swf", function(v) trace_swf = v end)
-
-local report_swf = logs.reporter("backend","swf")
-
-local activations = {
- click = "XA",
- page = "PO",
- focus = "PV",
-}
-
-local deactivations = {
- click = "XD",
- page = "PI",
- focus = "PC",
-}
-
-table.setmetatableindex(activations, function() return activations .click end)
-table.setmetatableindex(deactivations,function() return deactivations.focus end)
-
-local function insertswf(spec)
-
- local width = spec.width
- local height = spec.height
- local filename = spec.foundname
- local resources = spec.resources
- local display = spec.display
- local controls = spec.controls
-
- local resources = resources and parametersets[resources]
- local display = display and parametersets[display]
- local controls = controls and parametersets[controls] -- not yet used
-
- local preview = checkedkey(display,"preview","string")
- local toolbar = checkedkey(display,"toolbar","boolean")
-
- local embeddedreference = codeinjections.embedfile { file = filename }
-
- local flash = pdfdictionary {
- Subtype = pdfconstant("Flash"),
- Instances = pdfarray {
- pdfdictionary {
- Asset = embeddedreference,
- Params = pdfdictionary {
- Binding = pdfconstant("Background") -- Foreground makes swf behave erratic
- }
- },
- },
- }
-
- local flashreference = pdfreference(pdfflushobject(flash))
-
- local configuration = pdfdictionary {
- Configurations = pdfarray { flashreference },
- Assets = pdfdictionary {
- Names = pdfarray {
- pdfstring(filename),
- embeddedreference,
- }
- },
- }
-
- -- todo: check op subpath figuur (relatief)
-
- -- filename : ./test.swf (graphic)
- -- root : .
- -- prefix : ^%./
- -- fullname : ./assets/whatever.xml
- -- usedname : assets/whatever.xml
- -- filename : assets/whatever.xml
-
- local root = file.dirname(filename)
- local relativepaths = nil
- local paths = nil
-
- if resources then
- local names = configuration.Assets.Names
- local prefix = false
- if root ~= "" and root ~= "." then
- prefix = format("^%s/",string.topattern(root))
- end
- if prefix and trace_swf then
- report_swf("using strip pattern %a",prefix)
- end
- local function add(fullname,strip)
- local filename = gsub(fullname,"^%./","")
- local usedname = strip and prefix and gsub(filename,prefix,"") or filename
- local embeddedreference = codeinjections.embedfile {
- file = fullname,
- usedname = usedname,
- keepdir = true,
- }
- names[#names+1] = pdfstring(filename)
- names[#names+1] = embeddedreference
- if trace_swf then
- report_swf("embedding file %a as %a",fullname,usedname)
- end
- end
- relativepaths = resources.relativepaths
- if relativepaths then
- if trace_swf then
- report_swf("checking %s relative paths",#relativepaths)
- end
- for i=1,#relativepaths do
- local relativepath = relativepaths[i]
- if trace_swf then
- report_swf("checking path %a relative to %a",relativepath,root)
- end
- local path = file.join(root == "" and "." or root,relativepath)
- local files = dir.glob(path .. "/**")
- for i=1,#files do
- add(files[i],true)
- end
- end
- end
- paths = resources.paths
- if paths then
- if trace_swf then
- report_swf("checking absolute %s paths",#paths)
- end
- for i=1,#paths do
- local path = paths[i]
- if trace_swf then
- report_swf("checking path %a",path)
- end
- local files = dir.glob(path .. "/**")
- for i=1,#files do
- add(files[i],false)
- end
- end
- end
- local relativefiles = resources.relativefiles
- if relativefiles then
- if trace_swf then
- report_swf("checking %s relative files",#relativefiles)
- end
- for i=1,#relativefiles do
- add(relativefiles[i],true)
- end
- end
- local files = resources.files
- if files then
- if trace_swf then
- report_swf("checking absolute %s files",#files)
- end
- for i=1,#files do
- add(files[i],false)
- end
- end
- end
-
- local opendisplay = display and display.open or false
- local closedisplay = display and display.close or false
-
- local configurationreference = pdfreference(pdfflushobject(configuration))
-
- local activation = pdfdictionary {
- Type = pdfconstant("RichMediaActivation"),
- Condition = pdfconstant(activations[opendisplay]),
- Configuration = flashreference,
- Animation = pdfdictionary {
- Subtype = pdfconstant("Linear"),
- Speed = 1,
- Playcount = 1,
- },
- Presentation = pdfdictionary {
- PassContextClick = false,
- Style = pdfconstant("Embedded"),
- Toolbar = toolbar,
- NavigationPane = false,
- Transparent = true,
- Window = pdfdictionary {
- Type = pdfconstant("RichMediaWindow"),
- Width = pdfdictionary {
- Default = 100,
- Min = 100,
- Max = 100,
- },
- Height = pdfdictionary {
- Default = 100,
- Min = 100,
- Max = 100,
- },
- Position = pdfdictionary {
- Type = pdfconstant("RichMediaPosition"),
- HAlign = pdfconstant("Near"),
- VAlign = pdfconstant("Near"),
- HOffset = 0,
- VOffset = 0,
- }
- }
- },
- -- View
- -- Scripts
- }
-
- local deactivation = pdfdictionary {
- Type = pdfconstant("RichMediaDeactivation"),
- Condition = pdfconstant(deactivations[closedisplay]),
- }
-
- local richmediasettings = pdfdictionary {
- Type = pdfconstant("RichMediaSettings"),
- Activation = activation,
- Deactivation = deactivation,
- }
-
- local settingsreference = pdfreference(pdfflushobject(richmediasettings))
-
- local appearance
-
- if preview then
- preview = gsub(preview,"%*",file.nameonly(filename))
- local figure = codeinjections.getpreviewfigure { name = preview, width = width, height = height }
- if relativepaths and not figure then
- for i=1,#relativepaths do
- local path = file.join(root == "" and "." or root,relativepaths[i])
- if trace_swf then
- report_swf("checking preview on relative path %s",path)
- end
- local p = file.join(path,preview)
- figure = codeinjections.getpreviewfigure { name = p, width = width, height = height }
- if figure then
- preview = p
- break
- end
- end
- end
- if paths and not figure then
- for i=1,#paths do
- local path = paths[i]
- if trace_swf then
- report_swf("checking preview on absolute path %s",path)
- end
- local p = file.join(path,preview)
- figure = codeinjections.getpreviewfigure { name = p, width = width, height = height }
- if figure then
- preview = p
- break
- end
- end
- end
- if figure then
- local image = img.package(figure.status.private)
- appearance = pdfdictionary { N = pdfreference(image.objnum) }
- if trace_swf then
- report_swf("using preview %s",preview)
- end
- end
- end
-
- local annotation = pdfdictionary {
- Subtype = pdfconstant("RichMedia"),
- RichMediaContent = configurationreference,
- RichMediaSettings = settingsreference,
- AP = appearance,
- }
-
- return annotation, nil, nil
-
-end
-
-function backends.pdf.nodeinjections.insertswf(spec)
- local annotation, preview, ref = insertswf {
- foundname = spec.foundname,
- width = spec.width,
- height = spec.height,
- display = spec.display,
- controls = spec.controls,
- resources = spec.resources,
- -- factor = spec.factor,
- -- label = spec.label,
- }
- context(pdfannotation_node(spec.width,spec.height,0,annotation())) -- the context wrap is probably also needed elsewhere
-end
+if not modules then modules = { } end modules ['lpdf-swf'] = {
+ 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 tests by Luigi Scarso. His prototype
+-- was using tex code. This is the official implementation.
+
+local format, gsub = string.format, string.gsub
+
+local backends, lpdf = backends, lpdf
+
+local pdfconstant = lpdf.constant
+local pdfboolean = lpdf.boolean
+local pdfstring = lpdf.string
+local pdfunicode = lpdf.unicode
+local pdfdictionary = lpdf.dictionary
+local pdfarray = lpdf.array
+local pdfnull = lpdf.null
+local pdfreference = lpdf.reference
+local pdfflushobject = lpdf.flushobject
+
+local checkedkey = lpdf.checkedkey
+
+local codeinjections = backends.pdf.codeinjections
+local nodeinjections = backends.pdf.nodeinjections
+
+local pdfannotation_node = nodes.pool.pdfannotation
+
+local trace_swf = false trackers.register("backend.swf", function(v) trace_swf = v end)
+
+local report_swf = logs.reporter("backend","swf")
+
+local activations = {
+ click = "XA",
+ page = "PO",
+ focus = "PV",
+}
+
+local deactivations = {
+ click = "XD",
+ page = "PI",
+ focus = "PC",
+}
+
+table.setmetatableindex(activations, function() return activations .click end)
+table.setmetatableindex(deactivations,function() return deactivations.focus end)
+
+local function insertswf(spec)
+
+ local width = spec.width
+ local height = spec.height
+ local filename = spec.foundname
+ local resources = spec.resources
+ local display = spec.display
+ local controls = spec.controls
+
+ local resources = resources and parametersets[resources]
+ local display = display and parametersets[display]
+ local controls = controls and parametersets[controls] -- not yet used
+
+ local preview = checkedkey(display,"preview","string")
+ local toolbar = checkedkey(display,"toolbar","boolean")
+
+ local embeddedreference = codeinjections.embedfile { file = filename }
+
+ local flash = pdfdictionary {
+ Subtype = pdfconstant("Flash"),
+ Instances = pdfarray {
+ pdfdictionary {
+ Asset = embeddedreference,
+ Params = pdfdictionary {
+ Binding = pdfconstant("Background") -- Foreground makes swf behave erratic
+ }
+ },
+ },
+ }
+
+ local flashreference = pdfreference(pdfflushobject(flash))
+
+ local configuration = pdfdictionary {
+ Configurations = pdfarray { flashreference },
+ Assets = pdfdictionary {
+ Names = pdfarray {
+ pdfstring(filename),
+ embeddedreference,
+ }
+ },
+ }
+
+ -- todo: check op subpath figuur (relatief)
+
+ -- filename : ./test.swf (graphic)
+ -- root : .
+ -- prefix : ^%./
+ -- fullname : ./assets/whatever.xml
+ -- usedname : assets/whatever.xml
+ -- filename : assets/whatever.xml
+
+ local root = file.dirname(filename)
+ local relativepaths = nil
+ local paths = nil
+
+ if resources then
+ local names = configuration.Assets.Names
+ local prefix = false
+ if root ~= "" and root ~= "." then
+ prefix = format("^%s/",string.topattern(root))
+ end
+ if prefix and trace_swf then
+ report_swf("using strip pattern %a",prefix)
+ end
+ local function add(fullname,strip)
+ local filename = gsub(fullname,"^%./","")
+ local usedname = strip and prefix and gsub(filename,prefix,"") or filename
+ local embeddedreference = codeinjections.embedfile {
+ file = fullname,
+ usedname = usedname,
+ keepdir = true,
+ }
+ names[#names+1] = pdfstring(filename)
+ names[#names+1] = embeddedreference
+ if trace_swf then
+ report_swf("embedding file %a as %a",fullname,usedname)
+ end
+ end
+ relativepaths = resources.relativepaths
+ if relativepaths then
+ if trace_swf then
+ report_swf("checking %s relative paths",#relativepaths)
+ end
+ for i=1,#relativepaths do
+ local relativepath = relativepaths[i]
+ if trace_swf then
+ report_swf("checking path %a relative to %a",relativepath,root)
+ end
+ local path = file.join(root == "" and "." or root,relativepath)
+ local files = dir.glob(path .. "/**")
+ for i=1,#files do
+ add(files[i],true)
+ end
+ end
+ end
+ paths = resources.paths
+ if paths then
+ if trace_swf then
+ report_swf("checking absolute %s paths",#paths)
+ end
+ for i=1,#paths do
+ local path = paths[i]
+ if trace_swf then
+ report_swf("checking path %a",path)
+ end
+ local files = dir.glob(path .. "/**")
+ for i=1,#files do
+ add(files[i],false)
+ end
+ end
+ end
+ local relativefiles = resources.relativefiles
+ if relativefiles then
+ if trace_swf then
+ report_swf("checking %s relative files",#relativefiles)
+ end
+ for i=1,#relativefiles do
+ add(relativefiles[i],true)
+ end
+ end
+ local files = resources.files
+ if files then
+ if trace_swf then
+ report_swf("checking absolute %s files",#files)
+ end
+ for i=1,#files do
+ add(files[i],false)
+ end
+ end
+ end
+
+ local opendisplay = display and display.open or false
+ local closedisplay = display and display.close or false
+
+ local configurationreference = pdfreference(pdfflushobject(configuration))
+
+ local activation = pdfdictionary {
+ Type = pdfconstant("RichMediaActivation"),
+ Condition = pdfconstant(activations[opendisplay]),
+ Configuration = flashreference,
+ Animation = pdfdictionary {
+ Subtype = pdfconstant("Linear"),
+ Speed = 1,
+ Playcount = 1,
+ },
+ Presentation = pdfdictionary {
+ PassContextClick = false,
+ Style = pdfconstant("Embedded"),
+ Toolbar = toolbar,
+ NavigationPane = false,
+ Transparent = true,
+ Window = pdfdictionary {
+ Type = pdfconstant("RichMediaWindow"),
+ Width = pdfdictionary {
+ Default = 100,
+ Min = 100,
+ Max = 100,
+ },
+ Height = pdfdictionary {
+ Default = 100,
+ Min = 100,
+ Max = 100,
+ },
+ Position = pdfdictionary {
+ Type = pdfconstant("RichMediaPosition"),
+ HAlign = pdfconstant("Near"),
+ VAlign = pdfconstant("Near"),
+ HOffset = 0,
+ VOffset = 0,
+ }
+ }
+ },
+ -- View
+ -- Scripts
+ }
+
+ local deactivation = pdfdictionary {
+ Type = pdfconstant("RichMediaDeactivation"),
+ Condition = pdfconstant(deactivations[closedisplay]),
+ }
+
+ local richmediasettings = pdfdictionary {
+ Type = pdfconstant("RichMediaSettings"),
+ Activation = activation,
+ Deactivation = deactivation,
+ }
+
+ local settingsreference = pdfreference(pdfflushobject(richmediasettings))
+
+ local appearance
+
+ if preview then
+ preview = gsub(preview,"%*",file.nameonly(filename))
+ local figure = codeinjections.getpreviewfigure { name = preview, width = width, height = height }
+ if relativepaths and not figure then
+ for i=1,#relativepaths do
+ local path = file.join(root == "" and "." or root,relativepaths[i])
+ if trace_swf then
+ report_swf("checking preview on relative path %s",path)
+ end
+ local p = file.join(path,preview)
+ figure = codeinjections.getpreviewfigure { name = p, width = width, height = height }
+ if figure then
+ preview = p
+ break
+ end
+ end
+ end
+ if paths and not figure then
+ for i=1,#paths do
+ local path = paths[i]
+ if trace_swf then
+ report_swf("checking preview on absolute path %s",path)
+ end
+ local p = file.join(path,preview)
+ figure = codeinjections.getpreviewfigure { name = p, width = width, height = height }
+ if figure then
+ preview = p
+ break
+ end
+ end
+ end
+ if figure then
+ local image = img.package(figure.status.private)
+ appearance = pdfdictionary { N = pdfreference(image.objnum) }
+ if trace_swf then
+ report_swf("using preview %s",preview)
+ end
+ end
+ end
+
+ local annotation = pdfdictionary {
+ Subtype = pdfconstant("RichMedia"),
+ RichMediaContent = configurationreference,
+ RichMediaSettings = settingsreference,
+ AP = appearance,
+ }
+
+ return annotation, nil, nil
+
+end
+
+function backends.pdf.nodeinjections.insertswf(spec)
+ local annotation, preview, ref = insertswf {
+ foundname = spec.foundname,
+ width = spec.width,
+ height = spec.height,
+ display = spec.display,
+ controls = spec.controls,
+ resources = spec.resources,
+ -- factor = spec.factor,
+ -- label = spec.label,
+ }
+ context(pdfannotation_node(spec.width,spec.height,0,annotation())) -- the context wrap is probably also needed elsewhere
+end
diff --git a/tex/context/base/lpdf-tag.lua b/tex/context/base/lpdf-tag.lua
index f5766996c..8cdb5f6a4 100644
--- a/tex/context/base/lpdf-tag.lua
+++ b/tex/context/base/lpdf-tag.lua
@@ -1,313 +1,313 @@
-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 format, match, concat = string.format, string.match, table.concat
-local lpegmatch = lpeg.match
-local utfchar = utf.char
-
-local trace_tags = false trackers.register("structures.tags", function(v) trace_tags = v end)
-
-local report_tags = logs.reporter("backend","tags")
-
-local backends, lpdf, nodes = backends, lpdf, nodes
-
-local nodeinjections = backends.pdf.nodeinjections
-local codeinjections = backends.pdf.codeinjections
-
-local tasks = nodes.tasks
-
-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 pdfstring = lpdf.string
-local pdfflushobject = lpdf.flushobject
-local pdfreserveobject = lpdf.reserveobject
-local pdfpagereference = lpdf.pagereference
-
-local nodepool = nodes.pool
-
-local pdfliteral = nodepool.pdfliteral
-
-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 traverse_nodes = node.traverse
-local traverse_id = node.traverse_id
-local tosequence = nodes.tosequence
-local copy_node = node.copy
-local slide_nodelist = node.slide
-
-local structure_stack = { }
-local structure_kids = pdfarray()
-local structure_ref = pdfreserveobject()
-local parent_ref = pdfreserveobject()
-local root = { pref = pdfreference(structure_ref), kids = structure_kids }
-local tree = { }
-local elements = { }
-local names = pdfarray()
-local taglist = structures.tags.taglist
-local usedlabels = structures.tags.labels
-local properties = structures.tags.properties
-local usedmapping = { }
-
-local colonsplitter = lpeg.splitat(":")
-local dashsplitter = lpeg.splitat("-")
-
-local add_ids = false -- true
-
-
---~ function codeinjections.maptag(original,target,kind)
---~ mapping[original] = { target, kind or "inline" }
---~ end
-
-local function finishstructure()
- if #structure_kids > 0 then
- local nums, n = pdfarray(), 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
- }
- -- we need to split names into smaller parts (e.g. alphabetic or so)
- if add_ids then
- local kids = pdfdictionary {
- Limits = pdfarray { names[1], names[#names-1] },
- Names = names,
- }
- local idtree = pdfdictionary {
- Kids = pdfarray { pdfreference(pdfflushobject(kids)) },
- }
- end
- --
- 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 = (add_ids and pdfreference(pdfflushobject(idtree))) or nil,
- RoleMap = rolemap,
- }
- pdfflushobject(structure_ref,structuretree)
- lpdf.addtocatalog("StructTreeRoot",pdfreference(structure_ref))
- --
- local markinfo = pdfdictionary {
- Marked = pdfboolean(true),
- -- UserProperties = pdfboolean(true),
- -- Suspects = pdfboolean(true),
- }
- lpdf.addtocatalog("MarkInfo",pdfreference(pdfflushobject(markinfo)))
- --
- for fulltag, element in next, elements do
- 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 function initializepage()
- index = 0
- pagenum = tex.count.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
- lpdf.addtopageattributes("StructParents",pagenum-1)
-end
-
--- here we can flush and free elements that are finished
-
-local function makeelement(fulltag,parent)
- local tag, n = lpegmatch(dashsplitter,fulltag)
- local tg, detail = lpegmatch(colonsplitter,tag)
- local k, r = pdfarray(), pdfreserveobject()
- usedmapping[tg] = true
- tg = usedlabels[tg] or tg
- local d = pdfdictionary {
- Type = pdf_struct_element,
- S = pdfconstant(tg),
- ID = (add_ids and fulltag) or nil,
- T = detail and detail or nil,
- P = parent.pref,
- Pg = pageref,
- K = pdfreference(r),
- -- Alt = " Who cares ",
- -- ActualText = " Hi Hans ",
- }
- local s = pdfreference(pdfflushobject(d))
- if add_ids then
- names[#names+1] = fulltag
- names[#names+1] = s
- end
- local kids = parent.kids
- kids[#kids+1] = s
- elements[fulltag] = { tag = tag, pref = s, kids = k, knum = r, pnum = pagenum }
-end
-
-local function makecontent(parent,start,stop,slist,id)
- local tag, kids = parent.tag, parent.kids
- local last = index
- if id == "image" then
- local d = pdfdictionary {
- Type = pdf_mcr,
- Pg = pageref,
- MCID = last,
- Alt = "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
- --
- local bliteral = pdfliteral(format("/%s <</MCID %s>>BDC",tag,last))
- local prev = start.prev
- if prev then
- prev.next, bliteral.prev = bliteral, prev
- end
- start.prev, bliteral.next = bliteral, start
- if slist and slist.list == start then
- slist.list = bliteral
- elseif not prev then
- report_tags("this can't happen: injection in front of nothing")
- end
- --
- local eliteral = pdfliteral("EMC")
- local next = stop.next
- if next then
- next.prev, eliteral.next = eliteral, next
- end
- stop.next, eliteral.prev = eliteral, stop
- --
- index = index + 1
- list[index] = parent.pref
- return bliteral, eliteral
-end
-
--- -- --
-
-local level, last, ranges, range = 0, nil, { }, nil
-
-local function collectranges(head,list)
- for n in traverse_nodes(head) do
- local id = n.id -- 14: image, 8: literal (mp)
- if id == glyph_code then
- local at = 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 = n[a_image]
- if at then
- local at = 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 = n.list
- slide_nodelist(nl) -- temporary hack till math gets slided (tracker item)
- collectranges(nl,n)
- end
- end
- end
-end
-
-function nodeinjections.addtags(head)
- -- no need to adapt head, as we always operate on lists
- level, last, ranges, range = 0, nil, { }, nil
- initializepage()
- collectranges(head)
- if trace_tags then
- for i=1,#ranges do
- local range = ranges[i]
- local attr, id, start, stop = range[1], range[2], range[3], 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)
- end
- end
- end
- for i=1,#ranges do
- local range = ranges[i]
- local attr, id, start, stop, list = range[1], range[2], range[3], range[4], range[5]
- local tags = taglist[attr]
- local prev = root
- local noftags, tag = #tags, nil
- for j=1,noftags do
- local tag = tags[j]
- if not elements[tag] then
- makeelement(tag,prev)
- end
- prev = elements[tag]
- end
- local b, e = makecontent(prev,start,stop,list,id)
- if start == head then
- report_tags("this can't happen: parent list gets tagged")
- head = b
- end
- end
- finishpage()
- -- can be separate feature
- --
- -- injectspans(head) -- does to work yet
- --
- return head, true
-end
-
--- this belongs elsewhere (export is not pdf related)
-
-function codeinjections.enabletags(tg,lb)
- structures.tags.handler = nodeinjections.addtags
- tasks.enableaction("shipouts","structures.tags.handler")
- tasks.enableaction("shipouts","nodes.handlers.accessibility")
- tasks.enableaction("math","noads.handlers.tags")
- -- maybe also textblock
- if trace_tags then
- report_tags("enabling structure tags")
- end
-end
+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 format, match, concat = string.format, string.match, table.concat
+local lpegmatch = lpeg.match
+local utfchar = utf.char
+
+local trace_tags = false trackers.register("structures.tags", function(v) trace_tags = v end)
+
+local report_tags = logs.reporter("backend","tags")
+
+local backends, lpdf, nodes = backends, lpdf, nodes
+
+local nodeinjections = backends.pdf.nodeinjections
+local codeinjections = backends.pdf.codeinjections
+
+local tasks = nodes.tasks
+
+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 pdfstring = lpdf.string
+local pdfflushobject = lpdf.flushobject
+local pdfreserveobject = lpdf.reserveobject
+local pdfpagereference = lpdf.pagereference
+
+local nodepool = nodes.pool
+
+local pdfliteral = nodepool.pdfliteral
+
+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 traverse_nodes = node.traverse
+local traverse_id = node.traverse_id
+local tosequence = nodes.tosequence
+local copy_node = node.copy
+local slide_nodelist = node.slide
+
+local structure_stack = { }
+local structure_kids = pdfarray()
+local structure_ref = pdfreserveobject()
+local parent_ref = pdfreserveobject()
+local root = { pref = pdfreference(structure_ref), kids = structure_kids }
+local tree = { }
+local elements = { }
+local names = pdfarray()
+local taglist = structures.tags.taglist
+local usedlabels = structures.tags.labels
+local properties = structures.tags.properties
+local usedmapping = { }
+
+local colonsplitter = lpeg.splitat(":")
+local dashsplitter = lpeg.splitat("-")
+
+local add_ids = false -- true
+
+
+--~ function codeinjections.maptag(original,target,kind)
+--~ mapping[original] = { target, kind or "inline" }
+--~ end
+
+local function finishstructure()
+ if #structure_kids > 0 then
+ local nums, n = pdfarray(), 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
+ }
+ -- we need to split names into smaller parts (e.g. alphabetic or so)
+ if add_ids then
+ local kids = pdfdictionary {
+ Limits = pdfarray { names[1], names[#names-1] },
+ Names = names,
+ }
+ local idtree = pdfdictionary {
+ Kids = pdfarray { pdfreference(pdfflushobject(kids)) },
+ }
+ end
+ --
+ 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 = (add_ids and pdfreference(pdfflushobject(idtree))) or nil,
+ RoleMap = rolemap,
+ }
+ pdfflushobject(structure_ref,structuretree)
+ lpdf.addtocatalog("StructTreeRoot",pdfreference(structure_ref))
+ --
+ local markinfo = pdfdictionary {
+ Marked = pdfboolean(true),
+ -- UserProperties = pdfboolean(true),
+ -- Suspects = pdfboolean(true),
+ }
+ lpdf.addtocatalog("MarkInfo",pdfreference(pdfflushobject(markinfo)))
+ --
+ for fulltag, element in next, elements do
+ 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 function initializepage()
+ index = 0
+ pagenum = tex.count.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
+ lpdf.addtopageattributes("StructParents",pagenum-1)
+end
+
+-- here we can flush and free elements that are finished
+
+local function makeelement(fulltag,parent)
+ local tag, n = lpegmatch(dashsplitter,fulltag)
+ local tg, detail = lpegmatch(colonsplitter,tag)
+ local k, r = pdfarray(), pdfreserveobject()
+ usedmapping[tg] = true
+ tg = usedlabels[tg] or tg
+ local d = pdfdictionary {
+ Type = pdf_struct_element,
+ S = pdfconstant(tg),
+ ID = (add_ids and fulltag) or nil,
+ T = detail and detail or nil,
+ P = parent.pref,
+ Pg = pageref,
+ K = pdfreference(r),
+ -- Alt = " Who cares ",
+ -- ActualText = " Hi Hans ",
+ }
+ local s = pdfreference(pdfflushobject(d))
+ if add_ids then
+ names[#names+1] = fulltag
+ names[#names+1] = s
+ end
+ local kids = parent.kids
+ kids[#kids+1] = s
+ elements[fulltag] = { tag = tag, pref = s, kids = k, knum = r, pnum = pagenum }
+end
+
+local function makecontent(parent,start,stop,slist,id)
+ local tag, kids = parent.tag, parent.kids
+ local last = index
+ if id == "image" then
+ local d = pdfdictionary {
+ Type = pdf_mcr,
+ Pg = pageref,
+ MCID = last,
+ Alt = "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
+ --
+ local bliteral = pdfliteral(format("/%s <</MCID %s>>BDC",tag,last))
+ local prev = start.prev
+ if prev then
+ prev.next, bliteral.prev = bliteral, prev
+ end
+ start.prev, bliteral.next = bliteral, start
+ if slist and slist.list == start then
+ slist.list = bliteral
+ elseif not prev then
+ report_tags("this can't happen: injection in front of nothing")
+ end
+ --
+ local eliteral = pdfliteral("EMC")
+ local next = stop.next
+ if next then
+ next.prev, eliteral.next = eliteral, next
+ end
+ stop.next, eliteral.prev = eliteral, stop
+ --
+ index = index + 1
+ list[index] = parent.pref
+ return bliteral, eliteral
+end
+
+-- -- --
+
+local level, last, ranges, range = 0, nil, { }, nil
+
+local function collectranges(head,list)
+ for n in traverse_nodes(head) do
+ local id = n.id -- 14: image, 8: literal (mp)
+ if id == glyph_code then
+ local at = 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 = n[a_image]
+ if at then
+ local at = 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 = n.list
+ slide_nodelist(nl) -- temporary hack till math gets slided (tracker item)
+ collectranges(nl,n)
+ end
+ end
+ end
+end
+
+function nodeinjections.addtags(head)
+ -- no need to adapt head, as we always operate on lists
+ level, last, ranges, range = 0, nil, { }, nil
+ initializepage()
+ collectranges(head)
+ if trace_tags then
+ for i=1,#ranges do
+ local range = ranges[i]
+ local attr, id, start, stop = range[1], range[2], range[3], 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)
+ end
+ end
+ end
+ for i=1,#ranges do
+ local range = ranges[i]
+ local attr, id, start, stop, list = range[1], range[2], range[3], range[4], range[5]
+ local tags = taglist[attr]
+ local prev = root
+ local noftags, tag = #tags, nil
+ for j=1,noftags do
+ local tag = tags[j]
+ if not elements[tag] then
+ makeelement(tag,prev)
+ end
+ prev = elements[tag]
+ end
+ local b, e = makecontent(prev,start,stop,list,id)
+ if start == head then
+ report_tags("this can't happen: parent list gets tagged")
+ head = b
+ end
+ end
+ finishpage()
+ -- can be separate feature
+ --
+ -- injectspans(head) -- does to work yet
+ --
+ return head, true
+end
+
+-- this belongs elsewhere (export is not pdf related)
+
+function codeinjections.enabletags(tg,lb)
+ structures.tags.handler = nodeinjections.addtags
+ tasks.enableaction("shipouts","structures.tags.handler")
+ tasks.enableaction("shipouts","nodes.handlers.accessibility")
+ tasks.enableaction("math","noads.handlers.tags")
+ -- maybe also textblock
+ if trace_tags then
+ report_tags("enabling structure tags")
+ end
+end
diff --git a/tex/context/base/lpdf-u3d.lua b/tex/context/base/lpdf-u3d.lua
index 464ea6fa7..33269486c 100644
--- a/tex/context/base/lpdf-u3d.lua
+++ b/tex/context/base/lpdf-u3d.lua
@@ -1,488 +1,488 @@
-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 format, find = string.format, 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 pdfnumber = lpdf.number
-local pdfunicode = lpdf.unicode
-local pdfdictionary = lpdf.dictionary
-local pdfarray = lpdf.array
-local pdfnull = lpdf.null
-local pdfreference = lpdf.reference
-local pdfflushstreamobject = lpdf.flushstreamobject
-local pdfflushstreamfileobject = lpdf.flushstreamfileobject
-
-local checkedkey = lpdf.checkedkey
-local limited = lpdf.limited
-
-local pdfannotation_node = nodes.pool.pdfannotation
-
-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 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 = format("%s:%s:%s",label,stream,preview)
- local ref = stored_pr[tag]
- if not ref then
- local figure = img.immediatewrite {
- 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 zero, one = pdfnumber(0), pdfnumber(1) -- not really needed
- local pw = pdfdictionary {
- Type = pdfconstant("XObject"),
- Subtype = pdfconstant("Form"),
- FormType = one,
- BBox = pdfarray { zero, zero, pdfnumber(factor*width), pdfnumber(factor*height) },
- Matrix = pdfarray { one, zero, zero, one, zero, zero },
- Resources = pdfdictionary {
- XObject = pdfdictionary {
- IM = pdfreference(ref)
- }
- },
- ExtGState = pdfdictionary {
- GS = pdfdictionary {
- Type = pdfconstant("ExtGState"),
- CA = one,
- ca = one,
- }
- },
- ProcSet = pdfarray { pdfconstant("PDF"), pdfconstant("ImageC") },
- }
- local pwd = pdfflushstreamobject(format("q /GS gs %f 0 0 %f 0 0 cm /IM Do Q",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(pdfannotation_node(spec.width,spec.height,0,annotation()))
-end
+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 format, find = string.format, 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 pdfnumber = lpdf.number
+local pdfunicode = lpdf.unicode
+local pdfdictionary = lpdf.dictionary
+local pdfarray = lpdf.array
+local pdfnull = lpdf.null
+local pdfreference = lpdf.reference
+local pdfflushstreamobject = lpdf.flushstreamobject
+local pdfflushstreamfileobject = lpdf.flushstreamfileobject
+
+local checkedkey = lpdf.checkedkey
+local limited = lpdf.limited
+
+local pdfannotation_node = nodes.pool.pdfannotation
+
+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 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 = format("%s:%s:%s",label,stream,preview)
+ local ref = stored_pr[tag]
+ if not ref then
+ local figure = img.immediatewrite {
+ 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 zero, one = pdfnumber(0), pdfnumber(1) -- not really needed
+ local pw = pdfdictionary {
+ Type = pdfconstant("XObject"),
+ Subtype = pdfconstant("Form"),
+ FormType = one,
+ BBox = pdfarray { zero, zero, pdfnumber(factor*width), pdfnumber(factor*height) },
+ Matrix = pdfarray { one, zero, zero, one, zero, zero },
+ Resources = pdfdictionary {
+ XObject = pdfdictionary {
+ IM = pdfreference(ref)
+ }
+ },
+ ExtGState = pdfdictionary {
+ GS = pdfdictionary {
+ Type = pdfconstant("ExtGState"),
+ CA = one,
+ ca = one,
+ }
+ },
+ ProcSet = pdfarray { pdfconstant("PDF"), pdfconstant("ImageC") },
+ }
+ local pwd = pdfflushstreamobject(format("q /GS gs %f 0 0 %f 0 0 cm /IM Do Q",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(pdfannotation_node(spec.width,spec.height,0,annotation()))
+end
diff --git a/tex/context/base/lpdf-wid.lua b/tex/context/base/lpdf-wid.lua
index 20fc14679..9ea4744f1 100644
--- a/tex/context/base/lpdf-wid.lua
+++ b/tex/context/base/lpdf-wid.lua
@@ -1,645 +1,645 @@
-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"
-}
-
-local gmatch, gsub, find, lower, format = string.gmatch, string.gsub, string.find, string.lower, string.format
-local stripstring = string.strip
-local texbox, texcount = tex.box, tex.count
-local settings_to_array = utilities.parsers.settings_to_array
-local settings_to_hash = utilities.parsers.settings_to_hash
-
-local report_media = logs.reporter("backend","media")
-local report_attachment = logs.reporter("backend","attachment")
-
-local backends, lpdf, nodes = backends, lpdf, nodes
-
-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_normal = variables.normal
-local v_auto = variables.auto
-local v_embed = variables.embed
-local v_unknown = variables.unknown
-local v_max = variables.max
-
-local pdfconstant = lpdf.constant
-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 pdfcolorspec = lpdf.colorspec
-local pdfflushobject = lpdf.flushobject
-local pdfflushstreamobject = lpdf.flushstreamobject
-local pdfflushstreamfileobject = lpdf.flushstreamfileobject
-local pdfreserveannotation = lpdf.reserveannotation
-local pdfreserveobject = lpdf.reserveobject
-local pdfpagereference = lpdf.pagereference
-local pdfshareobjectreference = lpdf.shareobjectreference
-
-local nodepool = nodes.pool
-
-local pdfannotation_node = nodepool.pdfannotation
-
-local hpack_node = node.hpack
-local write_node = node.write -- test context(...) instead
-
-local pdf_border = pdfarray { 0, 0, 0 } -- can be shared
-
--- 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("GraphPushPin"),
- Paperclip = pdfconstant("PaperclipTag"),
- Pushpin = pdfconstant("PushPin"),
-}
-
-attachment_symbols.PushPin = attachment_symbols.Pushpin
-attachment_symbols.Default = attachment_symbols.Pushpin
-
-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
-
-local function analyzesymbol(symbol,collection)
- if not symbol or symbol == "" then
- return collection.Default, nil
- elseif 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 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 { lpdf.colorvalues(cmodel,cvalue) } or nil
-end
-
-local function analyzetransparency(transparencyvalue)
- local tvalue = transparencyvalue and tonumber(transparencyvalue)
- return tvalue and lpdf.transparencyvalue(tvalue) or nil
-end
-
--- Attachments
-
-local nofattachments, attachments, filestreams, referenced = 0, { }, { }, { }
-
-local ignorereferenced = true -- fuzzy pdf spec .. twice in attachment list, can become an option
-
-local function flushembeddedfiles()
- if next(filestreams) then
- local e = pdfarray()
- for tag, reference in next, 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
- else
- -- messy spec ... when annot not in named else twice in menu list acrobat
- end
- end
- lpdf.addtonames("EmbeddedFiles",pdfreference(pdfflushobject(pdfdictionary{ Names = e })))
- end
-end
-
-lpdf.registerdocumentfinalizer(flushembeddedfiles,"embeddedfiles")
-
-function codeinjections.embedfile(specification)
- 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
- if filename == "" then
- filename = nil
- 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 lfs.isfile(foundname) then
- filestreams[filename] = false
- return nil
- else
- specification.foundname = foundname
- end
- end
- end
- usedname = usedname ~= "" and usedname or filename
- local basename = keepdir == true and usedname or file.basename(usedname)
-local basename = gsub(basename,"%./","")
- local savename = file.addsuffix(name ~= "" and name or basename,"txt") -- else no valid file
- local a = pdfdictionary { Type = pdfconstant("EmbeddedFile") }
- local f
- if data then
- f = pdfflushstreamobject(data,a)
- specification.data = true -- signal that still data but already flushed
- else
- local foundname = specification.foundname or filename
- f = pdfflushstreamfileobject(foundname,a)
- end
- local d = pdfdictionary {
- Type = pdfconstant("Filespec"),
- F = pdfstring(savename),
- UF = pdfstring(savename),
- EF = pdfdictionary { F = pdfreference(f) },
- Desc = title ~= "" and pdfunicode(title) or nil,
- }
- local r = pdfreference(pdfflushobject(d))
- filestreams[hash] = r
- return r
-end
-
-function nodeinjections.attachfile(specification)
- 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 lfs.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 ""
- if registered == "" then
- registered = filename
- end
- if author == "" then
- author = title
- title = ""
- end
- if author == "" then
- author = filename or "<unknown>"
- end
- if title == "" then
- title = registered
- end
- local aref = attachments[registered]
- if not aref then
- aref = codeinjections.embedfile(specification)
- attachments[registered] = aref
- 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 d = pdfdictionary {
- Subtype = pdfconstant("FileAttachment"),
- FS = aref,
- Contents = pdfunicode(title),
- Name = name,
- NM = pdfstring(format("attachment:%s",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),
- }
- local width, height, depth = specification.width or 0, specification.height or 0, specification.depth
- local box = hpack_node(pdfannotation_node(width,height,depth,d()))
- box.width, box.height, box.depth = width, height, depth
- return box
- end
-end
-
-function codeinjections.attachmentid(filename) -- not used in context
- return filestreams[filename]
-end
-
-local nofcomments, usepopupcomments, stripleading = 0, false, true
-
-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 = stripstring(specification.data or "")
- if stripleading then
- text = gsub(text,"[\n\r] *","\n")
- end
- 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 title == "" then
- title = tag
- end
- else
- if subtitle == "" then
- subtitle = title
- elseif title ~= "" then
- subtitle = subtitle .. ", " .. title
- end
- title = author
- 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(format("comment:%s",nofcomments)),
- AP = appearance,
- }
- local width, height, depth = specification.width or 0, specification.height or 0, specification.depth
- local box
- if usepopupcomments then
- -- rather useless as we can hide/vide
- local nd = pdfreserveannotation()
- local nc = pdfreserveannotation()
- local c = pdfdictionary {
- Subtype = pdfconstant("Popup"),
- Parent = pdfreference(nd),
- }
- d.Popup = pdfreference(nc)
- box = hpack_node(
- pdfannotation_node(0,0,0,d(),nd),
- pdfannotation_node(width,height,depth,c(),nc)
- )
- else
- box = hpack_node(pdfannotation_node(width,height,depth,d()))
- end
- box.width, box.height, box.depth = width, height, 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 = pdfreserveannotation()
- 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 lpdf.action(openpage )) or nil,
- PC = (closepage and lpdf.action(closepage)) or nil,
- }
- end
- local page = tonumber(specification.page) or texcount.realpageno -- todo
- local r = mu[label] or pdfreserveannotation() -- why the reserve here?
- local a = pdfdictionary {
- S = pdfconstant("Rendition"),
- R = mf[label],
- OP = 0,
- AN = pdfreference(r),
- }
- 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 = pdf_border,
- 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(pdfannotation_node(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,"://")
- --~ 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 }
- end
- local clip = pdfdictionary {
- Type = pdfconstant("MediaClip"),
- S = pdfconstant("MCD"),
- N = label,
- CT = specification.mime,
- 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
+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"
+}
+
+local gmatch, gsub, find, lower, format = string.gmatch, string.gsub, string.find, string.lower, string.format
+local stripstring = string.strip
+local texbox, texcount = tex.box, tex.count
+local settings_to_array = utilities.parsers.settings_to_array
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+local report_media = logs.reporter("backend","media")
+local report_attachment = logs.reporter("backend","attachment")
+
+local backends, lpdf, nodes = backends, lpdf, nodes
+
+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_normal = variables.normal
+local v_auto = variables.auto
+local v_embed = variables.embed
+local v_unknown = variables.unknown
+local v_max = variables.max
+
+local pdfconstant = lpdf.constant
+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 pdfcolorspec = lpdf.colorspec
+local pdfflushobject = lpdf.flushobject
+local pdfflushstreamobject = lpdf.flushstreamobject
+local pdfflushstreamfileobject = lpdf.flushstreamfileobject
+local pdfreserveannotation = lpdf.reserveannotation
+local pdfreserveobject = lpdf.reserveobject
+local pdfpagereference = lpdf.pagereference
+local pdfshareobjectreference = lpdf.shareobjectreference
+
+local nodepool = nodes.pool
+
+local pdfannotation_node = nodepool.pdfannotation
+
+local hpack_node = node.hpack
+local write_node = node.write -- test context(...) instead
+
+local pdf_border = pdfarray { 0, 0, 0 } -- can be shared
+
+-- 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("GraphPushPin"),
+ Paperclip = pdfconstant("PaperclipTag"),
+ Pushpin = pdfconstant("PushPin"),
+}
+
+attachment_symbols.PushPin = attachment_symbols.Pushpin
+attachment_symbols.Default = attachment_symbols.Pushpin
+
+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
+
+local function analyzesymbol(symbol,collection)
+ if not symbol or symbol == "" then
+ return collection.Default, nil
+ elseif 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 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 { lpdf.colorvalues(cmodel,cvalue) } or nil
+end
+
+local function analyzetransparency(transparencyvalue)
+ local tvalue = transparencyvalue and tonumber(transparencyvalue)
+ return tvalue and lpdf.transparencyvalue(tvalue) or nil
+end
+
+-- Attachments
+
+local nofattachments, attachments, filestreams, referenced = 0, { }, { }, { }
+
+local ignorereferenced = true -- fuzzy pdf spec .. twice in attachment list, can become an option
+
+local function flushembeddedfiles()
+ if next(filestreams) then
+ local e = pdfarray()
+ for tag, reference in next, 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
+ else
+ -- messy spec ... when annot not in named else twice in menu list acrobat
+ end
+ end
+ lpdf.addtonames("EmbeddedFiles",pdfreference(pdfflushobject(pdfdictionary{ Names = e })))
+ end
+end
+
+lpdf.registerdocumentfinalizer(flushembeddedfiles,"embeddedfiles")
+
+function codeinjections.embedfile(specification)
+ 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
+ if filename == "" then
+ filename = nil
+ 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 lfs.isfile(foundname) then
+ filestreams[filename] = false
+ return nil
+ else
+ specification.foundname = foundname
+ end
+ end
+ end
+ usedname = usedname ~= "" and usedname or filename
+ local basename = keepdir == true and usedname or file.basename(usedname)
+local basename = gsub(basename,"%./","")
+ local savename = file.addsuffix(name ~= "" and name or basename,"txt") -- else no valid file
+ local a = pdfdictionary { Type = pdfconstant("EmbeddedFile") }
+ local f
+ if data then
+ f = pdfflushstreamobject(data,a)
+ specification.data = true -- signal that still data but already flushed
+ else
+ local foundname = specification.foundname or filename
+ f = pdfflushstreamfileobject(foundname,a)
+ end
+ local d = pdfdictionary {
+ Type = pdfconstant("Filespec"),
+ F = pdfstring(savename),
+ UF = pdfstring(savename),
+ EF = pdfdictionary { F = pdfreference(f) },
+ Desc = title ~= "" and pdfunicode(title) or nil,
+ }
+ local r = pdfreference(pdfflushobject(d))
+ filestreams[hash] = r
+ return r
+end
+
+function nodeinjections.attachfile(specification)
+ 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 lfs.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 ""
+ if registered == "" then
+ registered = filename
+ end
+ if author == "" then
+ author = title
+ title = ""
+ end
+ if author == "" then
+ author = filename or "<unknown>"
+ end
+ if title == "" then
+ title = registered
+ end
+ local aref = attachments[registered]
+ if not aref then
+ aref = codeinjections.embedfile(specification)
+ attachments[registered] = aref
+ 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 d = pdfdictionary {
+ Subtype = pdfconstant("FileAttachment"),
+ FS = aref,
+ Contents = pdfunicode(title),
+ Name = name,
+ NM = pdfstring(format("attachment:%s",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),
+ }
+ local width, height, depth = specification.width or 0, specification.height or 0, specification.depth
+ local box = hpack_node(pdfannotation_node(width,height,depth,d()))
+ box.width, box.height, box.depth = width, height, depth
+ return box
+ end
+end
+
+function codeinjections.attachmentid(filename) -- not used in context
+ return filestreams[filename]
+end
+
+local nofcomments, usepopupcomments, stripleading = 0, false, true
+
+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 = stripstring(specification.data or "")
+ if stripleading then
+ text = gsub(text,"[\n\r] *","\n")
+ end
+ 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 title == "" then
+ title = tag
+ end
+ else
+ if subtitle == "" then
+ subtitle = title
+ elseif title ~= "" then
+ subtitle = subtitle .. ", " .. title
+ end
+ title = author
+ 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(format("comment:%s",nofcomments)),
+ AP = appearance,
+ }
+ local width, height, depth = specification.width or 0, specification.height or 0, specification.depth
+ local box
+ if usepopupcomments then
+ -- rather useless as we can hide/vide
+ local nd = pdfreserveannotation()
+ local nc = pdfreserveannotation()
+ local c = pdfdictionary {
+ Subtype = pdfconstant("Popup"),
+ Parent = pdfreference(nd),
+ }
+ d.Popup = pdfreference(nc)
+ box = hpack_node(
+ pdfannotation_node(0,0,0,d(),nd),
+ pdfannotation_node(width,height,depth,c(),nc)
+ )
+ else
+ box = hpack_node(pdfannotation_node(width,height,depth,d()))
+ end
+ box.width, box.height, box.depth = width, height, 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 = pdfreserveannotation()
+ 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 lpdf.action(openpage )) or nil,
+ PC = (closepage and lpdf.action(closepage)) or nil,
+ }
+ end
+ local page = tonumber(specification.page) or texcount.realpageno -- todo
+ local r = mu[label] or pdfreserveannotation() -- why the reserve here?
+ local a = pdfdictionary {
+ S = pdfconstant("Rendition"),
+ R = mf[label],
+ OP = 0,
+ AN = pdfreference(r),
+ }
+ 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 = pdf_border,
+ 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(pdfannotation_node(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,"://")
+ --~ 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 }
+ end
+ local clip = pdfdictionary {
+ Type = pdfconstant("MediaClip"),
+ S = pdfconstant("MCD"),
+ N = label,
+ CT = specification.mime,
+ 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/tex/context/base/luat-bwc.lua b/tex/context/base/luat-bwc.lua
index b8672469e..993de7bf3 100644
--- a/tex/context/base/luat-bwc.lua
+++ b/tex/context/base/luat-bwc.lua
@@ -1,32 +1,32 @@
-if not modules then modules = { } end modules ['luat-bwc'] = {
- 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"
-}
-
--- backward compatibility
-
-if not tex.wd then
-
- local box = tex.box
-
- local wd = { } setmetatable(wd, {
- __index = function(t,k) local bk = box[k] return bk and bk.width or 0 end,
- __newindex = function(t,k,v) local bk = box[k] if bk then bk.width = v end end,
- } )
-
- local ht = { } setmetatable(ht, {
- __index = function(t,k) local bk = box[k] return bk and bk.height or 0 end,
- __newindex = function(t,k,v) local bk = box[k] if bk then bk.height = v end end,
- } )
-
- local dp = { } setmetatable(dp, {
- __index = function(t,k) local bk = box[k] return bk and bk.depth or 0 end,
- __newindex = function(t,k,v) local bk = box[k] if bk then bk.depth = v end end,
- } )
-
- -- tex.wd, tex.ht, tex.dp = wd, ht, dp
-
-end
+if not modules then modules = { } end modules ['luat-bwc'] = {
+ 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"
+}
+
+-- backward compatibility
+
+if not tex.wd then
+
+ local box = tex.box
+
+ local wd = { } setmetatable(wd, {
+ __index = function(t,k) local bk = box[k] return bk and bk.width or 0 end,
+ __newindex = function(t,k,v) local bk = box[k] if bk then bk.width = v end end,
+ } )
+
+ local ht = { } setmetatable(ht, {
+ __index = function(t,k) local bk = box[k] return bk and bk.height or 0 end,
+ __newindex = function(t,k,v) local bk = box[k] if bk then bk.height = v end end,
+ } )
+
+ local dp = { } setmetatable(dp, {
+ __index = function(t,k) local bk = box[k] return bk and bk.depth or 0 end,
+ __newindex = function(t,k,v) local bk = box[k] if bk then bk.depth = v end end,
+ } )
+
+ -- tex.wd, tex.ht, tex.dp = wd, ht, dp
+
+end
diff --git a/tex/context/base/luat-cbk.lua b/tex/context/base/luat-cbk.lua
index 4a88cfed7..5aa12005b 100644
--- a/tex/context/base/luat-cbk.lua
+++ b/tex/context/base/luat-cbk.lua
@@ -1,320 +1,320 @@
-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, find, format = table.insert, table.remove, string.find, string.format
-local collectgarbage, type, next = collectgarbage, type, next
-local round = math.round
-local sortedhash, tohash = table.sortedhash, table.tohash
-
-local trace_checking = false trackers.register("memory.checking", function(v) trace_checking = v end)
-
-local report_callbacks = logs.reporter("system","callbacks")
-local report_memory = logs.reporter("system","memory")
-
---[[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.</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 register_callback = callback.register
-local find_callback = callback.find
-local list_callbacks = callback.list
-
-local frozen, stack, list = { }, { }, callbacks.list
-
-if not list then -- otherwise counters get reset
-
- list = utilities.storage.allocate(list_callbacks())
-
- for k, _ in next, list do
- list[k] = 0
- end
-
- callbacks.list = list
-
-end
-
-local delayed = tohash {
- "buildpage_filter",
-}
-
-
-if trace_calls then
-
- local functions = { }
- local original = register_callback
-
- 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(name,cnuf)
- end
- else
- return original(name,func)
- end
- end
-
-end
-
-local function frozen_message(what,name)
- report_callbacks("not %s frozen %a to %a",what,name,frozen[name])
-end
-
-local function frozen_callback(name)
- return nil, format("callback '%s' is frozen to '%s'",name,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)
- freeze = type(freeze) == "string" and freeze
- if find(name,"%*") 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
-
-function callbacks.register(name,func,freeze)
- if frozen[name] then
- if trace_callbacks then
- frozen_message("registering",name)
- end
- return frozen_callback(name)
- 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 trace_callbacks then
- frozen_message("registering",name)
- end
- return frozen_callback(name)
-end
-
-function callbacks.push(name,func)
- if not frozen[name] then
- local sn = stack[name]
- if not sn then
- sn = { }
- stack[name] = sn
- end
- insert(sn,find_callback(name))
- register_callback(name, func)
- elseif trace_callbacks then
- frozen_message("pushing",name)
- end
-end
-
-function callbacks.pop(name)
- if not frozen[name] 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 t
- end)
-end
-
--- -- somehow crashes later on
---
--- callbacks.freeze("find_.*_file","finding file")
--- callbacks.freeze("read_.*_file","reading file")
--- callbacks.freeze("open_.*_file","opening file")
-
---[[ldx--
-<p>The simple case is to remove the callback:</p>
-
-<code>
-callbacks.push('linebreak_filter')
-... some actions ...
-callbacks.pop('linebreak_filter')
-</code>
-
-<p>Often, in such case, another callback or a macro call will pop
-the original.</p>
-
-<p>In practice one will install a new handler, like in:</p>
-
-<code>
-callbacks.push('linebreak_filter', function(...)
- return something_done(...)
-end)
-</code>
-
-<p>Even more interesting is:</p>
-
-<code>
-callbacks.push('linebreak_filter', function(...)
- callbacks.pop('linebreak_filter')
- return something_done(...)
-end)
-</code>
-
-<p>This does a one-shot.</p>
---ldx]]--
-
---[[ldx--
-<p>Callbacks may result in <l n='lua'/> doing some hard work
-which takes time and above all resourses. Sometimes it makes
-sense to disable or tune the garbage collector in order to
-keep the use of resources acceptable.</p>
-
-<p>At some point in the development we did some tests with counting
-nodes (in this case 121049).</p>
-
-<table>
-<tr><td>setstepmul</td><td>seconds</td><td>megabytes</td></tr>
-<tr><td>200</td><td>24.0</td><td>80.5</td></tr>
-<tr><td>175</td><td>21.0</td><td>78.2</td></tr>
-<tr><td>150</td><td>22.0</td><td>74.6</td></tr>
-<tr><td>160</td><td>22.0</td><td>74.6</td></tr>
-<tr><td>165</td><td>21.0</td><td>77.6</td></tr>
-<tr><td>125</td><td>21.5</td><td>89.2</td></tr>
-<tr><td>100</td><td>21.5</td><td>88.4</td></tr>
-</table>
-
-<p>The following code is kind of experimental. In the documents
-that describe the development of <l n='luatex'/> we report
-on speed tests. One observation is thta it sometimes helps to
-restart the collector. Okay, experimental code has been removed,
-because messing aroudn with the gc is too unpredictable.</p>
---ldx]]--
-
--- For the moment we keep this here and not in util-gbc.lua or so.
-
-utilities = utilities or { }
-utilities.garbagecollector = utilities.garbagecollector or { }
-local garbagecollector = utilities.garbagecollector
-
-garbagecollector.enabled = false -- could become a directive
-garbagecollector.criterium = 4*1024*1024
-
--- Lua allocates up to 12 times the amount of memory needed for
--- handling a string, and for large binary chunks (like chinese otf
--- files) we get a prominent memory consumption. Even when a variable
--- is nilled, there is some delay in freeing the associated memory (the
--- hashed string) because if we do the same thing directly afterwards,
--- we see only a slight increase in memory. For that reason it makes
--- sense to do a collector pass after a huge file.
---
--- test file:
---
--- function test()
--- local b = collectgarbage("count")
--- local s = io.loaddata("some font table, e.g. a big tmc file")
--- local a = collectgarbage("count")
--- print(">>> STATUS",b,a,a-b,#s,1000*(a-b)/#s)
--- end
---
--- test() test() test() test() collectgarbage("collect") test() test() test() test()
---
--- As a result of this, LuaTeX now uses an optimized version of f:read("*a"),
--- one that does not use the 4K allocations but allocates in one step.
-
-function garbagecollector.check(size,criterium)
- if garbagecollector.enabled then
- criterium = criterium or garbagecollector.criterium
- if not size or (criterium and criterium > 0 and size > criterium) then
- if trace_checking then
- local b = collectgarbage("count")
- collectgarbage("collect")
- local a = collectgarbage("count")
- report_memory("forced sweep, collected: %s MB, used: %s MB",round((b-a)/1000),round(a/1000))
- else
- collectgarbage("collect")
- end
- end
- end
-end
-
--- this will move
-
-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
+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, find, format = table.insert, table.remove, string.find, string.format
+local collectgarbage, type, next = collectgarbage, type, next
+local round = math.round
+local sortedhash, tohash = table.sortedhash, table.tohash
+
+local trace_checking = false trackers.register("memory.checking", function(v) trace_checking = v end)
+
+local report_callbacks = logs.reporter("system","callbacks")
+local report_memory = logs.reporter("system","memory")
+
+--[[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.</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 register_callback = callback.register
+local find_callback = callback.find
+local list_callbacks = callback.list
+
+local frozen, stack, list = { }, { }, callbacks.list
+
+if not list then -- otherwise counters get reset
+
+ list = utilities.storage.allocate(list_callbacks())
+
+ for k, _ in next, list do
+ list[k] = 0
+ end
+
+ callbacks.list = list
+
+end
+
+local delayed = tohash {
+ "buildpage_filter",
+}
+
+
+if trace_calls then
+
+ local functions = { }
+ local original = register_callback
+
+ 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(name,cnuf)
+ end
+ else
+ return original(name,func)
+ end
+ end
+
+end
+
+local function frozen_message(what,name)
+ report_callbacks("not %s frozen %a to %a",what,name,frozen[name])
+end
+
+local function frozen_callback(name)
+ return nil, format("callback '%s' is frozen to '%s'",name,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)
+ freeze = type(freeze) == "string" and freeze
+ if find(name,"%*") 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
+
+function callbacks.register(name,func,freeze)
+ if frozen[name] then
+ if trace_callbacks then
+ frozen_message("registering",name)
+ end
+ return frozen_callback(name)
+ 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 trace_callbacks then
+ frozen_message("registering",name)
+ end
+ return frozen_callback(name)
+end
+
+function callbacks.push(name,func)
+ if not frozen[name] then
+ local sn = stack[name]
+ if not sn then
+ sn = { }
+ stack[name] = sn
+ end
+ insert(sn,find_callback(name))
+ register_callback(name, func)
+ elseif trace_callbacks then
+ frozen_message("pushing",name)
+ end
+end
+
+function callbacks.pop(name)
+ if not frozen[name] 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 t
+ end)
+end
+
+-- -- somehow crashes later on
+--
+-- callbacks.freeze("find_.*_file","finding file")
+-- callbacks.freeze("read_.*_file","reading file")
+-- callbacks.freeze("open_.*_file","opening file")
+
+--[[ldx--
+<p>The simple case is to remove the callback:</p>
+
+<code>
+callbacks.push('linebreak_filter')
+... some actions ...
+callbacks.pop('linebreak_filter')
+</code>
+
+<p>Often, in such case, another callback or a macro call will pop
+the original.</p>
+
+<p>In practice one will install a new handler, like in:</p>
+
+<code>
+callbacks.push('linebreak_filter', function(...)
+ return something_done(...)
+end)
+</code>
+
+<p>Even more interesting is:</p>
+
+<code>
+callbacks.push('linebreak_filter', function(...)
+ callbacks.pop('linebreak_filter')
+ return something_done(...)
+end)
+</code>
+
+<p>This does a one-shot.</p>
+--ldx]]--
+
+--[[ldx--
+<p>Callbacks may result in <l n='lua'/> doing some hard work
+which takes time and above all resourses. Sometimes it makes
+sense to disable or tune the garbage collector in order to
+keep the use of resources acceptable.</p>
+
+<p>At some point in the development we did some tests with counting
+nodes (in this case 121049).</p>
+
+<table>
+<tr><td>setstepmul</td><td>seconds</td><td>megabytes</td></tr>
+<tr><td>200</td><td>24.0</td><td>80.5</td></tr>
+<tr><td>175</td><td>21.0</td><td>78.2</td></tr>
+<tr><td>150</td><td>22.0</td><td>74.6</td></tr>
+<tr><td>160</td><td>22.0</td><td>74.6</td></tr>
+<tr><td>165</td><td>21.0</td><td>77.6</td></tr>
+<tr><td>125</td><td>21.5</td><td>89.2</td></tr>
+<tr><td>100</td><td>21.5</td><td>88.4</td></tr>
+</table>
+
+<p>The following code is kind of experimental. In the documents
+that describe the development of <l n='luatex'/> we report
+on speed tests. One observation is thta it sometimes helps to
+restart the collector. Okay, experimental code has been removed,
+because messing aroudn with the gc is too unpredictable.</p>
+--ldx]]--
+
+-- For the moment we keep this here and not in util-gbc.lua or so.
+
+utilities = utilities or { }
+utilities.garbagecollector = utilities.garbagecollector or { }
+local garbagecollector = utilities.garbagecollector
+
+garbagecollector.enabled = false -- could become a directive
+garbagecollector.criterium = 4*1024*1024
+
+-- Lua allocates up to 12 times the amount of memory needed for
+-- handling a string, and for large binary chunks (like chinese otf
+-- files) we get a prominent memory consumption. Even when a variable
+-- is nilled, there is some delay in freeing the associated memory (the
+-- hashed string) because if we do the same thing directly afterwards,
+-- we see only a slight increase in memory. For that reason it makes
+-- sense to do a collector pass after a huge file.
+--
+-- test file:
+--
+-- function test()
+-- local b = collectgarbage("count")
+-- local s = io.loaddata("some font table, e.g. a big tmc file")
+-- local a = collectgarbage("count")
+-- print(">>> STATUS",b,a,a-b,#s,1000*(a-b)/#s)
+-- end
+--
+-- test() test() test() test() collectgarbage("collect") test() test() test() test()
+--
+-- As a result of this, LuaTeX now uses an optimized version of f:read("*a"),
+-- one that does not use the 4K allocations but allocates in one step.
+
+function garbagecollector.check(size,criterium)
+ if garbagecollector.enabled then
+ criterium = criterium or garbagecollector.criterium
+ if not size or (criterium and criterium > 0 and size > criterium) then
+ if trace_checking then
+ local b = collectgarbage("count")
+ collectgarbage("collect")
+ local a = collectgarbage("count")
+ report_memory("forced sweep, collected: %s MB, used: %s MB",round((b-a)/1000),round(a/1000))
+ else
+ collectgarbage("collect")
+ end
+ end
+ end
+end
+
+-- this will move
+
+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/tex/context/base/luat-cnf.lua b/tex/context/base/luat-cnf.lua
index 4020f0b12..3672c603e 100644
--- a/tex/context/base/luat-cnf.lua
+++ b/tex/context/base/luat-cnf.lua
@@ -1,197 +1,197 @@
-if not modules then modules = { } end modules ['luat-cnf'] = {
- 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 type, next, tostring, tonumber = type, next, tostring, tonumber
-local format, concat, find = string.format, table.concat, string.find
-
-local allocate = utilities.storage.allocate
-
-texconfig.kpse_init = false
-texconfig.shell_escape = 't'
-
-luatex = luatex or { }
-local luatex = luatex
-
-texconfig.error_line = 79 -- 79 -- obsolete
-texconfig.half_error_line = 50 -- 50 -- obsolete
-
-texconfig.expand_depth = 10000 -- 10000
-texconfig.hash_extra = 100000 -- 0
-texconfig.nest_size = 1000 -- 50
-texconfig.max_in_open = 500 -- 15
-texconfig.max_print_line = 10000 -- 79
-texconfig.max_strings = 500000 -- 15000
-texconfig.param_size = 25000 -- 60
-texconfig.save_size = 50000 -- 4000
-texconfig.stack_size = 10000 -- 300
-
--- local function initialize()
--- local t, variable = allocate(), resolvers.variable
--- for name, default in next, variablenames do
--- local name = variablenames[i]
--- local value = variable(name)
--- value = tonumber(value)
--- if not value or value == "" or value == 0 then
--- value = default
--- end
--- texconfig[name], t[name] = value, value
--- end
--- initialize = nil
--- return t
--- end
---
--- luatex.variables = initialize()
-
-local stub = [[
-
--- checking
-
-storage = storage or { }
-luatex = luatex or { }
-
--- we provide our own file handling
-
-texconfig.kpse_init = false
-texconfig.shell_escape = 't'
-
--- as soon as possible
-
-luatex.starttime = os.gettimeofday()
-
--- this will happen after the format is loaded
-
-function texconfig.init()
-
- -- development
-
- local builtin, globals = { }, { }
-
- libraries = { -- we set it here as we want libraries also 'indexed'
- basiclua = {
- "string", "table", "coroutine", "debug", "file", "io", "lpeg", "math", "os", "package", "bit32",
- },
- basictex = { -- noad
- "callback", "font", "img", "lang", "lua", "node", "pdf", "status", "tex", "texconfig", "texio", "token",
- },
- extralua = {
- "gzip", "zip", "zlib", "lfs", "ltn12", "mime", "socket", "md5", "profiler", "unicode", "utf",
- },
- extratex = {
- "epdf", "fontloader", "kpse", "mplib",
- },
- obsolete = {
- "fontforge", -- can be filled by luat-log
- "kpse",
- },
- functions = {
- "assert", "pcall", "xpcall", "error", "collectgarbage",
- "dofile", "load","loadfile", "require", "module",
- "getmetatable", "setmetatable",
- "ipairs", "pairs", "rawequal", "rawget", "rawset", "next",
- "tonumber", "tostring",
- "type", "unpack", "select", "print",
- },
- builtin = builtin, -- to be filled
- globals = globals, -- to be filled
- }
-
- for k, v in next, _G do
- globals[k] = tostring(v)
- end
-
- local function collect(t,fnc)
- local lib = { }
- for k, v in next, t do
- if fnc then
- lib[v] = _G[v]
- else
- local keys = { }
- local gv = _G[v]
- local tv = type(gv)
- if tv == "table" then
- for k, v in next, gv do
- keys[k] = tostring(v) -- true -- by tostring we cannot call overloades functions (security)
- end
- end
- lib[v] = keys
- builtin[v] = keys
- end
- end
- return lib
- end
-
- libraries.basiclua = collect(libraries.basiclua)
- libraries.basictex = collect(libraries.basictex)
- libraries.extralua = collect(libraries.extralua)
- libraries.extratex = collect(libraries.extratex)
- libraries.functions = collect(libraries.functions,true)
- libraries.obsolete = collect(libraries.obsolete)
-
- -- shortcut and helper
-
- local function init(start)
- local b = lua.bytecode
- local i = start
- local t = os.clock()
- while b[i] do
- b[i]() ;
- b[i] = nil ;
- i = i + 1
- -- collectgarbage('step')
- end
- return i - start, os.clock() - t
- end
-
- -- the stored tables and modules
-
- storage.noftables , storage.toftables = init(0)
- storage.nofmodules, storage.tofmodules = init(%s)
-
- if modules then
- local loaded = package.loaded
- for module, _ in next, modules do
- loaded[module] = true
- end
- end
-
-end
-
--- we provide a qualified path
-
-callback.register('find_format_file',function(name)
- texconfig.formatname = name
- return name
-end)
-
--- done, from now on input and callbacks are internal
-]]
-
-local variablenames = {
- "error_line", "half_error_line",
- "expand_depth", "hash_extra", "nest_size",
- "max_in_open", "max_print_line", "max_strings",
- "param_size", "save_size", "stack_size",
-}
-
-local function makestub()
- name = name or (environment.jobname .. ".lui")
- firsttable = firsttable or lua.firstbytecode
- local t = {
- "-- this file is generated, don't change it\n",
- "-- configuration (can be overloaded later)\n"
- }
- for _,v in next, variablenames do
- local tv = texconfig[v]
- if tv and tv ~= "" then
- t[#t+1] = format("texconfig.%s=%s",v,tv)
- end
- end
- io.savedata(name,format("%s\n\n%s",concat(t,"\n"),format(stub,firsttable)))
-end
-
-lua.registerfinalizer(makestub,"create stub file")
+if not modules then modules = { } end modules ['luat-cnf'] = {
+ 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 type, next, tostring, tonumber = type, next, tostring, tonumber
+local format, concat, find = string.format, table.concat, string.find
+
+local allocate = utilities.storage.allocate
+
+texconfig.kpse_init = false
+texconfig.shell_escape = 't'
+
+luatex = luatex or { }
+local luatex = luatex
+
+texconfig.error_line = 79 -- 79 -- obsolete
+texconfig.half_error_line = 50 -- 50 -- obsolete
+
+texconfig.expand_depth = 10000 -- 10000
+texconfig.hash_extra = 100000 -- 0
+texconfig.nest_size = 1000 -- 50
+texconfig.max_in_open = 500 -- 15
+texconfig.max_print_line = 10000 -- 79
+texconfig.max_strings = 500000 -- 15000
+texconfig.param_size = 25000 -- 60
+texconfig.save_size = 50000 -- 4000
+texconfig.stack_size = 10000 -- 300
+
+-- local function initialize()
+-- local t, variable = allocate(), resolvers.variable
+-- for name, default in next, variablenames do
+-- local name = variablenames[i]
+-- local value = variable(name)
+-- value = tonumber(value)
+-- if not value or value == "" or value == 0 then
+-- value = default
+-- end
+-- texconfig[name], t[name] = value, value
+-- end
+-- initialize = nil
+-- return t
+-- end
+--
+-- luatex.variables = initialize()
+
+local stub = [[
+
+-- checking
+
+storage = storage or { }
+luatex = luatex or { }
+
+-- we provide our own file handling
+
+texconfig.kpse_init = false
+texconfig.shell_escape = 't'
+
+-- as soon as possible
+
+luatex.starttime = os.gettimeofday()
+
+-- this will happen after the format is loaded
+
+function texconfig.init()
+
+ -- development
+
+ local builtin, globals = { }, { }
+
+ libraries = { -- we set it here as we want libraries also 'indexed'
+ basiclua = {
+ "string", "table", "coroutine", "debug", "file", "io", "lpeg", "math", "os", "package", "bit32",
+ },
+ basictex = { -- noad
+ "callback", "font", "img", "lang", "lua", "node", "pdf", "status", "tex", "texconfig", "texio", "token",
+ },
+ extralua = {
+ "gzip", "zip", "zlib", "lfs", "ltn12", "mime", "socket", "md5", "profiler", "unicode", "utf",
+ },
+ extratex = {
+ "epdf", "fontloader", "kpse", "mplib",
+ },
+ obsolete = {
+ "fontforge", -- can be filled by luat-log
+ "kpse",
+ },
+ functions = {
+ "assert", "pcall", "xpcall", "error", "collectgarbage",
+ "dofile", "load","loadfile", "require", "module",
+ "getmetatable", "setmetatable",
+ "ipairs", "pairs", "rawequal", "rawget", "rawset", "next",
+ "tonumber", "tostring",
+ "type", "unpack", "select", "print",
+ },
+ builtin = builtin, -- to be filled
+ globals = globals, -- to be filled
+ }
+
+ for k, v in next, _G do
+ globals[k] = tostring(v)
+ end
+
+ local function collect(t,fnc)
+ local lib = { }
+ for k, v in next, t do
+ if fnc then
+ lib[v] = _G[v]
+ else
+ local keys = { }
+ local gv = _G[v]
+ local tv = type(gv)
+ if tv == "table" then
+ for k, v in next, gv do
+ keys[k] = tostring(v) -- true -- by tostring we cannot call overloades functions (security)
+ end
+ end
+ lib[v] = keys
+ builtin[v] = keys
+ end
+ end
+ return lib
+ end
+
+ libraries.basiclua = collect(libraries.basiclua)
+ libraries.basictex = collect(libraries.basictex)
+ libraries.extralua = collect(libraries.extralua)
+ libraries.extratex = collect(libraries.extratex)
+ libraries.functions = collect(libraries.functions,true)
+ libraries.obsolete = collect(libraries.obsolete)
+
+ -- shortcut and helper
+
+ local function init(start)
+ local b = lua.bytecode
+ local i = start
+ local t = os.clock()
+ while b[i] do
+ b[i]() ;
+ b[i] = nil ;
+ i = i + 1
+ -- collectgarbage('step')
+ end
+ return i - start, os.clock() - t
+ end
+
+ -- the stored tables and modules
+
+ storage.noftables , storage.toftables = init(0)
+ storage.nofmodules, storage.tofmodules = init(%s)
+
+ if modules then
+ local loaded = package.loaded
+ for module, _ in next, modules do
+ loaded[module] = true
+ end
+ end
+
+end
+
+-- we provide a qualified path
+
+callback.register('find_format_file',function(name)
+ texconfig.formatname = name
+ return name
+end)
+
+-- done, from now on input and callbacks are internal
+]]
+
+local variablenames = {
+ "error_line", "half_error_line",
+ "expand_depth", "hash_extra", "nest_size",
+ "max_in_open", "max_print_line", "max_strings",
+ "param_size", "save_size", "stack_size",
+}
+
+local function makestub()
+ name = name or (environment.jobname .. ".lui")
+ firsttable = firsttable or lua.firstbytecode
+ local t = {
+ "-- this file is generated, don't change it\n",
+ "-- configuration (can be overloaded later)\n"
+ }
+ for _,v in next, variablenames do
+ local tv = texconfig[v]
+ if tv and tv ~= "" then
+ t[#t+1] = format("texconfig.%s=%s",v,tv)
+ end
+ end
+ io.savedata(name,format("%s\n\n%s",concat(t,"\n"),format(stub,firsttable)))
+end
+
+lua.registerfinalizer(makestub,"create stub file")
diff --git a/tex/context/base/luat-cod.lua b/tex/context/base/luat-cod.lua
index 8fc94779c..8b015477f 100644
--- a/tex/context/base/luat-cod.lua
+++ b/tex/context/base/luat-cod.lua
@@ -1,181 +1,181 @@
-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 = type, loadfile
-local match, gsub, find, format = string.match, string.gsub, string.find, string.format
-
-local texconfig, lua = texconfig, lua
-
--- some basic housekeeping
-
-texconfig.kpse_init = false
-texconfig.shell_escape = 't'
-texconfig.max_print_line = 100000
-texconfig.max_in_open = 127
-
--- registering bytecode chunks
-
-local bytecode = lua.bytecode or { }
-local bytedata = lua.bytedata or { }
-local bytedone = lua.bytedone or { }
-
-lua.bytecode = bytecode -- built in anyway
-lua.bytedata = bytedata
-lua.bytedone = bytedone
-
-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.* functions yet
-
-function lua.registercode(filename,version)
- local barename = gsub(filename,"%.[%a%d]+$","")
- if barename == filename then filename = filename .. ".lua" end
- local basename = match(barename,"^.+[/\\](.-)$") or barename
- if not bytedone[basename] then
- local code = environment.luafilechunk(filename)
- if code then
- bytedone[basename] = true
- if environment.initex then
- local n = lua.lastbytecode + 1
- bytedata[n] = { barename, version or "0.000" }
- bytecode[n] = code
- lua.lastbytecode = n
- end
- 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,"/") and gsub(sourcefile,"/[^/]+$","") or ""
-local targetpath = "."
-
--- delayed (via metatable):
---
--- environment.jobname = tex.jobname
--- environment.version = tostring(tex.toks.contextversiontoks)
-
-environment.initex = tex.formatname == ""
-
-if not environment.luafilechunk then
-
- function environment.luafilechunk(filename)
- if sourcepath ~= "" then
- filename = sourcepath .. "/" .. filename
- end
- local data = loadfile(filename)
- texio.write("<",data and "+ " or "- ",filename,">")
- if data then
- 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.
-
-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_read_file (id,name)
- return source_file(name)
-end
-
-local function find_write_file(id,name)
- return target_file(name)
-end
-
-local function open_read_file(name)
- local f = io.open(name,'rb')
- return {
- reader = function()
- return f:read("*line")
- end
- }
-end
-
-callback.register('find_read_file' , find_read_file )
-callback.register('open_read_file' , open_read_file )
-callback.register('find_write_file', find_write_file)
+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 = type, loadfile
+local match, gsub, find, format = string.match, string.gsub, string.find, string.format
+
+local texconfig, lua = texconfig, lua
+
+-- some basic housekeeping
+
+texconfig.kpse_init = false
+texconfig.shell_escape = 't'
+texconfig.max_print_line = 100000
+texconfig.max_in_open = 127
+
+-- registering bytecode chunks
+
+local bytecode = lua.bytecode or { }
+local bytedata = lua.bytedata or { }
+local bytedone = lua.bytedone or { }
+
+lua.bytecode = bytecode -- built in anyway
+lua.bytedata = bytedata
+lua.bytedone = bytedone
+
+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.* functions yet
+
+function lua.registercode(filename,version)
+ local barename = gsub(filename,"%.[%a%d]+$","")
+ if barename == filename then filename = filename .. ".lua" end
+ local basename = match(barename,"^.+[/\\](.-)$") or barename
+ if not bytedone[basename] then
+ local code = environment.luafilechunk(filename)
+ if code then
+ bytedone[basename] = true
+ if environment.initex then
+ local n = lua.lastbytecode + 1
+ bytedata[n] = { barename, version or "0.000" }
+ bytecode[n] = code
+ lua.lastbytecode = n
+ end
+ 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,"/") and gsub(sourcefile,"/[^/]+$","") or ""
+local targetpath = "."
+
+-- delayed (via metatable):
+--
+-- environment.jobname = tex.jobname
+-- environment.version = tostring(tex.toks.contextversiontoks)
+
+environment.initex = tex.formatname == ""
+
+if not environment.luafilechunk then
+
+ function environment.luafilechunk(filename)
+ if sourcepath ~= "" then
+ filename = sourcepath .. "/" .. filename
+ end
+ local data = loadfile(filename)
+ texio.write("<",data and "+ " or "- ",filename,">")
+ if data then
+ 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.
+
+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_read_file (id,name)
+ return source_file(name)
+end
+
+local function find_write_file(id,name)
+ return target_file(name)
+end
+
+local function open_read_file(name)
+ local f = io.open(name,'rb')
+ return {
+ reader = function()
+ return f:read("*line")
+ end
+ }
+end
+
+callback.register('find_read_file' , find_read_file )
+callback.register('open_read_file' , open_read_file )
+callback.register('find_write_file', find_write_file)
diff --git a/tex/context/base/luat-env.lua b/tex/context/base/luat-env.lua
index 2f8f9e28d..8753972c6 100644
--- a/tex/context/base/luat-env.lua
+++ b/tex/context/base/luat-env.lua
@@ -1,176 +1,176 @@
- if not modules then modules = { } end modules ['luat-env'] = {
- 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"
-}
-
--- A former version provided functionality for non embeded core scripts i.e. runtime
--- library loading. Given the amount of Lua code we use now, this no longer makes
--- sense. Much of this evolved before bytecode arrays were available and so a lot of
--- code has disappeared already.
-
-local rawset, rawget, loadfile, assert = rawset, rawget, loadfile, assert
-
-local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
-
-local report_lua = logs.reporter("resolvers","lua")
-
-local luautilities = utilities.lua
-local luasuffixes = luautilities.suffixes
-
-environment = environment or { }
-local environment = environment
-
--- environment
-
-local mt = {
- __index = function(_,k)
- if k == "version" then
- local version = tex.toks and tex.toks.contextversiontoks
- if version and version ~= "" then
- rawset(environment,"version",version)
- return version
- else
- return "unknown"
- end
- elseif k == "kind" then
- local kind = tex.toks and tex.toks.contextkindtoks
- if kind and kind ~= "" then
- rawset(environment,"kind",kind)
- return kind
- else
- return "unknown"
- end
- elseif k == "jobname" or k == "formatname" then
- local name = tex and tex[k]
- if name or name== "" then
- rawset(environment,k,name)
- return name
- else
- return "unknown"
- end
- elseif k == "outputfilename" then
- local name = environment.jobname
- rawset(environment,k,name)
- return name
- end
- end
-}
-
-setmetatable(environment,mt)
-
--- weird place ... depends on a not yet loaded module
-
-function environment.texfile(filename)
- return resolvers.findfile(filename,'tex')
-end
-
-function environment.luafile(filename) -- needs checking
- local resolved = resolvers.findfile(filename,'tex') or ""
- if resolved ~= "" then
- return resolved
- end
- resolved = resolvers.findfile(filename,'texmfscripts') or ""
- if resolved ~= "" then
- return resolved
- end
- return resolvers.findfile(filename,'luatexlibs') or ""
-end
-
--- local function checkstrip(filename)
--- local modu = modules[file.nameonly(filename)]
--- return modu and modu.dataonly
--- end
-
-local stripindeed = false directives.register("system.compile.strip", function(v) stripindeed = v end)
-
-local function strippable(filename)
- if stripindeed then
- local modu = modules[file.nameonly(filename)]
- return modu and modu.dataonly
- else
- return false
- end
-end
-
-function environment.luafilechunk(filename,silent) -- used for loading lua bytecode in the format
- filename = file.replacesuffix(filename, "lua")
- local fullname = environment.luafile(filename)
- if fullname and fullname ~= "" then
- local data = luautilities.loadedluacode(fullname,strippable,filename) -- can be overloaded
- if trace_locating then
- report_lua("loading file %a %s",fullname,not data and "failed" or "succeeded")
- elseif not silent then
- texio.write("<",data and "+ " or "- ",fullname,">")
- end
- return data
- else
- if trace_locating then
- report_lua("unknown file %a",filename)
- end
- return nil
- end
-end
-
--- the next ones can use the previous ones / combine
-
-function environment.loadluafile(filename, version)
- local lucname, luaname, chunk
- local basename = file.removesuffix(filename)
- if basename == filename then
- luaname = file.addsuffix(basename,luasuffixes.lua)
- lucname = file.addsuffix(basename,luasuffixes.luc)
- else
- luaname = basename -- forced suffix
- lucname = nil
- end
- -- when not overloaded by explicit suffix we look for a luc file first
- local fullname = (lucname and environment.luafile(lucname)) or ""
- if fullname ~= "" then
- if trace_locating then
- report_lua("loading %a",fullname)
- end
- -- maybe: package.loaded[file.nameonly(fullname)] = true
- chunk = loadfile(fullname) -- this way we don't need a file exists check
- end
- if chunk then
- assert(chunk)()
- if version then
- -- we check of the version number of this chunk matches
- local v = version -- can be nil
- if modules and modules[filename] then
- v = modules[filename].version -- new method
- elseif versions and versions[filename] then
- v = versions[filename] -- old method
- end
- if v == version then
- return true
- else
- if trace_locating then
- report_lua("version mismatch for %a, lua version %a, luc version %a",filename,v,version)
- end
- environment.loadluafile(filename)
- end
- else
- return true
- end
- end
- fullname = (luaname and environment.luafile(luaname)) or ""
- if fullname ~= "" then
- if trace_locating then
- report_lua("loading %a",fullname)
- end
- chunk = loadfile(fullname) -- this way we don't need a file exists check
- if not chunk then
- if trace_locating then
- report_lua("unknown file %a",filename)
- end
- else
- assert(chunk)()
- return true
- end
- end
- return false
-end
+ if not modules then modules = { } end modules ['luat-env'] = {
+ 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"
+}
+
+-- A former version provided functionality for non embeded core scripts i.e. runtime
+-- library loading. Given the amount of Lua code we use now, this no longer makes
+-- sense. Much of this evolved before bytecode arrays were available and so a lot of
+-- code has disappeared already.
+
+local rawset, rawget, loadfile, assert = rawset, rawget, loadfile, assert
+
+local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end)
+
+local report_lua = logs.reporter("resolvers","lua")
+
+local luautilities = utilities.lua
+local luasuffixes = luautilities.suffixes
+
+environment = environment or { }
+local environment = environment
+
+-- environment
+
+local mt = {
+ __index = function(_,k)
+ if k == "version" then
+ local version = tex.toks and tex.toks.contextversiontoks
+ if version and version ~= "" then
+ rawset(environment,"version",version)
+ return version
+ else
+ return "unknown"
+ end
+ elseif k == "kind" then
+ local kind = tex.toks and tex.toks.contextkindtoks
+ if kind and kind ~= "" then
+ rawset(environment,"kind",kind)
+ return kind
+ else
+ return "unknown"
+ end
+ elseif k == "jobname" or k == "formatname" then
+ local name = tex and tex[k]
+ if name or name== "" then
+ rawset(environment,k,name)
+ return name
+ else
+ return "unknown"
+ end
+ elseif k == "outputfilename" then
+ local name = environment.jobname
+ rawset(environment,k,name)
+ return name
+ end
+ end
+}
+
+setmetatable(environment,mt)
+
+-- weird place ... depends on a not yet loaded module
+
+function environment.texfile(filename)
+ return resolvers.findfile(filename,'tex')
+end
+
+function environment.luafile(filename) -- needs checking
+ local resolved = resolvers.findfile(filename,'tex') or ""
+ if resolved ~= "" then
+ return resolved
+ end
+ resolved = resolvers.findfile(filename,'texmfscripts') or ""
+ if resolved ~= "" then
+ return resolved
+ end
+ return resolvers.findfile(filename,'luatexlibs') or ""
+end
+
+-- local function checkstrip(filename)
+-- local modu = modules[file.nameonly(filename)]
+-- return modu and modu.dataonly
+-- end
+
+local stripindeed = false directives.register("system.compile.strip", function(v) stripindeed = v end)
+
+local function strippable(filename)
+ if stripindeed then
+ local modu = modules[file.nameonly(filename)]
+ return modu and modu.dataonly
+ else
+ return false
+ end
+end
+
+function environment.luafilechunk(filename,silent) -- used for loading lua bytecode in the format
+ filename = file.replacesuffix(filename, "lua")
+ local fullname = environment.luafile(filename)
+ if fullname and fullname ~= "" then
+ local data = luautilities.loadedluacode(fullname,strippable,filename) -- can be overloaded
+ if trace_locating then
+ report_lua("loading file %a %s",fullname,not data and "failed" or "succeeded")
+ elseif not silent then
+ texio.write("<",data and "+ " or "- ",fullname,">")
+ end
+ return data
+ else
+ if trace_locating then
+ report_lua("unknown file %a",filename)
+ end
+ return nil
+ end
+end
+
+-- the next ones can use the previous ones / combine
+
+function environment.loadluafile(filename, version)
+ local lucname, luaname, chunk
+ local basename = file.removesuffix(filename)
+ if basename == filename then
+ luaname = file.addsuffix(basename,luasuffixes.lua)
+ lucname = file.addsuffix(basename,luasuffixes.luc)
+ else
+ luaname = basename -- forced suffix
+ lucname = nil
+ end
+ -- when not overloaded by explicit suffix we look for a luc file first
+ local fullname = (lucname and environment.luafile(lucname)) or ""
+ if fullname ~= "" then
+ if trace_locating then
+ report_lua("loading %a",fullname)
+ end
+ -- maybe: package.loaded[file.nameonly(fullname)] = true
+ chunk = loadfile(fullname) -- this way we don't need a file exists check
+ end
+ if chunk then
+ assert(chunk)()
+ if version then
+ -- we check of the version number of this chunk matches
+ local v = version -- can be nil
+ if modules and modules[filename] then
+ v = modules[filename].version -- new method
+ elseif versions and versions[filename] then
+ v = versions[filename] -- old method
+ end
+ if v == version then
+ return true
+ else
+ if trace_locating then
+ report_lua("version mismatch for %a, lua version %a, luc version %a",filename,v,version)
+ end
+ environment.loadluafile(filename)
+ end
+ else
+ return true
+ end
+ end
+ fullname = (luaname and environment.luafile(luaname)) or ""
+ if fullname ~= "" then
+ if trace_locating then
+ report_lua("loading %a",fullname)
+ end
+ chunk = loadfile(fullname) -- this way we don't need a file exists check
+ if not chunk then
+ if trace_locating then
+ report_lua("unknown file %a",filename)
+ end
+ else
+ assert(chunk)()
+ return true
+ end
+ end
+ return false
+end
diff --git a/tex/context/base/luat-exe.lua b/tex/context/base/luat-exe.lua
index 6f7137cad..a57a5a006 100644
--- a/tex/context/base/luat-exe.lua
+++ b/tex/context/base/luat-exe.lua
@@ -1,126 +1,126 @@
-if not modules then modules = { } end modules ['luat-exe'] = {
- 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"
-}
-
--- this module needs checking (very old and never really used, not even enabled)
-
-local match, find, gmatch = string.match, string.find, string.gmatch
-local concat = table.concat
-local select = select
-
-local report_executers = logs.reporter("system","executers")
-
-resolvers.executers = resolvers.executers or { }
-local executers = resolvers.executers
-
-local permitted = { }
-
-local osexecute = os.execute
-local osexec = os.exec
-local osspawn = os.spawn
-local iopopen = io.popen
-
-local execute = osexecute
-local exec = osexec
-local spawn = osspawn
-local popen = iopopen
-
-local function register(...)
- for k=1,select("#",...) do
- local v = select(k,...)
- permitted[#permitted+1] = v == "*" and ".*" or v
- end
-end
-
-local function prepare(...)
- -- todo: make more clever first split
- local t = { ... }
- local n = #n
- local one = t[1]
- if n == 1 then
- if type(one) == 'table' then
- return one, concat(t," ",2,n)
- else
- local name, arguments = match(one,"^(.-)%s+(.+)$")
- if name and arguments then
- return name, arguments
- else
- return one, ""
- end
- end
- else
- return one, concat(t," ",2,n)
- end
-end
-
-local function executer(action)
- return function(...)
- local name, arguments = prepare(...)
- for k=1,#permitted do
- local v = permitted[k]
- if find(name,v) then
- return action(name .. " " .. arguments)
- else
- report_executers("not permitted: %s %s",name,arguments)
- end
- end
- return action("")
- end
-end
-
-local function finalize() -- todo: os.exec, todo: report ipv print
- execute = executer(osexecute)
- exec = executer(osexec)
- spawn = executer(osspawn)
- popen = executer(iopopen)
- finalize = function()
- report_executers("already finalized")
- end
- register = function()
- report_executers("already finalized, no registration permitted")
- end
- os.execute = execute
- os.exec = exec
- os.spawn = spawn
- io.popen = popen
-end
-
-executers.finalize = function(...) return finalize(...) end
-executers.register = function(...) return register(...) end
-executers.execute = function(...) return execute (...) end
-executers.exec = function(...) return exec (...) end
-executers.spawn = function(...) return spawn (...) end
-executers.popen = function(...) return popen (...) end
-
-local execution_mode directives.register("system.executionmode", function(v) execution_mode = v end)
-local execution_list directives.register("system.executionlist", function(v) execution_list = v end)
-
-function executers.check()
- if execution_mode == "none" then
- finalize()
- elseif execution_mode == "list" and execution_list ~= "" then
- for s in gmatch("[^%s,]",execution_list) do
- register(s)
- end
- finalize()
- else
- -- all
- end
-end
-
---~ resolvers.executers.register('.*')
---~ resolvers.executers.register('*')
---~ resolvers.executers.register('dir','ls')
---~ resolvers.executers.register('dir')
-
---~ resolvers.executers.finalize()
---~ resolvers.executers.execute('dir',"*.tex")
---~ resolvers.executers.execute("dir *.tex")
---~ resolvers.executers.execute("ls *.tex")
---~ os.execute('ls')
-
---~ resolvers.executers.check()
+if not modules then modules = { } end modules ['luat-exe'] = {
+ 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"
+}
+
+-- this module needs checking (very old and never really used, not even enabled)
+
+local match, find, gmatch = string.match, string.find, string.gmatch
+local concat = table.concat
+local select = select
+
+local report_executers = logs.reporter("system","executers")
+
+resolvers.executers = resolvers.executers or { }
+local executers = resolvers.executers
+
+local permitted = { }
+
+local osexecute = os.execute
+local osexec = os.exec
+local osspawn = os.spawn
+local iopopen = io.popen
+
+local execute = osexecute
+local exec = osexec
+local spawn = osspawn
+local popen = iopopen
+
+local function register(...)
+ for k=1,select("#",...) do
+ local v = select(k,...)
+ permitted[#permitted+1] = v == "*" and ".*" or v
+ end
+end
+
+local function prepare(...)
+ -- todo: make more clever first split
+ local t = { ... }
+ local n = #n
+ local one = t[1]
+ if n == 1 then
+ if type(one) == 'table' then
+ return one, concat(t," ",2,n)
+ else
+ local name, arguments = match(one,"^(.-)%s+(.+)$")
+ if name and arguments then
+ return name, arguments
+ else
+ return one, ""
+ end
+ end
+ else
+ return one, concat(t," ",2,n)
+ end
+end
+
+local function executer(action)
+ return function(...)
+ local name, arguments = prepare(...)
+ for k=1,#permitted do
+ local v = permitted[k]
+ if find(name,v) then
+ return action(name .. " " .. arguments)
+ else
+ report_executers("not permitted: %s %s",name,arguments)
+ end
+ end
+ return action("")
+ end
+end
+
+local function finalize() -- todo: os.exec, todo: report ipv print
+ execute = executer(osexecute)
+ exec = executer(osexec)
+ spawn = executer(osspawn)
+ popen = executer(iopopen)
+ finalize = function()
+ report_executers("already finalized")
+ end
+ register = function()
+ report_executers("already finalized, no registration permitted")
+ end
+ os.execute = execute
+ os.exec = exec
+ os.spawn = spawn
+ io.popen = popen
+end
+
+executers.finalize = function(...) return finalize(...) end
+executers.register = function(...) return register(...) end
+executers.execute = function(...) return execute (...) end
+executers.exec = function(...) return exec (...) end
+executers.spawn = function(...) return spawn (...) end
+executers.popen = function(...) return popen (...) end
+
+local execution_mode directives.register("system.executionmode", function(v) execution_mode = v end)
+local execution_list directives.register("system.executionlist", function(v) execution_list = v end)
+
+function executers.check()
+ if execution_mode == "none" then
+ finalize()
+ elseif execution_mode == "list" and execution_list ~= "" then
+ for s in gmatch("[^%s,]",execution_list) do
+ register(s)
+ end
+ finalize()
+ else
+ -- all
+ end
+end
+
+--~ resolvers.executers.register('.*')
+--~ resolvers.executers.register('*')
+--~ resolvers.executers.register('dir','ls')
+--~ resolvers.executers.register('dir')
+
+--~ resolvers.executers.finalize()
+--~ resolvers.executers.execute('dir',"*.tex")
+--~ resolvers.executers.execute("dir *.tex")
+--~ resolvers.executers.execute("ls *.tex")
+--~ os.execute('ls')
+
+--~ resolvers.executers.check()
diff --git a/tex/context/base/luat-fio.lua b/tex/context/base/luat-fio.lua
index bc8c6677b..d61c6f142 100644
--- a/tex/context/base/luat-fio.lua
+++ b/tex/context/base/luat-fio.lua
@@ -1,117 +1,117 @@
-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
-local sequenced = table.sequenced
-
-texconfig.kpse_init = false
-texconfig.shell_escape = 't'
-texconfig.max_in_open = 127
-texconfig.max_print_line = 100000
-
-if not resolvers.instance then
-
- resolvers.reset()
-
- resolvers.instance.validfile = resolvers.validctxfile
-
- -- we now load the file database as we might need files other than
- -- tex and lua file on the given path
-
- -- trackers.enable("resolvers.*")
- resolvers.load()
- -- trackers.disable("resolvers.*")
-
- local findbinfile, loadbinfile = resolvers.findbinfile, resolvers.loadbinfile
- local findtexfile, opentexfile = resolvers.findtexfile, resolvers.opentexfile
-
- if callback then
-
- local register = callbacks.register
-
- -- register('process_jobname' , function(name) return name end, true)
-
- register('find_read_file' , function(id,name) return findtexfile(name) end, true)
- register('open_read_file' , function( name) return opentexfile(name) end, true)
-
- register('find_data_file' , function(name) return findbinfile(name,"tex") end, true)
- register('find_enc_file' , function(name) return findbinfile(name,"enc") end, true)
- register('find_font_file' , function(name) return findbinfile(name,"tfm") end, true)
- register('find_format_file' , function(name) return findbinfile(name,"fmt") end, true)
- register('find_image_file' , function(name) return findbinfile(name,"tex") end, true)
- register('find_map_file' , function(name) return findbinfile(name,"map") end, true)
- register('find_opentype_file' , function(name) return findbinfile(name,"otf") end, true)
- register('find_output_file' , function(name) return name end, true)
- register('find_pk_file' , function(name) return findbinfile(name,"pk") end, true)
- register('find_sfd_file' , function(name) return findbinfile(name,"sfd") end, true)
- register('find_truetype_file' , function(name) return findbinfile(name,"ttf") end, true)
- register('find_type1_file' , function(name) return findbinfile(name,"pfb") end, true)
- register('find_vf_file' , function(name) return findbinfile(name,"vf") end, true)
- register('find_cidmap_file' , function(name) return findbinfile(name,"cidmap") end, true)
-
- register('read_data_file' , function(file) return loadbinfile(file,"tex") end, true)
- register('read_enc_file' , function(file) return loadbinfile(file,"enc") end, true)
- register('read_font_file' , function(file) return loadbinfile(file,"tfm") end, true)
- -- format
- -- image
- register('read_map_file' , function(file) return loadbinfile(file,"map") end, true)
- -- output
- register('read_pk_file' , function(file) return loadbinfile(file,"pk") end, true) -- 600dpi/manfnt.720pk
- register('read_sfd_file' , function(file) return loadbinfile(file,"sfd") end, true)
- register('read_vf_file' , function(file) return loadbinfile(file,"vf" ) end, true)
-
- register('find_font_file' , function(name) return findbinfile(name,"ofm") end, true)
- register('find_vf_file' , function(name) return findbinfile(name,"ovf") end, true)
-
- register('read_font_file' , function(file) return loadbinfile(file,"ofm") end, true)
- register('read_vf_file' , function(file) return loadbinfile(file,"ovf") end, true)
-
- -- register('read_opentype_file' , function(file) return loadbinfile(file,"otf") end, true)
- -- register('read_truetype_file' , function(file) return loadbinfile(file,"ttf") end, true)
- -- register('read_type1_file' , function(file) return loadbinfile(file,"pfb") end, true)
- -- register('read_cidmap_file' , function(file) return loadbinfile(file,"cidmap") end, true)
-
- register('find_write_file' , function(id,name) return name end, true)
- register('find_format_file' , function(name) return name end, true)
-
- end
-
-end
-
-local report_system = logs.reporter("system","files")
-local report_files = logs.reporter("used files")
-
-luatex.registerstopactions(function()
- local foundintrees = resolvers.instance.foundintrees
- if #foundintrees > 0 then
- logs.pushtarget("logfile")
- logs.newline()
- report_system("start used files")
- logs.newline()
- for i=1,#foundintrees do
- report_files("%4i: % T",i,foundintrees[i])
- end
- logs.newline()
- report_system("stop used files")
- logs.newline()
- logs.poptarget()
- 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.instance.foundintrees,
- #scandata.paths > 0 and concat(scandata.paths," ") or "<none>"
- )
-end)
+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
+local sequenced = table.sequenced
+
+texconfig.kpse_init = false
+texconfig.shell_escape = 't'
+texconfig.max_in_open = 127
+texconfig.max_print_line = 100000
+
+if not resolvers.instance then
+
+ resolvers.reset()
+
+ resolvers.instance.validfile = resolvers.validctxfile
+
+ -- we now load the file database as we might need files other than
+ -- tex and lua file on the given path
+
+ -- trackers.enable("resolvers.*")
+ resolvers.load()
+ -- trackers.disable("resolvers.*")
+
+ local findbinfile, loadbinfile = resolvers.findbinfile, resolvers.loadbinfile
+ local findtexfile, opentexfile = resolvers.findtexfile, resolvers.opentexfile
+
+ if callback then
+
+ local register = callbacks.register
+
+ -- register('process_jobname' , function(name) return name end, true)
+
+ register('find_read_file' , function(id,name) return findtexfile(name) end, true)
+ register('open_read_file' , function( name) return opentexfile(name) end, true)
+
+ register('find_data_file' , function(name) return findbinfile(name,"tex") end, true)
+ register('find_enc_file' , function(name) return findbinfile(name,"enc") end, true)
+ register('find_font_file' , function(name) return findbinfile(name,"tfm") end, true)
+ register('find_format_file' , function(name) return findbinfile(name,"fmt") end, true)
+ register('find_image_file' , function(name) return findbinfile(name,"tex") end, true)
+ register('find_map_file' , function(name) return findbinfile(name,"map") end, true)
+ register('find_opentype_file' , function(name) return findbinfile(name,"otf") end, true)
+ register('find_output_file' , function(name) return name end, true)
+ register('find_pk_file' , function(name) return findbinfile(name,"pk") end, true)
+ register('find_sfd_file' , function(name) return findbinfile(name,"sfd") end, true)
+ register('find_truetype_file' , function(name) return findbinfile(name,"ttf") end, true)
+ register('find_type1_file' , function(name) return findbinfile(name,"pfb") end, true)
+ register('find_vf_file' , function(name) return findbinfile(name,"vf") end, true)
+ register('find_cidmap_file' , function(name) return findbinfile(name,"cidmap") end, true)
+
+ register('read_data_file' , function(file) return loadbinfile(file,"tex") end, true)
+ register('read_enc_file' , function(file) return loadbinfile(file,"enc") end, true)
+ register('read_font_file' , function(file) return loadbinfile(file,"tfm") end, true)
+ -- format
+ -- image
+ register('read_map_file' , function(file) return loadbinfile(file,"map") end, true)
+ -- output
+ register('read_pk_file' , function(file) return loadbinfile(file,"pk") end, true) -- 600dpi/manfnt.720pk
+ register('read_sfd_file' , function(file) return loadbinfile(file,"sfd") end, true)
+ register('read_vf_file' , function(file) return loadbinfile(file,"vf" ) end, true)
+
+ register('find_font_file' , function(name) return findbinfile(name,"ofm") end, true)
+ register('find_vf_file' , function(name) return findbinfile(name,"ovf") end, true)
+
+ register('read_font_file' , function(file) return loadbinfile(file,"ofm") end, true)
+ register('read_vf_file' , function(file) return loadbinfile(file,"ovf") end, true)
+
+ -- register('read_opentype_file' , function(file) return loadbinfile(file,"otf") end, true)
+ -- register('read_truetype_file' , function(file) return loadbinfile(file,"ttf") end, true)
+ -- register('read_type1_file' , function(file) return loadbinfile(file,"pfb") end, true)
+ -- register('read_cidmap_file' , function(file) return loadbinfile(file,"cidmap") end, true)
+
+ register('find_write_file' , function(id,name) return name end, true)
+ register('find_format_file' , function(name) return name end, true)
+
+ end
+
+end
+
+local report_system = logs.reporter("system","files")
+local report_files = logs.reporter("used files")
+
+luatex.registerstopactions(function()
+ local foundintrees = resolvers.instance.foundintrees
+ if #foundintrees > 0 then
+ logs.pushtarget("logfile")
+ logs.newline()
+ report_system("start used files")
+ logs.newline()
+ for i=1,#foundintrees do
+ report_files("%4i: % T",i,foundintrees[i])
+ end
+ logs.newline()
+ report_system("stop used files")
+ logs.newline()
+ logs.poptarget()
+ 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.instance.foundintrees,
+ #scandata.paths > 0 and concat(scandata.paths," ") or "<none>"
+ )
+end)
diff --git a/tex/context/base/luat-fmt.lua b/tex/context/base/luat-fmt.lua
index 2eb5b89c9..20a4a8fcd 100644
--- a/tex/context/base/luat-fmt.lua
+++ b/tex/context/base/luat-fmt.lua
@@ -1,140 +1,140 @@
-if not modules then modules = { } end modules ['luat-fmt'] = {
- version = 1.001,
- comment = "companion to mtxrun",
- 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
-local quoted = string.quoted
-local luasuffixes = utilities.lua.suffixes
-
-local report_format = logs.reporter("resolvers","formats")
-
-local function primaryflags() -- not yet ok
- local trackers = environment.argument("trackers")
- local directives = environment.argument("directives")
- local flags = { }
- if trackers and trackers ~= "" then
- flags = { "--trackers=" .. quoted(trackers) }
- end
- if directives and directives ~= "" then
- flags = { "--directives=" .. quoted(directives) }
- end
- if environment.argument("jit") then
- flags = { "--jiton" }
- end
- return concat(flags," ")
-end
-
-function environment.make_format(name)
- local engine = environment.ownmain or "luatex"
- -- change to format path (early as we need expanded paths)
- local olddir = dir.current()
- local path = caches.getwritablepath("formats",engine) or "" -- maybe platform
- if path ~= "" then
- lfs.chdir(path)
- end
- report_format("using format path %a",dir.current())
- -- check source file
- local texsourcename = file.addsuffix(name,"mkiv")
- local fulltexsourcename = resolvers.findfile(texsourcename,"tex") or ""
- if fulltexsourcename == "" then
- texsourcename = file.addsuffix(name,"tex")
- fulltexsourcename = resolvers.findfile(texsourcename,"tex") or ""
- end
- if fulltexsourcename == "" then
- report_format("no tex source file with name %a (mkiv or tex)",name)
- lfs.chdir(olddir)
- return
- else
- report_format("using tex source file %a",fulltexsourcename)
- end
- local texsourcepath = dir.expandname(file.dirname(fulltexsourcename)) -- really needed
- -- check specification
- local specificationname = file.replacesuffix(fulltexsourcename,"lus")
- local fullspecificationname = resolvers.findfile(specificationname,"tex") or ""
- if fullspecificationname == "" then
- specificationname = file.join(texsourcepath,"context.lus")
- fullspecificationname = resolvers.findfile(specificationname,"tex") or ""
- end
- if fullspecificationname == "" then
- report_format("unknown stub specification %a",specificationname)
- lfs.chdir(olddir)
- return
- end
- local specificationpath = file.dirname(fullspecificationname)
- -- load specification
- local usedluastub = nil
- local usedlualibs = dofile(fullspecificationname)
- if type(usedlualibs) == "string" then
- usedluastub = file.join(file.dirname(fullspecificationname),usedlualibs)
- elseif type(usedlualibs) == "table" then
- report_format("using stub specification %a",fullspecificationname)
- local texbasename = file.basename(name)
- local luastubname = file.addsuffix(texbasename,luasuffixes.lua)
- local lucstubname = file.addsuffix(texbasename,luasuffixes.luc)
- -- pack libraries in stub
- report_format("creating initialization file %a",luastubname)
- utilities.merger.selfcreate(usedlualibs,specificationpath,luastubname)
- -- compile stub file (does not save that much as we don't use this stub at startup any more)
- if utilities.lua.compile(luastubname,lucstubname) and lfs.isfile(lucstubname) then
- report_format("using compiled initialization file %a",lucstubname)
- usedluastub = lucstubname
- else
- report_format("using uncompiled initialization file %a",luastubname)
- usedluastub = luastubname
- end
- else
- report_format("invalid stub specification %a",fullspecificationname)
- lfs.chdir(olddir)
- return
- end
- -- generate format
- local command = format("%s --ini %s --lua=%s %s %sdump",engine,primaryflags(),quoted(usedluastub),quoted(fulltexsourcename),os.platform == "unix" and "\\\\" or "\\")
- report_format("running command: %s\n",command)
- os.spawn(command)
- -- remove related mem files
- local pattern = file.removesuffix(file.basename(usedluastub)).."-*.mem"
- -- report_format("removing related mplib format with pattern %a", pattern)
- local mp = dir.glob(pattern)
- if mp then
- for i=1,#mp do
- local name = mp[i]
- report_format("removing related mplib format %a", file.basename(name))
- os.remove(name)
- end
- end
- lfs.chdir(olddir)
-end
-
-function environment.run_format(name,data,more)
- if name and name ~= "" then
- local engine = environment.ownmain or "luatex"
- local barename = file.removesuffix(name)
- local fmtname = caches.getfirstreadablefile(file.addsuffix(barename,"fmt"),"formats",engine)
- if fmtname == "" then
- fmtname = resolvers.findfile(file.addsuffix(barename,"fmt")) or ""
- end
- fmtname = resolvers.cleanpath(fmtname)
- if fmtname == "" then
- report_format("no format with name %a",name)
- else
- local barename = file.removesuffix(name) -- expanded name
- local luaname = file.addsuffix(barename,"luc")
- if not lfs.isfile(luaname) then
- luaname = file.addsuffix(barename,"lua")
- end
- if not lfs.isfile(luaname) then
- report_format("using format name %a",fmtname)
- report_format("no luc/lua file with name %a",barename)
- else
- local command = format("%s %s --fmt=%s --lua=%s %s %s",engine,primaryflags(),quoted(barename),quoted(luaname),quoted(data),more ~= "" and quoted(more) or "")
- report_format("running command: %s",command)
- os.spawn(command)
- end
- end
- end
-end
+if not modules then modules = { } end modules ['luat-fmt'] = {
+ version = 1.001,
+ comment = "companion to mtxrun",
+ 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
+local quoted = string.quoted
+local luasuffixes = utilities.lua.suffixes
+
+local report_format = logs.reporter("resolvers","formats")
+
+local function primaryflags() -- not yet ok
+ local trackers = environment.argument("trackers")
+ local directives = environment.argument("directives")
+ local flags = { }
+ if trackers and trackers ~= "" then
+ flags = { "--trackers=" .. quoted(trackers) }
+ end
+ if directives and directives ~= "" then
+ flags = { "--directives=" .. quoted(directives) }
+ end
+ if environment.argument("jit") then
+ flags = { "--jiton" }
+ end
+ return concat(flags," ")
+end
+
+function environment.make_format(name)
+ local engine = environment.ownmain or "luatex"
+ -- change to format path (early as we need expanded paths)
+ local olddir = dir.current()
+ local path = caches.getwritablepath("formats",engine) or "" -- maybe platform
+ if path ~= "" then
+ lfs.chdir(path)
+ end
+ report_format("using format path %a",dir.current())
+ -- check source file
+ local texsourcename = file.addsuffix(name,"mkiv")
+ local fulltexsourcename = resolvers.findfile(texsourcename,"tex") or ""
+ if fulltexsourcename == "" then
+ texsourcename = file.addsuffix(name,"tex")
+ fulltexsourcename = resolvers.findfile(texsourcename,"tex") or ""
+ end
+ if fulltexsourcename == "" then
+ report_format("no tex source file with name %a (mkiv or tex)",name)
+ lfs.chdir(olddir)
+ return
+ else
+ report_format("using tex source file %a",fulltexsourcename)
+ end
+ local texsourcepath = dir.expandname(file.dirname(fulltexsourcename)) -- really needed
+ -- check specification
+ local specificationname = file.replacesuffix(fulltexsourcename,"lus")
+ local fullspecificationname = resolvers.findfile(specificationname,"tex") or ""
+ if fullspecificationname == "" then
+ specificationname = file.join(texsourcepath,"context.lus")
+ fullspecificationname = resolvers.findfile(specificationname,"tex") or ""
+ end
+ if fullspecificationname == "" then
+ report_format("unknown stub specification %a",specificationname)
+ lfs.chdir(olddir)
+ return
+ end
+ local specificationpath = file.dirname(fullspecificationname)
+ -- load specification
+ local usedluastub = nil
+ local usedlualibs = dofile(fullspecificationname)
+ if type(usedlualibs) == "string" then
+ usedluastub = file.join(file.dirname(fullspecificationname),usedlualibs)
+ elseif type(usedlualibs) == "table" then
+ report_format("using stub specification %a",fullspecificationname)
+ local texbasename = file.basename(name)
+ local luastubname = file.addsuffix(texbasename,luasuffixes.lua)
+ local lucstubname = file.addsuffix(texbasename,luasuffixes.luc)
+ -- pack libraries in stub
+ report_format("creating initialization file %a",luastubname)
+ utilities.merger.selfcreate(usedlualibs,specificationpath,luastubname)
+ -- compile stub file (does not save that much as we don't use this stub at startup any more)
+ if utilities.lua.compile(luastubname,lucstubname) and lfs.isfile(lucstubname) then
+ report_format("using compiled initialization file %a",lucstubname)
+ usedluastub = lucstubname
+ else
+ report_format("using uncompiled initialization file %a",luastubname)
+ usedluastub = luastubname
+ end
+ else
+ report_format("invalid stub specification %a",fullspecificationname)
+ lfs.chdir(olddir)
+ return
+ end
+ -- generate format
+ local command = format("%s --ini %s --lua=%s %s %sdump",engine,primaryflags(),quoted(usedluastub),quoted(fulltexsourcename),os.platform == "unix" and "\\\\" or "\\")
+ report_format("running command: %s\n",command)
+ os.spawn(command)
+ -- remove related mem files
+ local pattern = file.removesuffix(file.basename(usedluastub)).."-*.mem"
+ -- report_format("removing related mplib format with pattern %a", pattern)
+ local mp = dir.glob(pattern)
+ if mp then
+ for i=1,#mp do
+ local name = mp[i]
+ report_format("removing related mplib format %a", file.basename(name))
+ os.remove(name)
+ end
+ end
+ lfs.chdir(olddir)
+end
+
+function environment.run_format(name,data,more)
+ if name and name ~= "" then
+ local engine = environment.ownmain or "luatex"
+ local barename = file.removesuffix(name)
+ local fmtname = caches.getfirstreadablefile(file.addsuffix(barename,"fmt"),"formats",engine)
+ if fmtname == "" then
+ fmtname = resolvers.findfile(file.addsuffix(barename,"fmt")) or ""
+ end
+ fmtname = resolvers.cleanpath(fmtname)
+ if fmtname == "" then
+ report_format("no format with name %a",name)
+ else
+ local barename = file.removesuffix(name) -- expanded name
+ local luaname = file.addsuffix(barename,"luc")
+ if not lfs.isfile(luaname) then
+ luaname = file.addsuffix(barename,"lua")
+ end
+ if not lfs.isfile(luaname) then
+ report_format("using format name %a",fmtname)
+ report_format("no luc/lua file with name %a",barename)
+ else
+ local command = format("%s %s --fmt=%s --lua=%s %s %s",engine,primaryflags(),quoted(barename),quoted(luaname),quoted(data),more ~= "" and quoted(more) or "")
+ report_format("running command: %s",command)
+ os.spawn(command)
+ end
+ end
+ end
+end
diff --git a/tex/context/base/luat-ini.lua b/tex/context/base/luat-ini.lua
index d4eee7123..587214b93 100644
--- a/tex/context/base/luat-ini.lua
+++ b/tex/context/base/luat-ini.lua
@@ -1,206 +1,206 @@
-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"
-}
-
--- rather experimental down here ... adapted to lua 5.2 ... but still
--- experimental
-
-local debug = require("debug")
-
-local string, table, lpeg, math, io, system = string, table, lpeg, math, io, system
-local rawset, rawget, next, setmetatable = rawset, rawget, next, setmetatable
-
---[[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 { } -- experimental for team
-
-table.setmetatableindex(moduledata,table.autokey)
-table.setmetatableindex(thirddata, table.autokey)
-
---[[ldx--
-<p>Please create a namespace within these tables before using them!</p>
-
-<typing>
-userdata ['my.name'] = { }
-thirddata['tricks' ] = { }
-</typing>
---ldx]]--
-
---[[ldx--
-<p>We could cook up a readonly model for global tables but it makes more sense
-to invite users to use one of the predefined namespaces. One can redefine the
-protector. After all, it's just a lightweight suggestive system, not a
-watertight one.</p>
---ldx]]--
-
-local global = _G
-global.global = global
-
-local dummy = function() end
-
---[[ldx--
-<p>Another approach is to freeze tables by using a metatable, this will be
-implemented stepwise.</p>
---ldx]]--
-
--- moduledata : no need for protection (only for developers)
--- isolatedata : full protection
--- userdata : protected
--- thirddata : protected
-
---[[ldx--
-<p>We could have a metatable that automaticaly creates a top level namespace.</p>
---ldx]]--
-
-local luanames = lua.name -- luatex itself
-
-lua.numbers = lua.numbers or { } local numbers = lua.numbers
-lua.messages = lua.messages or { } local messages = lua.messages
-
-storage.register("lua/numbers", numbers, "lua.numbers" )
-storage.register("lua/messages", messages, "lua.messages")
-
-local setfenv = setfenv or debug.setfenv -- < 5.2
-
-if setfenv then
-
- local protected = {
- -- global table
- global = global,
- -- user tables
- -- moduledata = moduledata,
- userdata = userdata,
- thirddata = thirddata,
- documentdata = documentdata,
- -- reserved
- protect = dummy,
- unprotect = dummy,
- -- luatex
- tex = tex,
- -- lua
- string = string,
- table = table,
- lpeg = lpeg,
- math = math,
- io = io,
- file = file,
- bit32 = bit32,
- --
- context = context,
- }
-
- local protect_full = function(name)
- local t = { }
- for k, v in next, protected do
- t[k] = v
- end
- return t
- end
-
- local protect_part = function(name) -- adds
- local t = rawget(global,name)
- if not t then
- t = { }
- for k, v in next, protected do
- t[k] = v
- end
- rawset(global,name,t)
- end
- return t
- end
-
- protect = function(name)
- if name == "isolateddata" then
- setfenv(2,protect_full(name))
- else
- setfenv(2,protect_part(name or "shareddata"))
- end
- end
-
- function lua.registername(name,message)
- local lnn = lua.numbers[name]
- if not lnn then
- lnn = #messages + 1
- messages[lnn] = message
- numbers[name] = lnn
- end
- luanames[lnn] = message
- context(lnn)
- -- initialize once
- if name ~= "isolateddata" then
- protect_full(name or "shareddata")
- end
- end
-
-elseif libraries then -- assume >= 5.2
-
- local shared
-
- protect = function(name)
- if not shared then
- -- e.g. context is not yet known
- local public = {
- global = global,
- -- moduledata = moduledata,
- userdata = userdata,
- thirddata = thirddata,
- documentdata = documentdata,
- protect = dummy,
- unprotect = dummy,
- context = context,
- }
- --
- for k, v in next, libraries.builtin do public[k] = v end
- for k, v in next, libraries.functions do public[k] = v end
- for k, v in next, libraries.obsolete do public[k] = nil end
- --
- shared = { __index = public }
- protect = function(name)
- local t = global[name] or { }
- setmetatable(t,shared) -- set each time
- return t
- end
- end
- return protect(name)
- end
-
- function lua.registername(name,message)
- local lnn = lua.numbers[name]
- if not lnn then
- lnn = #messages + 1
- messages[lnn] = message
- numbers[name] = lnn
- end
- luanames[lnn] = message
- context(lnn)
- end
-
-else
-
- protect = dummy
-
- function lua.registername(name,message)
- local lnn = lua.numbers[name]
- if not lnn then
- lnn = #messages + 1
- messages[lnn] = message
- numbers[name] = lnn
- end
- luanames[lnn] = message
- context(lnn)
- end
-
-end
-
+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"
+}
+
+-- rather experimental down here ... adapted to lua 5.2 ... but still
+-- experimental
+
+local debug = require("debug")
+
+local string, table, lpeg, math, io, system = string, table, lpeg, math, io, system
+local rawset, rawget, next, setmetatable = rawset, rawget, next, setmetatable
+
+--[[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 { } -- experimental for team
+
+table.setmetatableindex(moduledata,table.autokey)
+table.setmetatableindex(thirddata, table.autokey)
+
+--[[ldx--
+<p>Please create a namespace within these tables before using them!</p>
+
+<typing>
+userdata ['my.name'] = { }
+thirddata['tricks' ] = { }
+</typing>
+--ldx]]--
+
+--[[ldx--
+<p>We could cook up a readonly model for global tables but it makes more sense
+to invite users to use one of the predefined namespaces. One can redefine the
+protector. After all, it's just a lightweight suggestive system, not a
+watertight one.</p>
+--ldx]]--
+
+local global = _G
+global.global = global
+
+local dummy = function() end
+
+--[[ldx--
+<p>Another approach is to freeze tables by using a metatable, this will be
+implemented stepwise.</p>
+--ldx]]--
+
+-- moduledata : no need for protection (only for developers)
+-- isolatedata : full protection
+-- userdata : protected
+-- thirddata : protected
+
+--[[ldx--
+<p>We could have a metatable that automaticaly creates a top level namespace.</p>
+--ldx]]--
+
+local luanames = lua.name -- luatex itself
+
+lua.numbers = lua.numbers or { } local numbers = lua.numbers
+lua.messages = lua.messages or { } local messages = lua.messages
+
+storage.register("lua/numbers", numbers, "lua.numbers" )
+storage.register("lua/messages", messages, "lua.messages")
+
+local setfenv = setfenv or debug.setfenv -- < 5.2
+
+if setfenv then
+
+ local protected = {
+ -- global table
+ global = global,
+ -- user tables
+ -- moduledata = moduledata,
+ userdata = userdata,
+ thirddata = thirddata,
+ documentdata = documentdata,
+ -- reserved
+ protect = dummy,
+ unprotect = dummy,
+ -- luatex
+ tex = tex,
+ -- lua
+ string = string,
+ table = table,
+ lpeg = lpeg,
+ math = math,
+ io = io,
+ file = file,
+ bit32 = bit32,
+ --
+ context = context,
+ }
+
+ local protect_full = function(name)
+ local t = { }
+ for k, v in next, protected do
+ t[k] = v
+ end
+ return t
+ end
+
+ local protect_part = function(name) -- adds
+ local t = rawget(global,name)
+ if not t then
+ t = { }
+ for k, v in next, protected do
+ t[k] = v
+ end
+ rawset(global,name,t)
+ end
+ return t
+ end
+
+ protect = function(name)
+ if name == "isolateddata" then
+ setfenv(2,protect_full(name))
+ else
+ setfenv(2,protect_part(name or "shareddata"))
+ end
+ end
+
+ function lua.registername(name,message)
+ local lnn = lua.numbers[name]
+ if not lnn then
+ lnn = #messages + 1
+ messages[lnn] = message
+ numbers[name] = lnn
+ end
+ luanames[lnn] = message
+ context(lnn)
+ -- initialize once
+ if name ~= "isolateddata" then
+ protect_full(name or "shareddata")
+ end
+ end
+
+elseif libraries then -- assume >= 5.2
+
+ local shared
+
+ protect = function(name)
+ if not shared then
+ -- e.g. context is not yet known
+ local public = {
+ global = global,
+ -- moduledata = moduledata,
+ userdata = userdata,
+ thirddata = thirddata,
+ documentdata = documentdata,
+ protect = dummy,
+ unprotect = dummy,
+ context = context,
+ }
+ --
+ for k, v in next, libraries.builtin do public[k] = v end
+ for k, v in next, libraries.functions do public[k] = v end
+ for k, v in next, libraries.obsolete do public[k] = nil end
+ --
+ shared = { __index = public }
+ protect = function(name)
+ local t = global[name] or { }
+ setmetatable(t,shared) -- set each time
+ return t
+ end
+ end
+ return protect(name)
+ end
+
+ function lua.registername(name,message)
+ local lnn = lua.numbers[name]
+ if not lnn then
+ lnn = #messages + 1
+ messages[lnn] = message
+ numbers[name] = lnn
+ end
+ luanames[lnn] = message
+ context(lnn)
+ end
+
+else
+
+ protect = dummy
+
+ function lua.registername(name,message)
+ local lnn = lua.numbers[name]
+ if not lnn then
+ lnn = #messages + 1
+ messages[lnn] = message
+ numbers[name] = lnn
+ end
+ luanames[lnn] = message
+ context(lnn)
+ end
+
+end
+
diff --git a/tex/context/base/luat-iop.lua b/tex/context/base/luat-iop.lua
index bcbfac73a..52f14683e 100644
--- a/tex/context/base/luat-iop.lua
+++ b/tex/context/base/luat-iop.lua
@@ -1,195 +1,195 @@
-if not modules then modules = { } end modules ['luat-iop'] = {
- 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"
-}
-
--- this paranoid stuff in web2c ... we cannot hook checks into the
--- input functions because one can always change the callback but
--- we can feed back specific patterns and paths into the next
--- mechanism
-
--- os.execute os.exec os.spawn io.fopen
--- os.remove lfs.chdir lfs.mkdir
--- io.open zip.open epdf.open mlib.new
-
--- cache
-
-local topattern, find = string.topattern, string.find
-
-local report_limiter = logs.reporter("system","limiter")
-
--- the basic methods
-
-local function match(ruleset,name)
- local n = #ruleset
- if n > 0 then
- for i=1,n do
- local r = ruleset[i]
- if find(name,r[1]) then
- return r[2]
- end
- end
- return false
- else
- -- nothing defined (or any)
- return true
- end
-end
-
-local function protect(ruleset,proc)
- return function(name,...)
- if name == "" then
- -- report_limiter("no access permitted: <no name>") -- can happen in mplib code
- return nil, "no name given"
- elseif match(ruleset,name) then
- return proc(name,...)
- else
- report_limiter("no access permitted for %a",name)
- return nil, name .. ": no access permitted"
- end
- end
-end
-
-function io.limiter(preset)
- preset = preset or { }
- local ruleset = { }
- for i=1,#preset do
- local p = preset[i]
- local what, spec = p[1] or "", p[2] or ""
- if spec == "" then
- -- skip 'm
- elseif what == "tree" then
- resolvers.dowithpath(spec, function(r)
- local spec = resolvers.resolve(r) or ""
- if spec ~= "" then
- ruleset[#ruleset+1] = { topattern(spec,true), true }
- end
- end)
- elseif what == "permit" then
- ruleset[#ruleset+1] = { topattern(spec,true), true }
- elseif what == "forbid" then
- ruleset[#ruleset+1] = { topattern(spec,true), false }
- end
- end
- if #ruleset > 0 then
- return {
- match = function(name) return match (ruleset,name) end,
- protect = function(proc) return protect(ruleset,proc) end,
- }
- else
- return {
- match = function(name) return true end,
- protect = proc,
- }
- end
-end
-
--- a few handlers
-
-io.i_limiters = { }
-io.o_limiters = { }
-
-function io.i_limiter(v)
- local i = io.i_limiters[v]
- if i then
- local i_limiter = io.limiter(i)
- function io.i_limiter()
- return i_limiter
- end
- return i_limiter
- end
-end
-
-function io.o_limiter(v)
- local o = io.o_limiters[v]
- if o then
- local o_limiter = io.limiter(o)
- function io.o_limiter()
- return o_limiter
- end
- return o_limiter
- end
-end
-
--- the real thing (somewhat fuzzy as we need to know what gets done)
-
-local i_opener, i_limited = io.open, false
-local o_opener, o_limited = io.open, false
-
-local function i_register(v)
- if not i_limited then
- local i_limiter = io.i_limiter(v)
- if i_limiter then
- local protect = i_limiter.protect
- i_opener = protect(i_opener)
- i_limited = true
- report_limiter("input mode set to %a",v)
- end
- end
-end
-
-local function o_register(v)
- if not o_limited then
- local o_limiter = io.o_limiter(v)
- if o_limiter then
- local protect = o_limiter.protect
- o_opener = protect(o_opener)
- o_limited = true
- report_limiter("output mode set to %a",v)
- end
- end
-end
-
-function io.open(name,method)
- if method and find(method,"[wa]") then
- return o_opener(name,method)
- else
- return i_opener(name,method)
- end
-end
-
-directives.register("system.inputmode", i_register)
-directives.register("system.outputmode", o_register)
-
-local i_limited = false
-local o_limited = false
-
-local function i_register(v)
- if not i_limited then
- local i_limiter = io.i_limiter(v)
- if i_limiter then
- local protect = i_limiter.protect
- lfs.chdir = protect(lfs.chdir) -- needs checking
- i_limited = true
- end
- end
-end
-
-local function o_register(v)
- if not o_limited then
- local o_limiter = io.o_limiter(v)
- if o_limiter then
- local protect = o_limiter.protect
- os.remove = protect(os.remove) -- rather okay
- lfs.chdir = protect(lfs.chdir) -- needs checking
- lfs.mkdir = protect(lfs.mkdir) -- needs checking
- o_limited = true
- end
- end
-end
-
-directives.register("system.inputmode", i_register)
-directives.register("system.outputmode", o_register)
-
--- the definitions
-
-local limiters = resolvers.variable("limiters")
-
-if limiters then
- io.i_limiters = limiters.input or { }
- io.o_limiters = limiters.output or { }
-end
-
+if not modules then modules = { } end modules ['luat-iop'] = {
+ 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"
+}
+
+-- this paranoid stuff in web2c ... we cannot hook checks into the
+-- input functions because one can always change the callback but
+-- we can feed back specific patterns and paths into the next
+-- mechanism
+
+-- os.execute os.exec os.spawn io.fopen
+-- os.remove lfs.chdir lfs.mkdir
+-- io.open zip.open epdf.open mlib.new
+
+-- cache
+
+local topattern, find = string.topattern, string.find
+
+local report_limiter = logs.reporter("system","limiter")
+
+-- the basic methods
+
+local function match(ruleset,name)
+ local n = #ruleset
+ if n > 0 then
+ for i=1,n do
+ local r = ruleset[i]
+ if find(name,r[1]) then
+ return r[2]
+ end
+ end
+ return false
+ else
+ -- nothing defined (or any)
+ return true
+ end
+end
+
+local function protect(ruleset,proc)
+ return function(name,...)
+ if name == "" then
+ -- report_limiter("no access permitted: <no name>") -- can happen in mplib code
+ return nil, "no name given"
+ elseif match(ruleset,name) then
+ return proc(name,...)
+ else
+ report_limiter("no access permitted for %a",name)
+ return nil, name .. ": no access permitted"
+ end
+ end
+end
+
+function io.limiter(preset)
+ preset = preset or { }
+ local ruleset = { }
+ for i=1,#preset do
+ local p = preset[i]
+ local what, spec = p[1] or "", p[2] or ""
+ if spec == "" then
+ -- skip 'm
+ elseif what == "tree" then
+ resolvers.dowithpath(spec, function(r)
+ local spec = resolvers.resolve(r) or ""
+ if spec ~= "" then
+ ruleset[#ruleset+1] = { topattern(spec,true), true }
+ end
+ end)
+ elseif what == "permit" then
+ ruleset[#ruleset+1] = { topattern(spec,true), true }
+ elseif what == "forbid" then
+ ruleset[#ruleset+1] = { topattern(spec,true), false }
+ end
+ end
+ if #ruleset > 0 then
+ return {
+ match = function(name) return match (ruleset,name) end,
+ protect = function(proc) return protect(ruleset,proc) end,
+ }
+ else
+ return {
+ match = function(name) return true end,
+ protect = proc,
+ }
+ end
+end
+
+-- a few handlers
+
+io.i_limiters = { }
+io.o_limiters = { }
+
+function io.i_limiter(v)
+ local i = io.i_limiters[v]
+ if i then
+ local i_limiter = io.limiter(i)
+ function io.i_limiter()
+ return i_limiter
+ end
+ return i_limiter
+ end
+end
+
+function io.o_limiter(v)
+ local o = io.o_limiters[v]
+ if o then
+ local o_limiter = io.limiter(o)
+ function io.o_limiter()
+ return o_limiter
+ end
+ return o_limiter
+ end
+end
+
+-- the real thing (somewhat fuzzy as we need to know what gets done)
+
+local i_opener, i_limited = io.open, false
+local o_opener, o_limited = io.open, false
+
+local function i_register(v)
+ if not i_limited then
+ local i_limiter = io.i_limiter(v)
+ if i_limiter then
+ local protect = i_limiter.protect
+ i_opener = protect(i_opener)
+ i_limited = true
+ report_limiter("input mode set to %a",v)
+ end
+ end
+end
+
+local function o_register(v)
+ if not o_limited then
+ local o_limiter = io.o_limiter(v)
+ if o_limiter then
+ local protect = o_limiter.protect
+ o_opener = protect(o_opener)
+ o_limited = true
+ report_limiter("output mode set to %a",v)
+ end
+ end
+end
+
+function io.open(name,method)
+ if method and find(method,"[wa]") then
+ return o_opener(name,method)
+ else
+ return i_opener(name,method)
+ end
+end
+
+directives.register("system.inputmode", i_register)
+directives.register("system.outputmode", o_register)
+
+local i_limited = false
+local o_limited = false
+
+local function i_register(v)
+ if not i_limited then
+ local i_limiter = io.i_limiter(v)
+ if i_limiter then
+ local protect = i_limiter.protect
+ lfs.chdir = protect(lfs.chdir) -- needs checking
+ i_limited = true
+ end
+ end
+end
+
+local function o_register(v)
+ if not o_limited then
+ local o_limiter = io.o_limiter(v)
+ if o_limiter then
+ local protect = o_limiter.protect
+ os.remove = protect(os.remove) -- rather okay
+ lfs.chdir = protect(lfs.chdir) -- needs checking
+ lfs.mkdir = protect(lfs.mkdir) -- needs checking
+ o_limited = true
+ end
+ end
+end
+
+directives.register("system.inputmode", i_register)
+directives.register("system.outputmode", o_register)
+
+-- the definitions
+
+local limiters = resolvers.variable("limiters")
+
+if limiters then
+ io.i_limiters = limiters.input or { }
+ io.o_limiters = limiters.output or { }
+end
+
diff --git a/tex/context/base/luat-lua.lua b/tex/context/base/luat-lua.lua
index fd899871f..972004e88 100644
--- a/tex/context/base/luat-lua.lua
+++ b/tex/context/base/luat-lua.lua
@@ -1,45 +1,45 @@
-if not modules then modules = { } end modules ['luat-lua'] = {
- 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"
-}
-
-if lua then do
-
- local delayed = { }
-
- function lua.flushdelayed(...)
- local t = delayed
- delayed = { }
- for i=1, #t do
- t[i](...)
- end
- end
-
- function lua.delay(f)
- delayed[#delayed+1] = f
- end
-
- function lua.flush(...)
- context.directlua("lua.flushdelayed(%,t)",{...})
- end
-
-end end
-
--- See mk.pdf for an explanation of the following code:
---
--- function test(n)
--- lua.delay(function(...)
--- context("pi: %s %s %s",...)
--- context.par()
--- end)
--- lua.delay(function(...)
--- context("more pi: %s %s %s",...)
--- context.par()
--- end)
--- context("\\setbox0=\\hbox{%s}",math.pi*n)
--- local box = tex.box[0]
--- lua.flush(box.width,box.height,box.depth)
--- end
+if not modules then modules = { } end modules ['luat-lua'] = {
+ 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"
+}
+
+if lua then do
+
+ local delayed = { }
+
+ function lua.flushdelayed(...)
+ local t = delayed
+ delayed = { }
+ for i=1, #t do
+ t[i](...)
+ end
+ end
+
+ function lua.delay(f)
+ delayed[#delayed+1] = f
+ end
+
+ function lua.flush(...)
+ context.directlua("lua.flushdelayed(%,t)",{...})
+ end
+
+end end
+
+-- See mk.pdf for an explanation of the following code:
+--
+-- function test(n)
+-- lua.delay(function(...)
+-- context("pi: %s %s %s",...)
+-- context.par()
+-- end)
+-- lua.delay(function(...)
+-- context("more pi: %s %s %s",...)
+-- context.par()
+-- end)
+-- context("\\setbox0=\\hbox{%s}",math.pi*n)
+-- local box = tex.box[0]
+-- lua.flush(box.width,box.height,box.depth)
+-- end
diff --git a/tex/context/base/luat-mac.lua b/tex/context/base/luat-mac.lua
index 19f4d108b..c8be06b63 100644
--- a/tex/context/base/luat-mac.lua
+++ b/tex/context/base/luat-mac.lua
@@ -1,434 +1,434 @@
-if not modules then modules = { } end modules ['luat-mac'] = {
- 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"
-}
-
--- Sometimes we run into situations like:
---
--- \def\foo#1{\expandafter\def\csname#1\endcsname}
---
--- As this confuses the parser, the following should be used instead:
---
--- \def\foo#1{\expandafter\normaldef\csname#1\endcsname}
-
-local P, V, S, R, C, Cs, Cmt, Carg = lpeg.P, lpeg.V, lpeg.S, lpeg.R, lpeg.C, lpeg.Cs, lpeg.Cmt, lpeg.Carg
-local lpegmatch, patterns = lpeg.match, lpeg.patterns
-
-local insert, remove = table.insert, table.remove
-local rep, sub = string.rep, string.sub
-local setmetatable = setmetatable
-local filesuffix = file.suffix
-local convertlmxstring = lmx and lmx.convertstring
-
-local pushtarget, poptarget = logs.pushtarget, logs.poptarget
-
-local report_macros = logs.reporter("interface","macros")
-
-local stack, top, n, hashes = { }, nil, 0, { }
-
-local function set(s)
- if top then
- n = n + 1
- if n > 9 then
- report_macros("number of arguments > 9, ignoring %s",s)
- else
- local ns = #stack
- local h = hashes[ns]
- if not h then
- h = rep("#",2^(ns-1))
- hashes[ns] = h
- end
- m = h .. n
- top[s] = m
- return m
- end
- end
-end
-
-local function get(s)
- if not top then
- report_macros("keeping #%s, no stack",s)
- return "#" .. s -- can be lua
- end
- local m = top[s]
- if m then
- return m
- else
- report_macros("keeping #%s, not on stack",s)
- return "#" .. s -- quite likely an error
- end
-end
-
-local function push()
- top = { }
- n = 0
- local s = stack[#stack]
- if s then
- setmetatable(top,{ __index = s })
- end
- insert(stack,top)
-end
-
-local function pop()
- top = remove(stack)
-end
-
-local leftbrace = P("{") -- will be in patterns
-local rightbrace = P("}")
-local escape = P("\\")
-
-local space = patterns.space
-local spaces = space^1
-local newline = patterns.newline
-local nobrace = 1 - leftbrace - rightbrace
-
-local longleft = leftbrace -- P("(")
-local longright = rightbrace -- P(")")
-local nolong = 1 - longleft - longright
-
-local name = R("AZ","az")^1
-local csname = (R("AZ","az") + S("@?!_"))^1
-local longname = (longleft/"") * (nolong^1) * (longright/"")
-local variable = P("#") * Cs(name + longname)
-local escapedname = escape * csname
-local definer = escape * (P("def") + S("egx") * P("def")) -- tex
-local setter = escape * P("set") * (P("u")^-1 * S("egx")^-1) * P("value") -- context specific
---- + escape * P("install") * (1-P("handler"))^1 * P("handler") -- context specific
-local startcode = P("\\starttexdefinition") -- context specific
-local stopcode = P("\\stoptexdefinition") -- context specific
-local anything = patterns.anything
-local always = patterns.alwaysmatched
-
-local definer = escape * (P("u")^-1 * S("egx")^-1 * P("def")) -- tex
-
--- The comment nilling can become an option but it nicely compensates the Lua
--- parsing here with less parsing at the TeX end. We keep lines so the errors
--- get reported all right, but comments are never seen there anyway. We keep
--- comment that starts inline as it can be something special with a % (at some
--- point we can do that as well, esp if we never use \% or `% somewhere
--- unpredictable). We need to skip comments anyway. Hm, too tricky, this
--- stripping as we can have Lua code etc.
-
-local commenttoken = P("%")
-local crorlf = S("\n\r")
------ commentline = commenttoken * ((Carg(1) * C((1-crorlf)^0))/function(strip,s) return strip and "" or s end)
-local commentline = commenttoken * ((1-crorlf)^0)
-local leadingcomment = (commentline * crorlf^1)^1
-local furthercomment = (crorlf^1 * commentline)^1
-
-local pushlocal = always / push
-local poplocal = always / pop
-local declaration = variable / set
-local identifier = variable / get
-
-local argument = P { leftbrace * ((identifier + V(1) + (1 - leftbrace - rightbrace))^0) * rightbrace }
-
-local function matcherror(str,pos)
- report_macros("runaway definition at: %s",sub(str,pos-30,pos))
-end
-
-local csname_endcsname = P("\\csname") * (identifier + (1 - P("\\endcsname")))^1
-
-local grammar = { "converter",
- texcode = pushlocal
- * startcode
- * spaces
- * (csname * spaces)^1 -- new: multiple, new:csname instead of name
- -- * (declaration + furthercomment + (1 - newline - space))^0
- * ((declaration * (space^0/""))^1 + furthercomment + (1 - newline - space))^0 -- accepts #a #b #c
- * V("texbody")
- * stopcode
- * poplocal,
- texbody = ( V("definition")
- + identifier
- + V("braced")
- + (1 - stopcode)
- )^0,
- definition = pushlocal
- * definer
- * spaces^0
- * escapedname
--- * (declaration + furthercomment + commentline + (1-leftbrace))^0
- * (declaration + furthercomment + commentline + csname_endcsname + (1-leftbrace))^0
- * V("braced")
- * poplocal,
- setcode = pushlocal
- * setter
- * argument
- * (declaration + furthercomment + commentline + (1-leftbrace))^0
- * V("braced")
- * poplocal,
- braced = leftbrace
- * ( V("definition")
- + identifier
- + V("setcode")
- + V("texcode")
- + V("braced")
- + furthercomment
- + leadingcomment -- new per 2012-05-15 (message on mailing list)
- + nobrace
- )^0
- -- * rightbrace^-1, -- the -1 catches errors
- * (rightbrace + Cmt(always,matcherror)),
-
- pattern = leadingcomment
- + V("definition")
- + V("setcode")
- + V("texcode")
- + furthercomment
- + anything,
-
- converter = V("pattern")^1,
-}
-
-local parser = Cs(grammar)
-
-local checker = P("%") * (1 - newline - P("macros"))^0
- * P("macros") * space^0 * P("=") * space^0 * C(patterns.letter^1)
-
--- maybe namespace
-
-local macros = { } resolvers.macros = macros
-
-function macros.preprocessed(str,strip)
- return lpegmatch(parser,str,1,strip)
-end
-
-function macros.convertfile(oldname,newname) -- beware, no testing on oldname == newname
- local data = resolvers.loadtexfile(oldname)
- data = interfaces.preprocessed(data) or ""
- io.savedata(newname,data)
-end
-
-function macros.version(data)
- return lpegmatch(checker,data)
-end
-
--- function macros.processmkvi(str,filename)
--- if filename and filesuffix(filename) == "mkvi" or lpegmatch(checker,str) == "mkvi" then
--- local oldsize = #str
--- str = lpegmatch(parser,str,1,true) or str
--- pushtarget("log")
--- report_macros("processed mkvi file %a, delta %s",filename,oldsize-#str)
--- poptarget("log")
--- end
--- return str
--- end
---
--- utilities.sequencers.appendaction(resolvers.openers.helpers.textfileactions,"system","resolvers.macros.processmkvi")
-
--- the document variables hack is temporary
-
-local processors = { }
-
-function processors.mkvi(str,filename)
- local oldsize = #str
- str = lpegmatch(parser,str,1,true) or str
- pushtarget("log")
- report_macros("processed mkvi file %a, delta %s",filename,oldsize-#str)
- poptarget("log")
- return str
-end
-
-function processors.mkix(str,filename) -- we could intercept earlier so that caching works better
- if not document then -- because now we hash the string as well as the
- document = { }
- end
- if not document.variables then
- document.variables = { }
- end
- local oldsize = #str
- str = convertlmxstring(str,document.variables,false) or str
- pushtarget("log")
- report_macros("processed mkix file %a, delta %s",filename,oldsize-#str)
- poptarget("log")
- return str
-end
-
-function processors.mkxi(str,filename)
- if not document then
- document = { }
- end
- if not document.variables then
- document.variables = { }
- end
- local oldsize = #str
- str = convertlmxstring(str,document.variables,false) or str
- str = lpegmatch(parser,str,1,true) or str
- pushtarget("log")
- report_macros("processed mkxi file %a, delta %s",filename,oldsize-#str)
- poptarget("log")
- return str
-end
-
-function macros.processmk(str,filename)
- if filename then
- local suffix = filesuffix(filename)
- local processor = processors[suffix] or processors[lpegmatch(checker,str)]
- if processor then
- str = processor(str,filename)
- end
- end
- return str
-end
-
-function macros.processmkvi(str,filename)
- if filename and filesuffix(filename) == "mkvi" or lpegmatch(checker,str) == "mkvi" then
- local oldsize = #str
- str = lpegmatch(parser,str,1,true) or str
- pushtarget("log")
- report_macros("processed mkvi file %a, delta %s",filename,oldsize-#str)
- poptarget("log")
- end
- return str
-end
-
-local sequencers = utilities.sequencers
-
-if sequencers then
-
- sequencers.appendaction(resolvers.openers.helpers.textfileactions,"system","resolvers.macros.processmk")
- sequencers.appendaction(resolvers.openers.helpers.textfileactions,"system","resolvers.macros.processmkvi")
-
-end
-
--- bonus
-
-if resolvers.schemes then
-
- local function handler(protocol,name,cachename)
- local hashed = url.hashed(name)
- local path = hashed.path
- if path and path ~= "" then
- local str = resolvers.loadtexfile(path)
- if filesuffix(path) == "mkvi" or lpegmatch(checker,str) == "mkvi" then
- -- already done automatically
- io.savedata(cachename,str)
- else
- local result = lpegmatch(parser,str,1,true) or str
- pushtarget("log")
- report_macros("processed scheme %a, delta %s",filename,#str-#result)
- poptarget("log")
- io.savedata(cachename,result)
- end
- end
- return cachename
- end
-
- resolvers.schemes.install('mkvi',handler,1) -- this will cache !
-
-end
-
--- print(macros.preprocessed(
--- [[
--- \starttexdefinition unexpanded test #aa #bb #cc
--- test
--- \stoptexdefinition
--- ]]))
-
--- print(macros.preprocessed([[\checked \def \bla #bla{bla#{bla}}]]))
--- print(macros.preprocessed([[\def\bla#bla{#{bla}bla}]]))
--- print(macros.preprocessed([[\def\blä#{blá}{blà:#{blá}}]]))
--- print(macros.preprocessed([[\def\blä#bla{blà:#bla}]]))
--- print(macros.preprocessed([[\setvalue{xx}#bla{blà:#bla}]]))
--- print(macros.preprocessed([[\def\foo#bar{\setvalue{xx#bar}{#bar}}]]))
--- print(macros.preprocessed([[\def\bla#bla{bla:#{bla}}]]))
--- print(macros.preprocessed([[\def\bla_bla#bla{bla:#bla}]]))
--- print(macros.preprocessed([[\def\test#oeps{test:#oeps}]]))
--- print(macros.preprocessed([[\def\test_oeps#oeps{test:#oeps}]]))
--- print(macros.preprocessed([[\def\test#oeps{test:#{oeps}}]]))
--- print(macros.preprocessed([[\def\test#{oeps:1}{test:#{oeps:1}}]]))
--- print(macros.preprocessed([[\def\test#{oeps}{test:#oeps}]]))
--- print(macros.preprocessed([[\def\x[#a][#b][#c]{\setvalue{\y{#a}\z{#b}}{#c}}]]))
--- print(macros.preprocessed([[\def\test#{oeps}{test:#oeps \halign{##\cr #oeps\cr}]]))
--- print(macros.preprocessed([[\def\test#{oeps}{test:#oeps \halign{##\cr #oeps\cr}}]]))
--- print(macros.preprocessed([[% test
--- \def\test#oeps{#oeps} % {test}
--- % test
---
--- % test
--- two
--- %test]]))
--- print(macros.preprocessed([[
--- \def\scrn_button_make_normal#namespace#current#currentparameter#text%
--- {\ctxlua{structures.references.injectcurrentset(nil,nil)}%
--- % \hbox attr \referenceattribute \lastreferenceattribute {\localframed[#namespace:#current]{#text}}}
--- \hbox attr \referenceattribute \lastreferenceattribute {\directlocalframed[#namespace:#current]{#text}}}
--- ]]))
---
--- print(macros.preprocessed([[
--- \def\definefoo[#name]%
--- {\setvalue{start#name}{\dostartfoo{#name}}}
--- \def\dostartfoo#name%
--- {\def\noexpand\next#content\expandafter\noexpand\csname stop#name\endcsname{#name : #content}%
--- \next}
--- \def\dostartfoo#name%
--- {\normalexpanded{\def\noexpand\next#content\expandafter\noexpand\csname stop#name\endcsname}{#name : #content}%
--- \next}
--- ]]))
---
--- print(macros.preprocessed([[
--- \def\dosomething#content{%%% {{
--- % { }{{ %%
--- \bgroup\italic#content\egroup
--- }
--- ]]))
---
--- print(macros.preprocessed([[
--- \unexpanded\def\start#tag#stoptag%
--- {\initialize{#tag}%
--- \normalexpanded
--- {\def\yes[#one]#two\csname\e!stop#stoptag\endcsname{\command_yes[#one]{#two}}%
--- \def\nop #one\csname\e!stop#stoptag\endcsname{\command_nop {#one}}}%
--- \doifnextoptionalelse\yes\nop}
--- ]]))
---
--- print(macros.preprocessed([[
--- \normalexpanded{\long\def\expandafter\noexpand\csname\e!start\v!interactionmenu\endcsname[#tag]#content\expandafter\noexpand\csname\e!stop\v!interactionmenu\endcsname}%
--- {\def\currentinteractionmenu{#tag}%
--- \expandafter\settrue\csname\??menustate\interactionmenuparameter\c!category\endcsname
--- \setinteractionmenuparameter\c!menu{#content}}
--- ]]))
---
--- Just an experiment:
---
--- \catcode\numexpr"10FF25=\commentcatcode %% > 110000 is invalid
---
--- We could have a push/pop mechanism but binding to txtcatcodes
--- is okay too.
-
-local txtcatcodes = false -- also signal and yet unknown
-
-local commentsignal = utf.char(0x10FF25)
-
-local encodecomment = P("%%") / commentsignal --
------ encodepattern = Cs(((1-encodecomment)^0 * encodecomment)) -- strips but not nice for verbatim
-local encodepattern = Cs((encodecomment + 1)^0)
-local decodecomment = P(commentsignal) / "%%%%" -- why doubles here?
-local decodepattern = Cs((decodecomment + 1)^0)
-
-function resolvers.macros.encodecomment(str)
- if txtcatcodes and tex.catcodetable == txtcatcodes then
- return lpegmatch(encodepattern,str) or str
- else
- return str
- end
-end
-
-function resolvers.macros.decodecomment(str) -- normally not needed
- return txtcatcodes and lpegmatch(decodepattern,str) or str
-end
-
--- resolvers.macros.commentsignal = commentsignal
--- resolvers.macros.encodecommentpattern = encodepattern
--- resolvers.macros.decodecommentpattern = decodepattern
-
-function resolvers.macros.enablecomment(thecatcodes)
- if not txtcatcodes then
- txtcatcodes = thecatcodes or catcodes.numbers.txtcatcodes
- utilities.sequencers.appendaction(resolvers.openers.helpers.textlineactions,"system","resolvers.macros.encodecomment")
- end
-end
+if not modules then modules = { } end modules ['luat-mac'] = {
+ 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"
+}
+
+-- Sometimes we run into situations like:
+--
+-- \def\foo#1{\expandafter\def\csname#1\endcsname}
+--
+-- As this confuses the parser, the following should be used instead:
+--
+-- \def\foo#1{\expandafter\normaldef\csname#1\endcsname}
+
+local P, V, S, R, C, Cs, Cmt, Carg = lpeg.P, lpeg.V, lpeg.S, lpeg.R, lpeg.C, lpeg.Cs, lpeg.Cmt, lpeg.Carg
+local lpegmatch, patterns = lpeg.match, lpeg.patterns
+
+local insert, remove = table.insert, table.remove
+local rep, sub = string.rep, string.sub
+local setmetatable = setmetatable
+local filesuffix = file.suffix
+local convertlmxstring = lmx and lmx.convertstring
+
+local pushtarget, poptarget = logs.pushtarget, logs.poptarget
+
+local report_macros = logs.reporter("interface","macros")
+
+local stack, top, n, hashes = { }, nil, 0, { }
+
+local function set(s)
+ if top then
+ n = n + 1
+ if n > 9 then
+ report_macros("number of arguments > 9, ignoring %s",s)
+ else
+ local ns = #stack
+ local h = hashes[ns]
+ if not h then
+ h = rep("#",2^(ns-1))
+ hashes[ns] = h
+ end
+ m = h .. n
+ top[s] = m
+ return m
+ end
+ end
+end
+
+local function get(s)
+ if not top then
+ report_macros("keeping #%s, no stack",s)
+ return "#" .. s -- can be lua
+ end
+ local m = top[s]
+ if m then
+ return m
+ else
+ report_macros("keeping #%s, not on stack",s)
+ return "#" .. s -- quite likely an error
+ end
+end
+
+local function push()
+ top = { }
+ n = 0
+ local s = stack[#stack]
+ if s then
+ setmetatable(top,{ __index = s })
+ end
+ insert(stack,top)
+end
+
+local function pop()
+ top = remove(stack)
+end
+
+local leftbrace = P("{") -- will be in patterns
+local rightbrace = P("}")
+local escape = P("\\")
+
+local space = patterns.space
+local spaces = space^1
+local newline = patterns.newline
+local nobrace = 1 - leftbrace - rightbrace
+
+local longleft = leftbrace -- P("(")
+local longright = rightbrace -- P(")")
+local nolong = 1 - longleft - longright
+
+local name = R("AZ","az")^1
+local csname = (R("AZ","az") + S("@?!_"))^1
+local longname = (longleft/"") * (nolong^1) * (longright/"")
+local variable = P("#") * Cs(name + longname)
+local escapedname = escape * csname
+local definer = escape * (P("def") + S("egx") * P("def")) -- tex
+local setter = escape * P("set") * (P("u")^-1 * S("egx")^-1) * P("value") -- context specific
+--- + escape * P("install") * (1-P("handler"))^1 * P("handler") -- context specific
+local startcode = P("\\starttexdefinition") -- context specific
+local stopcode = P("\\stoptexdefinition") -- context specific
+local anything = patterns.anything
+local always = patterns.alwaysmatched
+
+local definer = escape * (P("u")^-1 * S("egx")^-1 * P("def")) -- tex
+
+-- The comment nilling can become an option but it nicely compensates the Lua
+-- parsing here with less parsing at the TeX end. We keep lines so the errors
+-- get reported all right, but comments are never seen there anyway. We keep
+-- comment that starts inline as it can be something special with a % (at some
+-- point we can do that as well, esp if we never use \% or `% somewhere
+-- unpredictable). We need to skip comments anyway. Hm, too tricky, this
+-- stripping as we can have Lua code etc.
+
+local commenttoken = P("%")
+local crorlf = S("\n\r")
+----- commentline = commenttoken * ((Carg(1) * C((1-crorlf)^0))/function(strip,s) return strip and "" or s end)
+local commentline = commenttoken * ((1-crorlf)^0)
+local leadingcomment = (commentline * crorlf^1)^1
+local furthercomment = (crorlf^1 * commentline)^1
+
+local pushlocal = always / push
+local poplocal = always / pop
+local declaration = variable / set
+local identifier = variable / get
+
+local argument = P { leftbrace * ((identifier + V(1) + (1 - leftbrace - rightbrace))^0) * rightbrace }
+
+local function matcherror(str,pos)
+ report_macros("runaway definition at: %s",sub(str,pos-30,pos))
+end
+
+local csname_endcsname = P("\\csname") * (identifier + (1 - P("\\endcsname")))^1
+
+local grammar = { "converter",
+ texcode = pushlocal
+ * startcode
+ * spaces
+ * (csname * spaces)^1 -- new: multiple, new:csname instead of name
+ -- * (declaration + furthercomment + (1 - newline - space))^0
+ * ((declaration * (space^0/""))^1 + furthercomment + (1 - newline - space))^0 -- accepts #a #b #c
+ * V("texbody")
+ * stopcode
+ * poplocal,
+ texbody = ( V("definition")
+ + identifier
+ + V("braced")
+ + (1 - stopcode)
+ )^0,
+ definition = pushlocal
+ * definer
+ * spaces^0
+ * escapedname
+-- * (declaration + furthercomment + commentline + (1-leftbrace))^0
+ * (declaration + furthercomment + commentline + csname_endcsname + (1-leftbrace))^0
+ * V("braced")
+ * poplocal,
+ setcode = pushlocal
+ * setter
+ * argument
+ * (declaration + furthercomment + commentline + (1-leftbrace))^0
+ * V("braced")
+ * poplocal,
+ braced = leftbrace
+ * ( V("definition")
+ + identifier
+ + V("setcode")
+ + V("texcode")
+ + V("braced")
+ + furthercomment
+ + leadingcomment -- new per 2012-05-15 (message on mailing list)
+ + nobrace
+ )^0
+ -- * rightbrace^-1, -- the -1 catches errors
+ * (rightbrace + Cmt(always,matcherror)),
+
+ pattern = leadingcomment
+ + V("definition")
+ + V("setcode")
+ + V("texcode")
+ + furthercomment
+ + anything,
+
+ converter = V("pattern")^1,
+}
+
+local parser = Cs(grammar)
+
+local checker = P("%") * (1 - newline - P("macros"))^0
+ * P("macros") * space^0 * P("=") * space^0 * C(patterns.letter^1)
+
+-- maybe namespace
+
+local macros = { } resolvers.macros = macros
+
+function macros.preprocessed(str,strip)
+ return lpegmatch(parser,str,1,strip)
+end
+
+function macros.convertfile(oldname,newname) -- beware, no testing on oldname == newname
+ local data = resolvers.loadtexfile(oldname)
+ data = interfaces.preprocessed(data) or ""
+ io.savedata(newname,data)
+end
+
+function macros.version(data)
+ return lpegmatch(checker,data)
+end
+
+-- function macros.processmkvi(str,filename)
+-- if filename and filesuffix(filename) == "mkvi" or lpegmatch(checker,str) == "mkvi" then
+-- local oldsize = #str
+-- str = lpegmatch(parser,str,1,true) or str
+-- pushtarget("log")
+-- report_macros("processed mkvi file %a, delta %s",filename,oldsize-#str)
+-- poptarget("log")
+-- end
+-- return str
+-- end
+--
+-- utilities.sequencers.appendaction(resolvers.openers.helpers.textfileactions,"system","resolvers.macros.processmkvi")
+
+-- the document variables hack is temporary
+
+local processors = { }
+
+function processors.mkvi(str,filename)
+ local oldsize = #str
+ str = lpegmatch(parser,str,1,true) or str
+ pushtarget("log")
+ report_macros("processed mkvi file %a, delta %s",filename,oldsize-#str)
+ poptarget("log")
+ return str
+end
+
+function processors.mkix(str,filename) -- we could intercept earlier so that caching works better
+ if not document then -- because now we hash the string as well as the
+ document = { }
+ end
+ if not document.variables then
+ document.variables = { }
+ end
+ local oldsize = #str
+ str = convertlmxstring(str,document.variables,false) or str
+ pushtarget("log")
+ report_macros("processed mkix file %a, delta %s",filename,oldsize-#str)
+ poptarget("log")
+ return str
+end
+
+function processors.mkxi(str,filename)
+ if not document then
+ document = { }
+ end
+ if not document.variables then
+ document.variables = { }
+ end
+ local oldsize = #str
+ str = convertlmxstring(str,document.variables,false) or str
+ str = lpegmatch(parser,str,1,true) or str
+ pushtarget("log")
+ report_macros("processed mkxi file %a, delta %s",filename,oldsize-#str)
+ poptarget("log")
+ return str
+end
+
+function macros.processmk(str,filename)
+ if filename then
+ local suffix = filesuffix(filename)
+ local processor = processors[suffix] or processors[lpegmatch(checker,str)]
+ if processor then
+ str = processor(str,filename)
+ end
+ end
+ return str
+end
+
+function macros.processmkvi(str,filename)
+ if filename and filesuffix(filename) == "mkvi" or lpegmatch(checker,str) == "mkvi" then
+ local oldsize = #str
+ str = lpegmatch(parser,str,1,true) or str
+ pushtarget("log")
+ report_macros("processed mkvi file %a, delta %s",filename,oldsize-#str)
+ poptarget("log")
+ end
+ return str
+end
+
+local sequencers = utilities.sequencers
+
+if sequencers then
+
+ sequencers.appendaction(resolvers.openers.helpers.textfileactions,"system","resolvers.macros.processmk")
+ sequencers.appendaction(resolvers.openers.helpers.textfileactions,"system","resolvers.macros.processmkvi")
+
+end
+
+-- bonus
+
+if resolvers.schemes then
+
+ local function handler(protocol,name,cachename)
+ local hashed = url.hashed(name)
+ local path = hashed.path
+ if path and path ~= "" then
+ local str = resolvers.loadtexfile(path)
+ if filesuffix(path) == "mkvi" or lpegmatch(checker,str) == "mkvi" then
+ -- already done automatically
+ io.savedata(cachename,str)
+ else
+ local result = lpegmatch(parser,str,1,true) or str
+ pushtarget("log")
+ report_macros("processed scheme %a, delta %s",filename,#str-#result)
+ poptarget("log")
+ io.savedata(cachename,result)
+ end
+ end
+ return cachename
+ end
+
+ resolvers.schemes.install('mkvi',handler,1) -- this will cache !
+
+end
+
+-- print(macros.preprocessed(
+-- [[
+-- \starttexdefinition unexpanded test #aa #bb #cc
+-- test
+-- \stoptexdefinition
+-- ]]))
+
+-- print(macros.preprocessed([[\checked \def \bla #bla{bla#{bla}}]]))
+-- print(macros.preprocessed([[\def\bla#bla{#{bla}bla}]]))
+-- print(macros.preprocessed([[\def\blä#{blá}{blà:#{blá}}]]))
+-- print(macros.preprocessed([[\def\blä#bla{blà:#bla}]]))
+-- print(macros.preprocessed([[\setvalue{xx}#bla{blà:#bla}]]))
+-- print(macros.preprocessed([[\def\foo#bar{\setvalue{xx#bar}{#bar}}]]))
+-- print(macros.preprocessed([[\def\bla#bla{bla:#{bla}}]]))
+-- print(macros.preprocessed([[\def\bla_bla#bla{bla:#bla}]]))
+-- print(macros.preprocessed([[\def\test#oeps{test:#oeps}]]))
+-- print(macros.preprocessed([[\def\test_oeps#oeps{test:#oeps}]]))
+-- print(macros.preprocessed([[\def\test#oeps{test:#{oeps}}]]))
+-- print(macros.preprocessed([[\def\test#{oeps:1}{test:#{oeps:1}}]]))
+-- print(macros.preprocessed([[\def\test#{oeps}{test:#oeps}]]))
+-- print(macros.preprocessed([[\def\x[#a][#b][#c]{\setvalue{\y{#a}\z{#b}}{#c}}]]))
+-- print(macros.preprocessed([[\def\test#{oeps}{test:#oeps \halign{##\cr #oeps\cr}]]))
+-- print(macros.preprocessed([[\def\test#{oeps}{test:#oeps \halign{##\cr #oeps\cr}}]]))
+-- print(macros.preprocessed([[% test
+-- \def\test#oeps{#oeps} % {test}
+-- % test
+--
+-- % test
+-- two
+-- %test]]))
+-- print(macros.preprocessed([[
+-- \def\scrn_button_make_normal#namespace#current#currentparameter#text%
+-- {\ctxlua{structures.references.injectcurrentset(nil,nil)}%
+-- % \hbox attr \referenceattribute \lastreferenceattribute {\localframed[#namespace:#current]{#text}}}
+-- \hbox attr \referenceattribute \lastreferenceattribute {\directlocalframed[#namespace:#current]{#text}}}
+-- ]]))
+--
+-- print(macros.preprocessed([[
+-- \def\definefoo[#name]%
+-- {\setvalue{start#name}{\dostartfoo{#name}}}
+-- \def\dostartfoo#name%
+-- {\def\noexpand\next#content\expandafter\noexpand\csname stop#name\endcsname{#name : #content}%
+-- \next}
+-- \def\dostartfoo#name%
+-- {\normalexpanded{\def\noexpand\next#content\expandafter\noexpand\csname stop#name\endcsname}{#name : #content}%
+-- \next}
+-- ]]))
+--
+-- print(macros.preprocessed([[
+-- \def\dosomething#content{%%% {{
+-- % { }{{ %%
+-- \bgroup\italic#content\egroup
+-- }
+-- ]]))
+--
+-- print(macros.preprocessed([[
+-- \unexpanded\def\start#tag#stoptag%
+-- {\initialize{#tag}%
+-- \normalexpanded
+-- {\def\yes[#one]#two\csname\e!stop#stoptag\endcsname{\command_yes[#one]{#two}}%
+-- \def\nop #one\csname\e!stop#stoptag\endcsname{\command_nop {#one}}}%
+-- \doifnextoptionalelse\yes\nop}
+-- ]]))
+--
+-- print(macros.preprocessed([[
+-- \normalexpanded{\long\def\expandafter\noexpand\csname\e!start\v!interactionmenu\endcsname[#tag]#content\expandafter\noexpand\csname\e!stop\v!interactionmenu\endcsname}%
+-- {\def\currentinteractionmenu{#tag}%
+-- \expandafter\settrue\csname\??menustate\interactionmenuparameter\c!category\endcsname
+-- \setinteractionmenuparameter\c!menu{#content}}
+-- ]]))
+--
+-- Just an experiment:
+--
+-- \catcode\numexpr"10FF25=\commentcatcode %% > 110000 is invalid
+--
+-- We could have a push/pop mechanism but binding to txtcatcodes
+-- is okay too.
+
+local txtcatcodes = false -- also signal and yet unknown
+
+local commentsignal = utf.char(0x10FF25)
+
+local encodecomment = P("%%") / commentsignal --
+----- encodepattern = Cs(((1-encodecomment)^0 * encodecomment)) -- strips but not nice for verbatim
+local encodepattern = Cs((encodecomment + 1)^0)
+local decodecomment = P(commentsignal) / "%%%%" -- why doubles here?
+local decodepattern = Cs((decodecomment + 1)^0)
+
+function resolvers.macros.encodecomment(str)
+ if txtcatcodes and tex.catcodetable == txtcatcodes then
+ return lpegmatch(encodepattern,str) or str
+ else
+ return str
+ end
+end
+
+function resolvers.macros.decodecomment(str) -- normally not needed
+ return txtcatcodes and lpegmatch(decodepattern,str) or str
+end
+
+-- resolvers.macros.commentsignal = commentsignal
+-- resolvers.macros.encodecommentpattern = encodepattern
+-- resolvers.macros.decodecommentpattern = decodepattern
+
+function resolvers.macros.enablecomment(thecatcodes)
+ if not txtcatcodes then
+ txtcatcodes = thecatcodes or catcodes.numbers.txtcatcodes
+ utilities.sequencers.appendaction(resolvers.openers.helpers.textlineactions,"system","resolvers.macros.encodecomment")
+ end
+end
diff --git a/tex/context/base/luat-run.lua b/tex/context/base/luat-run.lua
index 6291fef1b..eaede1030 100644
--- a/tex/context/base/luat-run.lua
+++ b/tex/context/base/luat-run.lua
@@ -1,158 +1,158 @@
-if not modules then modules = { } end modules ['luat-run'] = {
- 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 insert = table.insert
-
--- trace_job_status is also controlled by statistics.enable that is set via the directive system.nostatistics
-
-local trace_lua_dump = false trackers.register("system.dump", function(v) trace_lua_dump = v end)
-local trace_temp_files = false trackers.register("system.tempfiles", function(v) trace_temp_files = v end)
-local trace_job_status = true trackers.register("system.jobstatus", function(v) trace_job_status = v end)
-local trace_tex_status = false trackers.register("system.texstatus", function(v) trace_tex_status = v end)
-
-local report_lua = logs.reporter("system","lua")
-local report_tex = logs.reporter("system","status")
-local report_tempfiles = logs.reporter("resolvers","tempfiles")
-
-luatex = luatex or { }
-local luatex = luatex
-
-local startactions = { }
-local stopactions = { }
-
-function luatex.registerstartactions(...) insert(startactions, ...) end
-function luatex.registerstopactions (...) insert(stopactions, ...) end
-
-local function start_run()
- if logs.start_run then
- logs.start_run()
- end
- for i=1,#startactions do
- startactions[i]()
- end
-end
-
-local function stop_run()
- for i=1,#stopactions do
- stopactions[i]()
- end
- if trace_job_status then
- statistics.show()
- end
- if trace_tex_status then
- for k, v in table.sortedhash(status.list()) do
- report_tex("%S=%S",k,v)
- end
- end
- if logs.stop_run then
- logs.stop_run()
- end
-end
-
-local function start_shipout_page()
- logs.start_page_number()
-end
-
-local function stop_shipout_page()
- logs.stop_page_number()
-end
-
-local function report_output_pages()
-end
-
-local function report_output_log()
-end
-
--- local function show_open()
--- end
-
--- local function show_close()
--- end
-
-local function pre_dump_actions()
- lua.finalize(trace_lua_dump and report_lua or nil)
- -- statistics.savefmtstatus("\jobname","\contextversion","context.tex")
-end
-
--- this can be done later
-
-callbacks.register('start_run', start_run, "actions performed at the beginning of a run")
-callbacks.register('stop_run', stop_run, "actions performed at the end of a run")
-
----------.register('show_open', show_open, "actions performed when opening a file")
----------.register('show_close', show_close, "actions performed when closing a file")
-
-callbacks.register('report_output_pages', report_output_pages, "actions performed when reporting pages")
-callbacks.register('report_output_log', report_output_log, "actions performed when reporting log file")
-
-callbacks.register('start_page_number', start_shipout_page, "actions performed at the beginning of a shipout")
-callbacks.register('stop_page_number', stop_shipout_page, "actions performed at the end of a shipout")
-
-callbacks.register('process_input_buffer', false, "actions performed when reading data")
-callbacks.register('process_output_buffer', false, "actions performed when writing data")
-
-callbacks.register("pre_dump", pre_dump_actions, "lua related finalizers called before we dump the format") -- comes after \everydump
-
--- an example:
-
-local tempfiles = { }
-
-function luatex.registertempfile(name,extrasuffix)
- if extrasuffix then
- name = name .. ".mkiv-tmp" -- maybe just .tmp
- end
- if trace_temp_files and not tempfiles[name] then
- report_tempfiles("registering temporary file %a",name)
- end
- tempfiles[name] = true
- return name
-end
-
-function luatex.cleanuptempfiles()
- for name, _ in next, tempfiles do
- if trace_temp_files then
- report_tempfiles("removing temporary file %a",name)
- end
- os.remove(name)
- end
- tempfiles = { }
-end
-
-luatex.registerstopactions(luatex.cleanuptempfiles)
-
--- for the moment here
-
-local synctex = false
-
-local report_system = logs.reporter("system")
-
-directives.register("system.synctex", function(v)
- synctex = v
- if v then
- report_system("synctex functionality is enabled!")
- else
- report_system("synctex functionality is disabled!")
- end
- synctex = tonumber(synctex) or (toboolean(synctex,true) and 1) or (synctex == "zipped" and 1) or (synctex == "unzipped" and -1) or false
- -- currently this is bugged:
- tex.synctex = synctex
- -- so for the moment we need:
- context.normalsynctex()
- if synctex then
- context.plusone()
- else
- context.zerocount()
- end
-end)
-
-statistics.register("synctex tracing",function()
- if synctex or tex.synctex ~= 0 then
- return "synctex has been enabled (extra log file generated)"
- end
-end)
+if not modules then modules = { } end modules ['luat-run'] = {
+ 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 insert = table.insert
+
+-- trace_job_status is also controlled by statistics.enable that is set via the directive system.nostatistics
+
+local trace_lua_dump = false trackers.register("system.dump", function(v) trace_lua_dump = v end)
+local trace_temp_files = false trackers.register("system.tempfiles", function(v) trace_temp_files = v end)
+local trace_job_status = true trackers.register("system.jobstatus", function(v) trace_job_status = v end)
+local trace_tex_status = false trackers.register("system.texstatus", function(v) trace_tex_status = v end)
+
+local report_lua = logs.reporter("system","lua")
+local report_tex = logs.reporter("system","status")
+local report_tempfiles = logs.reporter("resolvers","tempfiles")
+
+luatex = luatex or { }
+local luatex = luatex
+
+local startactions = { }
+local stopactions = { }
+
+function luatex.registerstartactions(...) insert(startactions, ...) end
+function luatex.registerstopactions (...) insert(stopactions, ...) end
+
+local function start_run()
+ if logs.start_run then
+ logs.start_run()
+ end
+ for i=1,#startactions do
+ startactions[i]()
+ end
+end
+
+local function stop_run()
+ for i=1,#stopactions do
+ stopactions[i]()
+ end
+ if trace_job_status then
+ statistics.show()
+ end
+ if trace_tex_status then
+ for k, v in table.sortedhash(status.list()) do
+ report_tex("%S=%S",k,v)
+ end
+ end
+ if logs.stop_run then
+ logs.stop_run()
+ end
+end
+
+local function start_shipout_page()
+ logs.start_page_number()
+end
+
+local function stop_shipout_page()
+ logs.stop_page_number()
+end
+
+local function report_output_pages()
+end
+
+local function report_output_log()
+end
+
+-- local function show_open()
+-- end
+
+-- local function show_close()
+-- end
+
+local function pre_dump_actions()
+ lua.finalize(trace_lua_dump and report_lua or nil)
+ -- statistics.savefmtstatus("\jobname","\contextversion","context.tex")
+end
+
+-- this can be done later
+
+callbacks.register('start_run', start_run, "actions performed at the beginning of a run")
+callbacks.register('stop_run', stop_run, "actions performed at the end of a run")
+
+---------.register('show_open', show_open, "actions performed when opening a file")
+---------.register('show_close', show_close, "actions performed when closing a file")
+
+callbacks.register('report_output_pages', report_output_pages, "actions performed when reporting pages")
+callbacks.register('report_output_log', report_output_log, "actions performed when reporting log file")
+
+callbacks.register('start_page_number', start_shipout_page, "actions performed at the beginning of a shipout")
+callbacks.register('stop_page_number', stop_shipout_page, "actions performed at the end of a shipout")
+
+callbacks.register('process_input_buffer', false, "actions performed when reading data")
+callbacks.register('process_output_buffer', false, "actions performed when writing data")
+
+callbacks.register("pre_dump", pre_dump_actions, "lua related finalizers called before we dump the format") -- comes after \everydump
+
+-- an example:
+
+local tempfiles = { }
+
+function luatex.registertempfile(name,extrasuffix)
+ if extrasuffix then
+ name = name .. ".mkiv-tmp" -- maybe just .tmp
+ end
+ if trace_temp_files and not tempfiles[name] then
+ report_tempfiles("registering temporary file %a",name)
+ end
+ tempfiles[name] = true
+ return name
+end
+
+function luatex.cleanuptempfiles()
+ for name, _ in next, tempfiles do
+ if trace_temp_files then
+ report_tempfiles("removing temporary file %a",name)
+ end
+ os.remove(name)
+ end
+ tempfiles = { }
+end
+
+luatex.registerstopactions(luatex.cleanuptempfiles)
+
+-- for the moment here
+
+local synctex = false
+
+local report_system = logs.reporter("system")
+
+directives.register("system.synctex", function(v)
+ synctex = v
+ if v then
+ report_system("synctex functionality is enabled!")
+ else
+ report_system("synctex functionality is disabled!")
+ end
+ synctex = tonumber(synctex) or (toboolean(synctex,true) and 1) or (synctex == "zipped" and 1) or (synctex == "unzipped" and -1) or false
+ -- currently this is bugged:
+ tex.synctex = synctex
+ -- so for the moment we need:
+ context.normalsynctex()
+ if synctex then
+ context.plusone()
+ else
+ context.zerocount()
+ end
+end)
+
+statistics.register("synctex tracing",function()
+ if synctex or tex.synctex ~= 0 then
+ return "synctex has been enabled (extra log file generated)"
+ end
+end)
diff --git a/tex/context/base/luat-sta.lua b/tex/context/base/luat-sta.lua
index 1e83083cd..8b58774d3 100644
--- a/tex/context/base/luat-sta.lua
+++ b/tex/context/base/luat-sta.lua
@@ -1,211 +1,211 @@
-if not modules then modules = { } end modules ['luat-sta'] = {
- version = 1.001,
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- this code is used in the updater
-
-local gmatch, match = string.gmatch, string.match
-local type = type
-
-states = states or { }
-local states = states
-
-states.data = states.data or { }
-local data = states.data
-
-states.hash = states.hash or { }
-local hash = states.hash
-
-states.tag = states.tag or ""
-states.filename = states.filename or ""
-
-function states.save(filename,tag)
- tag = tag or states.tag
- filename = file.addsuffix(filename or states.filename,'lus')
- io.savedata(filename,
- "-- generator : luat-sta.lua\n" ..
- "-- state tag : " .. tag .. "\n\n" ..
- table.serialize(data[tag or states.tag] or {},true)
- )
-end
-
-function states.load(filename,tag)
- states.filename = filename
- states.tag = tag or "whatever"
- states.filename = file.addsuffix(states.filename,'lus')
- data[states.tag], hash[states.tag] = (io.exists(filename) and dofile(filename)) or { }, { }
-end
-
-local function set_by_tag(tag,key,value,default,persistent)
- local d, h = data[tag], hash[tag]
- if d then
- if type(d) == "table" then
- local dkey, hkey = key, key
- local pre, post = match(key,"(.+)%.([^%.]+)$")
- if pre and post then
- for k in gmatch(pre,"[^%.]+") do
- local dk = d[k]
- if not dk then
- dk = { }
- d[k] = dk
- elseif type(dk) == "string" then
- -- invalid table, unable to upgrade structure
- -- hope for the best or delete the state file
- break
- end
- d = dk
- end
- dkey, hkey = post, key
- end
- if value == nil then
- value = default
- elseif value == false then
- -- special case
- elseif persistent then
- value = value or d[dkey] or default
- else
- value = value or default
- end
- d[dkey], h[hkey] = value, value
- elseif type(d) == "string" then
- -- weird
- data[tag], hash[tag] = value, value
- end
- end
-end
-
-local function get_by_tag(tag,key,default)
- local h = hash[tag]
- if h and h[key] then
- return h[key]
- else
- local d = data[tag]
- if d then
- for k in gmatch(key,"[^%.]+") do
- local dk = d[k]
- if dk ~= nil then
- d = dk
- else
- return default
- end
- end
- if d == false then
- return false
- else
- return d or default
- end
- end
- end
-end
-
-states.set_by_tag = set_by_tag
-states.get_by_tag = get_by_tag
-
-function states.set(key,value,default,persistent)
- set_by_tag(states.tag,key,value,default,persistent)
-end
-
-function states.get(key,default)
- return get_by_tag(states.tag,key,default)
-end
-
---~ data.update = {
---~ ["version"] = {
---~ ["major"] = 0,
---~ ["minor"] = 1,
---~ },
---~ ["rsync"] = {
---~ ["server"] = "contextgarden.net",
---~ ["module"] = "minimals",
---~ ["repository"] = "current",
---~ ["flags"] = "-rpztlv --stats",
---~ },
---~ ["tasks"] = {
---~ ["update"] = true,
---~ ["make"] = true,
---~ ["delete"] = false,
---~ },
---~ ["platform"] = {
---~ ["host"] = true,
---~ ["other"] = {
---~ ["mswin"] = false,
---~ ["linux"] = false,
---~ ["linux-64"] = false,
---~ ["osx-intel"] = false,
---~ ["osx-ppc"] = false,
---~ ["sun"] = false,
---~ },
---~ },
---~ ["context"] = {
---~ ["available"] = {"current", "beta", "alpha", "experimental"},
---~ ["selected"] = "current",
---~ },
---~ ["formats"] = {
---~ ["cont-en"] = true,
---~ ["cont-nl"] = true,
---~ ["cont-de"] = false,
---~ ["cont-cz"] = false,
---~ ["cont-fr"] = false,
---~ ["cont-ro"] = false,
---~ },
---~ ["engine"] = {
---~ ["pdftex"] = {
---~ ["install"] = true,
---~ ["formats"] = {
---~ ["pdftex"] = true,
---~ },
---~ },
---~ ["luatex"] = {
---~ ["install"] = true,
---~ ["formats"] = {
---~ },
---~ },
---~ ["xetex"] = {
---~ ["install"] = true,
---~ ["formats"] = {
---~ ["xetex"] = false,
---~ },
---~ },
---~ ["metapost"] = {
---~ ["install"] = true,
---~ ["formats"] = {
---~ ["mpost"] = true,
---~ ["metafun"] = true,
---~ },
---~ },
---~ },
---~ ["fonts"] = {
---~ },
---~ ["doc"] = {
---~ },
---~ ["modules"] = {
---~ ["f-urwgaramond"] = false,
---~ ["f-urwgothic"] = false,
---~ ["t-bnf"] = false,
---~ ["t-chromato"] = false,
---~ ["t-cmscbf"] = false,
---~ ["t-cmttbf"] = false,
---~ ["t-construction-plan"] = false,
---~ ["t-degrade"] = false,
---~ ["t-french"] = false,
---~ ["t-lettrine"] = false,
---~ ["t-lilypond"] = false,
---~ ["t-mathsets"] = false,
---~ ["t-tikz"] = false,
---~ ["t-typearea"] = false,
---~ ["t-vim"] = false,
---~ },
---~ }
-
---~ states.save("teststate", "update")
---~ states.load("teststate", "update")
-
---~ print(states.get_by_tag("update","rsync.server","unknown"))
---~ states.set_by_tag("update","rsync.server","oeps")
---~ print(states.get_by_tag("update","rsync.server","unknown"))
---~ states.save("teststate", "update")
---~ states.load("teststate", "update")
---~ print(states.get_by_tag("update","rsync.server","unknown"))
+if not modules then modules = { } end modules ['luat-sta'] = {
+ version = 1.001,
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- this code is used in the updater
+
+local gmatch, match = string.gmatch, string.match
+local type = type
+
+states = states or { }
+local states = states
+
+states.data = states.data or { }
+local data = states.data
+
+states.hash = states.hash or { }
+local hash = states.hash
+
+states.tag = states.tag or ""
+states.filename = states.filename or ""
+
+function states.save(filename,tag)
+ tag = tag or states.tag
+ filename = file.addsuffix(filename or states.filename,'lus')
+ io.savedata(filename,
+ "-- generator : luat-sta.lua\n" ..
+ "-- state tag : " .. tag .. "\n\n" ..
+ table.serialize(data[tag or states.tag] or {},true)
+ )
+end
+
+function states.load(filename,tag)
+ states.filename = filename
+ states.tag = tag or "whatever"
+ states.filename = file.addsuffix(states.filename,'lus')
+ data[states.tag], hash[states.tag] = (io.exists(filename) and dofile(filename)) or { }, { }
+end
+
+local function set_by_tag(tag,key,value,default,persistent)
+ local d, h = data[tag], hash[tag]
+ if d then
+ if type(d) == "table" then
+ local dkey, hkey = key, key
+ local pre, post = match(key,"(.+)%.([^%.]+)$")
+ if pre and post then
+ for k in gmatch(pre,"[^%.]+") do
+ local dk = d[k]
+ if not dk then
+ dk = { }
+ d[k] = dk
+ elseif type(dk) == "string" then
+ -- invalid table, unable to upgrade structure
+ -- hope for the best or delete the state file
+ break
+ end
+ d = dk
+ end
+ dkey, hkey = post, key
+ end
+ if value == nil then
+ value = default
+ elseif value == false then
+ -- special case
+ elseif persistent then
+ value = value or d[dkey] or default
+ else
+ value = value or default
+ end
+ d[dkey], h[hkey] = value, value
+ elseif type(d) == "string" then
+ -- weird
+ data[tag], hash[tag] = value, value
+ end
+ end
+end
+
+local function get_by_tag(tag,key,default)
+ local h = hash[tag]
+ if h and h[key] then
+ return h[key]
+ else
+ local d = data[tag]
+ if d then
+ for k in gmatch(key,"[^%.]+") do
+ local dk = d[k]
+ if dk ~= nil then
+ d = dk
+ else
+ return default
+ end
+ end
+ if d == false then
+ return false
+ else
+ return d or default
+ end
+ end
+ end
+end
+
+states.set_by_tag = set_by_tag
+states.get_by_tag = get_by_tag
+
+function states.set(key,value,default,persistent)
+ set_by_tag(states.tag,key,value,default,persistent)
+end
+
+function states.get(key,default)
+ return get_by_tag(states.tag,key,default)
+end
+
+--~ data.update = {
+--~ ["version"] = {
+--~ ["major"] = 0,
+--~ ["minor"] = 1,
+--~ },
+--~ ["rsync"] = {
+--~ ["server"] = "contextgarden.net",
+--~ ["module"] = "minimals",
+--~ ["repository"] = "current",
+--~ ["flags"] = "-rpztlv --stats",
+--~ },
+--~ ["tasks"] = {
+--~ ["update"] = true,
+--~ ["make"] = true,
+--~ ["delete"] = false,
+--~ },
+--~ ["platform"] = {
+--~ ["host"] = true,
+--~ ["other"] = {
+--~ ["mswin"] = false,
+--~ ["linux"] = false,
+--~ ["linux-64"] = false,
+--~ ["osx-intel"] = false,
+--~ ["osx-ppc"] = false,
+--~ ["sun"] = false,
+--~ },
+--~ },
+--~ ["context"] = {
+--~ ["available"] = {"current", "beta", "alpha", "experimental"},
+--~ ["selected"] = "current",
+--~ },
+--~ ["formats"] = {
+--~ ["cont-en"] = true,
+--~ ["cont-nl"] = true,
+--~ ["cont-de"] = false,
+--~ ["cont-cz"] = false,
+--~ ["cont-fr"] = false,
+--~ ["cont-ro"] = false,
+--~ },
+--~ ["engine"] = {
+--~ ["pdftex"] = {
+--~ ["install"] = true,
+--~ ["formats"] = {
+--~ ["pdftex"] = true,
+--~ },
+--~ },
+--~ ["luatex"] = {
+--~ ["install"] = true,
+--~ ["formats"] = {
+--~ },
+--~ },
+--~ ["xetex"] = {
+--~ ["install"] = true,
+--~ ["formats"] = {
+--~ ["xetex"] = false,
+--~ },
+--~ },
+--~ ["metapost"] = {
+--~ ["install"] = true,
+--~ ["formats"] = {
+--~ ["mpost"] = true,
+--~ ["metafun"] = true,
+--~ },
+--~ },
+--~ },
+--~ ["fonts"] = {
+--~ },
+--~ ["doc"] = {
+--~ },
+--~ ["modules"] = {
+--~ ["f-urwgaramond"] = false,
+--~ ["f-urwgothic"] = false,
+--~ ["t-bnf"] = false,
+--~ ["t-chromato"] = false,
+--~ ["t-cmscbf"] = false,
+--~ ["t-cmttbf"] = false,
+--~ ["t-construction-plan"] = false,
+--~ ["t-degrade"] = false,
+--~ ["t-french"] = false,
+--~ ["t-lettrine"] = false,
+--~ ["t-lilypond"] = false,
+--~ ["t-mathsets"] = false,
+--~ ["t-tikz"] = false,
+--~ ["t-typearea"] = false,
+--~ ["t-vim"] = false,
+--~ },
+--~ }
+
+--~ states.save("teststate", "update")
+--~ states.load("teststate", "update")
+
+--~ print(states.get_by_tag("update","rsync.server","unknown"))
+--~ states.set_by_tag("update","rsync.server","oeps")
+--~ print(states.get_by_tag("update","rsync.server","unknown"))
+--~ states.save("teststate", "update")
+--~ states.load("teststate", "update")
+--~ print(states.get_by_tag("update","rsync.server","unknown"))
diff --git a/tex/context/base/luat-sto.lua b/tex/context/base/luat-sto.lua
index da2467708..7a11b7f5e 100644
--- a/tex/context/base/luat-sto.lua
+++ b/tex/context/base/luat-sto.lua
@@ -1,169 +1,169 @@
-if not modules then modules = { } end modules ['luat-sto'] = {
- 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"
-}
-
--- we could nil some function in the productionrun
-
-local type, next, setmetatable, getmetatable, collectgarbage = type, next, setmetatable, getmetatable, collectgarbage
-local gmatch, format = string.gmatch, string.format
-local serialize, concat, sortedhash = table.serialize, table.concat, table.sortedhash
-local bytecode = lua.bytecode
-local strippedloadstring = utilities.lua.strippedloadstring
-
-local trace_storage = false
-local report_storage = logs.reporter("system","storage")
-
-storage = storage or { }
-local storage = storage
-
-local data = { }
-storage.data = data
-
-storage.min = 0 -- 500
-storage.max = storage.min - 1
-storage.noftables = storage.noftables or 0
-storage.nofmodules = storage.nofmodules or 0
-
-storage.mark = utilities.storage.mark
-storage.allocate = utilities.storage.allocate
-storage.marked = utilities.storage.marked
-storage.strip = false
-
-directives.register("system.compile.strip", function(v) storage.strip = v end)
-
-function storage.register(...)
- local t = { ... }
- local d = t[2]
- if d then
- storage.mark(d)
- else
- report_storage("fatal error: invalid storage %a",t[1])
- os.exit()
- end
- data[#data+1] = t
- return t
-end
-
-local n = 0
-local function dump()
- local max = storage.max
- for i=1,#data do
- local d = data[i]
- local message, original, target = d[1], d[2] ,d[3]
- local c, code, name = 0, { }, nil
- -- we have a nice definer for this
- for str in gmatch(target,"([^%.]+)") do
- if name then
- name = name .. "." .. str
- else
- name = str
- end
- c = c + 1 ; code[c] = format("%s = %s or { }",name,name)
- end
- max = max + 1
- if trace_storage then
- c = c + 1 ; code[c] = format("print('restoring %s from slot %s')",message,max)
- end
- c = c + 1 ; code[c] = serialize(original,name)
- if trace_storage then
- report_storage('saving %a in slot %a, size %s',message,max,#code[c])
- end
- -- we don't need tracing in such tables
- bytecode[max] = strippedloadstring(concat(code,"\n"),storage.strip,format("slot %s (%s)",max,name))
- collectgarbage("step")
- end
- storage.max = max
-end
-
-lua.registerfinalizer(dump,"dump storage")
-
--- to be tested with otf caching:
-
-function lua.collectgarbage(threshold)
- local current = collectgarbage("count")
- local threshold = threshold or 256 * 1024
- while true do
- collectgarbage("collect")
- local previous = collectgarbage("count")
- if current - previous < threshold then
- break
- else
- current = previous
- end
- end
-end
-
--- -- we also need to count at generation time (nicer for message)
---
--- if lua.bytecode then -- from 0 upwards
--- local i, b = storage.min, lua.bytecode
--- while b[i] do
--- storage.noftables = i
--- b[i]()
--- b[i] = nil
--- i = i + 1
--- end
--- end
-
-statistics.register("stored bytecode data", function()
- local nofmodules = (storage.nofmodules > 0 and storage.nofmodules) or (status.luabytecodes - lua.firstbytecode - 1)
- local nofdumps = (storage.noftables > 0 and storage.noftables ) or storage.max-storage.min + 1
- local tofmodules = storage.tofmodules or 0
- local tofdumps = storage.toftables or 0
- if environment.initex then
- local luautilities = utilities.lua
- local nofstrippedbytes = luautilities.nofstrippedbytes
- local nofstrippedchunks = luautilities.nofstrippedchunks
- if nofstrippedbytes > 0 then
- return format("%s modules, %s tables, %s chunks, %s chunks stripped (%s bytes)",
- nofmodules,
- nofdumps,
- nofmodules + nofdumps,
- nofstrippedchunks,
- nofstrippedbytes
- )
- elseif nofstrippedchunks > 0 then
- return format("%s modules, %s tables, %s chunks, %s chunks stripped",
- nofmodules,
- nofdumps,
- nofmodules + nofdumps,
- nofstrippedchunks
- )
- else
- return format("%s modules, %s tables, %s chunks",
- nofmodules,
- nofdumps,
- nofmodules + nofdumps
- )
- end
- else
- return format("%s modules (%0.3f sec), %s tables (%0.3f sec), %s chunks (%0.3f sec)",
- nofmodules, tofmodules,
- nofdumps, tofdumps,
- nofmodules + nofdumps, tofmodules + tofdumps
- )
- end
-end)
-
-if lua.bytedata then
- storage.register("lua/bytedata",lua.bytedata,"lua.bytedata")
-end
-
--- Because the storage mechanism assumes tables, we define a table for storing
--- (non table) values.
-
-storage.shared = storage.shared or { }
-
-storage.register("storage/shared", storage.shared, "storage.shared")
-
-local mark = storage.mark
-
-if string.patterns then mark(string.patterns) end
-if lpeg.patterns then mark(lpeg.patterns) end
-if os.env then mark(os.env) end
-if number.dimenfactors then mark(number.dimenfactors) end
-if libraries then for k,v in next, libraries do mark(v) end end
+if not modules then modules = { } end modules ['luat-sto'] = {
+ 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"
+}
+
+-- we could nil some function in the productionrun
+
+local type, next, setmetatable, getmetatable, collectgarbage = type, next, setmetatable, getmetatable, collectgarbage
+local gmatch, format = string.gmatch, string.format
+local serialize, concat, sortedhash = table.serialize, table.concat, table.sortedhash
+local bytecode = lua.bytecode
+local strippedloadstring = utilities.lua.strippedloadstring
+
+local trace_storage = false
+local report_storage = logs.reporter("system","storage")
+
+storage = storage or { }
+local storage = storage
+
+local data = { }
+storage.data = data
+
+storage.min = 0 -- 500
+storage.max = storage.min - 1
+storage.noftables = storage.noftables or 0
+storage.nofmodules = storage.nofmodules or 0
+
+storage.mark = utilities.storage.mark
+storage.allocate = utilities.storage.allocate
+storage.marked = utilities.storage.marked
+storage.strip = false
+
+directives.register("system.compile.strip", function(v) storage.strip = v end)
+
+function storage.register(...)
+ local t = { ... }
+ local d = t[2]
+ if d then
+ storage.mark(d)
+ else
+ report_storage("fatal error: invalid storage %a",t[1])
+ os.exit()
+ end
+ data[#data+1] = t
+ return t
+end
+
+local n = 0
+local function dump()
+ local max = storage.max
+ for i=1,#data do
+ local d = data[i]
+ local message, original, target = d[1], d[2] ,d[3]
+ local c, code, name = 0, { }, nil
+ -- we have a nice definer for this
+ for str in gmatch(target,"([^%.]+)") do
+ if name then
+ name = name .. "." .. str
+ else
+ name = str
+ end
+ c = c + 1 ; code[c] = format("%s = %s or { }",name,name)
+ end
+ max = max + 1
+ if trace_storage then
+ c = c + 1 ; code[c] = format("print('restoring %s from slot %s')",message,max)
+ end
+ c = c + 1 ; code[c] = serialize(original,name)
+ if trace_storage then
+ report_storage('saving %a in slot %a, size %s',message,max,#code[c])
+ end
+ -- we don't need tracing in such tables
+ bytecode[max] = strippedloadstring(concat(code,"\n"),storage.strip,format("slot %s (%s)",max,name))
+ collectgarbage("step")
+ end
+ storage.max = max
+end
+
+lua.registerfinalizer(dump,"dump storage")
+
+-- to be tested with otf caching:
+
+function lua.collectgarbage(threshold)
+ local current = collectgarbage("count")
+ local threshold = threshold or 256 * 1024
+ while true do
+ collectgarbage("collect")
+ local previous = collectgarbage("count")
+ if current - previous < threshold then
+ break
+ else
+ current = previous
+ end
+ end
+end
+
+-- -- we also need to count at generation time (nicer for message)
+--
+-- if lua.bytecode then -- from 0 upwards
+-- local i, b = storage.min, lua.bytecode
+-- while b[i] do
+-- storage.noftables = i
+-- b[i]()
+-- b[i] = nil
+-- i = i + 1
+-- end
+-- end
+
+statistics.register("stored bytecode data", function()
+ local nofmodules = (storage.nofmodules > 0 and storage.nofmodules) or (status.luabytecodes - lua.firstbytecode - 1)
+ local nofdumps = (storage.noftables > 0 and storage.noftables ) or storage.max-storage.min + 1
+ local tofmodules = storage.tofmodules or 0
+ local tofdumps = storage.toftables or 0
+ if environment.initex then
+ local luautilities = utilities.lua
+ local nofstrippedbytes = luautilities.nofstrippedbytes
+ local nofstrippedchunks = luautilities.nofstrippedchunks
+ if nofstrippedbytes > 0 then
+ return format("%s modules, %s tables, %s chunks, %s chunks stripped (%s bytes)",
+ nofmodules,
+ nofdumps,
+ nofmodules + nofdumps,
+ nofstrippedchunks,
+ nofstrippedbytes
+ )
+ elseif nofstrippedchunks > 0 then
+ return format("%s modules, %s tables, %s chunks, %s chunks stripped",
+ nofmodules,
+ nofdumps,
+ nofmodules + nofdumps,
+ nofstrippedchunks
+ )
+ else
+ return format("%s modules, %s tables, %s chunks",
+ nofmodules,
+ nofdumps,
+ nofmodules + nofdumps
+ )
+ end
+ else
+ return format("%s modules (%0.3f sec), %s tables (%0.3f sec), %s chunks (%0.3f sec)",
+ nofmodules, tofmodules,
+ nofdumps, tofdumps,
+ nofmodules + nofdumps, tofmodules + tofdumps
+ )
+ end
+end)
+
+if lua.bytedata then
+ storage.register("lua/bytedata",lua.bytedata,"lua.bytedata")
+end
+
+-- Because the storage mechanism assumes tables, we define a table for storing
+-- (non table) values.
+
+storage.shared = storage.shared or { }
+
+storage.register("storage/shared", storage.shared, "storage.shared")
+
+local mark = storage.mark
+
+if string.patterns then mark(string.patterns) end
+if lpeg.patterns then mark(lpeg.patterns) end
+if os.env then mark(os.env) end
+if number.dimenfactors then mark(number.dimenfactors) end
+if libraries then for k,v in next, libraries do mark(v) end end
diff --git a/tex/context/base/lxml-aux.lua b/tex/context/base/lxml-aux.lua
index 812b14d50..0fffe261a 100644
--- a/tex/context/base/lxml-aux.lua
+++ b/tex/context/base/lxml-aux.lua
@@ -1,811 +1,811 @@
-if not modules then modules = { } end modules ['lxml-aux'] = {
- version = 1.001,
- comment = "this module is the basis for the lxml-* ones",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- not all functions here make sense anymore vbut we keep them for
--- compatibility reasons
-
-local trace_manipulations = false trackers.register("lxml.manipulations", function(v) trace_manipulations = v end)
-
-local report_xml = logs.reporter("xml")
-
-local xml = xml
-
-local xmlconvert, xmlcopy, xmlname = xml.convert, xml.copy, xml.name
-local xmlinheritedconvert = xml.inheritedconvert
-local xmlapplylpath = xml.applylpath
-local xmlfilter = xml.filter
-
-local type, setmetatable, getmetatable = type, setmetatable, getmetatable
-local insert, remove, fastcopy, concat = table.insert, table.remove, table.fastcopy, table.concat
-local gmatch, gsub, format, find, strip = string.gmatch, string.gsub, string.format, string.find, string.strip
-local utfbyte = utf.byte
-
-local function report(what,pattern,c,e)
- report_xml("%s element %a, root %a, position %a, index %a, pattern %a",what,xmlname(e),xmlname(e.__p__),c,e.ni,pattern)
-end
-
-local function withelements(e,handle,depth)
- if e and handle then
- local edt = e.dt
- if edt then
- depth = depth or 0
- for i=1,#edt do
- local e = edt[i]
- if type(e) == "table" then
- handle(e,depth)
- withelements(e,handle,depth+1)
- end
- end
- end
- end
-end
-
-xml.withelements = withelements
-
-function xml.withelement(e,n,handle) -- slow
- if e and n ~= 0 and handle then
- local edt = e.dt
- if edt then
- if n > 0 then
- for i=1,#edt do
- local ei = edt[i]
- if type(ei) == "table" then
- if n == 1 then
- handle(ei)
- return
- else
- n = n - 1
- end
- end
- end
- elseif n < 0 then
- for i=#edt,1,-1 do
- local ei = edt[i]
- if type(ei) == "table" then
- if n == -1 then
- handle(ei)
- return
- else
- n = n + 1
- end
- end
- end
- end
- end
- end
-end
-
-function xml.each(root,pattern,handle,reverse)
- local collected = xmlapplylpath(root,pattern)
- if collected then
- if reverse then
- for c=#collected,1,-1 do
- handle(collected[c])
- end
- else
- for c=1,#collected do
- handle(collected[c])
- end
- end
- return collected
- end
-end
-
-function xml.processattributes(root,pattern,handle)
- local collected = xmlapplylpath(root,pattern)
- if collected and handle then
- for c=1,#collected do
- handle(collected[c].at)
- end
- end
- return collected
-end
-
---[[ldx--
-<p>The following functions collect elements and texts.</p>
---ldx]]--
-
--- are these still needed -> lxml-cmp.lua
-
-function xml.collect(root, pattern)
- return xmlapplylpath(root,pattern)
-end
-
-function xml.collecttexts(root, pattern, flatten) -- todo: variant with handle
- local collected = xmlapplylpath(root,pattern)
- if collected and flatten then
- local xmltostring = xml.tostring
- for c=1,#collected do
- collected[c] = xmltostring(collected[c].dt)
- end
- end
- return collected or { }
-end
-
-function xml.collect_tags(root, pattern, nonamespace)
- local collected = xmlapplylpath(root,pattern)
- if collected then
- local t, n = { }, 0
- for c=1,#collected do
- local e = collected[c]
- local ns, tg = e.ns, e.tg
- n = n + 1
- if nonamespace then
- t[n] = tg
- elseif ns == "" then
- t[n] = tg
- else
- t[n] = ns .. ":" .. tg
- end
- end
- return t
- end
-end
-
---[[ldx--
-<p>We've now arrived at the functions that manipulate the tree.</p>
---ldx]]--
-
-local no_root = { no_root = true }
-
-local function redo_ni(d)
- for k=1,#d do
- local dk = d[k]
- if type(dk) == "table" then
- dk.ni = k
- end
- end
-end
-
-local function xmltoelement(whatever,root)
- if not whatever then
- return nil
- end
- local element
- if type(whatever) == "string" then
- element = xmlinheritedconvert(whatever,root) -- beware, not really a root
- else
- element = whatever -- we assume a table
- end
- if element.error then
- return whatever -- string
- end
- if element then
- --~ if element.ri then
- --~ element = element.dt[element.ri].dt
- --~ else
- --~ element = element.dt
- --~ end
- end
- return element
-end
-
-xml.toelement = xmltoelement
-
-local function copiedelement(element,newparent)
- if type(element) == "string" then
- return element
- else
- element = xmlcopy(element).dt
- if newparent and type(element) == "table" then
- element.__p__ = newparent
- end
- return element
- end
-end
-
-function xml.delete(root,pattern)
- if not pattern or pattern == "" then
- local p = root.__p__
- if p then
- if trace_manipulations then
- report('deleting',"--",c,root)
- end
- local d = p.dt
- remove(d,root.ni)
- redo_ni(d) -- can be made faster and inlined
- end
- else
- local collected = xmlapplylpath(root,pattern)
- if collected then
- for c=1,#collected do
- local e = collected[c]
- local p = e.__p__
- if p then
- if trace_manipulations then
- report('deleting',pattern,c,e)
- end
- local d = p.dt
- remove(d,e.ni)
- redo_ni(d) -- can be made faster and inlined
- end
- end
- end
- end
-end
-
-function xml.replace(root,pattern,whatever)
- local element = root and xmltoelement(whatever,root)
- local collected = element and xmlapplylpath(root,pattern)
- if collected then
- for c=1,#collected do
- local e = collected[c]
- local p = e.__p__
- if p then
- if trace_manipulations then
- report('replacing',pattern,c,e)
- end
- local d = p.dt
- d[e.ni] = copiedelement(element,p)
- redo_ni(d) -- probably not needed
- end
- end
- end
-end
-
-local function wrap(e,wrapper)
- local t = {
- rn = e.rn,
- tg = e.tg,
- ns = e.ns,
- at = e.at,
- dt = e.dt,
- __p__ = e,
- }
- setmetatable(t,getmetatable(e))
- e.rn = wrapper.rn or e.rn or ""
- e.tg = wrapper.tg or e.tg or ""
- e.ns = wrapper.ns or e.ns or ""
- e.at = fastcopy(wrapper.at)
- e.dt = { t }
-end
-
-function xml.wrap(root,pattern,whatever)
- if whatever then
- local wrapper = xmltoelement(whatever,root)
- local collected = xmlapplylpath(root,pattern)
- if collected then
- for c=1,#collected do
- local e = collected[c]
- if trace_manipulations then
- report('wrapping',pattern,c,e)
- end
- wrap(e,wrapper)
- end
- end
- else
- wrap(root,xmltoelement(pattern))
- end
-end
-
-local function inject_element(root,pattern,whatever,prepend)
- local element = root and xmltoelement(whatever,root)
- local collected = element and xmlapplylpath(root,pattern)
- local function inject_e(e)
- local r = e.__p__
- local d, k, rri = r.dt, e.ni, r.ri
- local edt = (rri and d[rri].dt) or (d and d[k] and d[k].dt)
- if edt then
- local be, af
- local cp = copiedelement(element,e)
- if prepend then
- be, af = cp, edt
- else
- be, af = edt, cp
- end
- local bn = #be
- for i=1,#af do
- bn = bn + 1
- be[bn] = af[i]
- end
- if rri then
- r.dt[rri].dt = be
- else
- d[k].dt = be
- end
- redo_ni(d)
- end
- end
- if not collected then
- -- nothing
- elseif collected.tg then
- -- first or so
- inject_e(collected)
- else
- for c=1,#collected do
- inject_e(collected[c])
- end
- end
-end
-
-local function insert_element(root,pattern,whatever,before) -- todo: element als functie
- local element = root and xmltoelement(whatever,root)
- local collected = element and xmlapplylpath(root,pattern)
- local function insert_e(e)
- local r = e.__p__
- local d, k = r.dt, e.ni
- if not before then
- k = k + 1
- end
- insert(d,k,copiedelement(element,r))
- redo_ni(d)
- end
- if not collected then
- -- nothing
- elseif collected.tg then
- -- first or so
- insert_e(collected)
- else
- for c=1,#collected do
- insert_e(collected[c])
- end
- end
-end
-
-xml.insert_element = insert_element
-xml.insertafter = insert_element
-xml.insertbefore = function(r,p,e) insert_element(r,p,e,true) end
-xml.injectafter = inject_element
-xml.injectbefore = function(r,p,e) inject_element(r,p,e,true) end
-
-local function include(xmldata,pattern,attribute,recursive,loaddata)
- -- parse="text" (default: xml), encoding="" (todo)
- -- attribute = attribute or 'href'
- pattern = pattern or 'include'
- loaddata = loaddata or io.loaddata
- local collected = xmlapplylpath(xmldata,pattern)
- if collected then
- for c=1,#collected do
- local ek = collected[c]
- local name = nil
- local ekdt = ek.dt
- local ekat = ek.at
- local epdt = ek.__p__.dt
- if not attribute or attribute == "" then
- name = (type(ekdt) == "table" and ekdt[1]) or ekdt -- check, probably always tab or str
- end
- if not name then
- for a in gmatch(attribute or "href","([^|]+)") do
- name = ekat[a]
- if name then break end
- end
- end
- local data = (name and name ~= "" and loaddata(name)) or ""
- if data == "" then
- epdt[ek.ni] = "" -- xml.empty(d,k)
- elseif ekat["parse"] == "text" then
- -- for the moment hard coded
- epdt[ek.ni] = xml.escaped(data) -- d[k] = xml.escaped(data)
- else
---~ local settings = xmldata.settings
---~ settings.parent_root = xmldata -- to be tested
---~ local xi = xmlconvert(data,settings)
- local xi = xmlinheritedconvert(data,xmldata)
- if not xi then
- epdt[ek.ni] = "" -- xml.empty(d,k)
- else
- if recursive then
- include(xi,pattern,attribute,recursive,loaddata)
- end
- epdt[ek.ni] = xml.body(xi) -- xml.assign(d,k,xi)
- end
- end
- end
- end
-end
-
-xml.include = include
-
-local function stripelement(e,nolines,anywhere)
- local edt = e.dt
- if edt then
- if anywhere then
- local t, n = { }, 0
- for e=1,#edt do
- local str = edt[e]
- if type(str) ~= "string" then
- n = n + 1
- t[n] = str
- elseif str ~= "" then
- -- todo: lpeg for each case
- if nolines then
- str = gsub(str,"%s+"," ")
- end
- str = gsub(str,"^%s*(.-)%s*$","%1")
- if str ~= "" then
- n = n + 1
- t[n] = str
- end
- end
- end
- e.dt = t
- else
- -- we can assume a regular sparse xml table with no successive strings
- -- otherwise we should use a while loop
- if #edt > 0 then
- -- strip front
- local str = edt[1]
- if type(str) ~= "string" then
- -- nothing
- elseif str == "" then
- remove(edt,1)
- else
- if nolines then
- str = gsub(str,"%s+"," ")
- end
- str = gsub(str,"^%s+","")
- if str == "" then
- remove(edt,1)
- else
- edt[1] = str
- end
- end
- end
- local nedt = #edt
- if nedt > 0 then
- -- strip end
- local str = edt[nedt]
- if type(str) ~= "string" then
- -- nothing
- elseif str == "" then
- remove(edt)
- else
- if nolines then
- str = gsub(str,"%s+"," ")
- end
- str = gsub(str,"%s+$","")
- if str == "" then
- remove(edt)
- else
- edt[nedt] = str
- end
- end
- end
- end
- end
- return e -- convenient
-end
-
-xml.stripelement = stripelement
-
-function xml.strip(root,pattern,nolines,anywhere) -- strips all leading and trailing spacing
- local collected = xmlapplylpath(root,pattern) -- beware, indices no longer are valid now
- if collected then
- for i=1,#collected do
- stripelement(collected[i],nolines,anywhere)
- end
- end
-end
-
-local function renamespace(root, oldspace, newspace) -- fast variant
- local ndt = #root.dt
- for i=1,ndt or 0 do
- local e = root[i]
- if type(e) == "table" then
- if e.ns == oldspace then
- e.ns = newspace
- if e.rn then
- e.rn = newspace
- end
- end
- local edt = e.dt
- if edt then
- renamespace(edt, oldspace, newspace)
- end
- end
- end
-end
-
-xml.renamespace = renamespace
-
-function xml.remaptag(root, pattern, newtg)
- local collected = xmlapplylpath(root,pattern)
- if collected then
- for c=1,#collected do
- collected[c].tg = newtg
- end
- end
-end
-
-function xml.remapnamespace(root, pattern, newns)
- local collected = xmlapplylpath(root,pattern)
- if collected then
- for c=1,#collected do
- collected[c].ns = newns
- end
- end
-end
-
-function xml.checknamespace(root, pattern, newns)
- local collected = xmlapplylpath(root,pattern)
- if collected then
- for c=1,#collected do
- local e = collected[c]
- if (not e.rn or e.rn == "") and e.ns == "" then
- e.rn = newns
- end
- end
- end
-end
-
-function xml.remapname(root, pattern, newtg, newns, newrn)
- local collected = xmlapplylpath(root,pattern)
- if collected then
- for c=1,#collected do
- local e = collected[c]
- e.tg, e.ns, e.rn = newtg, newns, newrn
- end
- end
-end
-
---[[ldx--
-<p>Helper (for q2p).</p>
---ldx]]--
-
-function xml.cdatatotext(e)
- local dt = e.dt
- if #dt == 1 then
- local first = dt[1]
- if first.tg == "@cd@" then
- e.dt = first.dt
- end
- else
- -- maybe option
- end
-end
-
--- local x = xml.convert("<x><a>1<b>2</b>3</a></x>")
--- xml.texttocdata(xml.first(x,"a"))
--- print(x) -- <x><![CDATA[1<b>2</b>3]]></x>
-
-function xml.texttocdata(e) -- could be a finalizer
- local dt = e.dt
- local s = xml.tostring(dt) -- no shortcut?
- e.tg = "@cd@"
- e.special = true
- e.ns = ""
- e.rn = ""
- e.dt = { s }
- e.at = nil
-end
-
--- local x = xml.convert("<x><a>1<b>2</b>3</a></x>")
--- xml.tocdata(xml.first(x,"a"))
--- print(x) -- <x><![CDATA[<a>1<b>2</b>3</a>]]></x>
-
-function xml.elementtocdata(e) -- could be a finalizer
- local dt = e.dt
- local s = xml.tostring(e) -- no shortcut?
- e.tg = "@cd@"
- e.special = true
- e.ns = ""
- e.rn = ""
- e.dt = { s }
- e.at = nil
-end
-
-xml.builtinentities = table.tohash { "amp", "quot", "apos", "lt", "gt" } -- used often so share
-
-local entities = characters and characters.entities or nil
-local builtinentities = xml.builtinentities
-
-function xml.addentitiesdoctype(root,option) -- we could also have a 'resolve' i.e. inline hex
- if not entities then
- require("char-ent")
- entities = characters.entities
- end
- if entities and root and root.tg == "@rt@" and root.statistics then
- local list = { }
- local hexify = option == "hexadecimal"
- for k, v in table.sortedhash(root.statistics.entities.names) do
- if not builtinentities[k] then
- local e = entities[k]
- if not e then
- e = format("[%s]",k)
- elseif hexify then
- e = format("&#%05X;",utfbyte(k))
- end
- list[#list+1] = format(" <!ENTITY %s %q >",k,e)
- end
- end
- local dt = root.dt
- local n = dt[1].tg == "@pi@" and 2 or 1
- if #list > 0 then
- insert(dt, n, { "\n" })
- insert(dt, n, {
- tg = "@dt@", -- beware, doctype is unparsed
- dt = { format("Something [\n%s\n] ",concat(list)) },
- ns = "",
- special = true,
- })
- insert(dt, n, { "\n\n" })
- else
- -- insert(dt, n, { table.serialize(root.statistics) })
- end
- end
-end
-
--- local str = [==[
--- <?xml version='1.0' standalone='yes' ?>
--- <root>
--- <a>test &nbsp; test &#123; test</a>
--- <b><![CDATA[oeps]]></b>
--- </root>
--- ]==]
---
--- local x = xml.convert(str)
--- xml.addentitiesdoctype(x,"hexadecimal")
--- print(x)
-
---[[ldx--
-<p>Here are a few synonyms.</p>
---ldx]]--
-
-xml.all = xml.each
-xml.insert = xml.insertafter
-xml.inject = xml.injectafter
-xml.after = xml.insertafter
-xml.before = xml.insertbefore
-xml.process = xml.each
-
--- obsolete
-
-xml.obsolete = xml.obsolete or { }
-local obsolete = xml.obsolete
-
-xml.strip_whitespace = xml.strip obsolete.strip_whitespace = xml.strip
-xml.collect_elements = xml.collect obsolete.collect_elements = xml.collect
-xml.delete_element = xml.delete obsolete.delete_element = xml.delete
-xml.replace_element = xml.replace obsolete.replace_element = xml.replacet
-xml.each_element = xml.each obsolete.each_element = xml.each
-xml.process_elements = xml.process obsolete.process_elements = xml.process
-xml.insert_element_after = xml.insertafter obsolete.insert_element_after = xml.insertafter
-xml.insert_element_before = xml.insertbefore obsolete.insert_element_before = xml.insertbefore
-xml.inject_element_after = xml.injectafter obsolete.inject_element_after = xml.injectafter
-xml.inject_element_before = xml.injectbefore obsolete.inject_element_before = xml.injectbefore
-xml.process_attributes = xml.processattributes obsolete.process_attributes = xml.processattributes
-xml.collect_texts = xml.collecttexts obsolete.collect_texts = xml.collecttexts
-xml.inject_element = xml.inject obsolete.inject_element = xml.inject
-xml.remap_tag = xml.remaptag obsolete.remap_tag = xml.remaptag
-xml.remap_name = xml.remapname obsolete.remap_name = xml.remapname
-xml.remap_namespace = xml.remapnamespace obsolete.remap_namespace = xml.remapnamespace
-
--- new (probably ok)
-
-function xml.cdata(e)
- if e then
- local dt = e.dt
- if dt and #dt == 1 then
- local first = dt[1]
- return first.tg == "@cd@" and first.dt[1] or ""
- end
- end
- return ""
-end
-
-function xml.finalizers.xml.cdata(collected)
- if collected then
- local e = collected[1]
- if e then
- local dt = e.dt
- if dt and #dt == 1 then
- local first = dt[1]
- return first.tg == "@cd@" and first.dt[1] or ""
- end
- end
- end
- return ""
-end
-
-function xml.insertcomment(e,str,n) -- also insertcdata
- table.insert(e.dt,n or 1,{
- tg = "@cm@",
- ns = "",
- special = true,
- at = { },
- dt = { str },
- })
-end
-
-function xml.setcdata(e,str) -- also setcomment
- e.dt = { {
- tg = "@cd@",
- ns = "",
- special = true,
- at = { },
- dt = { str },
- } }
-end
-
--- maybe helpers like this will move to an autoloader
-
-function xml.separate(x,pattern)
- local collected = xmlapplylpath(x,pattern)
- if collected then
- for c=1,#collected do
- local e = collected[c]
- local d = e.dt
- if d == x then
- report_xml("warning: xml.separate changes root")
- x = d
- end
- local t, n = { "\n" }, 1
- local i, nd = 1, #d
- while i <= nd do
- while i <= nd do
- local di = d[i]
- if type(di) == "string" then
- if di == "\n" or find(di,"^%s+$") then -- first test is speedup
- i = i + 1
- else
- d[i] = strip(di)
- break
- end
- else
- break
- end
- end
- if i > nd then
- break
- end
- t[n+1] = "\n"
- t[n+2] = d[i]
- t[n+3] = "\n"
- n = n + 3
- i = i + 1
- end
- t[n+1] = "\n"
- setmetatable(t,getmetatable(d))
- e.dt = t
- end
- end
- return x
-end
-
---
-
-local helpers = xml.helpers or { }
-xml.helpers = helpers
-
-local function normal(e,action)
- local edt = e.dt
- if edt then
- for i=1,#edt do
- local str = edt[i]
- if type(str) == "string" and str ~= "" then
- edt[i] = action(str)
- end
- end
- end
-end
-
-local function recurse(e,action)
- local edt = e.dt
- if edt then
- for i=1,#edt do
- local str = edt[i]
- if type(str) ~= "string" then
- recurse(str,action,recursive)
- elseif str ~= "" then
- edt[i] = action(str)
- end
- end
- end
-end
-
-function helpers.recursetext(collected,action,recursive)
- if recursive then
- for i=1,#collected do
- recurse(collected[i],action)
- end
- else
- for i=1,#collected do
- normal(collected[i],action)
- end
- end
-end
+if not modules then modules = { } end modules ['lxml-aux'] = {
+ version = 1.001,
+ comment = "this module is the basis for the lxml-* ones",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- not all functions here make sense anymore vbut we keep them for
+-- compatibility reasons
+
+local trace_manipulations = false trackers.register("lxml.manipulations", function(v) trace_manipulations = v end)
+
+local report_xml = logs.reporter("xml")
+
+local xml = xml
+
+local xmlconvert, xmlcopy, xmlname = xml.convert, xml.copy, xml.name
+local xmlinheritedconvert = xml.inheritedconvert
+local xmlapplylpath = xml.applylpath
+local xmlfilter = xml.filter
+
+local type, setmetatable, getmetatable = type, setmetatable, getmetatable
+local insert, remove, fastcopy, concat = table.insert, table.remove, table.fastcopy, table.concat
+local gmatch, gsub, format, find, strip = string.gmatch, string.gsub, string.format, string.find, string.strip
+local utfbyte = utf.byte
+
+local function report(what,pattern,c,e)
+ report_xml("%s element %a, root %a, position %a, index %a, pattern %a",what,xmlname(e),xmlname(e.__p__),c,e.ni,pattern)
+end
+
+local function withelements(e,handle,depth)
+ if e and handle then
+ local edt = e.dt
+ if edt then
+ depth = depth or 0
+ for i=1,#edt do
+ local e = edt[i]
+ if type(e) == "table" then
+ handle(e,depth)
+ withelements(e,handle,depth+1)
+ end
+ end
+ end
+ end
+end
+
+xml.withelements = withelements
+
+function xml.withelement(e,n,handle) -- slow
+ if e and n ~= 0 and handle then
+ local edt = e.dt
+ if edt then
+ if n > 0 then
+ for i=1,#edt do
+ local ei = edt[i]
+ if type(ei) == "table" then
+ if n == 1 then
+ handle(ei)
+ return
+ else
+ n = n - 1
+ end
+ end
+ end
+ elseif n < 0 then
+ for i=#edt,1,-1 do
+ local ei = edt[i]
+ if type(ei) == "table" then
+ if n == -1 then
+ handle(ei)
+ return
+ else
+ n = n + 1
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+function xml.each(root,pattern,handle,reverse)
+ local collected = xmlapplylpath(root,pattern)
+ if collected then
+ if reverse then
+ for c=#collected,1,-1 do
+ handle(collected[c])
+ end
+ else
+ for c=1,#collected do
+ handle(collected[c])
+ end
+ end
+ return collected
+ end
+end
+
+function xml.processattributes(root,pattern,handle)
+ local collected = xmlapplylpath(root,pattern)
+ if collected and handle then
+ for c=1,#collected do
+ handle(collected[c].at)
+ end
+ end
+ return collected
+end
+
+--[[ldx--
+<p>The following functions collect elements and texts.</p>
+--ldx]]--
+
+-- are these still needed -> lxml-cmp.lua
+
+function xml.collect(root, pattern)
+ return xmlapplylpath(root,pattern)
+end
+
+function xml.collecttexts(root, pattern, flatten) -- todo: variant with handle
+ local collected = xmlapplylpath(root,pattern)
+ if collected and flatten then
+ local xmltostring = xml.tostring
+ for c=1,#collected do
+ collected[c] = xmltostring(collected[c].dt)
+ end
+ end
+ return collected or { }
+end
+
+function xml.collect_tags(root, pattern, nonamespace)
+ local collected = xmlapplylpath(root,pattern)
+ if collected then
+ local t, n = { }, 0
+ for c=1,#collected do
+ local e = collected[c]
+ local ns, tg = e.ns, e.tg
+ n = n + 1
+ if nonamespace then
+ t[n] = tg
+ elseif ns == "" then
+ t[n] = tg
+ else
+ t[n] = ns .. ":" .. tg
+ end
+ end
+ return t
+ end
+end
+
+--[[ldx--
+<p>We've now arrived at the functions that manipulate the tree.</p>
+--ldx]]--
+
+local no_root = { no_root = true }
+
+local function redo_ni(d)
+ for k=1,#d do
+ local dk = d[k]
+ if type(dk) == "table" then
+ dk.ni = k
+ end
+ end
+end
+
+local function xmltoelement(whatever,root)
+ if not whatever then
+ return nil
+ end
+ local element
+ if type(whatever) == "string" then
+ element = xmlinheritedconvert(whatever,root) -- beware, not really a root
+ else
+ element = whatever -- we assume a table
+ end
+ if element.error then
+ return whatever -- string
+ end
+ if element then
+ --~ if element.ri then
+ --~ element = element.dt[element.ri].dt
+ --~ else
+ --~ element = element.dt
+ --~ end
+ end
+ return element
+end
+
+xml.toelement = xmltoelement
+
+local function copiedelement(element,newparent)
+ if type(element) == "string" then
+ return element
+ else
+ element = xmlcopy(element).dt
+ if newparent and type(element) == "table" then
+ element.__p__ = newparent
+ end
+ return element
+ end
+end
+
+function xml.delete(root,pattern)
+ if not pattern or pattern == "" then
+ local p = root.__p__
+ if p then
+ if trace_manipulations then
+ report('deleting',"--",c,root)
+ end
+ local d = p.dt
+ remove(d,root.ni)
+ redo_ni(d) -- can be made faster and inlined
+ end
+ else
+ local collected = xmlapplylpath(root,pattern)
+ if collected then
+ for c=1,#collected do
+ local e = collected[c]
+ local p = e.__p__
+ if p then
+ if trace_manipulations then
+ report('deleting',pattern,c,e)
+ end
+ local d = p.dt
+ remove(d,e.ni)
+ redo_ni(d) -- can be made faster and inlined
+ end
+ end
+ end
+ end
+end
+
+function xml.replace(root,pattern,whatever)
+ local element = root and xmltoelement(whatever,root)
+ local collected = element and xmlapplylpath(root,pattern)
+ if collected then
+ for c=1,#collected do
+ local e = collected[c]
+ local p = e.__p__
+ if p then
+ if trace_manipulations then
+ report('replacing',pattern,c,e)
+ end
+ local d = p.dt
+ d[e.ni] = copiedelement(element,p)
+ redo_ni(d) -- probably not needed
+ end
+ end
+ end
+end
+
+local function wrap(e,wrapper)
+ local t = {
+ rn = e.rn,
+ tg = e.tg,
+ ns = e.ns,
+ at = e.at,
+ dt = e.dt,
+ __p__ = e,
+ }
+ setmetatable(t,getmetatable(e))
+ e.rn = wrapper.rn or e.rn or ""
+ e.tg = wrapper.tg or e.tg or ""
+ e.ns = wrapper.ns or e.ns or ""
+ e.at = fastcopy(wrapper.at)
+ e.dt = { t }
+end
+
+function xml.wrap(root,pattern,whatever)
+ if whatever then
+ local wrapper = xmltoelement(whatever,root)
+ local collected = xmlapplylpath(root,pattern)
+ if collected then
+ for c=1,#collected do
+ local e = collected[c]
+ if trace_manipulations then
+ report('wrapping',pattern,c,e)
+ end
+ wrap(e,wrapper)
+ end
+ end
+ else
+ wrap(root,xmltoelement(pattern))
+ end
+end
+
+local function inject_element(root,pattern,whatever,prepend)
+ local element = root and xmltoelement(whatever,root)
+ local collected = element and xmlapplylpath(root,pattern)
+ local function inject_e(e)
+ local r = e.__p__
+ local d, k, rri = r.dt, e.ni, r.ri
+ local edt = (rri and d[rri].dt) or (d and d[k] and d[k].dt)
+ if edt then
+ local be, af
+ local cp = copiedelement(element,e)
+ if prepend then
+ be, af = cp, edt
+ else
+ be, af = edt, cp
+ end
+ local bn = #be
+ for i=1,#af do
+ bn = bn + 1
+ be[bn] = af[i]
+ end
+ if rri then
+ r.dt[rri].dt = be
+ else
+ d[k].dt = be
+ end
+ redo_ni(d)
+ end
+ end
+ if not collected then
+ -- nothing
+ elseif collected.tg then
+ -- first or so
+ inject_e(collected)
+ else
+ for c=1,#collected do
+ inject_e(collected[c])
+ end
+ end
+end
+
+local function insert_element(root,pattern,whatever,before) -- todo: element als functie
+ local element = root and xmltoelement(whatever,root)
+ local collected = element and xmlapplylpath(root,pattern)
+ local function insert_e(e)
+ local r = e.__p__
+ local d, k = r.dt, e.ni
+ if not before then
+ k = k + 1
+ end
+ insert(d,k,copiedelement(element,r))
+ redo_ni(d)
+ end
+ if not collected then
+ -- nothing
+ elseif collected.tg then
+ -- first or so
+ insert_e(collected)
+ else
+ for c=1,#collected do
+ insert_e(collected[c])
+ end
+ end
+end
+
+xml.insert_element = insert_element
+xml.insertafter = insert_element
+xml.insertbefore = function(r,p,e) insert_element(r,p,e,true) end
+xml.injectafter = inject_element
+xml.injectbefore = function(r,p,e) inject_element(r,p,e,true) end
+
+local function include(xmldata,pattern,attribute,recursive,loaddata)
+ -- parse="text" (default: xml), encoding="" (todo)
+ -- attribute = attribute or 'href'
+ pattern = pattern or 'include'
+ loaddata = loaddata or io.loaddata
+ local collected = xmlapplylpath(xmldata,pattern)
+ if collected then
+ for c=1,#collected do
+ local ek = collected[c]
+ local name = nil
+ local ekdt = ek.dt
+ local ekat = ek.at
+ local epdt = ek.__p__.dt
+ if not attribute or attribute == "" then
+ name = (type(ekdt) == "table" and ekdt[1]) or ekdt -- check, probably always tab or str
+ end
+ if not name then
+ for a in gmatch(attribute or "href","([^|]+)") do
+ name = ekat[a]
+ if name then break end
+ end
+ end
+ local data = (name and name ~= "" and loaddata(name)) or ""
+ if data == "" then
+ epdt[ek.ni] = "" -- xml.empty(d,k)
+ elseif ekat["parse"] == "text" then
+ -- for the moment hard coded
+ epdt[ek.ni] = xml.escaped(data) -- d[k] = xml.escaped(data)
+ else
+--~ local settings = xmldata.settings
+--~ settings.parent_root = xmldata -- to be tested
+--~ local xi = xmlconvert(data,settings)
+ local xi = xmlinheritedconvert(data,xmldata)
+ if not xi then
+ epdt[ek.ni] = "" -- xml.empty(d,k)
+ else
+ if recursive then
+ include(xi,pattern,attribute,recursive,loaddata)
+ end
+ epdt[ek.ni] = xml.body(xi) -- xml.assign(d,k,xi)
+ end
+ end
+ end
+ end
+end
+
+xml.include = include
+
+local function stripelement(e,nolines,anywhere)
+ local edt = e.dt
+ if edt then
+ if anywhere then
+ local t, n = { }, 0
+ for e=1,#edt do
+ local str = edt[e]
+ if type(str) ~= "string" then
+ n = n + 1
+ t[n] = str
+ elseif str ~= "" then
+ -- todo: lpeg for each case
+ if nolines then
+ str = gsub(str,"%s+"," ")
+ end
+ str = gsub(str,"^%s*(.-)%s*$","%1")
+ if str ~= "" then
+ n = n + 1
+ t[n] = str
+ end
+ end
+ end
+ e.dt = t
+ else
+ -- we can assume a regular sparse xml table with no successive strings
+ -- otherwise we should use a while loop
+ if #edt > 0 then
+ -- strip front
+ local str = edt[1]
+ if type(str) ~= "string" then
+ -- nothing
+ elseif str == "" then
+ remove(edt,1)
+ else
+ if nolines then
+ str = gsub(str,"%s+"," ")
+ end
+ str = gsub(str,"^%s+","")
+ if str == "" then
+ remove(edt,1)
+ else
+ edt[1] = str
+ end
+ end
+ end
+ local nedt = #edt
+ if nedt > 0 then
+ -- strip end
+ local str = edt[nedt]
+ if type(str) ~= "string" then
+ -- nothing
+ elseif str == "" then
+ remove(edt)
+ else
+ if nolines then
+ str = gsub(str,"%s+"," ")
+ end
+ str = gsub(str,"%s+$","")
+ if str == "" then
+ remove(edt)
+ else
+ edt[nedt] = str
+ end
+ end
+ end
+ end
+ end
+ return e -- convenient
+end
+
+xml.stripelement = stripelement
+
+function xml.strip(root,pattern,nolines,anywhere) -- strips all leading and trailing spacing
+ local collected = xmlapplylpath(root,pattern) -- beware, indices no longer are valid now
+ if collected then
+ for i=1,#collected do
+ stripelement(collected[i],nolines,anywhere)
+ end
+ end
+end
+
+local function renamespace(root, oldspace, newspace) -- fast variant
+ local ndt = #root.dt
+ for i=1,ndt or 0 do
+ local e = root[i]
+ if type(e) == "table" then
+ if e.ns == oldspace then
+ e.ns = newspace
+ if e.rn then
+ e.rn = newspace
+ end
+ end
+ local edt = e.dt
+ if edt then
+ renamespace(edt, oldspace, newspace)
+ end
+ end
+ end
+end
+
+xml.renamespace = renamespace
+
+function xml.remaptag(root, pattern, newtg)
+ local collected = xmlapplylpath(root,pattern)
+ if collected then
+ for c=1,#collected do
+ collected[c].tg = newtg
+ end
+ end
+end
+
+function xml.remapnamespace(root, pattern, newns)
+ local collected = xmlapplylpath(root,pattern)
+ if collected then
+ for c=1,#collected do
+ collected[c].ns = newns
+ end
+ end
+end
+
+function xml.checknamespace(root, pattern, newns)
+ local collected = xmlapplylpath(root,pattern)
+ if collected then
+ for c=1,#collected do
+ local e = collected[c]
+ if (not e.rn or e.rn == "") and e.ns == "" then
+ e.rn = newns
+ end
+ end
+ end
+end
+
+function xml.remapname(root, pattern, newtg, newns, newrn)
+ local collected = xmlapplylpath(root,pattern)
+ if collected then
+ for c=1,#collected do
+ local e = collected[c]
+ e.tg, e.ns, e.rn = newtg, newns, newrn
+ end
+ end
+end
+
+--[[ldx--
+<p>Helper (for q2p).</p>
+--ldx]]--
+
+function xml.cdatatotext(e)
+ local dt = e.dt
+ if #dt == 1 then
+ local first = dt[1]
+ if first.tg == "@cd@" then
+ e.dt = first.dt
+ end
+ else
+ -- maybe option
+ end
+end
+
+-- local x = xml.convert("<x><a>1<b>2</b>3</a></x>")
+-- xml.texttocdata(xml.first(x,"a"))
+-- print(x) -- <x><![CDATA[1<b>2</b>3]]></x>
+
+function xml.texttocdata(e) -- could be a finalizer
+ local dt = e.dt
+ local s = xml.tostring(dt) -- no shortcut?
+ e.tg = "@cd@"
+ e.special = true
+ e.ns = ""
+ e.rn = ""
+ e.dt = { s }
+ e.at = nil
+end
+
+-- local x = xml.convert("<x><a>1<b>2</b>3</a></x>")
+-- xml.tocdata(xml.first(x,"a"))
+-- print(x) -- <x><![CDATA[<a>1<b>2</b>3</a>]]></x>
+
+function xml.elementtocdata(e) -- could be a finalizer
+ local dt = e.dt
+ local s = xml.tostring(e) -- no shortcut?
+ e.tg = "@cd@"
+ e.special = true
+ e.ns = ""
+ e.rn = ""
+ e.dt = { s }
+ e.at = nil
+end
+
+xml.builtinentities = table.tohash { "amp", "quot", "apos", "lt", "gt" } -- used often so share
+
+local entities = characters and characters.entities or nil
+local builtinentities = xml.builtinentities
+
+function xml.addentitiesdoctype(root,option) -- we could also have a 'resolve' i.e. inline hex
+ if not entities then
+ require("char-ent")
+ entities = characters.entities
+ end
+ if entities and root and root.tg == "@rt@" and root.statistics then
+ local list = { }
+ local hexify = option == "hexadecimal"
+ for k, v in table.sortedhash(root.statistics.entities.names) do
+ if not builtinentities[k] then
+ local e = entities[k]
+ if not e then
+ e = format("[%s]",k)
+ elseif hexify then
+ e = format("&#%05X;",utfbyte(k))
+ end
+ list[#list+1] = format(" <!ENTITY %s %q >",k,e)
+ end
+ end
+ local dt = root.dt
+ local n = dt[1].tg == "@pi@" and 2 or 1
+ if #list > 0 then
+ insert(dt, n, { "\n" })
+ insert(dt, n, {
+ tg = "@dt@", -- beware, doctype is unparsed
+ dt = { format("Something [\n%s\n] ",concat(list)) },
+ ns = "",
+ special = true,
+ })
+ insert(dt, n, { "\n\n" })
+ else
+ -- insert(dt, n, { table.serialize(root.statistics) })
+ end
+ end
+end
+
+-- local str = [==[
+-- <?xml version='1.0' standalone='yes' ?>
+-- <root>
+-- <a>test &nbsp; test &#123; test</a>
+-- <b><![CDATA[oeps]]></b>
+-- </root>
+-- ]==]
+--
+-- local x = xml.convert(str)
+-- xml.addentitiesdoctype(x,"hexadecimal")
+-- print(x)
+
+--[[ldx--
+<p>Here are a few synonyms.</p>
+--ldx]]--
+
+xml.all = xml.each
+xml.insert = xml.insertafter
+xml.inject = xml.injectafter
+xml.after = xml.insertafter
+xml.before = xml.insertbefore
+xml.process = xml.each
+
+-- obsolete
+
+xml.obsolete = xml.obsolete or { }
+local obsolete = xml.obsolete
+
+xml.strip_whitespace = xml.strip obsolete.strip_whitespace = xml.strip
+xml.collect_elements = xml.collect obsolete.collect_elements = xml.collect
+xml.delete_element = xml.delete obsolete.delete_element = xml.delete
+xml.replace_element = xml.replace obsolete.replace_element = xml.replacet
+xml.each_element = xml.each obsolete.each_element = xml.each
+xml.process_elements = xml.process obsolete.process_elements = xml.process
+xml.insert_element_after = xml.insertafter obsolete.insert_element_after = xml.insertafter
+xml.insert_element_before = xml.insertbefore obsolete.insert_element_before = xml.insertbefore
+xml.inject_element_after = xml.injectafter obsolete.inject_element_after = xml.injectafter
+xml.inject_element_before = xml.injectbefore obsolete.inject_element_before = xml.injectbefore
+xml.process_attributes = xml.processattributes obsolete.process_attributes = xml.processattributes
+xml.collect_texts = xml.collecttexts obsolete.collect_texts = xml.collecttexts
+xml.inject_element = xml.inject obsolete.inject_element = xml.inject
+xml.remap_tag = xml.remaptag obsolete.remap_tag = xml.remaptag
+xml.remap_name = xml.remapname obsolete.remap_name = xml.remapname
+xml.remap_namespace = xml.remapnamespace obsolete.remap_namespace = xml.remapnamespace
+
+-- new (probably ok)
+
+function xml.cdata(e)
+ if e then
+ local dt = e.dt
+ if dt and #dt == 1 then
+ local first = dt[1]
+ return first.tg == "@cd@" and first.dt[1] or ""
+ end
+ end
+ return ""
+end
+
+function xml.finalizers.xml.cdata(collected)
+ if collected then
+ local e = collected[1]
+ if e then
+ local dt = e.dt
+ if dt and #dt == 1 then
+ local first = dt[1]
+ return first.tg == "@cd@" and first.dt[1] or ""
+ end
+ end
+ end
+ return ""
+end
+
+function xml.insertcomment(e,str,n) -- also insertcdata
+ table.insert(e.dt,n or 1,{
+ tg = "@cm@",
+ ns = "",
+ special = true,
+ at = { },
+ dt = { str },
+ })
+end
+
+function xml.setcdata(e,str) -- also setcomment
+ e.dt = { {
+ tg = "@cd@",
+ ns = "",
+ special = true,
+ at = { },
+ dt = { str },
+ } }
+end
+
+-- maybe helpers like this will move to an autoloader
+
+function xml.separate(x,pattern)
+ local collected = xmlapplylpath(x,pattern)
+ if collected then
+ for c=1,#collected do
+ local e = collected[c]
+ local d = e.dt
+ if d == x then
+ report_xml("warning: xml.separate changes root")
+ x = d
+ end
+ local t, n = { "\n" }, 1
+ local i, nd = 1, #d
+ while i <= nd do
+ while i <= nd do
+ local di = d[i]
+ if type(di) == "string" then
+ if di == "\n" or find(di,"^%s+$") then -- first test is speedup
+ i = i + 1
+ else
+ d[i] = strip(di)
+ break
+ end
+ else
+ break
+ end
+ end
+ if i > nd then
+ break
+ end
+ t[n+1] = "\n"
+ t[n+2] = d[i]
+ t[n+3] = "\n"
+ n = n + 3
+ i = i + 1
+ end
+ t[n+1] = "\n"
+ setmetatable(t,getmetatable(d))
+ e.dt = t
+ end
+ end
+ return x
+end
+
+--
+
+local helpers = xml.helpers or { }
+xml.helpers = helpers
+
+local function normal(e,action)
+ local edt = e.dt
+ if edt then
+ for i=1,#edt do
+ local str = edt[i]
+ if type(str) == "string" and str ~= "" then
+ edt[i] = action(str)
+ end
+ end
+ end
+end
+
+local function recurse(e,action)
+ local edt = e.dt
+ if edt then
+ for i=1,#edt do
+ local str = edt[i]
+ if type(str) ~= "string" then
+ recurse(str,action,recursive)
+ elseif str ~= "" then
+ edt[i] = action(str)
+ end
+ end
+ end
+end
+
+function helpers.recursetext(collected,action,recursive)
+ if recursive then
+ for i=1,#collected do
+ recurse(collected[i],action)
+ end
+ else
+ for i=1,#collected do
+ normal(collected[i],action)
+ end
+ end
+end
diff --git a/tex/context/base/lxml-css.lua b/tex/context/base/lxml-css.lua
index f9542029f..c5a85c2bd 100644
--- a/tex/context/base/lxml-css.lua
+++ b/tex/context/base/lxml-css.lua
@@ -1,158 +1,158 @@
-if not modules then modules = { } end modules ['lxml-css'] = {
- version = 1.001,
- comment = "companion to lxml-css.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local tonumber, rawset = tonumber, rawset
-local lower, format = string.lower, string.format
-local P, S, C, R, Cb, Cg, Carg, Ct, Cc, Cf = lpeg.P, lpeg.S, lpeg.C, lpeg.R, lpeg.Cb, lpeg.Cg, lpeg.Carg, lpeg.Ct, lpeg.Cc, lpeg.Cf
-local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
-
-xml.css = xml.css or { }
-local css = xml.css
-
-if not number.dimenfactors then
- require("util-dim.lua")
-end
-
-local dimenfactors = number.dimenfactors
-local bpf = 1/dimenfactors.bp
-local cmf = 1/dimenfactors.cm
-local mmf = 1/dimenfactors.mm
-local inf = 1/dimenfactors["in"]
-
-local percentage, exheight, emwidth, pixels
-
-if tex then
-
- local exheights = fonts.hashes.exheights
- local emwidths = fonts.hashes.emwidths
-
- percentage = function(s,pcf) return tonumber(s) * (pcf or tex.hsize) end
- exheight = function(s,exf) return tonumber(s) * (exf or exheights[true]) end
- emwidth = function(s,emf) return tonumber(s) * (emf or emwidths[true]) end
- pixels = function(s,pxf) return tonumber(s) * (pxf or emwidths[true]/300) end
-
-else
-
- local function generic(s,unit) return tonumber(s) * unit end
-
- percentage = generic
- exheight = generic
- emwidth = generic
- pixels = generic
-
-end
-
-local validdimen = Cg(lpegpatterns.number,'a') * (
- Cb('a') * P("pt") / function(s) return tonumber(s) * bpf end
- + Cb('a') * P("cm") / function(s) return tonumber(s) * cmf end
- + Cb('a') * P("mm") / function(s) return tonumber(s) * mmf end
- + Cb('a') * P("in") / function(s) return tonumber(s) * inf end
- + Cb('a') * P("px") * Carg(1) / pixels
- + Cb('a') * P("%") * Carg(2) / percentage
- + Cb('a') * P("ex") * Carg(3) / exheight
- + Cb('a') * P("em") * Carg(4) / emwidth
- + Cb('a') * Carg(1) / pixels
- )
-
-local pattern = (validdimen * lpegpatterns.whitespace^0)^1
-
--- todo: default if ""
-
-local function dimension(str,pixel,percent,exheight,emwidth)
- return (lpegmatch(pattern,str,1,pixel,percent,exheight,emwidth))
-end
-
-local function padding(str,pixel,percent,exheight,emwidth)
- local top, bottom, left, right = lpegmatch(pattern,str,1,pixel,percent,exheight,emwidth)
- if not bottom then
- bottom, left, right = top, top, top
- elseif not left then
- bottom, left, right = top, bottom, bottom
- elseif not right then
- bottom, left, right = left, bottom, bottom
- end
- return top, bottom, left, right
-end
-
-css.dimension = dimension
-css.padding = padding
-
--- local hsize = 655360*100
--- local exheight = 65536*4
--- local emwidth = 65536*10
--- local pixel = emwidth/100
---
--- print(padding("10px",pixel,hsize,exheight,emwidth))
--- print(padding("10px 20px",pixel,hsize,exheight,emwidth))
--- print(padding("10px 20px 30px",pixel,hsize,exheight,emwidth))
--- print(padding("10px 20px 30px 40px",pixel,hsize,exheight,emwidth))
---
--- print(padding("10%",pixel,hsize,exheight,emwidth))
--- print(padding("10% 20%",pixel,hsize,exheight,emwidth))
--- print(padding("10% 20% 30%",pixel,hsize,exheight,emwidth))
--- print(padding("10% 20% 30% 40%",pixel,hsize,exheight,emwidth))
---
--- print(padding("10",pixel,hsize,exheight,emwidth))
--- print(padding("10 20",pixel,hsize,exheight,emwidth))
--- print(padding("10 20 30",pixel,hsize,exheight,emwidth))
--- print(padding("10 20 30 40",pixel,hsize,exheight,emwidth))
---
--- print(padding("10pt",pixel,hsize,exheight,emwidth))
--- print(padding("10pt 20pt",pixel,hsize,exheight,emwidth))
--- print(padding("10pt 20pt 30pt",pixel,hsize,exheight,emwidth))
--- print(padding("10pt 20pt 30pt 40pt",pixel,hsize,exheight,emwidth))
-
--- print(padding("0",pixel,hsize,exheight,emwidth))
-
--- local currentfont = font.current
--- local texdimen = tex.dimen
--- local hashes = fonts.hashes
--- local quads = hashes.quads
--- local xheights = hashes.xheights
---
--- local function padding(str)
--- local font = currentfont()
--- local exheight = xheights[font]
--- local emwidth = quads[font]
--- local hsize = texdimen.hsize/100
--- local pixel = emwidth/100
--- return padding(str,pixel,hsize,exheight,emwidth)
--- end
---
--- function css.simplepadding(str)
--- context("%ssp",padding(str,pixel,hsize,exheight,emwidth))
--- end
-
-local pattern = Cf( Ct("") * (
- Cg(
- Cc("style") * (
- C("italic")
- + C("oblique")
- + C("slanted") / "oblique"
- )
- + Cc("variant") * (
- (C("smallcaps") + C("caps")) / "small-caps"
- )
- + Cc("weight") *
- C("bold")
- + Cc("family") * (
- (C("mono") + C("type")) / "monospace" -- just ignore the "space(d)"
- + (C("sansserif") + C("sans")) / "sans-serif" -- match before serif
- + C("serif")
- )
- ) + P(1)
-)^0 , rawset)
-
-function css.fontspecification(str)
- return str and lpegmatch(pattern,lower(str))
-end
-
-function css.colorspecification(str)
- local c = str and attributes.colors.values[tonumber(str)]
- return c and format("rgb(%s%%,%s%%,%s%%)",c[3]*100,c[4]*100,c[5]*100)
-end
+if not modules then modules = { } end modules ['lxml-css'] = {
+ version = 1.001,
+ comment = "companion to lxml-css.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local tonumber, rawset = tonumber, rawset
+local lower, format = string.lower, string.format
+local P, S, C, R, Cb, Cg, Carg, Ct, Cc, Cf = lpeg.P, lpeg.S, lpeg.C, lpeg.R, lpeg.Cb, lpeg.Cg, lpeg.Carg, lpeg.Ct, lpeg.Cc, lpeg.Cf
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+
+xml.css = xml.css or { }
+local css = xml.css
+
+if not number.dimenfactors then
+ require("util-dim.lua")
+end
+
+local dimenfactors = number.dimenfactors
+local bpf = 1/dimenfactors.bp
+local cmf = 1/dimenfactors.cm
+local mmf = 1/dimenfactors.mm
+local inf = 1/dimenfactors["in"]
+
+local percentage, exheight, emwidth, pixels
+
+if tex then
+
+ local exheights = fonts.hashes.exheights
+ local emwidths = fonts.hashes.emwidths
+
+ percentage = function(s,pcf) return tonumber(s) * (pcf or tex.hsize) end
+ exheight = function(s,exf) return tonumber(s) * (exf or exheights[true]) end
+ emwidth = function(s,emf) return tonumber(s) * (emf or emwidths[true]) end
+ pixels = function(s,pxf) return tonumber(s) * (pxf or emwidths[true]/300) end
+
+else
+
+ local function generic(s,unit) return tonumber(s) * unit end
+
+ percentage = generic
+ exheight = generic
+ emwidth = generic
+ pixels = generic
+
+end
+
+local validdimen = Cg(lpegpatterns.number,'a') * (
+ Cb('a') * P("pt") / function(s) return tonumber(s) * bpf end
+ + Cb('a') * P("cm") / function(s) return tonumber(s) * cmf end
+ + Cb('a') * P("mm") / function(s) return tonumber(s) * mmf end
+ + Cb('a') * P("in") / function(s) return tonumber(s) * inf end
+ + Cb('a') * P("px") * Carg(1) / pixels
+ + Cb('a') * P("%") * Carg(2) / percentage
+ + Cb('a') * P("ex") * Carg(3) / exheight
+ + Cb('a') * P("em") * Carg(4) / emwidth
+ + Cb('a') * Carg(1) / pixels
+ )
+
+local pattern = (validdimen * lpegpatterns.whitespace^0)^1
+
+-- todo: default if ""
+
+local function dimension(str,pixel,percent,exheight,emwidth)
+ return (lpegmatch(pattern,str,1,pixel,percent,exheight,emwidth))
+end
+
+local function padding(str,pixel,percent,exheight,emwidth)
+ local top, bottom, left, right = lpegmatch(pattern,str,1,pixel,percent,exheight,emwidth)
+ if not bottom then
+ bottom, left, right = top, top, top
+ elseif not left then
+ bottom, left, right = top, bottom, bottom
+ elseif not right then
+ bottom, left, right = left, bottom, bottom
+ end
+ return top, bottom, left, right
+end
+
+css.dimension = dimension
+css.padding = padding
+
+-- local hsize = 655360*100
+-- local exheight = 65536*4
+-- local emwidth = 65536*10
+-- local pixel = emwidth/100
+--
+-- print(padding("10px",pixel,hsize,exheight,emwidth))
+-- print(padding("10px 20px",pixel,hsize,exheight,emwidth))
+-- print(padding("10px 20px 30px",pixel,hsize,exheight,emwidth))
+-- print(padding("10px 20px 30px 40px",pixel,hsize,exheight,emwidth))
+--
+-- print(padding("10%",pixel,hsize,exheight,emwidth))
+-- print(padding("10% 20%",pixel,hsize,exheight,emwidth))
+-- print(padding("10% 20% 30%",pixel,hsize,exheight,emwidth))
+-- print(padding("10% 20% 30% 40%",pixel,hsize,exheight,emwidth))
+--
+-- print(padding("10",pixel,hsize,exheight,emwidth))
+-- print(padding("10 20",pixel,hsize,exheight,emwidth))
+-- print(padding("10 20 30",pixel,hsize,exheight,emwidth))
+-- print(padding("10 20 30 40",pixel,hsize,exheight,emwidth))
+--
+-- print(padding("10pt",pixel,hsize,exheight,emwidth))
+-- print(padding("10pt 20pt",pixel,hsize,exheight,emwidth))
+-- print(padding("10pt 20pt 30pt",pixel,hsize,exheight,emwidth))
+-- print(padding("10pt 20pt 30pt 40pt",pixel,hsize,exheight,emwidth))
+
+-- print(padding("0",pixel,hsize,exheight,emwidth))
+
+-- local currentfont = font.current
+-- local texdimen = tex.dimen
+-- local hashes = fonts.hashes
+-- local quads = hashes.quads
+-- local xheights = hashes.xheights
+--
+-- local function padding(str)
+-- local font = currentfont()
+-- local exheight = xheights[font]
+-- local emwidth = quads[font]
+-- local hsize = texdimen.hsize/100
+-- local pixel = emwidth/100
+-- return padding(str,pixel,hsize,exheight,emwidth)
+-- end
+--
+-- function css.simplepadding(str)
+-- context("%ssp",padding(str,pixel,hsize,exheight,emwidth))
+-- end
+
+local pattern = Cf( Ct("") * (
+ Cg(
+ Cc("style") * (
+ C("italic")
+ + C("oblique")
+ + C("slanted") / "oblique"
+ )
+ + Cc("variant") * (
+ (C("smallcaps") + C("caps")) / "small-caps"
+ )
+ + Cc("weight") *
+ C("bold")
+ + Cc("family") * (
+ (C("mono") + C("type")) / "monospace" -- just ignore the "space(d)"
+ + (C("sansserif") + C("sans")) / "sans-serif" -- match before serif
+ + C("serif")
+ )
+ ) + P(1)
+)^0 , rawset)
+
+function css.fontspecification(str)
+ return str and lpegmatch(pattern,lower(str))
+end
+
+function css.colorspecification(str)
+ local c = str and attributes.colors.values[tonumber(str)]
+ return c and format("rgb(%s%%,%s%%,%s%%)",c[3]*100,c[4]*100,c[5]*100)
+end
diff --git a/tex/context/base/lxml-ctx.lua b/tex/context/base/lxml-ctx.lua
index 2694839dd..968dbda71 100644
--- a/tex/context/base/lxml-ctx.lua
+++ b/tex/context/base/lxml-ctx.lua
@@ -1,135 +1,135 @@
-if not modules then modules = { } end modules ['lxml-ctx'] = {
- version = 1.001,
- comment = "companion to lxml-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- is this still used?
-
-local format, find = string.format, string.find
-
-local xml = xml
-
-xml.ctx = { }
-xml.ctx.enhancers = { }
-
--- hashen
-
-function xml.ctx.enhancers.compound(root,lpath,before,tokens,after) -- todo lpeg
- local before = before or "[%a%d][%a%d][%a%d]"
- local tokens = tokens or "[%/%-]"
- local after = after or "[%a%d][%a%d][%a%d]"
- local pattern = "(" .. before .. ")(" .. tokens .. ")(" .. after .. ")"
- local action = function(a,b,c)
- return a .. "<compound token=" .. format("%q",b) .. "/>" .. c -- formatters["%s<compound token=%q/>%s"](a,b,c)
- end
- xml.enhance(root,lpath,pattern,action) -- still present?
-end
-
-local loaded = { }
-
-local nodesettostring = xml.nodesettostring
-
--- maybe use detokenize instead of \type
-
-function xml.ctx.tshow(specification)
- local pattern = specification.pattern
- local xmlroot = specification.xmlroot
- local attribute = specification.attribute
- if context then
- local xmlpattern = pattern
- if not find(xmlpattern,"^[%a]+://") then
- xmlpattern = "xml://" .. pattern
- end
- local parsed = xml.lpath(xmlpattern)
- local titlecommand = specification.title or "type"
- if parsed.state then
- context[titlecommand]("pattern: " .. pattern .. " (".. parsed.state .. ")")
- else
- context[titlecommand]("pattern: " .. pattern)
- end
- context.starttabulate({ "|Tr|Tl|Tp|" } )
- if specification.warning then
- local comment = parsed.comment
- if comment then
- for k=1,#comment do
- context.NC()
- context("!")
- context.NC()
- context.rlap(comment[k])
- context.NR()
- end
- context.TB()
- end
- end
- for p=1,#parsed do
- local pp = parsed[p]
- local kind = pp.kind
- context.NC()
- context(p)
- context.NC()
- context(kind)
- context.NC()
- if kind == "axis" then
- context(pp.axis)
- elseif kind == "nodes" then
- context(nodesettostring(pp.nodes,pp.nodetest))
- elseif kind == "expression" then
---~ context("%s => %s",pp.expression,pp.converted)
- context(pp.expression)
- elseif kind == "finalizer" then
- context("%s(%s)",pp.name,pp.arguments)
- elseif kind == "error" and pp.error then
- context(pp.error)
- end
- context.NC()
- context.NR()
- end
- context.stoptabulate()
- if xmlroot and xmlroot ~= "" then
- if not loaded[xmlroot] then
- loaded[xmlroot] = xml.convert(buffers.getcontent(xmlroot))
- end
- local collected = xml.filter(loaded[xmlroot],xmlpattern)
- if collected then
- local tc = type(collected)
- if not tc then
- -- skip
- else
- context.blank()
- context.type("result : ")
- if tc == "string" then
- context.type(collected)
- elseif tc == "table" then
- if collected.tg then
- collected = { collected }
- end
- for c=1,#collected do
- local cc = collected[c]
- if attribute and attribute ~= "" then
- local ccat = cc.at
- local a = ccat and ccat[attribute]
- if a and a ~= "" then
- context.type(a)
- context.type(">")
- end
- end
- local ccns = cc.ns
- if ccns == "" then
- context.type(cc.tg)
- else
- context.type(ccns .. ":" .. cc.tg)
- end
- context.space()
- end
- else
- context.type(tostring(tc))
- end
- context.blank()
- end
- end
- end
- end
-end
+if not modules then modules = { } end modules ['lxml-ctx'] = {
+ version = 1.001,
+ comment = "companion to lxml-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- is this still used?
+
+local format, find = string.format, string.find
+
+local xml = xml
+
+xml.ctx = { }
+xml.ctx.enhancers = { }
+
+-- hashen
+
+function xml.ctx.enhancers.compound(root,lpath,before,tokens,after) -- todo lpeg
+ local before = before or "[%a%d][%a%d][%a%d]"
+ local tokens = tokens or "[%/%-]"
+ local after = after or "[%a%d][%a%d][%a%d]"
+ local pattern = "(" .. before .. ")(" .. tokens .. ")(" .. after .. ")"
+ local action = function(a,b,c)
+ return a .. "<compound token=" .. format("%q",b) .. "/>" .. c -- formatters["%s<compound token=%q/>%s"](a,b,c)
+ end
+ xml.enhance(root,lpath,pattern,action) -- still present?
+end
+
+local loaded = { }
+
+local nodesettostring = xml.nodesettostring
+
+-- maybe use detokenize instead of \type
+
+function xml.ctx.tshow(specification)
+ local pattern = specification.pattern
+ local xmlroot = specification.xmlroot
+ local attribute = specification.attribute
+ if context then
+ local xmlpattern = pattern
+ if not find(xmlpattern,"^[%a]+://") then
+ xmlpattern = "xml://" .. pattern
+ end
+ local parsed = xml.lpath(xmlpattern)
+ local titlecommand = specification.title or "type"
+ if parsed.state then
+ context[titlecommand]("pattern: " .. pattern .. " (".. parsed.state .. ")")
+ else
+ context[titlecommand]("pattern: " .. pattern)
+ end
+ context.starttabulate({ "|Tr|Tl|Tp|" } )
+ if specification.warning then
+ local comment = parsed.comment
+ if comment then
+ for k=1,#comment do
+ context.NC()
+ context("!")
+ context.NC()
+ context.rlap(comment[k])
+ context.NR()
+ end
+ context.TB()
+ end
+ end
+ for p=1,#parsed do
+ local pp = parsed[p]
+ local kind = pp.kind
+ context.NC()
+ context(p)
+ context.NC()
+ context(kind)
+ context.NC()
+ if kind == "axis" then
+ context(pp.axis)
+ elseif kind == "nodes" then
+ context(nodesettostring(pp.nodes,pp.nodetest))
+ elseif kind == "expression" then
+--~ context("%s => %s",pp.expression,pp.converted)
+ context(pp.expression)
+ elseif kind == "finalizer" then
+ context("%s(%s)",pp.name,pp.arguments)
+ elseif kind == "error" and pp.error then
+ context(pp.error)
+ end
+ context.NC()
+ context.NR()
+ end
+ context.stoptabulate()
+ if xmlroot and xmlroot ~= "" then
+ if not loaded[xmlroot] then
+ loaded[xmlroot] = xml.convert(buffers.getcontent(xmlroot))
+ end
+ local collected = xml.filter(loaded[xmlroot],xmlpattern)
+ if collected then
+ local tc = type(collected)
+ if not tc then
+ -- skip
+ else
+ context.blank()
+ context.type("result : ")
+ if tc == "string" then
+ context.type(collected)
+ elseif tc == "table" then
+ if collected.tg then
+ collected = { collected }
+ end
+ for c=1,#collected do
+ local cc = collected[c]
+ if attribute and attribute ~= "" then
+ local ccat = cc.at
+ local a = ccat and ccat[attribute]
+ if a and a ~= "" then
+ context.type(a)
+ context.type(">")
+ end
+ end
+ local ccns = cc.ns
+ if ccns == "" then
+ context.type(cc.tg)
+ else
+ context.type(ccns .. ":" .. cc.tg)
+ end
+ context.space()
+ end
+ else
+ context.type(tostring(tc))
+ end
+ context.blank()
+ end
+ end
+ end
+ end
+end
diff --git a/tex/context/base/lxml-dir.lua b/tex/context/base/lxml-dir.lua
index 4f0f61b71..3c68664ae 100644
--- a/tex/context/base/lxml-dir.lua
+++ b/tex/context/base/lxml-dir.lua
@@ -1,114 +1,114 @@
-if not modules then modules = { } end modules ['lxml-dir'] = {
- version = 1.001,
- comment = "this module is the basis for the lxml-* ones",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local gsub = string.gsub
-local formatters = string.formatters
-
--- <?xml version="1.0" standalone="yes"?>
--- <!-- demo.cdx -->
--- <directives>
--- <!--
--- <directive attribute='id' value="100" setup="cdx:100"/>
--- <directive attribute='id' value="101" setup="cdx:101"/>
--- -->
--- <!--
--- <directive attribute='cdx' value="colors" element="cals:table" setup="cdx:cals:table:colors"/>
--- <directive attribute='cdx' value="vertical" element="cals:table" setup="cdx:cals:table:vertical"/>
--- <directive attribute='cdx' value="noframe" element="cals:table" setup="cdx:cals:table:noframe"/>
--- -->
--- <directive attribute='cdx' value="*" element="cals:table" setup="cdx:cals:table:*"/>
--- </directives>
-
-local lxml, context = lxml, context
-
-local getid = lxml.getid
-
-lxml.directives = lxml.directives or { }
-local directives = lxml.directives
-
-local report_lxml = logs.reporter("xml","tex")
-
-local data = {
- setup = { },
- before = { },
- after = { }
-}
-
-local function load_setup(filename)
- local fullname = resolvers.findtexfile(filename) or ""
- if fullname ~= "" then
- filename = fullname
- end
- local collection = xml.applylpath({ getid(xml.load(filename)) },"directive") -- is { } needed ?
- if collection then
- local valid = 0
- for i=1,#collection do
- local at = collection[i].at
- local attribute, value, element = at.attribute or "", at.value or "", at.element or '*'
- local setup, before, after = at.setup or "", at.before or "", at.after or ""
- if attribute ~= "" and value ~= "" then
- local key = formatters["%s::%s::%s"](element,attribute,value)
- local t = data[key] or { }
- if setup ~= "" then t.setup = setup end
- if before ~= "" then t.before = before end
- if after ~= "" then t.after = after end
- data[key] = t
- valid = valid + 1
- end
- end
- report_lxml("%s directives found in %a, valid %s",#collection,filename,valid)
- else
- report_lxml("no directives found in %a",filename)
- end
-end
-
-local function handle_setup(category,root,attribute,element)
- root = getid(root)
- if attribute then
- local value = root.at[attribute]
- if value then
- if not element then
- local ns, tg = root.rn or root.ns, root.tg
- if ns == "" then
- element = tg
- else
- element = ns .. ':' .. tg
- end
- end
- local setup = data[formatters["%s::%s::%s"](element,attribute,value)]
- if setup then
- setup = setup[category]
- end
- if setup then
- context.directsetup(setup)
- else
- setup = data[formatters["%s::%s::*"](element,attribute)]
- if setup then
- setup = setup[category]
- end
- if setup then
- setup = gsub(setup,'%*',value)
- context.directsetup(setup)
- end
- end
- end
- end
-end
-
-directives.load = load_setup
-directives.handle = handle_setup
-
-function directives.setup(root,attribute,element)
- handle_setup('setup',root,attribute,element)
-end
-function directives.before(root,attribute,element)
- handle_setup('before',root,attribute,element)
-end
-function directives.after(root,attribute,element)
- handle_setup('after',root,attribute,element)
-end
+if not modules then modules = { } end modules ['lxml-dir'] = {
+ version = 1.001,
+ comment = "this module is the basis for the lxml-* ones",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local gsub = string.gsub
+local formatters = string.formatters
+
+-- <?xml version="1.0" standalone="yes"?>
+-- <!-- demo.cdx -->
+-- <directives>
+-- <!--
+-- <directive attribute='id' value="100" setup="cdx:100"/>
+-- <directive attribute='id' value="101" setup="cdx:101"/>
+-- -->
+-- <!--
+-- <directive attribute='cdx' value="colors" element="cals:table" setup="cdx:cals:table:colors"/>
+-- <directive attribute='cdx' value="vertical" element="cals:table" setup="cdx:cals:table:vertical"/>
+-- <directive attribute='cdx' value="noframe" element="cals:table" setup="cdx:cals:table:noframe"/>
+-- -->
+-- <directive attribute='cdx' value="*" element="cals:table" setup="cdx:cals:table:*"/>
+-- </directives>
+
+local lxml, context = lxml, context
+
+local getid = lxml.getid
+
+lxml.directives = lxml.directives or { }
+local directives = lxml.directives
+
+local report_lxml = logs.reporter("xml","tex")
+
+local data = {
+ setup = { },
+ before = { },
+ after = { }
+}
+
+local function load_setup(filename)
+ local fullname = resolvers.findtexfile(filename) or ""
+ if fullname ~= "" then
+ filename = fullname
+ end
+ local collection = xml.applylpath({ getid(xml.load(filename)) },"directive") -- is { } needed ?
+ if collection then
+ local valid = 0
+ for i=1,#collection do
+ local at = collection[i].at
+ local attribute, value, element = at.attribute or "", at.value or "", at.element or '*'
+ local setup, before, after = at.setup or "", at.before or "", at.after or ""
+ if attribute ~= "" and value ~= "" then
+ local key = formatters["%s::%s::%s"](element,attribute,value)
+ local t = data[key] or { }
+ if setup ~= "" then t.setup = setup end
+ if before ~= "" then t.before = before end
+ if after ~= "" then t.after = after end
+ data[key] = t
+ valid = valid + 1
+ end
+ end
+ report_lxml("%s directives found in %a, valid %s",#collection,filename,valid)
+ else
+ report_lxml("no directives found in %a",filename)
+ end
+end
+
+local function handle_setup(category,root,attribute,element)
+ root = getid(root)
+ if attribute then
+ local value = root.at[attribute]
+ if value then
+ if not element then
+ local ns, tg = root.rn or root.ns, root.tg
+ if ns == "" then
+ element = tg
+ else
+ element = ns .. ':' .. tg
+ end
+ end
+ local setup = data[formatters["%s::%s::%s"](element,attribute,value)]
+ if setup then
+ setup = setup[category]
+ end
+ if setup then
+ context.directsetup(setup)
+ else
+ setup = data[formatters["%s::%s::*"](element,attribute)]
+ if setup then
+ setup = setup[category]
+ end
+ if setup then
+ setup = gsub(setup,'%*',value)
+ context.directsetup(setup)
+ end
+ end
+ end
+ end
+end
+
+directives.load = load_setup
+directives.handle = handle_setup
+
+function directives.setup(root,attribute,element)
+ handle_setup('setup',root,attribute,element)
+end
+function directives.before(root,attribute,element)
+ handle_setup('before',root,attribute,element)
+end
+function directives.after(root,attribute,element)
+ handle_setup('after',root,attribute,element)
+end
diff --git a/tex/context/base/lxml-ent.lua b/tex/context/base/lxml-ent.lua
index e9fb0e2b8..a5c5bc389 100644
--- a/tex/context/base/lxml-ent.lua
+++ b/tex/context/base/lxml-ent.lua
@@ -1,57 +1,57 @@
-if not modules then modules = { } end modules ['lxml-ent'] = {
- version = 1.001,
- comment = "this module is the basis for the lxml-* ones",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local type, next, tonumber = type, next, tonumber
-local byte, format = string.byte, string.format
-local utfchar = utf.char
-local lpegmatch = lpeg.match
-
---[[ldx--
-<p>We provide (at least here) two entity handlers. The more extensive
-resolver consults a hash first, tries to convert to <l n='utf'/> next,
-and finaly calls a handler when defines. When this all fails, the
-original entity is returned.</p>
-
-<p>We do things different now but it's still somewhat experimental</p>
---ldx]]--
-
-local trace_entities = false trackers.register("xml.entities", function(v) trace_entities = v end)
-
-local report_xml = logs.reporter("xml")
-
-local xml = xml
-
-xml.entities = xml.entities or { }
-
-storage.register("xml/entities", xml.entities, "xml.entities" )
-
-local entities = xml.entities -- maybe some day properties
-
-function xml.registerentity(key,value)
- entities[key] = value
- if trace_entities then
- report_xml("registering entity %a as %a",key,value)
- end
-end
-
-if characters and characters.entities then
-
- function characters.registerentities(forcecopy)
- if forcecopy then
- table.setmetatableindex(entities,nil)
- for name, value in next, characters.entities do
- if not entities[name] then
- entities[name] = value
- end
- end
- else
- table.setmetatableindex(entities,characters.entities)
- end
- end
-
-end
+if not modules then modules = { } end modules ['lxml-ent'] = {
+ version = 1.001,
+ comment = "this module is the basis for the lxml-* ones",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local type, next, tonumber = type, next, tonumber
+local byte, format = string.byte, string.format
+local utfchar = utf.char
+local lpegmatch = lpeg.match
+
+--[[ldx--
+<p>We provide (at least here) two entity handlers. The more extensive
+resolver consults a hash first, tries to convert to <l n='utf'/> next,
+and finaly calls a handler when defines. When this all fails, the
+original entity is returned.</p>
+
+<p>We do things different now but it's still somewhat experimental</p>
+--ldx]]--
+
+local trace_entities = false trackers.register("xml.entities", function(v) trace_entities = v end)
+
+local report_xml = logs.reporter("xml")
+
+local xml = xml
+
+xml.entities = xml.entities or { }
+
+storage.register("xml/entities", xml.entities, "xml.entities" )
+
+local entities = xml.entities -- maybe some day properties
+
+function xml.registerentity(key,value)
+ entities[key] = value
+ if trace_entities then
+ report_xml("registering entity %a as %a",key,value)
+ end
+end
+
+if characters and characters.entities then
+
+ function characters.registerentities(forcecopy)
+ if forcecopy then
+ table.setmetatableindex(entities,nil)
+ for name, value in next, characters.entities do
+ if not entities[name] then
+ entities[name] = value
+ end
+ end
+ else
+ table.setmetatableindex(entities,characters.entities)
+ end
+ end
+
+end
diff --git a/tex/context/base/lxml-inf.lua b/tex/context/base/lxml-inf.lua
index 2c130791a..8f1157c7d 100644
--- a/tex/context/base/lxml-inf.lua
+++ b/tex/context/base/lxml-inf.lua
@@ -1,58 +1,58 @@
-if not modules then modules = { } end modules ['lxml-inf'] = {
- version = 1.001,
- comment = "this module is the basis for the lxml-* ones",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- This file will be loaded runtime by x-pending.tex.
-
-local concat = table.concat
-
-local xmlwithelements = xml.withelements
-local getid = lxml.getid
-
-local status, stack
-
-local function get(e,d)
- local ns, tg = e.ns, e.tg
- local name = tg
- if ns ~= "" then name = ns .. ":" .. tg end
- stack[d] = name
- local ec = e.command
- if ec == true then
- ec = "system: text"
- elseif ec == false then
- ec = "system: skip"
- elseif ec == nil then
- ec = "system: not set"
- elseif type(ec) == "string" then
- ec = "setup: " .. ec
- else -- function
- ec = tostring(ec)
- end
- local tag = concat(stack," => ",1,d)
- local s = status[tag]
- if not s then
- s = { }
- status[tag] = s
- end
- s[ec] = (s[ec] or 0) + 1
-end
-
-local function get_command_status(id)
- status, stack = {}, {}
- if id then
- xmlwithelements(getid(id),get)
- return status
- else
- local t = { }
- for id, _ in next, loaded do
- t[id] = get_command_status(id)
- end
- return t
- end
-end
-
-lxml.get_command_status = get_command_status
+if not modules then modules = { } end modules ['lxml-inf'] = {
+ version = 1.001,
+ comment = "this module is the basis for the lxml-* ones",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This file will be loaded runtime by x-pending.tex.
+
+local concat = table.concat
+
+local xmlwithelements = xml.withelements
+local getid = lxml.getid
+
+local status, stack
+
+local function get(e,d)
+ local ns, tg = e.ns, e.tg
+ local name = tg
+ if ns ~= "" then name = ns .. ":" .. tg end
+ stack[d] = name
+ local ec = e.command
+ if ec == true then
+ ec = "system: text"
+ elseif ec == false then
+ ec = "system: skip"
+ elseif ec == nil then
+ ec = "system: not set"
+ elseif type(ec) == "string" then
+ ec = "setup: " .. ec
+ else -- function
+ ec = tostring(ec)
+ end
+ local tag = concat(stack," => ",1,d)
+ local s = status[tag]
+ if not s then
+ s = { }
+ status[tag] = s
+ end
+ s[ec] = (s[ec] or 0) + 1
+end
+
+local function get_command_status(id)
+ status, stack = {}, {}
+ if id then
+ xmlwithelements(getid(id),get)
+ return status
+ else
+ local t = { }
+ for id, _ in next, loaded do
+ t[id] = get_command_status(id)
+ end
+ return t
+ end
+end
+
+lxml.get_command_status = get_command_status
diff --git a/tex/context/base/lxml-lpt.lua b/tex/context/base/lxml-lpt.lua
index 2f57ced5b..51ab321b9 100644
--- a/tex/context/base/lxml-lpt.lua
+++ b/tex/context/base/lxml-lpt.lua
@@ -1,1466 +1,1466 @@
-if not modules then modules = { } end modules ['lxml-lpt'] = {
- version = 1.001,
- comment = "this module is the basis for the lxml-* ones",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- e.ni is only valid after a filter run
--- todo: B/C/[get first match]
-
-local concat, remove, insert = table.concat, table.remove, table.insert
-local type, next, tonumber, tostring, setmetatable, load, select = type, next, tonumber, tostring, setmetatable, load, select
-local format, upper, lower, gmatch, gsub, find, rep = string.format, string.upper, string.lower, string.gmatch, string.gsub, string.find, string.rep
-local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
-
-local setmetatableindex = table.setmetatableindex
-local formatters = string.formatters -- no need (yet) as paths are cached anyway
-
--- beware, this is not xpath ... e.g. position is different (currently) and
--- we have reverse-sibling as reversed preceding sibling
-
---[[ldx--
-<p>This module can be used stand alone but also inside <l n='mkiv'/> in
-which case it hooks into the tracker code. Therefore we provide a few
-functions that set the tracers. Here we overload a previously defined
-function.</p>
-<p>If I can get in the mood I will make a variant that is XSLT compliant
-but I wonder if it makes sense.</P>
---ldx]]--
-
---[[ldx--
-<p>Expecially the lpath code is experimental, we will support some of xpath, but
-only things that make sense for us; as compensation it is possible to hook in your
-own functions. Apart from preprocessing content for <l n='context'/> we also need
-this module for process management, like handling <l n='ctx'/> and <l n='rlx'/>
-files.</p>
-
-<typing>
-a/b/c /*/c
-a/b/c/first() a/b/c/last() a/b/c/index(n) a/b/c/index(-n)
-a/b/c/text() a/b/c/text(1) a/b/c/text(-1) a/b/c/text(n)
-</typing>
---ldx]]--
-
-local trace_lpath = false if trackers then trackers.register("xml.path", function(v) trace_lpath = v end) end
-local trace_lparse = false if trackers then trackers.register("xml.parse", function(v) trace_lparse = v end) end
-local trace_lprofile = false if trackers then trackers.register("xml.profile", function(v) trace_lpath = v trace_lparse = v trace_lprofile = v end) end
-
-local report_lpath = logs.reporter("xml","lpath")
-
---[[ldx--
-<p>We've now arrived at an interesting part: accessing the tree using a subset
-of <l n='xpath'/> and since we're not compatible we call it <l n='lpath'/>. We
-will explain more about its usage in other documents.</p>
---ldx]]--
-
-local xml = xml
-
-local lpathcalls = 0 function xml.lpathcalls () return lpathcalls end
-local lpathcached = 0 function xml.lpathcached() return lpathcached end
-
-xml.functions = xml.functions or { } -- internal
-local functions = xml.functions
-
-xml.expressions = xml.expressions or { } -- in expressions
-local expressions = xml.expressions
-
-xml.finalizers = xml.finalizers or { } -- fast do-with ... (with return value other than collection)
-local finalizers = xml.finalizers
-
-xml.specialhandler = xml.specialhandler or { }
-local specialhandler = xml.specialhandler
-
-lpegpatterns.xml = lpegpatterns.xml or { }
-local xmlpatterns = lpegpatterns.xml
-
-finalizers.xml = finalizers.xml or { }
-finalizers.tex = finalizers.tex or { }
-
-local function fallback (t, name)
- local fn = finalizers[name]
- if fn then
- t[name] = fn
- else
- report_lpath("unknown sub finalizer %a",name)
- fn = function() end
- end
- return fn
-end
-
-setmetatableindex(finalizers.xml, fallback)
-setmetatableindex(finalizers.tex, fallback)
-
-xml.defaultprotocol = "xml"
-
--- as xsl does not follow xpath completely here we will also
--- be more liberal especially with regards to the use of | and
--- the rootpath:
---
--- test : all 'test' under current
--- /test : 'test' relative to current
--- a|b|c : set of names
--- (a|b|c) : idem
--- ! : not
---
--- after all, we're not doing transformations but filtering. in
--- addition we provide filter functions (last bit)
---
--- todo: optimizer
---
--- .. : parent
--- * : all kids
--- / : anchor here
--- // : /**/
--- ** : all in between
---
--- so far we had (more practical as we don't transform)
---
--- {/test} : kids 'test' under current node
--- {test} : any kid with tag 'test'
--- {//test} : same as above
-
--- evaluator (needs to be redone, for the moment copied)
-
--- todo: apply_axis(list,notable) and collection vs single
-
-local apply_axis = { }
-
-apply_axis['root'] = function(list)
- local collected = { }
- for l=1,#list do
- local ll = list[l]
- local rt = ll
- while ll do
- ll = ll.__p__
- if ll then
- rt = ll
- end
- end
- collected[l] = rt
- end
- return collected
-end
-
-apply_axis['self'] = function(list)
---~ local collected = { }
---~ for l=1,#list do
---~ collected[l] = list[l]
---~ end
---~ return collected
- return list
-end
-
-apply_axis['child'] = function(list)
- local collected, c = { }, 0
- for l=1,#list do
- local ll = list[l]
- local dt = ll.dt
- if dt then -- weird that this is needed
- local en = 0
- for k=1,#dt do
- local dk = dt[k]
- if dk.tg then
- c = c + 1
- collected[c] = dk
- dk.ni = k -- refresh
- en = en + 1
- dk.ei = en
- end
- end
- ll.en = en
- end
- end
- return collected
-end
-
-local function collect(list,collected,c)
- local dt = list.dt
- if dt then
- local en = 0
- for k=1,#dt do
- local dk = dt[k]
- if dk.tg then
- c = c + 1
- collected[c] = dk
- dk.ni = k -- refresh
- en = en + 1
- dk.ei = en
- c = collect(dk,collected,c)
- end
- end
- list.en = en
- end
- return c
-end
-
-apply_axis['descendant'] = function(list)
- local collected, c = { }, 0
- for l=1,#list do
- c = collect(list[l],collected,c)
- end
- return collected
-end
-
-local function collect(list,collected,c)
- local dt = list.dt
- if dt then
- local en = 0
- for k=1,#dt do
- local dk = dt[k]
- if dk.tg then
- c = c + 1
- collected[c] = dk
- dk.ni = k -- refresh
- en = en + 1
- dk.ei = en
- c = collect(dk,collected,c)
- end
- end
- list.en = en
- end
- return c
-end
-apply_axis['descendant-or-self'] = function(list)
- local collected, c = { }, 0
- for l=1,#list do
- local ll = list[l]
- if ll.special ~= true then -- catch double root
- c = c + 1
- collected[c] = ll
- end
- c = collect(ll,collected,c)
- end
- return collected
-end
-
-apply_axis['ancestor'] = function(list)
- local collected, c = { }, 0
- for l=1,#list do
- local ll = list[l]
- while ll do
- ll = ll.__p__
- if ll then
- c = c + 1
- collected[c] = ll
- end
- end
- end
- return collected
-end
-
-apply_axis['ancestor-or-self'] = function(list)
- local collected, c = { }, 0
- for l=1,#list do
- local ll = list[l]
- c = c + 1
- collected[c] = ll
- while ll do
- ll = ll.__p__
- if ll then
- c = c + 1
- collected[c] = ll
- end
- end
- end
- return collected
-end
-
-apply_axis['parent'] = function(list)
- local collected, c = { }, 0
- for l=1,#list do
- local pl = list[l].__p__
- if pl then
- c = c + 1
- collected[c] = pl
- end
- end
- return collected
-end
-
-apply_axis['attribute'] = function(list)
- return { }
-end
-
-apply_axis['namespace'] = function(list)
- return { }
-end
-
-apply_axis['following'] = function(list) -- incomplete
---~ local collected, c = { }, 0
---~ for l=1,#list do
---~ local ll = list[l]
---~ local p = ll.__p__
---~ local d = p.dt
---~ for i=ll.ni+1,#d do
---~ local di = d[i]
---~ if type(di) == "table" then
---~ c = c + 1
---~ collected[c] = di
---~ break
---~ end
---~ end
---~ end
---~ return collected
- return { }
-end
-
-apply_axis['preceding'] = function(list) -- incomplete
---~ local collected, c = { }, 0
---~ for l=1,#list do
---~ local ll = list[l]
---~ local p = ll.__p__
---~ local d = p.dt
---~ for i=ll.ni-1,1,-1 do
---~ local di = d[i]
---~ if type(di) == "table" then
---~ c = c + 1
---~ collected[c] = di
---~ break
---~ end
---~ end
---~ end
---~ return collected
- return { }
-end
-
-apply_axis['following-sibling'] = function(list)
- local collected, c = { }, 0
- for l=1,#list do
- local ll = list[l]
- local p = ll.__p__
- local d = p.dt
- for i=ll.ni+1,#d do
- local di = d[i]
- if type(di) == "table" then
- c = c + 1
- collected[c] = di
- end
- end
- end
- return collected
-end
-
-apply_axis['preceding-sibling'] = function(list)
- local collected, c = { }, 0
- for l=1,#list do
- local ll = list[l]
- local p = ll.__p__
- local d = p.dt
- for i=1,ll.ni-1 do
- local di = d[i]
- if type(di) == "table" then
- c = c + 1
- collected[c] = di
- end
- end
- end
- return collected
-end
-
-apply_axis['reverse-sibling'] = function(list) -- reverse preceding
- local collected, c = { }, 0
- for l=1,#list do
- local ll = list[l]
- local p = ll.__p__
- local d = p.dt
- for i=ll.ni-1,1,-1 do
- local di = d[i]
- if type(di) == "table" then
- c = c + 1
- collected[c] = di
- end
- end
- end
- return collected
-end
-
-apply_axis['auto-descendant-or-self'] = apply_axis['descendant-or-self']
-apply_axis['auto-descendant'] = apply_axis['descendant']
-apply_axis['auto-child'] = apply_axis['child']
-apply_axis['auto-self'] = apply_axis['self']
-apply_axis['initial-child'] = apply_axis['child']
-
-local function apply_nodes(list,directive,nodes)
- -- todo: nodes[1] etc ... negated node name in set ... when needed
- -- ... currently ignored
- local maxn = #nodes
- if maxn == 3 then --optimized loop
- local nns, ntg = nodes[2], nodes[3]
- if not nns and not ntg then -- wildcard
- if directive then
- return list
- else
- return { }
- end
- else
- local collected, c, m, p = { }, 0, 0, nil
- if not nns then -- only check tag
- for l=1,#list do
- local ll = list[l]
- local ltg = ll.tg
- if ltg then
- if directive then
- if ntg == ltg then
- local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
- c = c + 1
- collected[c], ll.mi = ll, m
- end
- elseif ntg ~= ltg then
- local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
- c = c + 1
- collected[c], ll.mi = ll, m
- end
- end
- end
- elseif not ntg then -- only check namespace
- for l=1,#list do
- local ll = list[l]
- local lns = ll.rn or ll.ns
- if lns then
- if directive then
- if lns == nns then
- local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
- c = c + 1
- collected[c], ll.mi = ll, m
- end
- elseif lns ~= nns then
- local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
- c = c + 1
- collected[c], ll.mi = ll, m
- end
- end
- end
- else -- check both
- for l=1,#list do
- local ll = list[l]
- local ltg = ll.tg
- if ltg then
- local lns = ll.rn or ll.ns
- local ok = ltg == ntg and lns == nns
- if directive then
- if ok then
- local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
- c = c + 1
- collected[c], ll.mi = ll, m
- end
- elseif not ok then
- local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
- c = c + 1
- collected[c], ll.mi = ll, m
- end
- end
- end
- end
- return collected
- end
- else
- local collected, c, m, p = { }, 0, 0, nil
- for l=1,#list do
- local ll = list[l]
- local ltg = ll.tg
- if ltg then
- local lns = ll.rn or ll.ns
- local ok = false
- for n=1,maxn,3 do
- local nns, ntg = nodes[n+1], nodes[n+2]
- ok = (not ntg or ltg == ntg) and (not nns or lns == nns)
- if ok then
- break
- end
- end
- if directive then
- if ok then
- local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
- c = c + 1
- collected[c], ll.mi = ll, m
- end
- elseif not ok then
- local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
- c = c + 1
- collected[c], ll.mi = ll, m
- end
- end
- end
- return collected
- end
-end
-
-local quit_expression = false
-
-local function apply_expression(list,expression,order)
- local collected, c = { }, 0
- quit_expression = false
- for l=1,#list do
- local ll = list[l]
- if expression(list,ll,l,order) then -- nasty, order alleen valid als n=1
- c = c + 1
- collected[c] = ll
- end
- if quit_expression then
- break
- end
- end
- return collected
-end
-
-local P, V, C, Cs, Cc, Ct, R, S, Cg, Cb = lpeg.P, lpeg.V, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.Ct, lpeg.R, lpeg.S, lpeg.Cg, lpeg.Cb
-
-local spaces = S(" \n\r\t\f")^0
-local lp_space = S(" \n\r\t\f")
-local lp_any = P(1)
-local lp_noequal = P("!=") / "~=" + P("<=") + P(">=") + P("==")
-local lp_doequal = P("=") / "=="
-local lp_or = P("|") / " or "
-local lp_and = P("&") / " and "
-
-local lp_builtin = P (
- P("text") / "(ll.dt[1] or '')" + -- fragile
- P("content") / "ll.dt" +
- -- P("name") / "(ll.ns~='' and ll.ns..':'..ll.tg)" +
- P("name") / "((ll.ns~='' and ll.ns..':'..ll.tg) or ll.tg)" +
- P("tag") / "ll.tg" +
- P("position") / "l" + -- is element in finalizer
- P("firstindex") / "1" +
- P("lastindex") / "(#ll.__p__.dt or 1)" +
- P("firstelement") / "1" +
- P("lastelement") / "(ll.__p__.en or 1)" +
- P("first") / "1" +
- P("last") / "#list" +
- P("rootposition") / "order" +
- P("order") / "order" +
- P("element") / "(ll.ei or 1)" +
- P("index") / "(ll.ni or 1)" +
- P("match") / "(ll.mi or 1)" +
- -- P("namespace") / "ll.ns" +
- P("ns") / "ll.ns"
- ) * ((spaces * P("(") * spaces * P(")"))/"")
-
--- for the moment we keep namespaces with attributes
-
-local lp_attribute = (P("@") + P("attribute::")) / "" * Cc("(ll.at and ll.at['") * ((R("az","AZ") + S("-_:"))^1) * Cc("'])")
-
--- lp_fastpos_p = (P("+")^0 * R("09")^1 * P(-1)) / function(s) return "l==" .. s end
--- lp_fastpos_n = (P("-") * R("09")^1 * P(-1)) / function(s) return "(" .. s .. "<0 and (#list+".. s .. "==l))" end
-
-lp_fastpos_p = P("+")^0 * R("09")^1 * P(-1) / "l==%0"
-lp_fastpos_n = P("-") * R("09")^1 * P(-1) / "(%0<0 and (#list+%0==l))"
-
-local lp_fastpos = lp_fastpos_n + lp_fastpos_p
-
-local lp_reserved = C("and") + C("or") + C("not") + C("div") + C("mod") + C("true") + C("false")
-
--- local lp_lua_function = C(R("az","AZ","__")^1 * (P(".") * R("az","AZ","__")^1)^1) * ("(") / function(t) -- todo: better . handling
--- return t .. "("
--- end
-
--- local lp_lua_function = (R("az","AZ","__")^1 * (P(".") * R("az","AZ","__")^1)^1) * ("(") / "%0("
-local lp_lua_function = Cs((R("az","AZ","__")^1 * (P(".") * R("az","AZ","__")^1)^1) * ("(")) / "%0"
-
-local lp_function = C(R("az","AZ","__")^1) * P("(") / function(t) -- todo: better . handling
- if expressions[t] then
- return "expr." .. t .. "("
- else
- return "expr.error("
- end
-end
-
-local lparent = P("(")
-local rparent = P(")")
-local noparent = 1 - (lparent+rparent)
-local nested = P{lparent * (noparent + V(1))^0 * rparent}
-local value = P(lparent * C((noparent + nested)^0) * rparent) -- P{"("*C(((1-S("()"))+V(1))^0)*")"}
-
-local lp_child = Cc("expr.child(ll,'") * R("az","AZ","--","__")^1 * Cc("')")
-local lp_number = S("+-") * R("09")^1
-local lp_string = Cc("'") * R("az","AZ","--","__")^1 * Cc("'")
-local lp_content = (P("'") * (1-P("'"))^0 * P("'") + P('"') * (1-P('"'))^0 * P('"'))
-
-local cleaner
-
-local lp_special = (C(P("name")+P("text")+P("tag")+P("count")+P("child"))) * value / function(t,s)
- if expressions[t] then
- s = s and s ~= "" and lpegmatch(cleaner,s)
- if s and s ~= "" then
- return "expr." .. t .. "(ll," .. s ..")"
- else
- return "expr." .. t .. "(ll)"
- end
- else
- return "expr.error(" .. t .. ")"
- end
-end
-
-local content =
- lp_builtin +
- lp_attribute +
- lp_special +
- lp_noequal + lp_doequal +
- lp_or + lp_and +
- lp_reserved +
- lp_lua_function + lp_function +
- lp_content + -- too fragile
- lp_child +
- lp_any
-
-local converter = Cs (
- lp_fastpos + (P { lparent * (V(1))^0 * rparent + content } )^0
-)
-
-cleaner = Cs ( (
- -- lp_fastpos +
- lp_reserved +
- lp_number +
- lp_string +
-1 )^1 )
-
-local template_e = [[
- local expr = xml.expressions
- return function(list,ll,l,order)
- return %s
- end
-]]
-
-local template_f_y = [[
- local finalizer = xml.finalizers['%s']['%s']
- return function(collection)
- return finalizer(collection,%s)
- end
-]]
-
-local template_f_n = [[
- return xml.finalizers['%s']['%s']
-]]
-
---
-
-local register_self = { kind = "axis", axis = "self" } -- , apply = apply_axis["self"] }
-local register_parent = { kind = "axis", axis = "parent" } -- , apply = apply_axis["parent"] }
-local register_descendant = { kind = "axis", axis = "descendant" } -- , apply = apply_axis["descendant"] }
-local register_child = { kind = "axis", axis = "child" } -- , apply = apply_axis["child"] }
-local register_descendant_or_self = { kind = "axis", axis = "descendant-or-self" } -- , apply = apply_axis["descendant-or-self"] }
-local register_root = { kind = "axis", axis = "root" } -- , apply = apply_axis["root"] }
-local register_ancestor = { kind = "axis", axis = "ancestor" } -- , apply = apply_axis["ancestor"] }
-local register_ancestor_or_self = { kind = "axis", axis = "ancestor-or-self" } -- , apply = apply_axis["ancestor-or-self"] }
-local register_attribute = { kind = "axis", axis = "attribute" } -- , apply = apply_axis["attribute"] }
-local register_namespace = { kind = "axis", axis = "namespace" } -- , apply = apply_axis["namespace"] }
-local register_following = { kind = "axis", axis = "following" } -- , apply = apply_axis["following"] }
-local register_following_sibling = { kind = "axis", axis = "following-sibling" } -- , apply = apply_axis["following-sibling"] }
-local register_preceding = { kind = "axis", axis = "preceding" } -- , apply = apply_axis["preceding"] }
-local register_preceding_sibling = { kind = "axis", axis = "preceding-sibling" } -- , apply = apply_axis["preceding-sibling"] }
-local register_reverse_sibling = { kind = "axis", axis = "reverse-sibling" } -- , apply = apply_axis["reverse-sibling"] }
-
-local register_auto_descendant_or_self = { kind = "axis", axis = "auto-descendant-or-self" } -- , apply = apply_axis["auto-descendant-or-self"] }
-local register_auto_descendant = { kind = "axis", axis = "auto-descendant" } -- , apply = apply_axis["auto-descendant"] }
-local register_auto_self = { kind = "axis", axis = "auto-self" } -- , apply = apply_axis["auto-self"] }
-local register_auto_child = { kind = "axis", axis = "auto-child" } -- , apply = apply_axis["auto-child"] }
-
-local register_initial_child = { kind = "axis", axis = "initial-child" } -- , apply = apply_axis["initial-child"] }
-
-local register_all_nodes = { kind = "nodes", nodetest = true, nodes = { true, false, false } }
-
-local skip = { }
-
-local function errorrunner_e(str,cnv)
- if not skip[str] then
- report_lpath("error in expression: %s => %s",str,cnv)
- skip[str] = cnv or str
- end
- return false
-end
-
-local function errorrunner_f(str,arg)
- report_lpath("error in finalizer: %s(%s)",str,arg or "")
- return false
-end
-
-local function register_nodes(nodetest,nodes)
- return { kind = "nodes", nodetest = nodetest, nodes = nodes }
-end
-
-local function register_expression(expression)
- local converted = lpegmatch(converter,expression)
- local runner = load(format(template_e,converted))
- runner = (runner and runner()) or function() errorrunner_e(expression,converted) end
- return { kind = "expression", expression = expression, converted = converted, evaluator = runner }
-end
-
-local function register_finalizer(protocol,name,arguments)
- local runner
- if arguments and arguments ~= "" then
- runner = load(format(template_f_y,protocol or xml.defaultprotocol,name,arguments))
- else
- runner = load(format(template_f_n,protocol or xml.defaultprotocol,name))
- end
- runner = (runner and runner()) or function() errorrunner_f(name,arguments) end
- return { kind = "finalizer", name = name, arguments = arguments, finalizer = runner }
-end
-
-local expression = P { "ex",
- ex = "[" * C((V("sq") + V("dq") + (1 - S("[]")) + V("ex"))^0) * "]",
- sq = "'" * (1 - S("'"))^0 * "'",
- dq = '"' * (1 - S('"'))^0 * '"',
-}
-
-local arguments = P { "ar",
- ar = "(" * Cs((V("sq") + V("dq") + V("nq") + P(1-P(")")))^0) * ")",
- nq = ((1 - S("),'\""))^1) / function(s) return format("%q",s) end,
- sq = P("'") * (1 - P("'"))^0 * P("'"),
- dq = P('"') * (1 - P('"'))^0 * P('"'),
-}
-
--- todo: better arg parser
-
-local function register_error(str)
- return { kind = "error", error = format("unparsed: %s",str) }
-end
-
--- there is a difference in * and /*/ and so we need to catch a few special cases
-
-local special_1 = P("*") * Cc(register_auto_descendant) * Cc(register_all_nodes) -- last one not needed
-local special_2 = P("/") * Cc(register_auto_self)
-local special_3 = P("") * Cc(register_auto_self)
-
-local no_nextcolon = P(-1) + #(1-P(":")) -- newer lpeg needs the P(-1)
-local no_nextlparent = P(-1) + #(1-P("(")) -- newer lpeg needs the P(-1)
-
-local pathparser = Ct { "patterns", -- can be made a bit faster by moving some patterns outside
-
- patterns = spaces * V("protocol") * spaces * (
- ( V("special") * spaces * P(-1) ) +
- ( V("initial") * spaces * V("step") * spaces * (P("/") * spaces * V("step") * spaces)^0 )
- ),
-
- protocol = Cg(V("letters"),"protocol") * P("://") + Cg(Cc(nil),"protocol"),
-
- -- the / is needed for // as descendant or self is somewhat special
- -- step = (V("shortcuts") + V("axis") * spaces * V("nodes")^0 + V("error")) * spaces * V("expressions")^0 * spaces * V("finalizer")^0,
- step = ((V("shortcuts") + P("/") + V("axis")) * spaces * V("nodes")^0 + V("error")) * spaces * V("expressions")^0 * spaces * V("finalizer")^0,
-
- axis = V("descendant") + V("child") + V("parent") + V("self") + V("root") + V("ancestor") +
- V("descendant_or_self") + V("following_sibling") + V("following") +
- V("reverse_sibling") + V("preceding_sibling") + V("preceding") + V("ancestor_or_self") +
- #(1-P(-1)) * Cc(register_auto_child),
-
- special = special_1 + special_2 + special_3,
-
- initial = (P("/") * spaces * Cc(register_initial_child))^-1,
-
- error = (P(1)^1) / register_error,
-
- shortcuts_a = V("s_descendant_or_self") + V("s_descendant") + V("s_child") + V("s_parent") + V("s_self") + V("s_root") + V("s_ancestor"),
-
- shortcuts = V("shortcuts_a") * (spaces * "/" * spaces * V("shortcuts_a"))^0,
-
- s_descendant_or_self = (P("***/") + P("/")) * Cc(register_descendant_or_self), --- *** is a bonus
- s_descendant = P("**") * Cc(register_descendant),
- s_child = P("*") * no_nextcolon * Cc(register_child ),
- s_parent = P("..") * Cc(register_parent ),
- s_self = P("." ) * Cc(register_self ),
- s_root = P("^^") * Cc(register_root ),
- s_ancestor = P("^") * Cc(register_ancestor ),
-
- descendant = P("descendant::") * Cc(register_descendant ),
- child = P("child::") * Cc(register_child ),
- parent = P("parent::") * Cc(register_parent ),
- self = P("self::") * Cc(register_self ),
- root = P('root::') * Cc(register_root ),
- ancestor = P('ancestor::') * Cc(register_ancestor ),
- descendant_or_self = P('descendant-or-self::') * Cc(register_descendant_or_self ),
- ancestor_or_self = P('ancestor-or-self::') * Cc(register_ancestor_or_self ),
- -- attribute = P('attribute::') * Cc(register_attribute ),
- -- namespace = P('namespace::') * Cc(register_namespace ),
- following = P('following::') * Cc(register_following ),
- following_sibling = P('following-sibling::') * Cc(register_following_sibling ),
- preceding = P('preceding::') * Cc(register_preceding ),
- preceding_sibling = P('preceding-sibling::') * Cc(register_preceding_sibling ),
- reverse_sibling = P('reverse-sibling::') * Cc(register_reverse_sibling ),
-
- nodes = (V("nodefunction") * spaces * P("(") * V("nodeset") * P(")") + V("nodetest") * V("nodeset")) / register_nodes,
-
- expressions = expression / register_expression,
-
- letters = R("az")^1,
- name = (1-S("/[]()|:*!"))^1, -- make inline
- negate = P("!") * Cc(false),
-
- nodefunction = V("negate") + P("not") * Cc(false) + Cc(true),
- nodetest = V("negate") + Cc(true),
- nodename = (V("negate") + Cc(true)) * spaces * ((V("wildnodename") * P(":") * V("wildnodename")) + (Cc(false) * V("wildnodename"))),
- wildnodename = (C(V("name")) + P("*") * Cc(false)) * no_nextlparent,
- nodeset = spaces * Ct(V("nodename") * (spaces * P("|") * spaces * V("nodename"))^0) * spaces,
-
- finalizer = (Cb("protocol") * P("/")^-1 * C(V("name")) * arguments * P(-1)) / register_finalizer,
-
-}
-
-xmlpatterns.pathparser = pathparser
-
-local cache = { }
-
-local function nodesettostring(set,nodetest)
- local t = { }
- for i=1,#set,3 do
- local directive, ns, tg = set[i], set[i+1], set[i+2]
- if not ns or ns == "" then ns = "*" end
- if not tg or tg == "" then tg = "*" end
- tg = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg)
- t[i] = (directive and tg) or format("not(%s)",tg)
- end
- if nodetest == false then
- return format("not(%s)",concat(t,"|"))
- else
- return concat(t,"|")
- end
-end
-
-local function tagstostring(list)
- if #list == 0 then
- return "no elements"
- else
- local t = { }
- for i=1, #list do
- local li = list[i]
- local ns, tg = li.ns, li.tg
- if not ns or ns == "" then ns = "*" end
- if not tg or tg == "" then tg = "*" end
- t[i] = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg)
- end
- return concat(t," ")
- end
-end
-
-xml.nodesettostring = nodesettostring
-
-local lpath -- we have a harmless kind of circular reference
-
-local lshowoptions = { functions = false }
-
-local function lshow(parsed)
- if type(parsed) == "string" then
- parsed = lpath(parsed)
- end
- report_lpath("%s://%s => %s",parsed.protocol or xml.defaultprotocol,parsed.pattern,
- table.serialize(parsed,false,lshowoptions))
-end
-
-xml.lshow = lshow
-
-local function add_comment(p,str)
- local pc = p.comment
- if not pc then
- p.comment = { str }
- else
- pc[#pc+1] = str
- end
-end
-
-lpath = function (pattern) -- the gain of caching is rather minimal
- lpathcalls = lpathcalls + 1
- if type(pattern) == "table" then
- return pattern
- else
- local parsed = cache[pattern]
- if parsed then
- lpathcached = lpathcached + 1
- else
- parsed = lpegmatch(pathparser,pattern)
- if parsed then
- parsed.pattern = pattern
- local np = #parsed
- if np == 0 then
- parsed = { pattern = pattern, register_self, state = "parsing error" }
- report_lpath("parsing error in pattern: %s",pattern)
- lshow(parsed)
- else
- -- we could have done this with a more complex parser but this
- -- is cleaner
- local pi = parsed[1]
- if pi.axis == "auto-child" then
- if false then
- add_comment(parsed, "auto-child replaced by auto-descendant-or-self")
- parsed[1] = register_auto_descendant_or_self
- else
- add_comment(parsed, "auto-child replaced by auto-descendant")
- parsed[1] = register_auto_descendant
- end
- elseif pi.axis == "initial-child" and np > 1 and parsed[2].axis then
- add_comment(parsed, "initial-child removed") -- we could also make it a auto-self
- remove(parsed,1)
- end
- local np = #parsed -- can have changed
- if np > 1 then
- local pnp = parsed[np]
- if pnp.kind == "nodes" and pnp.nodetest == true then
- local nodes = pnp.nodes
- if nodes[1] == true and nodes[2] == false and nodes[3] == false then
- add_comment(parsed, "redundant final wildcard filter removed")
- remove(parsed,np)
- end
- end
- end
- end
- else
- parsed = { pattern = pattern }
- end
- cache[pattern] = parsed
- if trace_lparse and not trace_lprofile then
- lshow(parsed)
- end
- end
- return parsed
- end
-end
-
-xml.lpath = lpath
-
--- we can move all calls inline and then merge the trace back
--- technically we can combine axis and the next nodes which is
--- what we did before but this a bit cleaner (but slower too)
--- but interesting is that it's not that much faster when we
--- go inline
---
--- beware: we need to return a collection even when we filter
--- else the (simple) cache gets messed up
-
--- caching found lookups saves not that much (max .1 sec on a 8 sec run)
--- and it also messes up finalizers
-
--- watch out: when there is a finalizer, it's always called as there
--- can be cases that a finalizer returns (or does) something in case
--- there is no match; an example of this is count()
-
-local profiled = { } xml.profiled = profiled
-
-local function profiled_apply(list,parsed,nofparsed,order)
- local p = profiled[parsed.pattern]
- if p then
- p.tested = p.tested + 1
- else
- p = { tested = 1, matched = 0, finalized = 0 }
- profiled[parsed.pattern] = p
- end
- local collected = list
- for i=1,nofparsed do
- local pi = parsed[i]
- local kind = pi.kind
- if kind == "axis" then
- collected = apply_axis[pi.axis](collected)
- elseif kind == "nodes" then
- collected = apply_nodes(collected,pi.nodetest,pi.nodes)
- elseif kind == "expression" then
- collected = apply_expression(collected,pi.evaluator,order)
- elseif kind == "finalizer" then
- collected = pi.finalizer(collected) -- no check on # here
- p.matched = p.matched + 1
- p.finalized = p.finalized + 1
- return collected
- end
- if not collected or #collected == 0 then
- local pn = i < nofparsed and parsed[nofparsed]
- if pn and pn.kind == "finalizer" then
- collected = pn.finalizer(collected)
- p.finalized = p.finalized + 1
- return collected
- end
- return nil
- end
- end
- if collected then
- p.matched = p.matched + 1
- end
- return collected
-end
-
-local function traced_apply(list,parsed,nofparsed,order)
- if trace_lparse then
- lshow(parsed)
- end
- report_lpath("collecting: %s",parsed.pattern)
- report_lpath("root tags : %s",tagstostring(list))
- report_lpath("order : %s",order or "unset")
- local collected = list
- for i=1,nofparsed do
- local pi = parsed[i]
- local kind = pi.kind
- if kind == "axis" then
- collected = apply_axis[pi.axis](collected)
- report_lpath("% 10i : ax : %s",(collected and #collected) or 0,pi.axis)
- elseif kind == "nodes" then
- collected = apply_nodes(collected,pi.nodetest,pi.nodes)
- report_lpath("% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest))
- elseif kind == "expression" then
- collected = apply_expression(collected,pi.evaluator,order)
- report_lpath("% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted)
- elseif kind == "finalizer" then
- collected = pi.finalizer(collected)
- report_lpath("% 10i : fi : %s : %s(%s)",(type(collected) == "table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "")
- return collected
- end
- if not collected or #collected == 0 then
- local pn = i < nofparsed and parsed[nofparsed]
- if pn and pn.kind == "finalizer" then
- collected = pn.finalizer(collected)
- report_lpath("% 10i : fi : %s : %s(%s)",(type(collected) == "table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pn.name,pn.arguments or "")
- return collected
- end
- return nil
- end
- end
- return collected
-end
-
-local function normal_apply(list,parsed,nofparsed,order)
- local collected = list
- for i=1,nofparsed do
- local pi = parsed[i]
- local kind = pi.kind
- if kind == "axis" then
- local axis = pi.axis
- if axis ~= "self" then
- collected = apply_axis[axis](collected)
- end
- elseif kind == "nodes" then
- collected = apply_nodes(collected,pi.nodetest,pi.nodes)
- elseif kind == "expression" then
- collected = apply_expression(collected,pi.evaluator,order)
- elseif kind == "finalizer" then
- return pi.finalizer(collected)
- end
- if not collected or #collected == 0 then
- local pf = i < nofparsed and parsed[nofparsed].finalizer
- if pf then
- return pf(collected) -- can be anything
- end
- return nil
- end
- end
- return collected
-end
-
---~ local function applylpath(list,pattern)
---~ -- we avoid an extra call
---~ local parsed = cache[pattern]
---~ if parsed then
---~ lpathcalls = lpathcalls + 1
---~ lpathcached = lpathcached + 1
---~ elseif type(pattern) == "table" then
---~ lpathcalls = lpathcalls + 1
---~ parsed = pattern
---~ else
---~ parsed = lpath(pattern) or pattern
---~ end
---~ if not parsed then
---~ return
---~ end
---~ local nofparsed = #parsed
---~ if nofparsed == 0 then
---~ return -- something is wrong
---~ end
---~ local one = list[1] -- we could have a third argument: isroot and list or list[1] or whatever we like ... todo
---~ if not one then
---~ return -- something is wrong
---~ elseif not trace_lpath then
---~ return normal_apply(list,parsed,nofparsed,one.mi)
---~ elseif trace_lprofile then
---~ return profiled_apply(list,parsed,nofparsed,one.mi)
---~ else
---~ return traced_apply(list,parsed,nofparsed,one.mi)
---~ end
---~ end
-
-local function applylpath(list,pattern)
- if not list then
- return
- end
- local parsed = cache[pattern]
- if parsed then
- lpathcalls = lpathcalls + 1
- lpathcached = lpathcached + 1
- elseif type(pattern) == "table" then
- lpathcalls = lpathcalls + 1
- parsed = pattern
- else
- parsed = lpath(pattern) or pattern
- end
- if not parsed then
- return
- end
- local nofparsed = #parsed
- if nofparsed == 0 then
- return -- something is wrong
- end
- if not trace_lpath then
- return normal_apply ({ list },parsed,nofparsed,list.mi)
- elseif trace_lprofile then
- return profiled_apply({ list },parsed,nofparsed,list.mi)
- else
- return traced_apply ({ list },parsed,nofparsed,list.mi)
- end
-end
-
-xml.applylpath = applylpath -- takes a table as first argment, which is what xml.filter will do
-
---[[ldx--
-<p>This is the main filter function. It returns whatever is asked for.</p>
---ldx]]--
-
-function xml.filter(root,pattern) -- no longer funny attribute handling here
- return applylpath(root,pattern)
-end
-
--- internal (parsed)
-
-expressions.child = function(e,pattern)
- return applylpath(e,pattern) -- todo: cache
-end
-
-expressions.count = function(e,pattern) -- what if pattern == empty or nil
- local collected = applylpath(e,pattern) -- todo: cache
- return pattern and (collected and #collected) or 0
-end
-
--- external
-
--- expressions.oneof = function(s,...)
--- local t = {...}
--- for i=1,#t do
--- if s == t[i] then
--- return true
--- end
--- end
--- return false
--- end
-
-expressions.oneof = function(s,...)
- for i=1,select("#",...) do
- if s == select(i,...) then
- return true
- end
- end
- return false
-end
-
-expressions.error = function(str)
- xml.errorhandler(format("unknown function in lpath expression: %s",tostring(str or "?")))
- return false
-end
-
-expressions.undefined = function(s)
- return s == nil
-end
-
-expressions.quit = function(s)
- if s or s == nil then
- quit_expression = true
- end
- return true
-end
-
-expressions.print = function(...)
- print(...)
- return true
-end
-
-expressions.contains = find
-expressions.find = find
-expressions.upper = upper
-expressions.lower = lower
-expressions.number = tonumber
-expressions.boolean = toboolean
-
-function expressions.contains(str,pattern)
- local t = type(str)
- if t == "string" then
- if find(str,pattern) then
- return true
- end
- elseif t == "table" then
- for i=1,#str do
- local d = str[i]
- if type(d) == "string" and find(d,pattern) then
- return true
- end
- end
- end
- return false
-end
-
--- user interface
-
-local function traverse(root,pattern,handle)
- -- report_lpath("use 'xml.selection' instead for pattern: %s",pattern)
- local collected = applylpath(root,pattern)
- if collected then
- for c=1,#collected do
- local e = collected[c]
- local r = e.__p__
- handle(r,r.dt,e.ni)
- end
- end
-end
-
-local function selection(root,pattern,handle)
- local collected = applylpath(root,pattern)
- if collected then
- if handle then
- for c=1,#collected do
- handle(collected[c])
- end
- else
- return collected
- end
- end
-end
-
-xml.traverse = traverse -- old method, r, d, k
-xml.selection = selection -- new method, simple handle
-
---~ function xml.cachedpatterns()
---~ return cache
---~ end
-
--- generic function finalizer (independant namespace)
-
-local function dofunction(collected,fnc,...)
- if collected then
- local f = functions[fnc]
- if f then
- for c=1,#collected do
- f(collected[c],...)
- end
- else
- report_lpath("unknown function %a",fnc)
- end
- end
-end
-
-finalizers.xml["function"] = dofunction
-finalizers.tex["function"] = dofunction
-
--- functions
-
-expressions.text = function(e,n)
- local rdt = e.__p__.dt
- return rdt and rdt[n] or ""
-end
-
-expressions.name = function(e,n) -- ns + tg
- local found = false
- n = tonumber(n) or 0
- if n == 0 then
- found = type(e) == "table" and e
- elseif n < 0 then
- local d, k = e.__p__.dt, e.ni
- for i=k-1,1,-1 do
- local di = d[i]
- if type(di) == "table" then
- if n == -1 then
- found = di
- break
- else
- n = n + 1
- end
- end
- end
- else
- local d, k = e.__p__.dt, e.ni
- for i=k+1,#d,1 do
- local di = d[i]
- if type(di) == "table" then
- if n == 1 then
- found = di
- break
- else
- n = n - 1
- end
- end
- end
- end
- if found then
- local ns, tg = found.rn or found.ns or "", found.tg
- if ns ~= "" then
- return ns .. ":" .. tg
- else
- return tg
- end
- else
- return ""
- end
-end
-
-expressions.tag = function(e,n) -- only tg
- if not e then
- return ""
- else
- local found = false
- n = tonumber(n) or 0
- if n == 0 then
- found = (type(e) == "table") and e -- seems to fail
- elseif n < 0 then
- local d, k = e.__p__.dt, e.ni
- for i=k-1,1,-1 do
- local di = d[i]
- if type(di) == "table" then
- if n == -1 then
- found = di
- break
- else
- n = n + 1
- end
- end
- end
- else
- local d, k = e.__p__.dt, e.ni
- for i=k+1,#d,1 do
- local di = d[i]
- if type(di) == "table" then
- if n == 1 then
- found = di
- break
- else
- n = n - 1
- end
- end
- end
- end
- return (found and found.tg) or ""
- end
-end
-
---[[ldx--
-<p>Often using an iterators looks nicer in the code than passing handler
-functions. The <l n='lua'/> book describes how to use coroutines for that
-purpose (<url href='http://www.lua.org/pil/9.3.html'/>). This permits
-code like:</p>
-
-<typing>
-for r, d, k in xml.elements(xml.load('text.xml'),"title") do
- print(d[k]) -- old method
-end
-for e in xml.collected(xml.load('text.xml'),"title") do
- print(e) -- new one
-end
-</typing>
---ldx]]--
-
--- local wrap, yield = coroutine.wrap, coroutine.yield
--- local dummy = function() end
---
--- function xml.elements(root,pattern,reverse) -- r, d, k
--- local collected = applylpath(root,pattern)
--- if collected then
--- if reverse then
--- return wrap(function() for c=#collected,1,-1 do
--- local e = collected[c] local r = e.__p__ yield(r,r.dt,e.ni)
--- end end)
--- else
--- return wrap(function() for c=1,#collected do
--- local e = collected[c] local r = e.__p__ yield(r,r.dt,e.ni)
--- end end)
--- end
--- end
--- return wrap(dummy)
--- end
---
--- function xml.collected(root,pattern,reverse) -- e
--- local collected = applylpath(root,pattern)
--- if collected then
--- if reverse then
--- return wrap(function() for c=#collected,1,-1 do yield(collected[c]) end end)
--- else
--- return wrap(function() for c=1,#collected do yield(collected[c]) end end)
--- end
--- end
--- return wrap(dummy)
--- end
-
--- faster:
-
-local dummy = function() end
-
-function xml.elements(root,pattern,reverse) -- r, d, k
- local collected = applylpath(root,pattern)
- if not collected then
- return dummy
- elseif reverse then
- local c = #collected + 1
- return function()
- if c > 1 then
- c = c - 1
- local e = collected[c]
- local r = e.__p__
- return r, r.dt, e.ni
- end
- end
- else
- local n, c = #collected, 0
- return function()
- if c < n then
- c = c + 1
- local e = collected[c]
- local r = e.__p__
- return r, r.dt, e.ni
- end
- end
- end
-end
-
-function xml.collected(root,pattern,reverse) -- e
- local collected = applylpath(root,pattern)
- if not collected then
- return dummy
- elseif reverse then
- local c = #collected + 1
- return function()
- if c > 1 then
- c = c - 1
- return collected[c]
- end
- end
- else
- local n, c = #collected, 0
- return function()
- if c < n then
- c = c + 1
- return collected[c]
- end
- end
- end
-end
-
--- handy
-
-function xml.inspect(collection,pattern)
- pattern = pattern or "."
- for e in xml.collected(collection,pattern or ".") do
- report_lpath("pattern: %s\n\n%s\n",pattern,xml.tostring(e))
- end
-end
-
--- texy (see xfdf):
-
-local function split(e)
- local dt = e.dt
- if dt then
- for i=1,#dt do
- local dti = dt[i]
- if type(dti) == "string" then
- dti = gsub(dti,"^[\n\r]*(.-)[\n\r]*","%1")
- dti = gsub(dti,"[\n\r]+","\n\n")
- dt[i] = dti
- else
- split(dti)
- end
- end
- end
- return e
-end
-
-function xml.finalizers.paragraphs(c)
- for i=1,#c do
- split(c[i])
- end
- return c
-end
+if not modules then modules = { } end modules ['lxml-lpt'] = {
+ version = 1.001,
+ comment = "this module is the basis for the lxml-* ones",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- e.ni is only valid after a filter run
+-- todo: B/C/[get first match]
+
+local concat, remove, insert = table.concat, table.remove, table.insert
+local type, next, tonumber, tostring, setmetatable, load, select = type, next, tonumber, tostring, setmetatable, load, select
+local format, upper, lower, gmatch, gsub, find, rep = string.format, string.upper, string.lower, string.gmatch, string.gsub, string.find, string.rep
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+
+local setmetatableindex = table.setmetatableindex
+local formatters = string.formatters -- no need (yet) as paths are cached anyway
+
+-- beware, this is not xpath ... e.g. position is different (currently) and
+-- we have reverse-sibling as reversed preceding sibling
+
+--[[ldx--
+<p>This module can be used stand alone but also inside <l n='mkiv'/> in
+which case it hooks into the tracker code. Therefore we provide a few
+functions that set the tracers. Here we overload a previously defined
+function.</p>
+<p>If I can get in the mood I will make a variant that is XSLT compliant
+but I wonder if it makes sense.</P>
+--ldx]]--
+
+--[[ldx--
+<p>Expecially the lpath code is experimental, we will support some of xpath, but
+only things that make sense for us; as compensation it is possible to hook in your
+own functions. Apart from preprocessing content for <l n='context'/> we also need
+this module for process management, like handling <l n='ctx'/> and <l n='rlx'/>
+files.</p>
+
+<typing>
+a/b/c /*/c
+a/b/c/first() a/b/c/last() a/b/c/index(n) a/b/c/index(-n)
+a/b/c/text() a/b/c/text(1) a/b/c/text(-1) a/b/c/text(n)
+</typing>
+--ldx]]--
+
+local trace_lpath = false if trackers then trackers.register("xml.path", function(v) trace_lpath = v end) end
+local trace_lparse = false if trackers then trackers.register("xml.parse", function(v) trace_lparse = v end) end
+local trace_lprofile = false if trackers then trackers.register("xml.profile", function(v) trace_lpath = v trace_lparse = v trace_lprofile = v end) end
+
+local report_lpath = logs.reporter("xml","lpath")
+
+--[[ldx--
+<p>We've now arrived at an interesting part: accessing the tree using a subset
+of <l n='xpath'/> and since we're not compatible we call it <l n='lpath'/>. We
+will explain more about its usage in other documents.</p>
+--ldx]]--
+
+local xml = xml
+
+local lpathcalls = 0 function xml.lpathcalls () return lpathcalls end
+local lpathcached = 0 function xml.lpathcached() return lpathcached end
+
+xml.functions = xml.functions or { } -- internal
+local functions = xml.functions
+
+xml.expressions = xml.expressions or { } -- in expressions
+local expressions = xml.expressions
+
+xml.finalizers = xml.finalizers or { } -- fast do-with ... (with return value other than collection)
+local finalizers = xml.finalizers
+
+xml.specialhandler = xml.specialhandler or { }
+local specialhandler = xml.specialhandler
+
+lpegpatterns.xml = lpegpatterns.xml or { }
+local xmlpatterns = lpegpatterns.xml
+
+finalizers.xml = finalizers.xml or { }
+finalizers.tex = finalizers.tex or { }
+
+local function fallback (t, name)
+ local fn = finalizers[name]
+ if fn then
+ t[name] = fn
+ else
+ report_lpath("unknown sub finalizer %a",name)
+ fn = function() end
+ end
+ return fn
+end
+
+setmetatableindex(finalizers.xml, fallback)
+setmetatableindex(finalizers.tex, fallback)
+
+xml.defaultprotocol = "xml"
+
+-- as xsl does not follow xpath completely here we will also
+-- be more liberal especially with regards to the use of | and
+-- the rootpath:
+--
+-- test : all 'test' under current
+-- /test : 'test' relative to current
+-- a|b|c : set of names
+-- (a|b|c) : idem
+-- ! : not
+--
+-- after all, we're not doing transformations but filtering. in
+-- addition we provide filter functions (last bit)
+--
+-- todo: optimizer
+--
+-- .. : parent
+-- * : all kids
+-- / : anchor here
+-- // : /**/
+-- ** : all in between
+--
+-- so far we had (more practical as we don't transform)
+--
+-- {/test} : kids 'test' under current node
+-- {test} : any kid with tag 'test'
+-- {//test} : same as above
+
+-- evaluator (needs to be redone, for the moment copied)
+
+-- todo: apply_axis(list,notable) and collection vs single
+
+local apply_axis = { }
+
+apply_axis['root'] = function(list)
+ local collected = { }
+ for l=1,#list do
+ local ll = list[l]
+ local rt = ll
+ while ll do
+ ll = ll.__p__
+ if ll then
+ rt = ll
+ end
+ end
+ collected[l] = rt
+ end
+ return collected
+end
+
+apply_axis['self'] = function(list)
+--~ local collected = { }
+--~ for l=1,#list do
+--~ collected[l] = list[l]
+--~ end
+--~ return collected
+ return list
+end
+
+apply_axis['child'] = function(list)
+ local collected, c = { }, 0
+ for l=1,#list do
+ local ll = list[l]
+ local dt = ll.dt
+ if dt then -- weird that this is needed
+ local en = 0
+ for k=1,#dt do
+ local dk = dt[k]
+ if dk.tg then
+ c = c + 1
+ collected[c] = dk
+ dk.ni = k -- refresh
+ en = en + 1
+ dk.ei = en
+ end
+ end
+ ll.en = en
+ end
+ end
+ return collected
+end
+
+local function collect(list,collected,c)
+ local dt = list.dt
+ if dt then
+ local en = 0
+ for k=1,#dt do
+ local dk = dt[k]
+ if dk.tg then
+ c = c + 1
+ collected[c] = dk
+ dk.ni = k -- refresh
+ en = en + 1
+ dk.ei = en
+ c = collect(dk,collected,c)
+ end
+ end
+ list.en = en
+ end
+ return c
+end
+
+apply_axis['descendant'] = function(list)
+ local collected, c = { }, 0
+ for l=1,#list do
+ c = collect(list[l],collected,c)
+ end
+ return collected
+end
+
+local function collect(list,collected,c)
+ local dt = list.dt
+ if dt then
+ local en = 0
+ for k=1,#dt do
+ local dk = dt[k]
+ if dk.tg then
+ c = c + 1
+ collected[c] = dk
+ dk.ni = k -- refresh
+ en = en + 1
+ dk.ei = en
+ c = collect(dk,collected,c)
+ end
+ end
+ list.en = en
+ end
+ return c
+end
+apply_axis['descendant-or-self'] = function(list)
+ local collected, c = { }, 0
+ for l=1,#list do
+ local ll = list[l]
+ if ll.special ~= true then -- catch double root
+ c = c + 1
+ collected[c] = ll
+ end
+ c = collect(ll,collected,c)
+ end
+ return collected
+end
+
+apply_axis['ancestor'] = function(list)
+ local collected, c = { }, 0
+ for l=1,#list do
+ local ll = list[l]
+ while ll do
+ ll = ll.__p__
+ if ll then
+ c = c + 1
+ collected[c] = ll
+ end
+ end
+ end
+ return collected
+end
+
+apply_axis['ancestor-or-self'] = function(list)
+ local collected, c = { }, 0
+ for l=1,#list do
+ local ll = list[l]
+ c = c + 1
+ collected[c] = ll
+ while ll do
+ ll = ll.__p__
+ if ll then
+ c = c + 1
+ collected[c] = ll
+ end
+ end
+ end
+ return collected
+end
+
+apply_axis['parent'] = function(list)
+ local collected, c = { }, 0
+ for l=1,#list do
+ local pl = list[l].__p__
+ if pl then
+ c = c + 1
+ collected[c] = pl
+ end
+ end
+ return collected
+end
+
+apply_axis['attribute'] = function(list)
+ return { }
+end
+
+apply_axis['namespace'] = function(list)
+ return { }
+end
+
+apply_axis['following'] = function(list) -- incomplete
+--~ local collected, c = { }, 0
+--~ for l=1,#list do
+--~ local ll = list[l]
+--~ local p = ll.__p__
+--~ local d = p.dt
+--~ for i=ll.ni+1,#d do
+--~ local di = d[i]
+--~ if type(di) == "table" then
+--~ c = c + 1
+--~ collected[c] = di
+--~ break
+--~ end
+--~ end
+--~ end
+--~ return collected
+ return { }
+end
+
+apply_axis['preceding'] = function(list) -- incomplete
+--~ local collected, c = { }, 0
+--~ for l=1,#list do
+--~ local ll = list[l]
+--~ local p = ll.__p__
+--~ local d = p.dt
+--~ for i=ll.ni-1,1,-1 do
+--~ local di = d[i]
+--~ if type(di) == "table" then
+--~ c = c + 1
+--~ collected[c] = di
+--~ break
+--~ end
+--~ end
+--~ end
+--~ return collected
+ return { }
+end
+
+apply_axis['following-sibling'] = function(list)
+ local collected, c = { }, 0
+ for l=1,#list do
+ local ll = list[l]
+ local p = ll.__p__
+ local d = p.dt
+ for i=ll.ni+1,#d do
+ local di = d[i]
+ if type(di) == "table" then
+ c = c + 1
+ collected[c] = di
+ end
+ end
+ end
+ return collected
+end
+
+apply_axis['preceding-sibling'] = function(list)
+ local collected, c = { }, 0
+ for l=1,#list do
+ local ll = list[l]
+ local p = ll.__p__
+ local d = p.dt
+ for i=1,ll.ni-1 do
+ local di = d[i]
+ if type(di) == "table" then
+ c = c + 1
+ collected[c] = di
+ end
+ end
+ end
+ return collected
+end
+
+apply_axis['reverse-sibling'] = function(list) -- reverse preceding
+ local collected, c = { }, 0
+ for l=1,#list do
+ local ll = list[l]
+ local p = ll.__p__
+ local d = p.dt
+ for i=ll.ni-1,1,-1 do
+ local di = d[i]
+ if type(di) == "table" then
+ c = c + 1
+ collected[c] = di
+ end
+ end
+ end
+ return collected
+end
+
+apply_axis['auto-descendant-or-self'] = apply_axis['descendant-or-self']
+apply_axis['auto-descendant'] = apply_axis['descendant']
+apply_axis['auto-child'] = apply_axis['child']
+apply_axis['auto-self'] = apply_axis['self']
+apply_axis['initial-child'] = apply_axis['child']
+
+local function apply_nodes(list,directive,nodes)
+ -- todo: nodes[1] etc ... negated node name in set ... when needed
+ -- ... currently ignored
+ local maxn = #nodes
+ if maxn == 3 then --optimized loop
+ local nns, ntg = nodes[2], nodes[3]
+ if not nns and not ntg then -- wildcard
+ if directive then
+ return list
+ else
+ return { }
+ end
+ else
+ local collected, c, m, p = { }, 0, 0, nil
+ if not nns then -- only check tag
+ for l=1,#list do
+ local ll = list[l]
+ local ltg = ll.tg
+ if ltg then
+ if directive then
+ if ntg == ltg then
+ local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
+ c = c + 1
+ collected[c], ll.mi = ll, m
+ end
+ elseif ntg ~= ltg then
+ local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
+ c = c + 1
+ collected[c], ll.mi = ll, m
+ end
+ end
+ end
+ elseif not ntg then -- only check namespace
+ for l=1,#list do
+ local ll = list[l]
+ local lns = ll.rn or ll.ns
+ if lns then
+ if directive then
+ if lns == nns then
+ local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
+ c = c + 1
+ collected[c], ll.mi = ll, m
+ end
+ elseif lns ~= nns then
+ local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
+ c = c + 1
+ collected[c], ll.mi = ll, m
+ end
+ end
+ end
+ else -- check both
+ for l=1,#list do
+ local ll = list[l]
+ local ltg = ll.tg
+ if ltg then
+ local lns = ll.rn or ll.ns
+ local ok = ltg == ntg and lns == nns
+ if directive then
+ if ok then
+ local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
+ c = c + 1
+ collected[c], ll.mi = ll, m
+ end
+ elseif not ok then
+ local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
+ c = c + 1
+ collected[c], ll.mi = ll, m
+ end
+ end
+ end
+ end
+ return collected
+ end
+ else
+ local collected, c, m, p = { }, 0, 0, nil
+ for l=1,#list do
+ local ll = list[l]
+ local ltg = ll.tg
+ if ltg then
+ local lns = ll.rn or ll.ns
+ local ok = false
+ for n=1,maxn,3 do
+ local nns, ntg = nodes[n+1], nodes[n+2]
+ ok = (not ntg or ltg == ntg) and (not nns or lns == nns)
+ if ok then
+ break
+ end
+ end
+ if directive then
+ if ok then
+ local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
+ c = c + 1
+ collected[c], ll.mi = ll, m
+ end
+ elseif not ok then
+ local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end
+ c = c + 1
+ collected[c], ll.mi = ll, m
+ end
+ end
+ end
+ return collected
+ end
+end
+
+local quit_expression = false
+
+local function apply_expression(list,expression,order)
+ local collected, c = { }, 0
+ quit_expression = false
+ for l=1,#list do
+ local ll = list[l]
+ if expression(list,ll,l,order) then -- nasty, order alleen valid als n=1
+ c = c + 1
+ collected[c] = ll
+ end
+ if quit_expression then
+ break
+ end
+ end
+ return collected
+end
+
+local P, V, C, Cs, Cc, Ct, R, S, Cg, Cb = lpeg.P, lpeg.V, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.Ct, lpeg.R, lpeg.S, lpeg.Cg, lpeg.Cb
+
+local spaces = S(" \n\r\t\f")^0
+local lp_space = S(" \n\r\t\f")
+local lp_any = P(1)
+local lp_noequal = P("!=") / "~=" + P("<=") + P(">=") + P("==")
+local lp_doequal = P("=") / "=="
+local lp_or = P("|") / " or "
+local lp_and = P("&") / " and "
+
+local lp_builtin = P (
+ P("text") / "(ll.dt[1] or '')" + -- fragile
+ P("content") / "ll.dt" +
+ -- P("name") / "(ll.ns~='' and ll.ns..':'..ll.tg)" +
+ P("name") / "((ll.ns~='' and ll.ns..':'..ll.tg) or ll.tg)" +
+ P("tag") / "ll.tg" +
+ P("position") / "l" + -- is element in finalizer
+ P("firstindex") / "1" +
+ P("lastindex") / "(#ll.__p__.dt or 1)" +
+ P("firstelement") / "1" +
+ P("lastelement") / "(ll.__p__.en or 1)" +
+ P("first") / "1" +
+ P("last") / "#list" +
+ P("rootposition") / "order" +
+ P("order") / "order" +
+ P("element") / "(ll.ei or 1)" +
+ P("index") / "(ll.ni or 1)" +
+ P("match") / "(ll.mi or 1)" +
+ -- P("namespace") / "ll.ns" +
+ P("ns") / "ll.ns"
+ ) * ((spaces * P("(") * spaces * P(")"))/"")
+
+-- for the moment we keep namespaces with attributes
+
+local lp_attribute = (P("@") + P("attribute::")) / "" * Cc("(ll.at and ll.at['") * ((R("az","AZ") + S("-_:"))^1) * Cc("'])")
+
+-- lp_fastpos_p = (P("+")^0 * R("09")^1 * P(-1)) / function(s) return "l==" .. s end
+-- lp_fastpos_n = (P("-") * R("09")^1 * P(-1)) / function(s) return "(" .. s .. "<0 and (#list+".. s .. "==l))" end
+
+lp_fastpos_p = P("+")^0 * R("09")^1 * P(-1) / "l==%0"
+lp_fastpos_n = P("-") * R("09")^1 * P(-1) / "(%0<0 and (#list+%0==l))"
+
+local lp_fastpos = lp_fastpos_n + lp_fastpos_p
+
+local lp_reserved = C("and") + C("or") + C("not") + C("div") + C("mod") + C("true") + C("false")
+
+-- local lp_lua_function = C(R("az","AZ","__")^1 * (P(".") * R("az","AZ","__")^1)^1) * ("(") / function(t) -- todo: better . handling
+-- return t .. "("
+-- end
+
+-- local lp_lua_function = (R("az","AZ","__")^1 * (P(".") * R("az","AZ","__")^1)^1) * ("(") / "%0("
+local lp_lua_function = Cs((R("az","AZ","__")^1 * (P(".") * R("az","AZ","__")^1)^1) * ("(")) / "%0"
+
+local lp_function = C(R("az","AZ","__")^1) * P("(") / function(t) -- todo: better . handling
+ if expressions[t] then
+ return "expr." .. t .. "("
+ else
+ return "expr.error("
+ end
+end
+
+local lparent = P("(")
+local rparent = P(")")
+local noparent = 1 - (lparent+rparent)
+local nested = P{lparent * (noparent + V(1))^0 * rparent}
+local value = P(lparent * C((noparent + nested)^0) * rparent) -- P{"("*C(((1-S("()"))+V(1))^0)*")"}
+
+local lp_child = Cc("expr.child(ll,'") * R("az","AZ","--","__")^1 * Cc("')")
+local lp_number = S("+-") * R("09")^1
+local lp_string = Cc("'") * R("az","AZ","--","__")^1 * Cc("'")
+local lp_content = (P("'") * (1-P("'"))^0 * P("'") + P('"') * (1-P('"'))^0 * P('"'))
+
+local cleaner
+
+local lp_special = (C(P("name")+P("text")+P("tag")+P("count")+P("child"))) * value / function(t,s)
+ if expressions[t] then
+ s = s and s ~= "" and lpegmatch(cleaner,s)
+ if s and s ~= "" then
+ return "expr." .. t .. "(ll," .. s ..")"
+ else
+ return "expr." .. t .. "(ll)"
+ end
+ else
+ return "expr.error(" .. t .. ")"
+ end
+end
+
+local content =
+ lp_builtin +
+ lp_attribute +
+ lp_special +
+ lp_noequal + lp_doequal +
+ lp_or + lp_and +
+ lp_reserved +
+ lp_lua_function + lp_function +
+ lp_content + -- too fragile
+ lp_child +
+ lp_any
+
+local converter = Cs (
+ lp_fastpos + (P { lparent * (V(1))^0 * rparent + content } )^0
+)
+
+cleaner = Cs ( (
+ -- lp_fastpos +
+ lp_reserved +
+ lp_number +
+ lp_string +
+1 )^1 )
+
+local template_e = [[
+ local expr = xml.expressions
+ return function(list,ll,l,order)
+ return %s
+ end
+]]
+
+local template_f_y = [[
+ local finalizer = xml.finalizers['%s']['%s']
+ return function(collection)
+ return finalizer(collection,%s)
+ end
+]]
+
+local template_f_n = [[
+ return xml.finalizers['%s']['%s']
+]]
+
+--
+
+local register_self = { kind = "axis", axis = "self" } -- , apply = apply_axis["self"] }
+local register_parent = { kind = "axis", axis = "parent" } -- , apply = apply_axis["parent"] }
+local register_descendant = { kind = "axis", axis = "descendant" } -- , apply = apply_axis["descendant"] }
+local register_child = { kind = "axis", axis = "child" } -- , apply = apply_axis["child"] }
+local register_descendant_or_self = { kind = "axis", axis = "descendant-or-self" } -- , apply = apply_axis["descendant-or-self"] }
+local register_root = { kind = "axis", axis = "root" } -- , apply = apply_axis["root"] }
+local register_ancestor = { kind = "axis", axis = "ancestor" } -- , apply = apply_axis["ancestor"] }
+local register_ancestor_or_self = { kind = "axis", axis = "ancestor-or-self" } -- , apply = apply_axis["ancestor-or-self"] }
+local register_attribute = { kind = "axis", axis = "attribute" } -- , apply = apply_axis["attribute"] }
+local register_namespace = { kind = "axis", axis = "namespace" } -- , apply = apply_axis["namespace"] }
+local register_following = { kind = "axis", axis = "following" } -- , apply = apply_axis["following"] }
+local register_following_sibling = { kind = "axis", axis = "following-sibling" } -- , apply = apply_axis["following-sibling"] }
+local register_preceding = { kind = "axis", axis = "preceding" } -- , apply = apply_axis["preceding"] }
+local register_preceding_sibling = { kind = "axis", axis = "preceding-sibling" } -- , apply = apply_axis["preceding-sibling"] }
+local register_reverse_sibling = { kind = "axis", axis = "reverse-sibling" } -- , apply = apply_axis["reverse-sibling"] }
+
+local register_auto_descendant_or_self = { kind = "axis", axis = "auto-descendant-or-self" } -- , apply = apply_axis["auto-descendant-or-self"] }
+local register_auto_descendant = { kind = "axis", axis = "auto-descendant" } -- , apply = apply_axis["auto-descendant"] }
+local register_auto_self = { kind = "axis", axis = "auto-self" } -- , apply = apply_axis["auto-self"] }
+local register_auto_child = { kind = "axis", axis = "auto-child" } -- , apply = apply_axis["auto-child"] }
+
+local register_initial_child = { kind = "axis", axis = "initial-child" } -- , apply = apply_axis["initial-child"] }
+
+local register_all_nodes = { kind = "nodes", nodetest = true, nodes = { true, false, false } }
+
+local skip = { }
+
+local function errorrunner_e(str,cnv)
+ if not skip[str] then
+ report_lpath("error in expression: %s => %s",str,cnv)
+ skip[str] = cnv or str
+ end
+ return false
+end
+
+local function errorrunner_f(str,arg)
+ report_lpath("error in finalizer: %s(%s)",str,arg or "")
+ return false
+end
+
+local function register_nodes(nodetest,nodes)
+ return { kind = "nodes", nodetest = nodetest, nodes = nodes }
+end
+
+local function register_expression(expression)
+ local converted = lpegmatch(converter,expression)
+ local runner = load(format(template_e,converted))
+ runner = (runner and runner()) or function() errorrunner_e(expression,converted) end
+ return { kind = "expression", expression = expression, converted = converted, evaluator = runner }
+end
+
+local function register_finalizer(protocol,name,arguments)
+ local runner
+ if arguments and arguments ~= "" then
+ runner = load(format(template_f_y,protocol or xml.defaultprotocol,name,arguments))
+ else
+ runner = load(format(template_f_n,protocol or xml.defaultprotocol,name))
+ end
+ runner = (runner and runner()) or function() errorrunner_f(name,arguments) end
+ return { kind = "finalizer", name = name, arguments = arguments, finalizer = runner }
+end
+
+local expression = P { "ex",
+ ex = "[" * C((V("sq") + V("dq") + (1 - S("[]")) + V("ex"))^0) * "]",
+ sq = "'" * (1 - S("'"))^0 * "'",
+ dq = '"' * (1 - S('"'))^0 * '"',
+}
+
+local arguments = P { "ar",
+ ar = "(" * Cs((V("sq") + V("dq") + V("nq") + P(1-P(")")))^0) * ")",
+ nq = ((1 - S("),'\""))^1) / function(s) return format("%q",s) end,
+ sq = P("'") * (1 - P("'"))^0 * P("'"),
+ dq = P('"') * (1 - P('"'))^0 * P('"'),
+}
+
+-- todo: better arg parser
+
+local function register_error(str)
+ return { kind = "error", error = format("unparsed: %s",str) }
+end
+
+-- there is a difference in * and /*/ and so we need to catch a few special cases
+
+local special_1 = P("*") * Cc(register_auto_descendant) * Cc(register_all_nodes) -- last one not needed
+local special_2 = P("/") * Cc(register_auto_self)
+local special_3 = P("") * Cc(register_auto_self)
+
+local no_nextcolon = P(-1) + #(1-P(":")) -- newer lpeg needs the P(-1)
+local no_nextlparent = P(-1) + #(1-P("(")) -- newer lpeg needs the P(-1)
+
+local pathparser = Ct { "patterns", -- can be made a bit faster by moving some patterns outside
+
+ patterns = spaces * V("protocol") * spaces * (
+ ( V("special") * spaces * P(-1) ) +
+ ( V("initial") * spaces * V("step") * spaces * (P("/") * spaces * V("step") * spaces)^0 )
+ ),
+
+ protocol = Cg(V("letters"),"protocol") * P("://") + Cg(Cc(nil),"protocol"),
+
+ -- the / is needed for // as descendant or self is somewhat special
+ -- step = (V("shortcuts") + V("axis") * spaces * V("nodes")^0 + V("error")) * spaces * V("expressions")^0 * spaces * V("finalizer")^0,
+ step = ((V("shortcuts") + P("/") + V("axis")) * spaces * V("nodes")^0 + V("error")) * spaces * V("expressions")^0 * spaces * V("finalizer")^0,
+
+ axis = V("descendant") + V("child") + V("parent") + V("self") + V("root") + V("ancestor") +
+ V("descendant_or_self") + V("following_sibling") + V("following") +
+ V("reverse_sibling") + V("preceding_sibling") + V("preceding") + V("ancestor_or_self") +
+ #(1-P(-1)) * Cc(register_auto_child),
+
+ special = special_1 + special_2 + special_3,
+
+ initial = (P("/") * spaces * Cc(register_initial_child))^-1,
+
+ error = (P(1)^1) / register_error,
+
+ shortcuts_a = V("s_descendant_or_self") + V("s_descendant") + V("s_child") + V("s_parent") + V("s_self") + V("s_root") + V("s_ancestor"),
+
+ shortcuts = V("shortcuts_a") * (spaces * "/" * spaces * V("shortcuts_a"))^0,
+
+ s_descendant_or_self = (P("***/") + P("/")) * Cc(register_descendant_or_self), --- *** is a bonus
+ s_descendant = P("**") * Cc(register_descendant),
+ s_child = P("*") * no_nextcolon * Cc(register_child ),
+ s_parent = P("..") * Cc(register_parent ),
+ s_self = P("." ) * Cc(register_self ),
+ s_root = P("^^") * Cc(register_root ),
+ s_ancestor = P("^") * Cc(register_ancestor ),
+
+ descendant = P("descendant::") * Cc(register_descendant ),
+ child = P("child::") * Cc(register_child ),
+ parent = P("parent::") * Cc(register_parent ),
+ self = P("self::") * Cc(register_self ),
+ root = P('root::') * Cc(register_root ),
+ ancestor = P('ancestor::') * Cc(register_ancestor ),
+ descendant_or_self = P('descendant-or-self::') * Cc(register_descendant_or_self ),
+ ancestor_or_self = P('ancestor-or-self::') * Cc(register_ancestor_or_self ),
+ -- attribute = P('attribute::') * Cc(register_attribute ),
+ -- namespace = P('namespace::') * Cc(register_namespace ),
+ following = P('following::') * Cc(register_following ),
+ following_sibling = P('following-sibling::') * Cc(register_following_sibling ),
+ preceding = P('preceding::') * Cc(register_preceding ),
+ preceding_sibling = P('preceding-sibling::') * Cc(register_preceding_sibling ),
+ reverse_sibling = P('reverse-sibling::') * Cc(register_reverse_sibling ),
+
+ nodes = (V("nodefunction") * spaces * P("(") * V("nodeset") * P(")") + V("nodetest") * V("nodeset")) / register_nodes,
+
+ expressions = expression / register_expression,
+
+ letters = R("az")^1,
+ name = (1-S("/[]()|:*!"))^1, -- make inline
+ negate = P("!") * Cc(false),
+
+ nodefunction = V("negate") + P("not") * Cc(false) + Cc(true),
+ nodetest = V("negate") + Cc(true),
+ nodename = (V("negate") + Cc(true)) * spaces * ((V("wildnodename") * P(":") * V("wildnodename")) + (Cc(false) * V("wildnodename"))),
+ wildnodename = (C(V("name")) + P("*") * Cc(false)) * no_nextlparent,
+ nodeset = spaces * Ct(V("nodename") * (spaces * P("|") * spaces * V("nodename"))^0) * spaces,
+
+ finalizer = (Cb("protocol") * P("/")^-1 * C(V("name")) * arguments * P(-1)) / register_finalizer,
+
+}
+
+xmlpatterns.pathparser = pathparser
+
+local cache = { }
+
+local function nodesettostring(set,nodetest)
+ local t = { }
+ for i=1,#set,3 do
+ local directive, ns, tg = set[i], set[i+1], set[i+2]
+ if not ns or ns == "" then ns = "*" end
+ if not tg or tg == "" then tg = "*" end
+ tg = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg)
+ t[i] = (directive and tg) or format("not(%s)",tg)
+ end
+ if nodetest == false then
+ return format("not(%s)",concat(t,"|"))
+ else
+ return concat(t,"|")
+ end
+end
+
+local function tagstostring(list)
+ if #list == 0 then
+ return "no elements"
+ else
+ local t = { }
+ for i=1, #list do
+ local li = list[i]
+ local ns, tg = li.ns, li.tg
+ if not ns or ns == "" then ns = "*" end
+ if not tg or tg == "" then tg = "*" end
+ t[i] = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg)
+ end
+ return concat(t," ")
+ end
+end
+
+xml.nodesettostring = nodesettostring
+
+local lpath -- we have a harmless kind of circular reference
+
+local lshowoptions = { functions = false }
+
+local function lshow(parsed)
+ if type(parsed) == "string" then
+ parsed = lpath(parsed)
+ end
+ report_lpath("%s://%s => %s",parsed.protocol or xml.defaultprotocol,parsed.pattern,
+ table.serialize(parsed,false,lshowoptions))
+end
+
+xml.lshow = lshow
+
+local function add_comment(p,str)
+ local pc = p.comment
+ if not pc then
+ p.comment = { str }
+ else
+ pc[#pc+1] = str
+ end
+end
+
+lpath = function (pattern) -- the gain of caching is rather minimal
+ lpathcalls = lpathcalls + 1
+ if type(pattern) == "table" then
+ return pattern
+ else
+ local parsed = cache[pattern]
+ if parsed then
+ lpathcached = lpathcached + 1
+ else
+ parsed = lpegmatch(pathparser,pattern)
+ if parsed then
+ parsed.pattern = pattern
+ local np = #parsed
+ if np == 0 then
+ parsed = { pattern = pattern, register_self, state = "parsing error" }
+ report_lpath("parsing error in pattern: %s",pattern)
+ lshow(parsed)
+ else
+ -- we could have done this with a more complex parser but this
+ -- is cleaner
+ local pi = parsed[1]
+ if pi.axis == "auto-child" then
+ if false then
+ add_comment(parsed, "auto-child replaced by auto-descendant-or-self")
+ parsed[1] = register_auto_descendant_or_self
+ else
+ add_comment(parsed, "auto-child replaced by auto-descendant")
+ parsed[1] = register_auto_descendant
+ end
+ elseif pi.axis == "initial-child" and np > 1 and parsed[2].axis then
+ add_comment(parsed, "initial-child removed") -- we could also make it a auto-self
+ remove(parsed,1)
+ end
+ local np = #parsed -- can have changed
+ if np > 1 then
+ local pnp = parsed[np]
+ if pnp.kind == "nodes" and pnp.nodetest == true then
+ local nodes = pnp.nodes
+ if nodes[1] == true and nodes[2] == false and nodes[3] == false then
+ add_comment(parsed, "redundant final wildcard filter removed")
+ remove(parsed,np)
+ end
+ end
+ end
+ end
+ else
+ parsed = { pattern = pattern }
+ end
+ cache[pattern] = parsed
+ if trace_lparse and not trace_lprofile then
+ lshow(parsed)
+ end
+ end
+ return parsed
+ end
+end
+
+xml.lpath = lpath
+
+-- we can move all calls inline and then merge the trace back
+-- technically we can combine axis and the next nodes which is
+-- what we did before but this a bit cleaner (but slower too)
+-- but interesting is that it's not that much faster when we
+-- go inline
+--
+-- beware: we need to return a collection even when we filter
+-- else the (simple) cache gets messed up
+
+-- caching found lookups saves not that much (max .1 sec on a 8 sec run)
+-- and it also messes up finalizers
+
+-- watch out: when there is a finalizer, it's always called as there
+-- can be cases that a finalizer returns (or does) something in case
+-- there is no match; an example of this is count()
+
+local profiled = { } xml.profiled = profiled
+
+local function profiled_apply(list,parsed,nofparsed,order)
+ local p = profiled[parsed.pattern]
+ if p then
+ p.tested = p.tested + 1
+ else
+ p = { tested = 1, matched = 0, finalized = 0 }
+ profiled[parsed.pattern] = p
+ end
+ local collected = list
+ for i=1,nofparsed do
+ local pi = parsed[i]
+ local kind = pi.kind
+ if kind == "axis" then
+ collected = apply_axis[pi.axis](collected)
+ elseif kind == "nodes" then
+ collected = apply_nodes(collected,pi.nodetest,pi.nodes)
+ elseif kind == "expression" then
+ collected = apply_expression(collected,pi.evaluator,order)
+ elseif kind == "finalizer" then
+ collected = pi.finalizer(collected) -- no check on # here
+ p.matched = p.matched + 1
+ p.finalized = p.finalized + 1
+ return collected
+ end
+ if not collected or #collected == 0 then
+ local pn = i < nofparsed and parsed[nofparsed]
+ if pn and pn.kind == "finalizer" then
+ collected = pn.finalizer(collected)
+ p.finalized = p.finalized + 1
+ return collected
+ end
+ return nil
+ end
+ end
+ if collected then
+ p.matched = p.matched + 1
+ end
+ return collected
+end
+
+local function traced_apply(list,parsed,nofparsed,order)
+ if trace_lparse then
+ lshow(parsed)
+ end
+ report_lpath("collecting: %s",parsed.pattern)
+ report_lpath("root tags : %s",tagstostring(list))
+ report_lpath("order : %s",order or "unset")
+ local collected = list
+ for i=1,nofparsed do
+ local pi = parsed[i]
+ local kind = pi.kind
+ if kind == "axis" then
+ collected = apply_axis[pi.axis](collected)
+ report_lpath("% 10i : ax : %s",(collected and #collected) or 0,pi.axis)
+ elseif kind == "nodes" then
+ collected = apply_nodes(collected,pi.nodetest,pi.nodes)
+ report_lpath("% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest))
+ elseif kind == "expression" then
+ collected = apply_expression(collected,pi.evaluator,order)
+ report_lpath("% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted)
+ elseif kind == "finalizer" then
+ collected = pi.finalizer(collected)
+ report_lpath("% 10i : fi : %s : %s(%s)",(type(collected) == "table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "")
+ return collected
+ end
+ if not collected or #collected == 0 then
+ local pn = i < nofparsed and parsed[nofparsed]
+ if pn and pn.kind == "finalizer" then
+ collected = pn.finalizer(collected)
+ report_lpath("% 10i : fi : %s : %s(%s)",(type(collected) == "table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pn.name,pn.arguments or "")
+ return collected
+ end
+ return nil
+ end
+ end
+ return collected
+end
+
+local function normal_apply(list,parsed,nofparsed,order)
+ local collected = list
+ for i=1,nofparsed do
+ local pi = parsed[i]
+ local kind = pi.kind
+ if kind == "axis" then
+ local axis = pi.axis
+ if axis ~= "self" then
+ collected = apply_axis[axis](collected)
+ end
+ elseif kind == "nodes" then
+ collected = apply_nodes(collected,pi.nodetest,pi.nodes)
+ elseif kind == "expression" then
+ collected = apply_expression(collected,pi.evaluator,order)
+ elseif kind == "finalizer" then
+ return pi.finalizer(collected)
+ end
+ if not collected or #collected == 0 then
+ local pf = i < nofparsed and parsed[nofparsed].finalizer
+ if pf then
+ return pf(collected) -- can be anything
+ end
+ return nil
+ end
+ end
+ return collected
+end
+
+--~ local function applylpath(list,pattern)
+--~ -- we avoid an extra call
+--~ local parsed = cache[pattern]
+--~ if parsed then
+--~ lpathcalls = lpathcalls + 1
+--~ lpathcached = lpathcached + 1
+--~ elseif type(pattern) == "table" then
+--~ lpathcalls = lpathcalls + 1
+--~ parsed = pattern
+--~ else
+--~ parsed = lpath(pattern) or pattern
+--~ end
+--~ if not parsed then
+--~ return
+--~ end
+--~ local nofparsed = #parsed
+--~ if nofparsed == 0 then
+--~ return -- something is wrong
+--~ end
+--~ local one = list[1] -- we could have a third argument: isroot and list or list[1] or whatever we like ... todo
+--~ if not one then
+--~ return -- something is wrong
+--~ elseif not trace_lpath then
+--~ return normal_apply(list,parsed,nofparsed,one.mi)
+--~ elseif trace_lprofile then
+--~ return profiled_apply(list,parsed,nofparsed,one.mi)
+--~ else
+--~ return traced_apply(list,parsed,nofparsed,one.mi)
+--~ end
+--~ end
+
+local function applylpath(list,pattern)
+ if not list then
+ return
+ end
+ local parsed = cache[pattern]
+ if parsed then
+ lpathcalls = lpathcalls + 1
+ lpathcached = lpathcached + 1
+ elseif type(pattern) == "table" then
+ lpathcalls = lpathcalls + 1
+ parsed = pattern
+ else
+ parsed = lpath(pattern) or pattern
+ end
+ if not parsed then
+ return
+ end
+ local nofparsed = #parsed
+ if nofparsed == 0 then
+ return -- something is wrong
+ end
+ if not trace_lpath then
+ return normal_apply ({ list },parsed,nofparsed,list.mi)
+ elseif trace_lprofile then
+ return profiled_apply({ list },parsed,nofparsed,list.mi)
+ else
+ return traced_apply ({ list },parsed,nofparsed,list.mi)
+ end
+end
+
+xml.applylpath = applylpath -- takes a table as first argment, which is what xml.filter will do
+
+--[[ldx--
+<p>This is the main filter function. It returns whatever is asked for.</p>
+--ldx]]--
+
+function xml.filter(root,pattern) -- no longer funny attribute handling here
+ return applylpath(root,pattern)
+end
+
+-- internal (parsed)
+
+expressions.child = function(e,pattern)
+ return applylpath(e,pattern) -- todo: cache
+end
+
+expressions.count = function(e,pattern) -- what if pattern == empty or nil
+ local collected = applylpath(e,pattern) -- todo: cache
+ return pattern and (collected and #collected) or 0
+end
+
+-- external
+
+-- expressions.oneof = function(s,...)
+-- local t = {...}
+-- for i=1,#t do
+-- if s == t[i] then
+-- return true
+-- end
+-- end
+-- return false
+-- end
+
+expressions.oneof = function(s,...)
+ for i=1,select("#",...) do
+ if s == select(i,...) then
+ return true
+ end
+ end
+ return false
+end
+
+expressions.error = function(str)
+ xml.errorhandler(format("unknown function in lpath expression: %s",tostring(str or "?")))
+ return false
+end
+
+expressions.undefined = function(s)
+ return s == nil
+end
+
+expressions.quit = function(s)
+ if s or s == nil then
+ quit_expression = true
+ end
+ return true
+end
+
+expressions.print = function(...)
+ print(...)
+ return true
+end
+
+expressions.contains = find
+expressions.find = find
+expressions.upper = upper
+expressions.lower = lower
+expressions.number = tonumber
+expressions.boolean = toboolean
+
+function expressions.contains(str,pattern)
+ local t = type(str)
+ if t == "string" then
+ if find(str,pattern) then
+ return true
+ end
+ elseif t == "table" then
+ for i=1,#str do
+ local d = str[i]
+ if type(d) == "string" and find(d,pattern) then
+ return true
+ end
+ end
+ end
+ return false
+end
+
+-- user interface
+
+local function traverse(root,pattern,handle)
+ -- report_lpath("use 'xml.selection' instead for pattern: %s",pattern)
+ local collected = applylpath(root,pattern)
+ if collected then
+ for c=1,#collected do
+ local e = collected[c]
+ local r = e.__p__
+ handle(r,r.dt,e.ni)
+ end
+ end
+end
+
+local function selection(root,pattern,handle)
+ local collected = applylpath(root,pattern)
+ if collected then
+ if handle then
+ for c=1,#collected do
+ handle(collected[c])
+ end
+ else
+ return collected
+ end
+ end
+end
+
+xml.traverse = traverse -- old method, r, d, k
+xml.selection = selection -- new method, simple handle
+
+--~ function xml.cachedpatterns()
+--~ return cache
+--~ end
+
+-- generic function finalizer (independant namespace)
+
+local function dofunction(collected,fnc,...)
+ if collected then
+ local f = functions[fnc]
+ if f then
+ for c=1,#collected do
+ f(collected[c],...)
+ end
+ else
+ report_lpath("unknown function %a",fnc)
+ end
+ end
+end
+
+finalizers.xml["function"] = dofunction
+finalizers.tex["function"] = dofunction
+
+-- functions
+
+expressions.text = function(e,n)
+ local rdt = e.__p__.dt
+ return rdt and rdt[n] or ""
+end
+
+expressions.name = function(e,n) -- ns + tg
+ local found = false
+ n = tonumber(n) or 0
+ if n == 0 then
+ found = type(e) == "table" and e
+ elseif n < 0 then
+ local d, k = e.__p__.dt, e.ni
+ for i=k-1,1,-1 do
+ local di = d[i]
+ if type(di) == "table" then
+ if n == -1 then
+ found = di
+ break
+ else
+ n = n + 1
+ end
+ end
+ end
+ else
+ local d, k = e.__p__.dt, e.ni
+ for i=k+1,#d,1 do
+ local di = d[i]
+ if type(di) == "table" then
+ if n == 1 then
+ found = di
+ break
+ else
+ n = n - 1
+ end
+ end
+ end
+ end
+ if found then
+ local ns, tg = found.rn or found.ns or "", found.tg
+ if ns ~= "" then
+ return ns .. ":" .. tg
+ else
+ return tg
+ end
+ else
+ return ""
+ end
+end
+
+expressions.tag = function(e,n) -- only tg
+ if not e then
+ return ""
+ else
+ local found = false
+ n = tonumber(n) or 0
+ if n == 0 then
+ found = (type(e) == "table") and e -- seems to fail
+ elseif n < 0 then
+ local d, k = e.__p__.dt, e.ni
+ for i=k-1,1,-1 do
+ local di = d[i]
+ if type(di) == "table" then
+ if n == -1 then
+ found = di
+ break
+ else
+ n = n + 1
+ end
+ end
+ end
+ else
+ local d, k = e.__p__.dt, e.ni
+ for i=k+1,#d,1 do
+ local di = d[i]
+ if type(di) == "table" then
+ if n == 1 then
+ found = di
+ break
+ else
+ n = n - 1
+ end
+ end
+ end
+ end
+ return (found and found.tg) or ""
+ end
+end
+
+--[[ldx--
+<p>Often using an iterators looks nicer in the code than passing handler
+functions. The <l n='lua'/> book describes how to use coroutines for that
+purpose (<url href='http://www.lua.org/pil/9.3.html'/>). This permits
+code like:</p>
+
+<typing>
+for r, d, k in xml.elements(xml.load('text.xml'),"title") do
+ print(d[k]) -- old method
+end
+for e in xml.collected(xml.load('text.xml'),"title") do
+ print(e) -- new one
+end
+</typing>
+--ldx]]--
+
+-- local wrap, yield = coroutine.wrap, coroutine.yield
+-- local dummy = function() end
+--
+-- function xml.elements(root,pattern,reverse) -- r, d, k
+-- local collected = applylpath(root,pattern)
+-- if collected then
+-- if reverse then
+-- return wrap(function() for c=#collected,1,-1 do
+-- local e = collected[c] local r = e.__p__ yield(r,r.dt,e.ni)
+-- end end)
+-- else
+-- return wrap(function() for c=1,#collected do
+-- local e = collected[c] local r = e.__p__ yield(r,r.dt,e.ni)
+-- end end)
+-- end
+-- end
+-- return wrap(dummy)
+-- end
+--
+-- function xml.collected(root,pattern,reverse) -- e
+-- local collected = applylpath(root,pattern)
+-- if collected then
+-- if reverse then
+-- return wrap(function() for c=#collected,1,-1 do yield(collected[c]) end end)
+-- else
+-- return wrap(function() for c=1,#collected do yield(collected[c]) end end)
+-- end
+-- end
+-- return wrap(dummy)
+-- end
+
+-- faster:
+
+local dummy = function() end
+
+function xml.elements(root,pattern,reverse) -- r, d, k
+ local collected = applylpath(root,pattern)
+ if not collected then
+ return dummy
+ elseif reverse then
+ local c = #collected + 1
+ return function()
+ if c > 1 then
+ c = c - 1
+ local e = collected[c]
+ local r = e.__p__
+ return r, r.dt, e.ni
+ end
+ end
+ else
+ local n, c = #collected, 0
+ return function()
+ if c < n then
+ c = c + 1
+ local e = collected[c]
+ local r = e.__p__
+ return r, r.dt, e.ni
+ end
+ end
+ end
+end
+
+function xml.collected(root,pattern,reverse) -- e
+ local collected = applylpath(root,pattern)
+ if not collected then
+ return dummy
+ elseif reverse then
+ local c = #collected + 1
+ return function()
+ if c > 1 then
+ c = c - 1
+ return collected[c]
+ end
+ end
+ else
+ local n, c = #collected, 0
+ return function()
+ if c < n then
+ c = c + 1
+ return collected[c]
+ end
+ end
+ end
+end
+
+-- handy
+
+function xml.inspect(collection,pattern)
+ pattern = pattern or "."
+ for e in xml.collected(collection,pattern or ".") do
+ report_lpath("pattern: %s\n\n%s\n",pattern,xml.tostring(e))
+ end
+end
+
+-- texy (see xfdf):
+
+local function split(e)
+ local dt = e.dt
+ if dt then
+ for i=1,#dt do
+ local dti = dt[i]
+ if type(dti) == "string" then
+ dti = gsub(dti,"^[\n\r]*(.-)[\n\r]*","%1")
+ dti = gsub(dti,"[\n\r]+","\n\n")
+ dt[i] = dti
+ else
+ split(dti)
+ end
+ end
+ end
+ return e
+end
+
+function xml.finalizers.paragraphs(c)
+ for i=1,#c do
+ split(c[i])
+ end
+ return c
+end
diff --git a/tex/context/base/lxml-mis.lua b/tex/context/base/lxml-mis.lua
index 6afc45002..94a26b974 100644
--- a/tex/context/base/lxml-mis.lua
+++ b/tex/context/base/lxml-mis.lua
@@ -1,103 +1,103 @@
-if not modules then modules = { } end modules ['lxml-mis'] = {
- version = 1.001,
- comment = "this module is the basis for the lxml-* ones",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local xml, lpeg, string = xml, lpeg, string
-
-local concat = table.concat
-local type, next, tonumber, tostring, setmetatable, loadstring = type, next, tonumber, tostring, setmetatable, loadstring
-local format, gsub, match = string.format, string.gsub, string.match
-local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
-local P, S, R, C, V, Cc, Cs = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc, lpeg.Cs
-
-lpegpatterns.xml = lpegpatterns.xml or { }
-local xmlpatterns = lpegpatterns.xml
-
---[[ldx--
-<p>The following helper functions best belong to the <t>lxml-ini</t>
-module. Some are here because we need then in the <t>mk</t>
-document and other manuals, others came up when playing with
-this module. Since this module is also used in <l n='mtxrun'/> we've
-put them here instead of loading mode modules there then needed.</p>
---ldx]]--
-
-local function xmlgsub(t,old,new) -- will be replaced
- local dt = t.dt
- if dt then
- for k=1,#dt do
- local v = dt[k]
- if type(v) == "string" then
- dt[k] = gsub(v,old,new)
- else
- xmlgsub(v,old,new)
- end
- end
- end
-end
-
---~ xml.gsub = xmlgsub
-
-function xml.stripleadingspaces(dk,d,k) -- cosmetic, for manual
- if d and k then
- local dkm = d[k-1]
- if dkm and type(dkm) == "string" then
- local s = match(dkm,"\n(%s+)")
- xmlgsub(dk,"\n"..rep(" ",#s),"\n")
- end
- end
-end
-
---~ xml.escapes = { ['&'] = '&amp;', ['<'] = '&lt;', ['>'] = '&gt;', ['"'] = '&quot;' }
---~ xml.unescapes = { } for k,v in next, xml.escapes do xml.unescapes[v] = k end
-
---~ function xml.escaped (str) return (gsub(str,"(.)" , xml.escapes )) end
---~ function xml.unescaped(str) return (gsub(str,"(&.-;)", xml.unescapes)) end
---~ function xml.cleansed (str) return (gsub(str,"<.->" , '' )) end -- "%b<>"
-
--- 100 * 2500 * "oeps< oeps> oeps&" : gsub:lpeg|lpeg|lpeg
---
--- 1021:0335:0287:0247
-
--- 10 * 1000 * "oeps< oeps> oeps& asfjhalskfjh alskfjh alskfjh alskfjh ;al J;LSFDJ"
---
--- 1559:0257:0288:0190 (last one suggested by roberto)
-
--- escaped = Cs((S("<&>") / xml.escapes + 1)^0)
--- escaped = Cs((S("<")/"&lt;" + S(">")/"&gt;" + S("&")/"&amp;" + 1)^0)
-local normal = (1 - S("<&>"))^0
-local special = P("<")/"&lt;" + P(">")/"&gt;" + P("&")/"&amp;"
-local escaped = Cs(normal * (special * normal)^0)
-
--- 100 * 1000 * "oeps&lt; oeps&gt; oeps&amp;" : gsub:lpeg == 0153:0280:0151:0080 (last one by roberto)
-
-local normal = (1 - S"&")^0
-local special = P("&lt;")/"<" + P("&gt;")/">" + P("&amp;")/"&"
-local unescaped = Cs(normal * (special * normal)^0)
-
--- 100 * 5000 * "oeps <oeps bla='oeps' foo='bar'> oeps </oeps> oeps " : gsub:lpeg == 623:501 msec (short tags, less difference)
-
-local cleansed = Cs(((P("<") * (1-P(">"))^0 * P(">"))/"" + 1)^0)
-
-xmlpatterns.escaped = escaped
-xmlpatterns.unescaped = unescaped
-xmlpatterns.cleansed = cleansed
-
-function xml.escaped (str) return lpegmatch(escaped,str) end
-function xml.unescaped(str) return lpegmatch(unescaped,str) end
-function xml.cleansed (str) return lpegmatch(cleansed,str) end
-
--- this might move
-
-function xml.fillin(root,pattern,str,check)
- local e = xml.first(root,pattern)
- if e then
- local n = #e.dt
- if not check or n == 0 or (n == 1 and e.dt[1] == "") then
- e.dt = { str }
- end
- end
-end
+if not modules then modules = { } end modules ['lxml-mis'] = {
+ version = 1.001,
+ comment = "this module is the basis for the lxml-* ones",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local xml, lpeg, string = xml, lpeg, string
+
+local concat = table.concat
+local type, next, tonumber, tostring, setmetatable, loadstring = type, next, tonumber, tostring, setmetatable, loadstring
+local format, gsub, match = string.format, string.gsub, string.match
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+local P, S, R, C, V, Cc, Cs = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc, lpeg.Cs
+
+lpegpatterns.xml = lpegpatterns.xml or { }
+local xmlpatterns = lpegpatterns.xml
+
+--[[ldx--
+<p>The following helper functions best belong to the <t>lxml-ini</t>
+module. Some are here because we need then in the <t>mk</t>
+document and other manuals, others came up when playing with
+this module. Since this module is also used in <l n='mtxrun'/> we've
+put them here instead of loading mode modules there then needed.</p>
+--ldx]]--
+
+local function xmlgsub(t,old,new) -- will be replaced
+ local dt = t.dt
+ if dt then
+ for k=1,#dt do
+ local v = dt[k]
+ if type(v) == "string" then
+ dt[k] = gsub(v,old,new)
+ else
+ xmlgsub(v,old,new)
+ end
+ end
+ end
+end
+
+--~ xml.gsub = xmlgsub
+
+function xml.stripleadingspaces(dk,d,k) -- cosmetic, for manual
+ if d and k then
+ local dkm = d[k-1]
+ if dkm and type(dkm) == "string" then
+ local s = match(dkm,"\n(%s+)")
+ xmlgsub(dk,"\n"..rep(" ",#s),"\n")
+ end
+ end
+end
+
+--~ xml.escapes = { ['&'] = '&amp;', ['<'] = '&lt;', ['>'] = '&gt;', ['"'] = '&quot;' }
+--~ xml.unescapes = { } for k,v in next, xml.escapes do xml.unescapes[v] = k end
+
+--~ function xml.escaped (str) return (gsub(str,"(.)" , xml.escapes )) end
+--~ function xml.unescaped(str) return (gsub(str,"(&.-;)", xml.unescapes)) end
+--~ function xml.cleansed (str) return (gsub(str,"<.->" , '' )) end -- "%b<>"
+
+-- 100 * 2500 * "oeps< oeps> oeps&" : gsub:lpeg|lpeg|lpeg
+--
+-- 1021:0335:0287:0247
+
+-- 10 * 1000 * "oeps< oeps> oeps& asfjhalskfjh alskfjh alskfjh alskfjh ;al J;LSFDJ"
+--
+-- 1559:0257:0288:0190 (last one suggested by roberto)
+
+-- escaped = Cs((S("<&>") / xml.escapes + 1)^0)
+-- escaped = Cs((S("<")/"&lt;" + S(">")/"&gt;" + S("&")/"&amp;" + 1)^0)
+local normal = (1 - S("<&>"))^0
+local special = P("<")/"&lt;" + P(">")/"&gt;" + P("&")/"&amp;"
+local escaped = Cs(normal * (special * normal)^0)
+
+-- 100 * 1000 * "oeps&lt; oeps&gt; oeps&amp;" : gsub:lpeg == 0153:0280:0151:0080 (last one by roberto)
+
+local normal = (1 - S"&")^0
+local special = P("&lt;")/"<" + P("&gt;")/">" + P("&amp;")/"&"
+local unescaped = Cs(normal * (special * normal)^0)
+
+-- 100 * 5000 * "oeps <oeps bla='oeps' foo='bar'> oeps </oeps> oeps " : gsub:lpeg == 623:501 msec (short tags, less difference)
+
+local cleansed = Cs(((P("<") * (1-P(">"))^0 * P(">"))/"" + 1)^0)
+
+xmlpatterns.escaped = escaped
+xmlpatterns.unescaped = unescaped
+xmlpatterns.cleansed = cleansed
+
+function xml.escaped (str) return lpegmatch(escaped,str) end
+function xml.unescaped(str) return lpegmatch(unescaped,str) end
+function xml.cleansed (str) return lpegmatch(cleansed,str) end
+
+-- this might move
+
+function xml.fillin(root,pattern,str,check)
+ local e = xml.first(root,pattern)
+ if e then
+ local n = #e.dt
+ if not check or n == 0 or (n == 1 and e.dt[1] == "") then
+ e.dt = { str }
+ end
+ end
+end
diff --git a/tex/context/base/lxml-sor.lua b/tex/context/base/lxml-sor.lua
index a31d0ebb8..951017bcd 100644
--- a/tex/context/base/lxml-sor.lua
+++ b/tex/context/base/lxml-sor.lua
@@ -1,159 +1,159 @@
-if not modules then modules = { } end modules ['lxml-sor'] = {
- version = 1.001,
- comment = "companion to lxml-sor.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local format, concat, rep = string.format, table.concat, string.rep
-local lpegmatch = lpeg.match
-
-local xml, lxml = xml, lxml
-
-lxml.sorters = lxml.sorters or { }
-
-if not lxml.splitid then
- local splitter = lpeg.C((1-lpeg.P(":"))^1) * lpeg.P("::") * lpeg.C(lpeg.P(1)^1)
- function lxml.splitid(id)
- local d, i = lpegmatch(splitter,id)
- if d then
- return d, i
- else
- return "", id
- end
- end
-end
-
-local lists = { }
-
-function lxml.sorters.reset(name)
- lists[name] = {
- sorted = false,
- entries = { },
- reverse = { },
- results = { },
- }
-end
-
-function lxml.sorters.add(name,n,key)
- local list = lists[name]
- if list.sorted then
- -- reverse is messed up, we could regenerate it and go on
- else
- local entries = list and list.entries
- if entries then
- local reverse = list.reverse
- local e = reverse[n]
- if e then
- local keys = entries[e][2]
- keys[#keys+1] = key
- else
- entries[#entries+1] = { n, { key } }
- reverse[n] = #entries
- end
- end
- end
-end
-
-function lxml.sorters.show(name)
- local list = lists[name]
- local entries = list and list.entries
- local NC, NR, bold = context.NC, context.NR, context.bold -- somehow bold is not working
- if entries then
- local maxn = 1
- for i=1,#entries do
- if #entries[i][2] > maxn then maxn = #entries[i][2] end
- end
- context.starttabulate { "|Tr|Tr|" .. rep("Tlp|",maxn) }
- NC() bold("n")
- NC() bold("id")
- if maxn > 1 then
- for i=1,maxn do
- NC() bold("entry " .. i)
- end
- else
- NC() bold("entry")
- end
- NC() NR()
- context.HL()
- for i=1,#entries do
- local entry = entries[i]
- local document, node = lxml.splitid(entry[1])
- NC() context(i)
- NC() context(node)
- local e = entry[2]
- for i=1,#e do
- NC() context.detokenize(e[i])
- end
- NC() NR()
- end
- context.stoptabulate()
- end
-end
-
-lxml.sorters.compare = sorters.comparers.basic -- (a,b)
-
-function lxml.sorters.sort(name)
- local list = lists[name]
- local entries = list and list.entries
- if entries then
- -- filtering
- local results = { }
- list.results = results
- for i=1,#entries do
- local entry = entries[i]
- results[i] = {
- entry = entry[1],
- key = concat(entry[2], " "),
- }
- end
- -- preparation
- local strip = sorters.strip
- local splitter = sorters.splitters.utf
- local firstofsplit = sorters.firstofsplit
- for i=1, #results do
- local r = results[i]
- r.split = splitter(strip(r.key))
- end
- -- sorting
- sorters.sort(results,lxml.sorters.compare)
- -- finalizing
- list.nofsorted = #results
- local split = { }
- for k=1,#results do -- rather generic so maybe we need a function
- local v = results[k]
- local entry, tag = firstofsplit(v)
- local s = split[entry] -- keeps track of change
- if not s then
- s = { tag = tag, data = { } }
- split[entry] = s
- end
- s.data[#s.data+1] = v
- end
- list.results = split
- -- done
- list.sorted = true
- end
-end
-
-function lxml.sorters.flush(name,setup)
- local list = lists[name]
- local results = list and list.results
- local xmlw = context.xmlw
- if results and next(results) then
- for key, result in next, results do
- local tag, data = result.tag, result.data
- for d=1,#data do
- xmlw(setup,data[d].entry)
- end
- end
- else
- local entries = list and list.entries
- if entries then
- for i=1,#entries do
- xmlw(setup,entries[i][1])
- end
- end
- end
-end
+if not modules then modules = { } end modules ['lxml-sor'] = {
+ version = 1.001,
+ comment = "companion to lxml-sor.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local format, concat, rep = string.format, table.concat, string.rep
+local lpegmatch = lpeg.match
+
+local xml, lxml = xml, lxml
+
+lxml.sorters = lxml.sorters or { }
+
+if not lxml.splitid then
+ local splitter = lpeg.C((1-lpeg.P(":"))^1) * lpeg.P("::") * lpeg.C(lpeg.P(1)^1)
+ function lxml.splitid(id)
+ local d, i = lpegmatch(splitter,id)
+ if d then
+ return d, i
+ else
+ return "", id
+ end
+ end
+end
+
+local lists = { }
+
+function lxml.sorters.reset(name)
+ lists[name] = {
+ sorted = false,
+ entries = { },
+ reverse = { },
+ results = { },
+ }
+end
+
+function lxml.sorters.add(name,n,key)
+ local list = lists[name]
+ if list.sorted then
+ -- reverse is messed up, we could regenerate it and go on
+ else
+ local entries = list and list.entries
+ if entries then
+ local reverse = list.reverse
+ local e = reverse[n]
+ if e then
+ local keys = entries[e][2]
+ keys[#keys+1] = key
+ else
+ entries[#entries+1] = { n, { key } }
+ reverse[n] = #entries
+ end
+ end
+ end
+end
+
+function lxml.sorters.show(name)
+ local list = lists[name]
+ local entries = list and list.entries
+ local NC, NR, bold = context.NC, context.NR, context.bold -- somehow bold is not working
+ if entries then
+ local maxn = 1
+ for i=1,#entries do
+ if #entries[i][2] > maxn then maxn = #entries[i][2] end
+ end
+ context.starttabulate { "|Tr|Tr|" .. rep("Tlp|",maxn) }
+ NC() bold("n")
+ NC() bold("id")
+ if maxn > 1 then
+ for i=1,maxn do
+ NC() bold("entry " .. i)
+ end
+ else
+ NC() bold("entry")
+ end
+ NC() NR()
+ context.HL()
+ for i=1,#entries do
+ local entry = entries[i]
+ local document, node = lxml.splitid(entry[1])
+ NC() context(i)
+ NC() context(node)
+ local e = entry[2]
+ for i=1,#e do
+ NC() context.detokenize(e[i])
+ end
+ NC() NR()
+ end
+ context.stoptabulate()
+ end
+end
+
+lxml.sorters.compare = sorters.comparers.basic -- (a,b)
+
+function lxml.sorters.sort(name)
+ local list = lists[name]
+ local entries = list and list.entries
+ if entries then
+ -- filtering
+ local results = { }
+ list.results = results
+ for i=1,#entries do
+ local entry = entries[i]
+ results[i] = {
+ entry = entry[1],
+ key = concat(entry[2], " "),
+ }
+ end
+ -- preparation
+ local strip = sorters.strip
+ local splitter = sorters.splitters.utf
+ local firstofsplit = sorters.firstofsplit
+ for i=1, #results do
+ local r = results[i]
+ r.split = splitter(strip(r.key))
+ end
+ -- sorting
+ sorters.sort(results,lxml.sorters.compare)
+ -- finalizing
+ list.nofsorted = #results
+ local split = { }
+ for k=1,#results do -- rather generic so maybe we need a function
+ local v = results[k]
+ local entry, tag = firstofsplit(v)
+ local s = split[entry] -- keeps track of change
+ if not s then
+ s = { tag = tag, data = { } }
+ split[entry] = s
+ end
+ s.data[#s.data+1] = v
+ end
+ list.results = split
+ -- done
+ list.sorted = true
+ end
+end
+
+function lxml.sorters.flush(name,setup)
+ local list = lists[name]
+ local results = list and list.results
+ local xmlw = context.xmlw
+ if results and next(results) then
+ for key, result in next, results do
+ local tag, data = result.tag, result.data
+ for d=1,#data do
+ xmlw(setup,data[d].entry)
+ end
+ end
+ else
+ local entries = list and list.entries
+ if entries then
+ for i=1,#entries do
+ xmlw(setup,entries[i][1])
+ end
+ end
+ end
+end
diff --git a/tex/context/base/lxml-tab.lua b/tex/context/base/lxml-tab.lua
index b6c2b1b13..2bb5844fc 100644
--- a/tex/context/base/lxml-tab.lua
+++ b/tex/context/base/lxml-tab.lua
@@ -1,1367 +1,1367 @@
-if not modules then modules = { } end modules ['lxml-tab'] = {
- version = 1.001,
- comment = "this module is the basis for the lxml-* ones",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- this module needs a cleanup: check latest lpeg, passing args, (sub)grammar, etc etc
--- stripping spaces from e.g. cont-en.xml saves .2 sec runtime so it's not worth the
--- trouble
-
--- todo: when serializing optionally remap named entities to hex (if known in char-ent.lua)
--- maybe when letter -> utf, else name .. then we need an option to the serializer .. a bit
--- of work so we delay this till we cleanup
-
-local trace_entities = false trackers.register("xml.entities", function(v) trace_entities = v end)
-
-local report_xml = logs and logs.reporter("xml","core") or function(...) print(string.format(...)) end
-
---[[ldx--
-<p>The parser used here is inspired by the variant discussed in the lua book, but
-handles comment and processing instructions, has a different structure, provides
-parent access; a first version used different trickery but was less optimized to we
-went this route. First we had a find based parser, now we have an <l n='lpeg'/> based one.
-The find based parser can be found in l-xml-edu.lua along with other older code.</p>
-
-<p>Beware, the interface may change. For instance at, ns, tg, dt may get more
-verbose names. Once the code is stable we will also remove some tracing and
-optimize the code.</p>
-
-<p>I might even decide to reimplement the parser using the latest <l n='lpeg'/> trickery
-as the current variant was written when <l n='lpeg'/> showed up and it's easier now to
-build tables in one go.</p>
---ldx]]--
-
-xml = xml or { }
-local xml = xml
-
---~ local xml = xml
-
-local concat, remove, insert = table.concat, table.remove, table.insert
-local type, next, setmetatable, getmetatable, tonumber = type, next, setmetatable, getmetatable, tonumber
-local lower, find, match, gsub = string.lower, string.find, string.match, string.gsub
-local utfchar = utf.char
-local lpegmatch = lpeg.match
-local P, S, R, C, V, C, Cs = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.C, lpeg.Cs
-local formatters = string.formatters
-
---[[ldx--
-<p>First a hack to enable namespace resolving. A namespace is characterized by
-a <l n='url'/>. The following function associates a namespace prefix with a
-pattern. We use <l n='lpeg'/>, which in this case is more than twice as fast as a
-find based solution where we loop over an array of patterns. Less code and
-much cleaner.</p>
---ldx]]--
-
-xml.xmlns = xml.xmlns or { }
-
-local check = P(false)
-local parse = check
-
---[[ldx--
-<p>The next function associates a namespace prefix with an <l n='url'/>. This
-normally happens independent of parsing.</p>
-
-<typing>
-xml.registerns("mml","mathml")
-</typing>
---ldx]]--
-
-function xml.registerns(namespace, pattern) -- pattern can be an lpeg
- check = check + C(P(lower(pattern))) / namespace
- parse = P { P(check) + 1 * V(1) }
-end
-
---[[ldx--
-<p>The next function also registers a namespace, but this time we map a
-given namespace prefix onto a registered one, using the given
-<l n='url'/>. This used for attributes like <t>xmlns:m</t>.</p>
-
-<typing>
-xml.checkns("m","http://www.w3.org/mathml")
-</typing>
---ldx]]--
-
-function xml.checkns(namespace,url)
- local ns = lpegmatch(parse,lower(url))
- if ns and namespace ~= ns then
- xml.xmlns[namespace] = ns
- end
-end
-
---[[ldx--
-<p>Next we provide a way to turn an <l n='url'/> into a registered
-namespace. This used for the <t>xmlns</t> attribute.</p>
-
-<typing>
-resolvedns = xml.resolvens("http://www.w3.org/mathml")
-</typing>
-
-This returns <t>mml</t>.
---ldx]]--
-
-function xml.resolvens(url)
- return lpegmatch(parse,lower(url)) or ""
-end
-
---[[ldx--
-<p>A namespace in an element can be remapped onto the registered
-one efficiently by using the <t>xml.xmlns</t> table.</p>
---ldx]]--
-
---[[ldx--
-<p>This version uses <l n='lpeg'/>. We follow the same approach as before, stack and top and
-such. This version is about twice as fast which is mostly due to the fact that
-we don't have to prepare the stream for cdata, doctype etc etc. This variant is
-is dedicated to Luigi Scarso, who challenged me with 40 megabyte <l n='xml'/> files that
-took 12.5 seconds to load (1.5 for file io and the rest for tree building). With
-the <l n='lpeg'/> implementation we got that down to less 7.3 seconds. Loading the 14
-<l n='context'/> interface definition files (2.6 meg) went down from 1.05 seconds to 0.55.</p>
-
-<p>Next comes the parser. The rather messy doctype definition comes in many
-disguises so it is no surprice that later on have to dedicate quite some
-<l n='lpeg'/> code to it.</p>
-
-<typing>
-<!DOCTYPE Something PUBLIC "... ..." "..." [ ... ] >
-<!DOCTYPE Something PUBLIC "... ..." "..." >
-<!DOCTYPE Something SYSTEM "... ..." [ ... ] >
-<!DOCTYPE Something SYSTEM "... ..." >
-<!DOCTYPE Something [ ... ] >
-<!DOCTYPE Something >
-</typing>
-
-<p>The code may look a bit complex but this is mostly due to the fact that we
-resolve namespaces and attach metatables. There is only one public function:</p>
-
-<typing>
-local x = xml.convert(somestring)
-</typing>
-
-<p>An optional second boolean argument tells this function not to create a root
-element.</p>
-
-<p>Valid entities are:</p>
-
-<typing>
-<!ENTITY xxxx SYSTEM "yyyy" NDATA zzzz>
-<!ENTITY xxxx PUBLIC "yyyy" >
-<!ENTITY xxxx "yyyy" >
-</typing>
---ldx]]--
-
--- not just one big nested table capture (lpeg overflow)
-
-local nsremap, resolvens = xml.xmlns, xml.resolvens
-
-local stack = { }
-local top = { }
-local dt = { }
-local at = { }
-local xmlns = { }
-local errorstr = nil
-local entities = { }
-local strip = false
-local cleanup = false
-local utfize = false
-local resolve_predefined = false
-local unify_predefined = false
-
-local dcache = { }
-local hcache = { }
-local acache = { }
-
-local mt = { }
-
-local function initialize_mt(root)
- mt = { __index = root } -- will be redefined later
-end
-
-function xml.setproperty(root,k,v)
- getmetatable(root).__index[k] = v
-end
-
-function xml.checkerror(top,toclose)
- return "" -- can be set
-end
-
-local function add_attribute(namespace,tag,value)
- if cleanup and #value > 0 then
- value = cleanup(value) -- new
- end
- if tag == "xmlns" then
- xmlns[#xmlns+1] = resolvens(value)
- at[tag] = value
- elseif namespace == "" then
- at[tag] = value
- elseif namespace == "xmlns" then
- xml.checkns(tag,value)
- at["xmlns:" .. tag] = value
- else
- -- for the moment this way:
- at[namespace .. ":" .. tag] = value
- end
-end
-
-local function add_empty(spacing, namespace, tag)
- if #spacing > 0 then
- dt[#dt+1] = spacing
- end
- local resolved = namespace == "" and xmlns[#xmlns] or nsremap[namespace] or namespace
- top = stack[#stack]
- dt = top.dt
- local t = { ns=namespace or "", rn=resolved, tg=tag, at=at, dt={}, __p__ = top }
- dt[#dt+1] = t
- setmetatable(t, mt)
- if at.xmlns then
- remove(xmlns)
- end
- at = { }
-end
-
-local function add_begin(spacing, namespace, tag)
- if #spacing > 0 then
- dt[#dt+1] = spacing
- end
- local resolved = namespace == "" and xmlns[#xmlns] or nsremap[namespace] or namespace
- top = { ns=namespace or "", rn=resolved, tg=tag, at=at, dt={}, __p__ = stack[#stack] }
- setmetatable(top, mt)
- dt = top.dt
- stack[#stack+1] = top
- at = { }
-end
-
-local function add_end(spacing, namespace, tag)
- if #spacing > 0 then
- dt[#dt+1] = spacing
- end
- local toclose = remove(stack)
- top = stack[#stack]
- if #stack < 1 then
- errorstr = formatters["unable to close %s %s"](tag,xml.checkerror(top,toclose) or "")
- elseif toclose.tg ~= tag then -- no namespace check
- errorstr = formatters["unable to close %s with %s %s"](toclose.tg,tag,xml.checkerror(top,toclose) or "")
- end
- dt = top.dt
- dt[#dt+1] = toclose
- -- dt[0] = top -- nasty circular reference when serializing table
- if toclose.at.xmlns then
- remove(xmlns)
- end
-end
-
-local function add_text(text)
- if cleanup and #text > 0 then
- dt[#dt+1] = cleanup(text)
- else
- dt[#dt+1] = text
- end
-end
-
-local function add_special(what, spacing, text)
- if #spacing > 0 then
- dt[#dt+1] = spacing
- end
- if strip and (what == "@cm@" or what == "@dt@") then
- -- forget it
- else
- dt[#dt+1] = { special=true, ns="", tg=what, dt={ text } }
- end
-end
-
-local function set_message(txt)
- errorstr = "garbage at the end of the file: " .. gsub(txt,"([ \n\r\t]*)","")
-end
-
-local reported_attribute_errors = { }
-
-local function attribute_value_error(str)
- if not reported_attribute_errors[str] then
- report_xml("invalid attribute value %a",str)
- reported_attribute_errors[str] = true
- at._error_ = str
- end
- return str
-end
-
-local function attribute_specification_error(str)
- if not reported_attribute_errors[str] then
- report_xml("invalid attribute specification %a",str)
- reported_attribute_errors[str] = true
- at._error_ = str
- end
- return str
-end
-
-xml.placeholders = {
- unknown_dec_entity = function(str) return str == "" and "&error;" or formatters["&%s;"](str) end,
- unknown_hex_entity = function(str) return formatters["&#x%s;"](str) end,
- unknown_any_entity = function(str) return formatters["&#x%s;"](str) end,
-}
-
-local placeholders = xml.placeholders
-
-local function fromhex(s)
- local n = tonumber(s,16)
- if n then
- return utfchar(n)
- else
- return formatters["h:%s"](s), true
- end
-end
-
-local function fromdec(s)
- local n = tonumber(s)
- if n then
- return utfchar(n)
- else
- return formatters["d:%s"](s), true
- end
-end
-
--- one level expansion (simple case), no checking done
-
-local rest = (1-P(";"))^0
-local many = P(1)^0
-
-local parsedentity =
- P("&") * (P("#x")*(rest/fromhex) + P("#")*(rest/fromdec)) * P(";") * P(-1) +
- (P("#x")*(many/fromhex) + P("#")*(many/fromdec))
-
--- parsing in the xml file
-
-local predefined_unified = {
- [38] = "&amp;",
- [42] = "&quot;",
- [47] = "&apos;",
- [74] = "&lt;",
- [76] = "&gt;",
-}
-
-local predefined_simplified = {
- [38] = "&", amp = "&",
- [42] = '"', quot = '"',
- [47] = "'", apos = "'",
- [74] = "<", lt = "<",
- [76] = ">", gt = ">",
-}
-
-local nofprivates = 0xF0000 -- shared but seldom used
-
-local privates_u = { -- unescaped
- [ [[&]] ] = "&amp;",
- [ [["]] ] = "&quot;",
- [ [[']] ] = "&apos;",
- [ [[<]] ] = "&lt;",
- [ [[>]] ] = "&gt;",
-}
-
-local privates_p = {
-}
-
-local privates_n = {
- -- keeps track of defined ones
-}
-
-local escaped = utf.remapper(privates_u)
-
-local function unescaped(s)
- local p = privates_n[s]
- if not p then
- nofprivates = nofprivates + 1
- p = utfchar(nofprivates)
- privates_n[s] = p
- s = "&" .. s .. ";" -- todo: use char-ent to map to hex
- privates_u[p] = s
- privates_p[p] = s
- end
- return p
-end
-
-local unprivatized = utf.remapper(privates_p)
-
-xml.privatetoken = unescaped
-xml.unprivatized = unprivatized
-xml.privatecodes = privates_n
-
-local function handle_hex_entity(str)
- local h = hcache[str]
- if not h then
- local n = tonumber(str,16)
- h = unify_predefined and predefined_unified[n]
- if h then
- if trace_entities then
- report_xml("utfize, converting hex entity &#x%s; into %a",str,h)
- end
- elseif utfize then
- h = (n and utfchar(n)) or xml.unknown_hex_entity(str) or ""
- if not n then
- report_xml("utfize, ignoring hex entity &#x%s;",str)
- elseif trace_entities then
- report_xml("utfize, converting hex entity &#x%s; into %a",str,h)
- end
- else
- if trace_entities then
- report_xml("found entity &#x%s;",str)
- end
- h = "&#x" .. str .. ";"
- end
- hcache[str] = h
- end
- return h
-end
-
-local function handle_dec_entity(str)
- local d = dcache[str]
- if not d then
- local n = tonumber(str)
- d = unify_predefined and predefined_unified[n]
- if d then
- if trace_entities then
- report_xml("utfize, converting dec entity &#%s; into %a",str,d)
- end
- elseif utfize then
- d = (n and utfchar(n)) or placeholders.unknown_dec_entity(str) or ""
- if not n then
- report_xml("utfize, ignoring dec entity &#%s;",str)
- elseif trace_entities then
- report_xml("utfize, converting dec entity &#%s; into %a",str,d)
- end
- else
- if trace_entities then
- report_xml("found entity &#%s;",str)
- end
- d = "&#" .. str .. ";"
- end
- dcache[str] = d
- end
- return d
-end
-
-xml.parsedentitylpeg = parsedentity
-
-local function handle_any_entity(str)
- if resolve then
- local a = acache[str] -- per instance ! todo
- if not a then
- a = resolve_predefined and predefined_simplified[str]
- if a then
- if trace_entities then
- report_xml("resolving entity &%s; to predefined %a",str,a)
- end
- else
- if type(resolve) == "function" then
- a = resolve(str) or entities[str]
- else
- a = entities[str]
- end
- if a then
- if type(a) == "function" then
- if trace_entities then
- report_xml("expanding entity &%s; to function call",str)
- end
- a = a(str) or ""
- end
- a = lpegmatch(parsedentity,a) or a -- for nested
- if trace_entities then
- report_xml("resolving entity &%s; to internal %a",str,a)
- end
- else
- local unknown_any_entity = placeholders.unknown_any_entity
- if unknown_any_entity then
- a = unknown_any_entity(str) or ""
- end
- if a then
- if trace_entities then
- report_xml("resolving entity &%s; to external %s",str,a)
- end
- else
- if trace_entities then
- report_xml("keeping entity &%s;",str)
- end
- if str == "" then
- a = "&error;"
- else
- a = "&" .. str .. ";"
- end
- end
- end
- end
- acache[str] = a
- elseif trace_entities then
- if not acache[str] then
- report_xml("converting entity &%s; to %a",str,a)
- acache[str] = a
- end
- end
- return a
- else
- local a = acache[str]
- if not a then
- a = resolve_predefined and predefined_simplified[str]
- if a then
- -- one of the predefined
- acache[str] = a
- if trace_entities then
- report_xml("entity &%s; becomes %a",str,a)
- end
- elseif str == "" then
- if trace_entities then
- report_xml("invalid entity &%s;",str)
- end
- a = "&error;"
- acache[str] = a
- else
- if trace_entities then
- report_xml("entity &%s; is made private",str)
- end
- -- a = "&" .. str .. ";"
- a = unescaped(str)
- acache[str] = a
- end
- end
- return a
- end
-end
-
-local function handle_end_entity(chr)
- report_xml("error in entity, %a found instead of %a",chr,";")
-end
-
-local space = S(' \r\n\t')
-local open = P('<')
-local close = P('>')
-local squote = S("'")
-local dquote = S('"')
-local equal = P('=')
-local slash = P('/')
-local colon = P(':')
-local semicolon = P(';')
-local ampersand = P('&')
-local valid = R('az', 'AZ', '09') + S('_-.')
-local name_yes = C(valid^1) * colon * C(valid^1)
-local name_nop = C(P(true)) * C(valid^1)
-local name = name_yes + name_nop
-local utfbom = lpeg.patterns.utfbom -- no capture
-local spacing = C(space^0)
-
------ entitycontent = (1-open-semicolon)^0
-local anyentitycontent = (1-open-semicolon-space-close)^0
-local hexentitycontent = R("AF","af","09")^0
-local decentitycontent = R("09")^0
-local parsedentity = P("#")/"" * (
- P("x")/"" * (hexentitycontent/handle_hex_entity) +
- (decentitycontent/handle_dec_entity)
- ) + (anyentitycontent/handle_any_entity)
-local entity = ampersand/"" * parsedentity * ( (semicolon/"") + #(P(1)/handle_end_entity))
-
-local text_unparsed = C((1-open)^1)
-local text_parsed = Cs(((1-open-ampersand)^1 + entity)^1)
-
-local somespace = space^1
-local optionalspace = space^0
-
------ value = (squote * C((1 - squote)^0) * squote) + (dquote * C((1 - dquote)^0) * dquote) -- ampersand and < also invalid in value
-local value = (squote * Cs((entity + (1 - squote))^0) * squote) + (dquote * Cs((entity + (1 - dquote))^0) * dquote) -- ampersand and < also invalid in value
-
-local endofattributes = slash * close + close -- recovery of flacky html
-local whatever = space * name * optionalspace * equal
------ wrongvalue = C(P(1-whatever-close)^1 + P(1-close)^1) / attribute_value_error
------ wrongvalue = C(P(1-whatever-endofattributes)^1 + P(1-endofattributes)^1) / attribute_value_error
------ wrongvalue = C(P(1-space-endofattributes)^1) / attribute_value_error
-local wrongvalue = Cs(P(entity + (1-space-endofattributes))^1) / attribute_value_error
-
-local attributevalue = value + wrongvalue
-
-local attribute = (somespace * name * optionalspace * equal * optionalspace * attributevalue) / add_attribute
------ attributes = (attribute)^0
-
-local attributes = (attribute + somespace^-1 * (((1-endofattributes)^1)/attribute_specification_error))^0
-
-local parsedtext = text_parsed / add_text
-local unparsedtext = text_unparsed / add_text
-local balanced = P { "[" * ((1 - S"[]") + V(1))^0 * "]" } -- taken from lpeg manual, () example
-
-local emptyelement = (spacing * open * name * attributes * optionalspace * slash * close) / add_empty
-local beginelement = (spacing * open * name * attributes * optionalspace * close) / add_begin
-local endelement = (spacing * open * slash * name * optionalspace * close) / add_end
-
-local begincomment = open * P("!--")
-local endcomment = P("--") * close
-local begininstruction = open * P("?")
-local endinstruction = P("?") * close
-local begincdata = open * P("![CDATA[")
-local endcdata = P("]]") * close
-
-local someinstruction = C((1 - endinstruction)^0)
-local somecomment = C((1 - endcomment )^0)
-local somecdata = C((1 - endcdata )^0)
-
-local function normalentity(k,v ) entities[k] = v end
-local function systementity(k,v,n) entities[k] = v end
-local function publicentity(k,v,n) entities[k] = v end
-
--- todo: separate dtd parser
-
-local begindoctype = open * P("!DOCTYPE")
-local enddoctype = close
-local beginset = P("[")
-local endset = P("]")
-local doctypename = C((1-somespace-close)^0)
-local elementdoctype = optionalspace * P("<!ELEMENT") * (1-close)^0 * close
-
-local basiccomment = begincomment * ((1 - endcomment)^0) * endcomment
-
-local normalentitytype = (doctypename * somespace * value)/normalentity
-local publicentitytype = (doctypename * somespace * P("PUBLIC") * somespace * value)/publicentity
-local systementitytype = (doctypename * somespace * P("SYSTEM") * somespace * value * somespace * P("NDATA") * somespace * doctypename)/systementity
-local entitydoctype = optionalspace * P("<!ENTITY") * somespace * (systementitytype + publicentitytype + normalentitytype) * optionalspace * close
-
--- we accept comments in doctypes
-
-local doctypeset = beginset * optionalspace * P(elementdoctype + entitydoctype + basiccomment + space)^0 * optionalspace * endset
-local definitiondoctype= doctypename * somespace * doctypeset
-local publicdoctype = doctypename * somespace * P("PUBLIC") * somespace * value * somespace * value * somespace * doctypeset
-local systemdoctype = doctypename * somespace * P("SYSTEM") * somespace * value * somespace * doctypeset
-local simpledoctype = (1-close)^1 -- * balanced^0
-local somedoctype = C((somespace * (publicdoctype + systemdoctype + definitiondoctype + simpledoctype) * optionalspace)^0)
-local somedoctype = C((somespace * (publicdoctype + systemdoctype + definitiondoctype + simpledoctype) * optionalspace)^0)
-
-local instruction = (spacing * begininstruction * someinstruction * endinstruction) / function(...) add_special("@pi@",...) end
-local comment = (spacing * begincomment * somecomment * endcomment ) / function(...) add_special("@cm@",...) end
-local cdata = (spacing * begincdata * somecdata * endcdata ) / function(...) add_special("@cd@",...) end
-local doctype = (spacing * begindoctype * somedoctype * enddoctype ) / function(...) add_special("@dt@",...) end
-
--- nicer but slower:
---
--- local instruction = (Cc("@pi@") * spacing * begininstruction * someinstruction * endinstruction) / add_special
--- local comment = (Cc("@cm@") * spacing * begincomment * somecomment * endcomment ) / add_special
--- local cdata = (Cc("@cd@") * spacing * begincdata * somecdata * endcdata ) / add_special
--- local doctype = (Cc("@dt@") * spacing * begindoctype * somedoctype * enddoctype ) / add_special
-
-local trailer = space^0 * (text_unparsed/set_message)^0
-
--- comment + emptyelement + text + cdata + instruction + V("parent"), -- 6.5 seconds on 40 MB database file
--- text + comment + emptyelement + cdata + instruction + V("parent"), -- 5.8
--- text + V("parent") + emptyelement + comment + cdata + instruction, -- 5.5
-
-local grammar_parsed_text = P { "preamble",
- preamble = utfbom^0 * instruction^0 * (doctype + comment + instruction)^0 * V("parent") * trailer,
- parent = beginelement * V("children")^0 * endelement,
- children = parsedtext + V("parent") + emptyelement + comment + cdata + instruction,
-}
-
-local grammar_unparsed_text = P { "preamble",
- preamble = utfbom^0 * instruction^0 * (doctype + comment + instruction)^0 * V("parent") * trailer,
- parent = beginelement * V("children")^0 * endelement,
- children = unparsedtext + V("parent") + emptyelement + comment + cdata + instruction,
-}
-
--- maybe we will add settings to result as well
-
-local function _xmlconvert_(data, settings)
- settings = settings or { } -- no_root strip_cm_and_dt given_entities parent_root error_handler
- --
- strip = settings.strip_cm_and_dt
- utfize = settings.utfize_entities
- resolve = settings.resolve_entities
- resolve_predefined = settings.resolve_predefined_entities -- in case we have escaped entities
- unify_predefined = settings.unify_predefined_entities -- &#038; -> &amp;
- cleanup = settings.text_cleanup
- entities = settings.entities or { }
- --
- if utfize == nil then
- settings.utfize_entities = true
- utfize = true
- end
- if resolve_predefined == nil then
- settings.resolve_predefined_entities = true
- resolve_predefined = true
- end
- --
- stack, top, at, xmlns, errorstr = { }, { }, { }, { }, nil
- acache, hcache, dcache = { }, { }, { } -- not stored
- reported_attribute_errors = { }
- if settings.parent_root then
- mt = getmetatable(settings.parent_root)
- else
- initialize_mt(top)
- end
- stack[#stack+1] = top
- top.dt = { }
- dt = top.dt
- if not data or data == "" then
- errorstr = "empty xml file"
- elseif utfize or resolve then
- if lpegmatch(grammar_parsed_text,data) then
- errorstr = ""
- else
- errorstr = "invalid xml file - parsed text"
- end
- elseif type(data) == "string" then
- if lpegmatch(grammar_unparsed_text,data) then
- errorstr = ""
- else
- errorstr = "invalid xml file - unparsed text"
- end
- else
- errorstr = "invalid xml file - no text at all"
- end
- local result
- if errorstr and errorstr ~= "" then
- result = { dt = { { ns = "", tg = "error", dt = { errorstr }, at={ }, er = true } } }
- setmetatable(stack, mt)
- local errorhandler = settings.error_handler
- if errorhandler == false then
- -- no error message
- else
- errorhandler = errorhandler or xml.errorhandler
- if errorhandler then
- local currentresource = settings.currentresource
- if currentresource and currentresource ~= "" then
- xml.errorhandler(formatters["load error in [%s]: %s"](currentresource,errorstr))
- else
- xml.errorhandler(formatters["load error: %s"](errorstr))
- end
- end
- end
- else
- result = stack[1]
- end
- if not settings.no_root then
- result = { special = true, ns = "", tg = '@rt@', dt = result.dt, at={ }, entities = entities, settings = settings }
- setmetatable(result, mt)
- local rdt = result.dt
- for k=1,#rdt do
- local v = rdt[k]
- if type(v) == "table" and not v.special then -- always table -)
- result.ri = k -- rootindex
- v.__p__ = result -- new, experiment, else we cannot go back to settings, we need to test this !
- break
- end
- end
- end
- if errorstr and errorstr ~= "" then
- result.error = true
- end
- result.statistics = {
- entities = {
- decimals = dcache,
- hexadecimals = hcache,
- names = acache,
- }
- }
- strip, utfize, resolve, resolve_predefined = nil, nil, nil, nil
- unify_predefined, cleanup, entities = nil, nil, nil
- stack, top, at, xmlns, errorstr = nil, nil, nil, nil, nil
- acache, hcache, dcache = nil, nil, nil
- reported_attribute_errors, mt, errorhandler = nil, nil, nil
- return result
-end
-
--- Because we can have a crash (stack issues) with faulty xml, we wrap this one
--- in a protector:
-
-function xmlconvert(data,settings)
- local ok, result = pcall(function() return _xmlconvert_(data,settings) end)
- if ok then
- return result
- else
- return _xmlconvert_("",settings)
- end
-end
-
-xml.convert = xmlconvert
-
-function xml.inheritedconvert(data,xmldata) -- xmldata is parent
- local settings = xmldata.settings
- if settings then
- settings.parent_root = xmldata -- to be tested
- end
- -- settings.no_root = true
- local xc = xmlconvert(data,settings) -- hm, we might need to locate settings
- -- xc.settings = nil
- -- xc.entities = nil
- -- xc.special = nil
- -- xc.ri = nil
- -- print(xc.tg)
- return xc
-end
-
---[[ldx--
-<p>Packaging data in an xml like table is done with the following
-function. Maybe it will go away (when not used).</p>
---ldx]]--
-
-function xml.is_valid(root)
- return root and root.dt and root.dt[1] and type(root.dt[1]) == "table" and not root.dt[1].er
-end
-
-function xml.package(tag,attributes,data)
- local ns, tg = match(tag,"^(.-):?([^:]+)$")
- local t = { ns = ns, tg = tg, dt = data or "", at = attributes or {} }
- setmetatable(t, mt)
- return t
-end
-
-function xml.is_valid(root)
- return root and not root.error
-end
-
-xml.errorhandler = report_xml
-
---[[ldx--
-<p>We cannot load an <l n='lpeg'/> from a filehandle so we need to load
-the whole file first. The function accepts a string representing
-a filename or a file handle.</p>
---ldx]]--
-
-function xml.load(filename,settings)
- local data = ""
- if type(filename) == "string" then
- -- local data = io.loaddata(filename) - -todo: check type in io.loaddata
- local f = io.open(filename,'r') -- why not 'rb'
- if f then
- data = f:read("*all") -- io.readall(f) ... only makes sense for large files
- f:close()
- end
- elseif filename then -- filehandle
- data = filename:read("*all") -- io.readall(f) ... only makes sense for large files
- end
- if settings then
- settings.currentresource = filename
- local result = xmlconvert(data,settings)
- settings.currentresource = nil
- return result
- else
- return xmlconvert(data,{ currentresource = filename })
- end
-end
-
---[[ldx--
-<p>When we inject new elements, we need to convert strings to
-valid trees, which is what the next function does.</p>
---ldx]]--
-
-local no_root = { no_root = true }
-
-function xml.toxml(data)
- if type(data) == "string" then
- local root = { xmlconvert(data,no_root) }
- return (#root > 1 and root) or root[1]
- else
- return data
- end
-end
-
---[[ldx--
-<p>For copying a tree we use a dedicated function instead of the
-generic table copier. Since we know what we're dealing with we
-can speed up things a bit. The second argument is not to be used!</p>
---ldx]]--
-
-local function copy(old,tables)
- if old then
- tables = tables or { }
- local new = { }
- if not tables[old] then
- tables[old] = new
- end
- for k,v in next, old do
- new[k] = (type(v) == "table" and (tables[v] or copy(v, tables))) or v
- end
- local mt = getmetatable(old)
- if mt then
- setmetatable(new,mt)
- end
- return new
- else
- return { }
- end
-end
-
-xml.copy = copy
-
---[[ldx--
-<p>In <l n='context'/> serializing the tree or parts of the tree is a major
-actitivity which is why the following function is pretty optimized resulting
-in a few more lines of code than needed. The variant that uses the formatting
-function for all components is about 15% slower than the concatinating
-alternative.</p>
---ldx]]--
-
--- todo: add <?xml version='1.0' standalone='yes'?> when not present
-
-function xml.checkbom(root) -- can be made faster
- if root.ri then
- local dt = root.dt
- for k=1,#dt do
- local v = dt[k]
- if type(v) == "table" and v.special and v.tg == "@pi@" and find(v.dt[1],"xml.*version=") then
- return
- end
- end
- insert(dt, 1, { special = true, ns = "", tg = "@pi@", dt = { "xml version='1.0' standalone='yes'" } } )
- insert(dt, 2, "\n" )
- end
-end
-
---[[ldx--
-<p>At the cost of some 25% runtime overhead you can first convert the tree to a string
-and then handle the lot.</p>
---ldx]]--
-
--- new experimental reorganized serialize
-
-local function verbose_element(e,handlers) -- options
- local handle = handlers.handle
- local serialize = handlers.serialize
- local ens, etg, eat, edt, ern = e.ns, e.tg, e.at, e.dt, e.rn
- local ats = eat and next(eat) and { }
- if ats then
- for k,v in next, eat do
- ats[#ats+1] = formatters['%s=%q'](k,escaped(v))
- end
- end
- if ern and trace_entities and ern ~= ens then
- ens = ern
- end
- if ens ~= "" then
- if edt and #edt > 0 then
- if ats then
- handle("<",ens,":",etg," ",concat(ats," "),">")
- else
- handle("<",ens,":",etg,">")
- end
- for i=1,#edt do
- local e = edt[i]
- if type(e) == "string" then
- handle(escaped(e))
- else
- serialize(e,handlers)
- end
- end
- handle("</",ens,":",etg,">")
- else
- if ats then
- handle("<",ens,":",etg," ",concat(ats," "),"/>")
- else
- handle("<",ens,":",etg,"/>")
- end
- end
- else
- if edt and #edt > 0 then
- if ats then
- handle("<",etg," ",concat(ats," "),">")
- else
- handle("<",etg,">")
- end
- for i=1,#edt do
- local e = edt[i]
- if type(e) == "string" then
- handle(escaped(e)) -- option: hexify escaped entities
- else
- serialize(e,handlers)
- end
- end
- handle("</",etg,">")
- else
- if ats then
- handle("<",etg," ",concat(ats," "),"/>")
- else
- handle("<",etg,"/>")
- end
- end
- end
-end
-
-local function verbose_pi(e,handlers)
- handlers.handle("<?",e.dt[1],"?>")
-end
-
-local function verbose_comment(e,handlers)
- handlers.handle("<!--",e.dt[1],"-->")
-end
-
-local function verbose_cdata(e,handlers)
- handlers.handle("<![CDATA[", e.dt[1],"]]>")
-end
-
-local function verbose_doctype(e,handlers)
- handlers.handle("<!DOCTYPE ",e.dt[1],">")
-end
-
-local function verbose_root(e,handlers)
- handlers.serialize(e.dt,handlers)
-end
-
-local function verbose_text(e,handlers)
- handlers.handle(escaped(e))
-end
-
-local function verbose_document(e,handlers)
- local serialize = handlers.serialize
- local functions = handlers.functions
- for i=1,#e do
- local ei = e[i]
- if type(ei) == "string" then
- functions["@tx@"](ei,handlers)
- else
- serialize(ei,handlers)
- end
- end
-end
-
-local function serialize(e,handlers,...)
- local initialize = handlers.initialize
- local finalize = handlers.finalize
- local functions = handlers.functions
- if initialize then
- local state = initialize(...)
- if not state == true then
- return state
- end
- end
- local etg = e.tg
- if etg then
- (functions[etg] or functions["@el@"])(e,handlers)
- -- elseif type(e) == "string" then
- -- functions["@tx@"](e,handlers)
- else
- functions["@dc@"](e,handlers) -- dc ?
- end
- if finalize then
- return finalize()
- end
-end
-
-local function xserialize(e,handlers)
- local functions = handlers.functions
- local etg = e.tg
- if etg then
- (functions[etg] or functions["@el@"])(e,handlers)
- -- elseif type(e) == "string" then
- -- functions["@tx@"](e,handlers)
- else
- functions["@dc@"](e,handlers)
- end
-end
-
-local handlers = { }
-
-local function newhandlers(settings)
- local t = table.copy(handlers[settings and settings.parent or "verbose"] or { }) -- merge
- if settings then
- for k,v in next, settings do
- if type(v) == "table" then
- local tk = t[k] if not tk then tk = { } t[k] = tk end
- for kk,vv in next, v do
- tk[kk] = vv
- end
- else
- t[k] = v
- end
- end
- if settings.name then
- handlers[settings.name] = t
- end
- end
- utilities.storage.mark(t)
- return t
-end
-
-local nofunction = function() end
-
-function xml.sethandlersfunction(handler,name,fnc)
- handler.functions[name] = fnc or nofunction
-end
-
-function xml.gethandlersfunction(handler,name)
- return handler.functions[name]
-end
-
-function xml.gethandlers(name)
- return handlers[name]
-end
-
-newhandlers {
- name = "verbose",
- initialize = false, -- faster than nil and mt lookup
- finalize = false, -- faster than nil and mt lookup
- serialize = xserialize,
- handle = print,
- functions = {
- ["@dc@"] = verbose_document,
- ["@dt@"] = verbose_doctype,
- ["@rt@"] = verbose_root,
- ["@el@"] = verbose_element,
- ["@pi@"] = verbose_pi,
- ["@cm@"] = verbose_comment,
- ["@cd@"] = verbose_cdata,
- ["@tx@"] = verbose_text,
- }
-}
-
---[[ldx--
-<p>How you deal with saving data depends on your preferences. For a 40 MB database
-file the timing on a 2.3 Core Duo are as follows (time in seconds):</p>
-
-<lines>
-1.3 : load data from file to string
-6.1 : convert string into tree
-5.3 : saving in file using xmlsave
-6.8 : converting to string using xml.tostring
-3.6 : saving converted string in file
-</lines>
-
-<p>Beware, these were timing with the old routine but measurements will not be that
-much different I guess.</p>
---ldx]]--
-
--- maybe this will move to lxml-xml
-
-local result
-
-local xmlfilehandler = newhandlers {
- name = "file",
- initialize = function(name)
- result = io.open(name,"wb")
- return result
- end,
- finalize = function()
- result:close()
- return true
- end,
- handle = function(...)
- result:write(...)
- end,
-}
-
--- no checking on writeability here but not faster either
---
--- local xmlfilehandler = newhandlers {
--- initialize = function(name)
--- io.output(name,"wb")
--- return true
--- end,
--- finalize = function()
--- io.close()
--- return true
--- end,
--- handle = io.write,
--- }
-
-function xml.save(root,name)
- serialize(root,xmlfilehandler,name)
-end
-
-local result
-
-local xmlstringhandler = newhandlers {
- name = "string",
- initialize = function()
- result = { }
- return result
- end,
- finalize = function()
- return concat(result)
- end,
- handle = function(...)
- result[#result+1] = concat { ... }
- end,
-}
-
-local function xmltostring(root) -- 25% overhead due to collecting
- if not root then
- return ""
- elseif type(root) == "string" then
- return root
- else -- if next(root) then -- next is faster than type (and >0 test)
- return serialize(root,xmlstringhandler) or ""
- end
-end
-
-local function __tostring(root) -- inline
- return (root and xmltostring(root)) or ""
-end
-
-initialize_mt = function(root) -- redefinition
- mt = { __tostring = __tostring, __index = root }
-end
-
-xml.defaulthandlers = handlers
-xml.newhandlers = newhandlers
-xml.serialize = serialize
-xml.tostring = xmltostring
-
---[[ldx--
-<p>The next function operated on the content only and needs a handle function
-that accepts a string.</p>
---ldx]]--
-
-local function xmlstring(e,handle)
- if not handle or (e.special and e.tg ~= "@rt@") then
- -- nothing
- elseif e.tg then
- local edt = e.dt
- if edt then
- for i=1,#edt do
- xmlstring(edt[i],handle)
- end
- end
- else
- handle(e)
- end
-end
-
-xml.string = xmlstring
-
---[[ldx--
-<p>A few helpers:</p>
---ldx]]--
-
---~ xmlsetproperty(root,"settings",settings)
-
-function xml.settings(e)
- while e do
- local s = e.settings
- if s then
- return s
- else
- e = e.__p__
- end
- end
- return nil
-end
-
-function xml.root(e)
- local r = e
- while e do
- e = e.__p__
- if e then
- r = e
- end
- end
- return r
-end
-
-function xml.parent(root)
- return root.__p__
-end
-
-function xml.body(root)
- return root.ri and root.dt[root.ri] or root -- not ok yet
-end
-
-function xml.name(root)
- if not root then
- return ""
- end
- local ns = root.ns
- local tg = root.tg
- if ns == "" then
- return tg
- else
- return ns .. ":" .. tg
- end
-end
-
---[[ldx--
-<p>The next helper erases an element but keeps the table as it is,
-and since empty strings are not serialized (effectively) it does
-not harm. Copying the table would take more time. Usage:</p>
---ldx]]--
-
-function xml.erase(dt,k)
- if dt then
- if k then
- dt[k] = ""
- else for k=1,#dt do
- dt[1] = { "" }
- end end
- end
-end
-
---[[ldx--
-<p>The next helper assigns a tree (or string). Usage:</p>
-
-<typing>
-dt[k] = xml.assign(root) or xml.assign(dt,k,root)
-</typing>
---ldx]]--
-
-function xml.assign(dt,k,root)
- if dt and k then
- dt[k] = type(root) == "table" and xml.body(root) or root
- return dt[k]
- else
- return xml.body(root)
- end
-end
-
--- the following helpers may move
-
---[[ldx--
-<p>The next helper assigns a tree (or string). Usage:</p>
-<typing>
-xml.tocdata(e)
-xml.tocdata(e,"error")
-</typing>
---ldx]]--
-
-function xml.tocdata(e,wrapper) -- a few more in the aux module
- local whatever = type(e) == "table" and xmltostring(e.dt) or e or ""
- if wrapper then
- whatever = formatters["<%s>%s</%s>"](wrapper,whatever,wrapper)
- end
- local t = { special = true, ns = "", tg = "@cd@", at = { }, rn = "", dt = { whatever }, __p__ = e }
- setmetatable(t,getmetatable(e))
- e.dt = { t }
-end
-
-function xml.makestandalone(root)
- if root.ri then
- local dt = root.dt
- for k=1,#dt do
- local v = dt[k]
- if type(v) == "table" and v.special and v.tg == "@pi@" then
- local txt = v.dt[1]
- if find(txt,"xml.*version=") then
- v.dt[1] = txt .. " standalone='yes'"
- break
- end
- end
- end
- end
- return root
-end
-
-function xml.kind(e)
- local dt = e and e.dt
- if dt then
- local n = #dt
- if n == 1 then
- local d = dt[1]
- if d.special then
- local tg = d.tg
- if tg == "@cd@" then
- return "cdata"
- elseif tg == "@cm" then
- return "comment"
- elseif tg == "@pi@" then
- return "instruction"
- elseif tg == "@dt@" then
- return "declaration"
- end
- elseif type(d) == "string" then
- return "text"
- end
- return "element"
- elseif n > 0 then
- return "mixed"
- end
- end
- return "empty"
-end
+if not modules then modules = { } end modules ['lxml-tab'] = {
+ version = 1.001,
+ comment = "this module is the basis for the lxml-* ones",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- this module needs a cleanup: check latest lpeg, passing args, (sub)grammar, etc etc
+-- stripping spaces from e.g. cont-en.xml saves .2 sec runtime so it's not worth the
+-- trouble
+
+-- todo: when serializing optionally remap named entities to hex (if known in char-ent.lua)
+-- maybe when letter -> utf, else name .. then we need an option to the serializer .. a bit
+-- of work so we delay this till we cleanup
+
+local trace_entities = false trackers.register("xml.entities", function(v) trace_entities = v end)
+
+local report_xml = logs and logs.reporter("xml","core") or function(...) print(string.format(...)) end
+
+--[[ldx--
+<p>The parser used here is inspired by the variant discussed in the lua book, but
+handles comment and processing instructions, has a different structure, provides
+parent access; a first version used different trickery but was less optimized to we
+went this route. First we had a find based parser, now we have an <l n='lpeg'/> based one.
+The find based parser can be found in l-xml-edu.lua along with other older code.</p>
+
+<p>Beware, the interface may change. For instance at, ns, tg, dt may get more
+verbose names. Once the code is stable we will also remove some tracing and
+optimize the code.</p>
+
+<p>I might even decide to reimplement the parser using the latest <l n='lpeg'/> trickery
+as the current variant was written when <l n='lpeg'/> showed up and it's easier now to
+build tables in one go.</p>
+--ldx]]--
+
+xml = xml or { }
+local xml = xml
+
+--~ local xml = xml
+
+local concat, remove, insert = table.concat, table.remove, table.insert
+local type, next, setmetatable, getmetatable, tonumber = type, next, setmetatable, getmetatable, tonumber
+local lower, find, match, gsub = string.lower, string.find, string.match, string.gsub
+local utfchar = utf.char
+local lpegmatch = lpeg.match
+local P, S, R, C, V, C, Cs = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.C, lpeg.Cs
+local formatters = string.formatters
+
+--[[ldx--
+<p>First a hack to enable namespace resolving. A namespace is characterized by
+a <l n='url'/>. The following function associates a namespace prefix with a
+pattern. We use <l n='lpeg'/>, which in this case is more than twice as fast as a
+find based solution where we loop over an array of patterns. Less code and
+much cleaner.</p>
+--ldx]]--
+
+xml.xmlns = xml.xmlns or { }
+
+local check = P(false)
+local parse = check
+
+--[[ldx--
+<p>The next function associates a namespace prefix with an <l n='url'/>. This
+normally happens independent of parsing.</p>
+
+<typing>
+xml.registerns("mml","mathml")
+</typing>
+--ldx]]--
+
+function xml.registerns(namespace, pattern) -- pattern can be an lpeg
+ check = check + C(P(lower(pattern))) / namespace
+ parse = P { P(check) + 1 * V(1) }
+end
+
+--[[ldx--
+<p>The next function also registers a namespace, but this time we map a
+given namespace prefix onto a registered one, using the given
+<l n='url'/>. This used for attributes like <t>xmlns:m</t>.</p>
+
+<typing>
+xml.checkns("m","http://www.w3.org/mathml")
+</typing>
+--ldx]]--
+
+function xml.checkns(namespace,url)
+ local ns = lpegmatch(parse,lower(url))
+ if ns and namespace ~= ns then
+ xml.xmlns[namespace] = ns
+ end
+end
+
+--[[ldx--
+<p>Next we provide a way to turn an <l n='url'/> into a registered
+namespace. This used for the <t>xmlns</t> attribute.</p>
+
+<typing>
+resolvedns = xml.resolvens("http://www.w3.org/mathml")
+</typing>
+
+This returns <t>mml</t>.
+--ldx]]--
+
+function xml.resolvens(url)
+ return lpegmatch(parse,lower(url)) or ""
+end
+
+--[[ldx--
+<p>A namespace in an element can be remapped onto the registered
+one efficiently by using the <t>xml.xmlns</t> table.</p>
+--ldx]]--
+
+--[[ldx--
+<p>This version uses <l n='lpeg'/>. We follow the same approach as before, stack and top and
+such. This version is about twice as fast which is mostly due to the fact that
+we don't have to prepare the stream for cdata, doctype etc etc. This variant is
+is dedicated to Luigi Scarso, who challenged me with 40 megabyte <l n='xml'/> files that
+took 12.5 seconds to load (1.5 for file io and the rest for tree building). With
+the <l n='lpeg'/> implementation we got that down to less 7.3 seconds. Loading the 14
+<l n='context'/> interface definition files (2.6 meg) went down from 1.05 seconds to 0.55.</p>
+
+<p>Next comes the parser. The rather messy doctype definition comes in many
+disguises so it is no surprice that later on have to dedicate quite some
+<l n='lpeg'/> code to it.</p>
+
+<typing>
+<!DOCTYPE Something PUBLIC "... ..." "..." [ ... ] >
+<!DOCTYPE Something PUBLIC "... ..." "..." >
+<!DOCTYPE Something SYSTEM "... ..." [ ... ] >
+<!DOCTYPE Something SYSTEM "... ..." >
+<!DOCTYPE Something [ ... ] >
+<!DOCTYPE Something >
+</typing>
+
+<p>The code may look a bit complex but this is mostly due to the fact that we
+resolve namespaces and attach metatables. There is only one public function:</p>
+
+<typing>
+local x = xml.convert(somestring)
+</typing>
+
+<p>An optional second boolean argument tells this function not to create a root
+element.</p>
+
+<p>Valid entities are:</p>
+
+<typing>
+<!ENTITY xxxx SYSTEM "yyyy" NDATA zzzz>
+<!ENTITY xxxx PUBLIC "yyyy" >
+<!ENTITY xxxx "yyyy" >
+</typing>
+--ldx]]--
+
+-- not just one big nested table capture (lpeg overflow)
+
+local nsremap, resolvens = xml.xmlns, xml.resolvens
+
+local stack = { }
+local top = { }
+local dt = { }
+local at = { }
+local xmlns = { }
+local errorstr = nil
+local entities = { }
+local strip = false
+local cleanup = false
+local utfize = false
+local resolve_predefined = false
+local unify_predefined = false
+
+local dcache = { }
+local hcache = { }
+local acache = { }
+
+local mt = { }
+
+local function initialize_mt(root)
+ mt = { __index = root } -- will be redefined later
+end
+
+function xml.setproperty(root,k,v)
+ getmetatable(root).__index[k] = v
+end
+
+function xml.checkerror(top,toclose)
+ return "" -- can be set
+end
+
+local function add_attribute(namespace,tag,value)
+ if cleanup and #value > 0 then
+ value = cleanup(value) -- new
+ end
+ if tag == "xmlns" then
+ xmlns[#xmlns+1] = resolvens(value)
+ at[tag] = value
+ elseif namespace == "" then
+ at[tag] = value
+ elseif namespace == "xmlns" then
+ xml.checkns(tag,value)
+ at["xmlns:" .. tag] = value
+ else
+ -- for the moment this way:
+ at[namespace .. ":" .. tag] = value
+ end
+end
+
+local function add_empty(spacing, namespace, tag)
+ if #spacing > 0 then
+ dt[#dt+1] = spacing
+ end
+ local resolved = namespace == "" and xmlns[#xmlns] or nsremap[namespace] or namespace
+ top = stack[#stack]
+ dt = top.dt
+ local t = { ns=namespace or "", rn=resolved, tg=tag, at=at, dt={}, __p__ = top }
+ dt[#dt+1] = t
+ setmetatable(t, mt)
+ if at.xmlns then
+ remove(xmlns)
+ end
+ at = { }
+end
+
+local function add_begin(spacing, namespace, tag)
+ if #spacing > 0 then
+ dt[#dt+1] = spacing
+ end
+ local resolved = namespace == "" and xmlns[#xmlns] or nsremap[namespace] or namespace
+ top = { ns=namespace or "", rn=resolved, tg=tag, at=at, dt={}, __p__ = stack[#stack] }
+ setmetatable(top, mt)
+ dt = top.dt
+ stack[#stack+1] = top
+ at = { }
+end
+
+local function add_end(spacing, namespace, tag)
+ if #spacing > 0 then
+ dt[#dt+1] = spacing
+ end
+ local toclose = remove(stack)
+ top = stack[#stack]
+ if #stack < 1 then
+ errorstr = formatters["unable to close %s %s"](tag,xml.checkerror(top,toclose) or "")
+ elseif toclose.tg ~= tag then -- no namespace check
+ errorstr = formatters["unable to close %s with %s %s"](toclose.tg,tag,xml.checkerror(top,toclose) or "")
+ end
+ dt = top.dt
+ dt[#dt+1] = toclose
+ -- dt[0] = top -- nasty circular reference when serializing table
+ if toclose.at.xmlns then
+ remove(xmlns)
+ end
+end
+
+local function add_text(text)
+ if cleanup and #text > 0 then
+ dt[#dt+1] = cleanup(text)
+ else
+ dt[#dt+1] = text
+ end
+end
+
+local function add_special(what, spacing, text)
+ if #spacing > 0 then
+ dt[#dt+1] = spacing
+ end
+ if strip and (what == "@cm@" or what == "@dt@") then
+ -- forget it
+ else
+ dt[#dt+1] = { special=true, ns="", tg=what, dt={ text } }
+ end
+end
+
+local function set_message(txt)
+ errorstr = "garbage at the end of the file: " .. gsub(txt,"([ \n\r\t]*)","")
+end
+
+local reported_attribute_errors = { }
+
+local function attribute_value_error(str)
+ if not reported_attribute_errors[str] then
+ report_xml("invalid attribute value %a",str)
+ reported_attribute_errors[str] = true
+ at._error_ = str
+ end
+ return str
+end
+
+local function attribute_specification_error(str)
+ if not reported_attribute_errors[str] then
+ report_xml("invalid attribute specification %a",str)
+ reported_attribute_errors[str] = true
+ at._error_ = str
+ end
+ return str
+end
+
+xml.placeholders = {
+ unknown_dec_entity = function(str) return str == "" and "&error;" or formatters["&%s;"](str) end,
+ unknown_hex_entity = function(str) return formatters["&#x%s;"](str) end,
+ unknown_any_entity = function(str) return formatters["&#x%s;"](str) end,
+}
+
+local placeholders = xml.placeholders
+
+local function fromhex(s)
+ local n = tonumber(s,16)
+ if n then
+ return utfchar(n)
+ else
+ return formatters["h:%s"](s), true
+ end
+end
+
+local function fromdec(s)
+ local n = tonumber(s)
+ if n then
+ return utfchar(n)
+ else
+ return formatters["d:%s"](s), true
+ end
+end
+
+-- one level expansion (simple case), no checking done
+
+local rest = (1-P(";"))^0
+local many = P(1)^0
+
+local parsedentity =
+ P("&") * (P("#x")*(rest/fromhex) + P("#")*(rest/fromdec)) * P(";") * P(-1) +
+ (P("#x")*(many/fromhex) + P("#")*(many/fromdec))
+
+-- parsing in the xml file
+
+local predefined_unified = {
+ [38] = "&amp;",
+ [42] = "&quot;",
+ [47] = "&apos;",
+ [74] = "&lt;",
+ [76] = "&gt;",
+}
+
+local predefined_simplified = {
+ [38] = "&", amp = "&",
+ [42] = '"', quot = '"',
+ [47] = "'", apos = "'",
+ [74] = "<", lt = "<",
+ [76] = ">", gt = ">",
+}
+
+local nofprivates = 0xF0000 -- shared but seldom used
+
+local privates_u = { -- unescaped
+ [ [[&]] ] = "&amp;",
+ [ [["]] ] = "&quot;",
+ [ [[']] ] = "&apos;",
+ [ [[<]] ] = "&lt;",
+ [ [[>]] ] = "&gt;",
+}
+
+local privates_p = {
+}
+
+local privates_n = {
+ -- keeps track of defined ones
+}
+
+local escaped = utf.remapper(privates_u)
+
+local function unescaped(s)
+ local p = privates_n[s]
+ if not p then
+ nofprivates = nofprivates + 1
+ p = utfchar(nofprivates)
+ privates_n[s] = p
+ s = "&" .. s .. ";" -- todo: use char-ent to map to hex
+ privates_u[p] = s
+ privates_p[p] = s
+ end
+ return p
+end
+
+local unprivatized = utf.remapper(privates_p)
+
+xml.privatetoken = unescaped
+xml.unprivatized = unprivatized
+xml.privatecodes = privates_n
+
+local function handle_hex_entity(str)
+ local h = hcache[str]
+ if not h then
+ local n = tonumber(str,16)
+ h = unify_predefined and predefined_unified[n]
+ if h then
+ if trace_entities then
+ report_xml("utfize, converting hex entity &#x%s; into %a",str,h)
+ end
+ elseif utfize then
+ h = (n and utfchar(n)) or xml.unknown_hex_entity(str) or ""
+ if not n then
+ report_xml("utfize, ignoring hex entity &#x%s;",str)
+ elseif trace_entities then
+ report_xml("utfize, converting hex entity &#x%s; into %a",str,h)
+ end
+ else
+ if trace_entities then
+ report_xml("found entity &#x%s;",str)
+ end
+ h = "&#x" .. str .. ";"
+ end
+ hcache[str] = h
+ end
+ return h
+end
+
+local function handle_dec_entity(str)
+ local d = dcache[str]
+ if not d then
+ local n = tonumber(str)
+ d = unify_predefined and predefined_unified[n]
+ if d then
+ if trace_entities then
+ report_xml("utfize, converting dec entity &#%s; into %a",str,d)
+ end
+ elseif utfize then
+ d = (n and utfchar(n)) or placeholders.unknown_dec_entity(str) or ""
+ if not n then
+ report_xml("utfize, ignoring dec entity &#%s;",str)
+ elseif trace_entities then
+ report_xml("utfize, converting dec entity &#%s; into %a",str,d)
+ end
+ else
+ if trace_entities then
+ report_xml("found entity &#%s;",str)
+ end
+ d = "&#" .. str .. ";"
+ end
+ dcache[str] = d
+ end
+ return d
+end
+
+xml.parsedentitylpeg = parsedentity
+
+local function handle_any_entity(str)
+ if resolve then
+ local a = acache[str] -- per instance ! todo
+ if not a then
+ a = resolve_predefined and predefined_simplified[str]
+ if a then
+ if trace_entities then
+ report_xml("resolving entity &%s; to predefined %a",str,a)
+ end
+ else
+ if type(resolve) == "function" then
+ a = resolve(str) or entities[str]
+ else
+ a = entities[str]
+ end
+ if a then
+ if type(a) == "function" then
+ if trace_entities then
+ report_xml("expanding entity &%s; to function call",str)
+ end
+ a = a(str) or ""
+ end
+ a = lpegmatch(parsedentity,a) or a -- for nested
+ if trace_entities then
+ report_xml("resolving entity &%s; to internal %a",str,a)
+ end
+ else
+ local unknown_any_entity = placeholders.unknown_any_entity
+ if unknown_any_entity then
+ a = unknown_any_entity(str) or ""
+ end
+ if a then
+ if trace_entities then
+ report_xml("resolving entity &%s; to external %s",str,a)
+ end
+ else
+ if trace_entities then
+ report_xml("keeping entity &%s;",str)
+ end
+ if str == "" then
+ a = "&error;"
+ else
+ a = "&" .. str .. ";"
+ end
+ end
+ end
+ end
+ acache[str] = a
+ elseif trace_entities then
+ if not acache[str] then
+ report_xml("converting entity &%s; to %a",str,a)
+ acache[str] = a
+ end
+ end
+ return a
+ else
+ local a = acache[str]
+ if not a then
+ a = resolve_predefined and predefined_simplified[str]
+ if a then
+ -- one of the predefined
+ acache[str] = a
+ if trace_entities then
+ report_xml("entity &%s; becomes %a",str,a)
+ end
+ elseif str == "" then
+ if trace_entities then
+ report_xml("invalid entity &%s;",str)
+ end
+ a = "&error;"
+ acache[str] = a
+ else
+ if trace_entities then
+ report_xml("entity &%s; is made private",str)
+ end
+ -- a = "&" .. str .. ";"
+ a = unescaped(str)
+ acache[str] = a
+ end
+ end
+ return a
+ end
+end
+
+local function handle_end_entity(chr)
+ report_xml("error in entity, %a found instead of %a",chr,";")
+end
+
+local space = S(' \r\n\t')
+local open = P('<')
+local close = P('>')
+local squote = S("'")
+local dquote = S('"')
+local equal = P('=')
+local slash = P('/')
+local colon = P(':')
+local semicolon = P(';')
+local ampersand = P('&')
+local valid = R('az', 'AZ', '09') + S('_-.')
+local name_yes = C(valid^1) * colon * C(valid^1)
+local name_nop = C(P(true)) * C(valid^1)
+local name = name_yes + name_nop
+local utfbom = lpeg.patterns.utfbom -- no capture
+local spacing = C(space^0)
+
+----- entitycontent = (1-open-semicolon)^0
+local anyentitycontent = (1-open-semicolon-space-close)^0
+local hexentitycontent = R("AF","af","09")^0
+local decentitycontent = R("09")^0
+local parsedentity = P("#")/"" * (
+ P("x")/"" * (hexentitycontent/handle_hex_entity) +
+ (decentitycontent/handle_dec_entity)
+ ) + (anyentitycontent/handle_any_entity)
+local entity = ampersand/"" * parsedentity * ( (semicolon/"") + #(P(1)/handle_end_entity))
+
+local text_unparsed = C((1-open)^1)
+local text_parsed = Cs(((1-open-ampersand)^1 + entity)^1)
+
+local somespace = space^1
+local optionalspace = space^0
+
+----- value = (squote * C((1 - squote)^0) * squote) + (dquote * C((1 - dquote)^0) * dquote) -- ampersand and < also invalid in value
+local value = (squote * Cs((entity + (1 - squote))^0) * squote) + (dquote * Cs((entity + (1 - dquote))^0) * dquote) -- ampersand and < also invalid in value
+
+local endofattributes = slash * close + close -- recovery of flacky html
+local whatever = space * name * optionalspace * equal
+----- wrongvalue = C(P(1-whatever-close)^1 + P(1-close)^1) / attribute_value_error
+----- wrongvalue = C(P(1-whatever-endofattributes)^1 + P(1-endofattributes)^1) / attribute_value_error
+----- wrongvalue = C(P(1-space-endofattributes)^1) / attribute_value_error
+local wrongvalue = Cs(P(entity + (1-space-endofattributes))^1) / attribute_value_error
+
+local attributevalue = value + wrongvalue
+
+local attribute = (somespace * name * optionalspace * equal * optionalspace * attributevalue) / add_attribute
+----- attributes = (attribute)^0
+
+local attributes = (attribute + somespace^-1 * (((1-endofattributes)^1)/attribute_specification_error))^0
+
+local parsedtext = text_parsed / add_text
+local unparsedtext = text_unparsed / add_text
+local balanced = P { "[" * ((1 - S"[]") + V(1))^0 * "]" } -- taken from lpeg manual, () example
+
+local emptyelement = (spacing * open * name * attributes * optionalspace * slash * close) / add_empty
+local beginelement = (spacing * open * name * attributes * optionalspace * close) / add_begin
+local endelement = (spacing * open * slash * name * optionalspace * close) / add_end
+
+local begincomment = open * P("!--")
+local endcomment = P("--") * close
+local begininstruction = open * P("?")
+local endinstruction = P("?") * close
+local begincdata = open * P("![CDATA[")
+local endcdata = P("]]") * close
+
+local someinstruction = C((1 - endinstruction)^0)
+local somecomment = C((1 - endcomment )^0)
+local somecdata = C((1 - endcdata )^0)
+
+local function normalentity(k,v ) entities[k] = v end
+local function systementity(k,v,n) entities[k] = v end
+local function publicentity(k,v,n) entities[k] = v end
+
+-- todo: separate dtd parser
+
+local begindoctype = open * P("!DOCTYPE")
+local enddoctype = close
+local beginset = P("[")
+local endset = P("]")
+local doctypename = C((1-somespace-close)^0)
+local elementdoctype = optionalspace * P("<!ELEMENT") * (1-close)^0 * close
+
+local basiccomment = begincomment * ((1 - endcomment)^0) * endcomment
+
+local normalentitytype = (doctypename * somespace * value)/normalentity
+local publicentitytype = (doctypename * somespace * P("PUBLIC") * somespace * value)/publicentity
+local systementitytype = (doctypename * somespace * P("SYSTEM") * somespace * value * somespace * P("NDATA") * somespace * doctypename)/systementity
+local entitydoctype = optionalspace * P("<!ENTITY") * somespace * (systementitytype + publicentitytype + normalentitytype) * optionalspace * close
+
+-- we accept comments in doctypes
+
+local doctypeset = beginset * optionalspace * P(elementdoctype + entitydoctype + basiccomment + space)^0 * optionalspace * endset
+local definitiondoctype= doctypename * somespace * doctypeset
+local publicdoctype = doctypename * somespace * P("PUBLIC") * somespace * value * somespace * value * somespace * doctypeset
+local systemdoctype = doctypename * somespace * P("SYSTEM") * somespace * value * somespace * doctypeset
+local simpledoctype = (1-close)^1 -- * balanced^0
+local somedoctype = C((somespace * (publicdoctype + systemdoctype + definitiondoctype + simpledoctype) * optionalspace)^0)
+local somedoctype = C((somespace * (publicdoctype + systemdoctype + definitiondoctype + simpledoctype) * optionalspace)^0)
+
+local instruction = (spacing * begininstruction * someinstruction * endinstruction) / function(...) add_special("@pi@",...) end
+local comment = (spacing * begincomment * somecomment * endcomment ) / function(...) add_special("@cm@",...) end
+local cdata = (spacing * begincdata * somecdata * endcdata ) / function(...) add_special("@cd@",...) end
+local doctype = (spacing * begindoctype * somedoctype * enddoctype ) / function(...) add_special("@dt@",...) end
+
+-- nicer but slower:
+--
+-- local instruction = (Cc("@pi@") * spacing * begininstruction * someinstruction * endinstruction) / add_special
+-- local comment = (Cc("@cm@") * spacing * begincomment * somecomment * endcomment ) / add_special
+-- local cdata = (Cc("@cd@") * spacing * begincdata * somecdata * endcdata ) / add_special
+-- local doctype = (Cc("@dt@") * spacing * begindoctype * somedoctype * enddoctype ) / add_special
+
+local trailer = space^0 * (text_unparsed/set_message)^0
+
+-- comment + emptyelement + text + cdata + instruction + V("parent"), -- 6.5 seconds on 40 MB database file
+-- text + comment + emptyelement + cdata + instruction + V("parent"), -- 5.8
+-- text + V("parent") + emptyelement + comment + cdata + instruction, -- 5.5
+
+local grammar_parsed_text = P { "preamble",
+ preamble = utfbom^0 * instruction^0 * (doctype + comment + instruction)^0 * V("parent") * trailer,
+ parent = beginelement * V("children")^0 * endelement,
+ children = parsedtext + V("parent") + emptyelement + comment + cdata + instruction,
+}
+
+local grammar_unparsed_text = P { "preamble",
+ preamble = utfbom^0 * instruction^0 * (doctype + comment + instruction)^0 * V("parent") * trailer,
+ parent = beginelement * V("children")^0 * endelement,
+ children = unparsedtext + V("parent") + emptyelement + comment + cdata + instruction,
+}
+
+-- maybe we will add settings to result as well
+
+local function _xmlconvert_(data, settings)
+ settings = settings or { } -- no_root strip_cm_and_dt given_entities parent_root error_handler
+ --
+ strip = settings.strip_cm_and_dt
+ utfize = settings.utfize_entities
+ resolve = settings.resolve_entities
+ resolve_predefined = settings.resolve_predefined_entities -- in case we have escaped entities
+ unify_predefined = settings.unify_predefined_entities -- &#038; -> &amp;
+ cleanup = settings.text_cleanup
+ entities = settings.entities or { }
+ --
+ if utfize == nil then
+ settings.utfize_entities = true
+ utfize = true
+ end
+ if resolve_predefined == nil then
+ settings.resolve_predefined_entities = true
+ resolve_predefined = true
+ end
+ --
+ stack, top, at, xmlns, errorstr = { }, { }, { }, { }, nil
+ acache, hcache, dcache = { }, { }, { } -- not stored
+ reported_attribute_errors = { }
+ if settings.parent_root then
+ mt = getmetatable(settings.parent_root)
+ else
+ initialize_mt(top)
+ end
+ stack[#stack+1] = top
+ top.dt = { }
+ dt = top.dt
+ if not data or data == "" then
+ errorstr = "empty xml file"
+ elseif utfize or resolve then
+ if lpegmatch(grammar_parsed_text,data) then
+ errorstr = ""
+ else
+ errorstr = "invalid xml file - parsed text"
+ end
+ elseif type(data) == "string" then
+ if lpegmatch(grammar_unparsed_text,data) then
+ errorstr = ""
+ else
+ errorstr = "invalid xml file - unparsed text"
+ end
+ else
+ errorstr = "invalid xml file - no text at all"
+ end
+ local result
+ if errorstr and errorstr ~= "" then
+ result = { dt = { { ns = "", tg = "error", dt = { errorstr }, at={ }, er = true } } }
+ setmetatable(stack, mt)
+ local errorhandler = settings.error_handler
+ if errorhandler == false then
+ -- no error message
+ else
+ errorhandler = errorhandler or xml.errorhandler
+ if errorhandler then
+ local currentresource = settings.currentresource
+ if currentresource and currentresource ~= "" then
+ xml.errorhandler(formatters["load error in [%s]: %s"](currentresource,errorstr))
+ else
+ xml.errorhandler(formatters["load error: %s"](errorstr))
+ end
+ end
+ end
+ else
+ result = stack[1]
+ end
+ if not settings.no_root then
+ result = { special = true, ns = "", tg = '@rt@', dt = result.dt, at={ }, entities = entities, settings = settings }
+ setmetatable(result, mt)
+ local rdt = result.dt
+ for k=1,#rdt do
+ local v = rdt[k]
+ if type(v) == "table" and not v.special then -- always table -)
+ result.ri = k -- rootindex
+ v.__p__ = result -- new, experiment, else we cannot go back to settings, we need to test this !
+ break
+ end
+ end
+ end
+ if errorstr and errorstr ~= "" then
+ result.error = true
+ end
+ result.statistics = {
+ entities = {
+ decimals = dcache,
+ hexadecimals = hcache,
+ names = acache,
+ }
+ }
+ strip, utfize, resolve, resolve_predefined = nil, nil, nil, nil
+ unify_predefined, cleanup, entities = nil, nil, nil
+ stack, top, at, xmlns, errorstr = nil, nil, nil, nil, nil
+ acache, hcache, dcache = nil, nil, nil
+ reported_attribute_errors, mt, errorhandler = nil, nil, nil
+ return result
+end
+
+-- Because we can have a crash (stack issues) with faulty xml, we wrap this one
+-- in a protector:
+
+function xmlconvert(data,settings)
+ local ok, result = pcall(function() return _xmlconvert_(data,settings) end)
+ if ok then
+ return result
+ else
+ return _xmlconvert_("",settings)
+ end
+end
+
+xml.convert = xmlconvert
+
+function xml.inheritedconvert(data,xmldata) -- xmldata is parent
+ local settings = xmldata.settings
+ if settings then
+ settings.parent_root = xmldata -- to be tested
+ end
+ -- settings.no_root = true
+ local xc = xmlconvert(data,settings) -- hm, we might need to locate settings
+ -- xc.settings = nil
+ -- xc.entities = nil
+ -- xc.special = nil
+ -- xc.ri = nil
+ -- print(xc.tg)
+ return xc
+end
+
+--[[ldx--
+<p>Packaging data in an xml like table is done with the following
+function. Maybe it will go away (when not used).</p>
+--ldx]]--
+
+function xml.is_valid(root)
+ return root and root.dt and root.dt[1] and type(root.dt[1]) == "table" and not root.dt[1].er
+end
+
+function xml.package(tag,attributes,data)
+ local ns, tg = match(tag,"^(.-):?([^:]+)$")
+ local t = { ns = ns, tg = tg, dt = data or "", at = attributes or {} }
+ setmetatable(t, mt)
+ return t
+end
+
+function xml.is_valid(root)
+ return root and not root.error
+end
+
+xml.errorhandler = report_xml
+
+--[[ldx--
+<p>We cannot load an <l n='lpeg'/> from a filehandle so we need to load
+the whole file first. The function accepts a string representing
+a filename or a file handle.</p>
+--ldx]]--
+
+function xml.load(filename,settings)
+ local data = ""
+ if type(filename) == "string" then
+ -- local data = io.loaddata(filename) - -todo: check type in io.loaddata
+ local f = io.open(filename,'r') -- why not 'rb'
+ if f then
+ data = f:read("*all") -- io.readall(f) ... only makes sense for large files
+ f:close()
+ end
+ elseif filename then -- filehandle
+ data = filename:read("*all") -- io.readall(f) ... only makes sense for large files
+ end
+ if settings then
+ settings.currentresource = filename
+ local result = xmlconvert(data,settings)
+ settings.currentresource = nil
+ return result
+ else
+ return xmlconvert(data,{ currentresource = filename })
+ end
+end
+
+--[[ldx--
+<p>When we inject new elements, we need to convert strings to
+valid trees, which is what the next function does.</p>
+--ldx]]--
+
+local no_root = { no_root = true }
+
+function xml.toxml(data)
+ if type(data) == "string" then
+ local root = { xmlconvert(data,no_root) }
+ return (#root > 1 and root) or root[1]
+ else
+ return data
+ end
+end
+
+--[[ldx--
+<p>For copying a tree we use a dedicated function instead of the
+generic table copier. Since we know what we're dealing with we
+can speed up things a bit. The second argument is not to be used!</p>
+--ldx]]--
+
+local function copy(old,tables)
+ if old then
+ tables = tables or { }
+ local new = { }
+ if not tables[old] then
+ tables[old] = new
+ end
+ for k,v in next, old do
+ new[k] = (type(v) == "table" and (tables[v] or copy(v, tables))) or v
+ end
+ local mt = getmetatable(old)
+ if mt then
+ setmetatable(new,mt)
+ end
+ return new
+ else
+ return { }
+ end
+end
+
+xml.copy = copy
+
+--[[ldx--
+<p>In <l n='context'/> serializing the tree or parts of the tree is a major
+actitivity which is why the following function is pretty optimized resulting
+in a few more lines of code than needed. The variant that uses the formatting
+function for all components is about 15% slower than the concatinating
+alternative.</p>
+--ldx]]--
+
+-- todo: add <?xml version='1.0' standalone='yes'?> when not present
+
+function xml.checkbom(root) -- can be made faster
+ if root.ri then
+ local dt = root.dt
+ for k=1,#dt do
+ local v = dt[k]
+ if type(v) == "table" and v.special and v.tg == "@pi@" and find(v.dt[1],"xml.*version=") then
+ return
+ end
+ end
+ insert(dt, 1, { special = true, ns = "", tg = "@pi@", dt = { "xml version='1.0' standalone='yes'" } } )
+ insert(dt, 2, "\n" )
+ end
+end
+
+--[[ldx--
+<p>At the cost of some 25% runtime overhead you can first convert the tree to a string
+and then handle the lot.</p>
+--ldx]]--
+
+-- new experimental reorganized serialize
+
+local function verbose_element(e,handlers) -- options
+ local handle = handlers.handle
+ local serialize = handlers.serialize
+ local ens, etg, eat, edt, ern = e.ns, e.tg, e.at, e.dt, e.rn
+ local ats = eat and next(eat) and { }
+ if ats then
+ for k,v in next, eat do
+ ats[#ats+1] = formatters['%s=%q'](k,escaped(v))
+ end
+ end
+ if ern and trace_entities and ern ~= ens then
+ ens = ern
+ end
+ if ens ~= "" then
+ if edt and #edt > 0 then
+ if ats then
+ handle("<",ens,":",etg," ",concat(ats," "),">")
+ else
+ handle("<",ens,":",etg,">")
+ end
+ for i=1,#edt do
+ local e = edt[i]
+ if type(e) == "string" then
+ handle(escaped(e))
+ else
+ serialize(e,handlers)
+ end
+ end
+ handle("</",ens,":",etg,">")
+ else
+ if ats then
+ handle("<",ens,":",etg," ",concat(ats," "),"/>")
+ else
+ handle("<",ens,":",etg,"/>")
+ end
+ end
+ else
+ if edt and #edt > 0 then
+ if ats then
+ handle("<",etg," ",concat(ats," "),">")
+ else
+ handle("<",etg,">")
+ end
+ for i=1,#edt do
+ local e = edt[i]
+ if type(e) == "string" then
+ handle(escaped(e)) -- option: hexify escaped entities
+ else
+ serialize(e,handlers)
+ end
+ end
+ handle("</",etg,">")
+ else
+ if ats then
+ handle("<",etg," ",concat(ats," "),"/>")
+ else
+ handle("<",etg,"/>")
+ end
+ end
+ end
+end
+
+local function verbose_pi(e,handlers)
+ handlers.handle("<?",e.dt[1],"?>")
+end
+
+local function verbose_comment(e,handlers)
+ handlers.handle("<!--",e.dt[1],"-->")
+end
+
+local function verbose_cdata(e,handlers)
+ handlers.handle("<![CDATA[", e.dt[1],"]]>")
+end
+
+local function verbose_doctype(e,handlers)
+ handlers.handle("<!DOCTYPE ",e.dt[1],">")
+end
+
+local function verbose_root(e,handlers)
+ handlers.serialize(e.dt,handlers)
+end
+
+local function verbose_text(e,handlers)
+ handlers.handle(escaped(e))
+end
+
+local function verbose_document(e,handlers)
+ local serialize = handlers.serialize
+ local functions = handlers.functions
+ for i=1,#e do
+ local ei = e[i]
+ if type(ei) == "string" then
+ functions["@tx@"](ei,handlers)
+ else
+ serialize(ei,handlers)
+ end
+ end
+end
+
+local function serialize(e,handlers,...)
+ local initialize = handlers.initialize
+ local finalize = handlers.finalize
+ local functions = handlers.functions
+ if initialize then
+ local state = initialize(...)
+ if not state == true then
+ return state
+ end
+ end
+ local etg = e.tg
+ if etg then
+ (functions[etg] or functions["@el@"])(e,handlers)
+ -- elseif type(e) == "string" then
+ -- functions["@tx@"](e,handlers)
+ else
+ functions["@dc@"](e,handlers) -- dc ?
+ end
+ if finalize then
+ return finalize()
+ end
+end
+
+local function xserialize(e,handlers)
+ local functions = handlers.functions
+ local etg = e.tg
+ if etg then
+ (functions[etg] or functions["@el@"])(e,handlers)
+ -- elseif type(e) == "string" then
+ -- functions["@tx@"](e,handlers)
+ else
+ functions["@dc@"](e,handlers)
+ end
+end
+
+local handlers = { }
+
+local function newhandlers(settings)
+ local t = table.copy(handlers[settings and settings.parent or "verbose"] or { }) -- merge
+ if settings then
+ for k,v in next, settings do
+ if type(v) == "table" then
+ local tk = t[k] if not tk then tk = { } t[k] = tk end
+ for kk,vv in next, v do
+ tk[kk] = vv
+ end
+ else
+ t[k] = v
+ end
+ end
+ if settings.name then
+ handlers[settings.name] = t
+ end
+ end
+ utilities.storage.mark(t)
+ return t
+end
+
+local nofunction = function() end
+
+function xml.sethandlersfunction(handler,name,fnc)
+ handler.functions[name] = fnc or nofunction
+end
+
+function xml.gethandlersfunction(handler,name)
+ return handler.functions[name]
+end
+
+function xml.gethandlers(name)
+ return handlers[name]
+end
+
+newhandlers {
+ name = "verbose",
+ initialize = false, -- faster than nil and mt lookup
+ finalize = false, -- faster than nil and mt lookup
+ serialize = xserialize,
+ handle = print,
+ functions = {
+ ["@dc@"] = verbose_document,
+ ["@dt@"] = verbose_doctype,
+ ["@rt@"] = verbose_root,
+ ["@el@"] = verbose_element,
+ ["@pi@"] = verbose_pi,
+ ["@cm@"] = verbose_comment,
+ ["@cd@"] = verbose_cdata,
+ ["@tx@"] = verbose_text,
+ }
+}
+
+--[[ldx--
+<p>How you deal with saving data depends on your preferences. For a 40 MB database
+file the timing on a 2.3 Core Duo are as follows (time in seconds):</p>
+
+<lines>
+1.3 : load data from file to string
+6.1 : convert string into tree
+5.3 : saving in file using xmlsave
+6.8 : converting to string using xml.tostring
+3.6 : saving converted string in file
+</lines>
+
+<p>Beware, these were timing with the old routine but measurements will not be that
+much different I guess.</p>
+--ldx]]--
+
+-- maybe this will move to lxml-xml
+
+local result
+
+local xmlfilehandler = newhandlers {
+ name = "file",
+ initialize = function(name)
+ result = io.open(name,"wb")
+ return result
+ end,
+ finalize = function()
+ result:close()
+ return true
+ end,
+ handle = function(...)
+ result:write(...)
+ end,
+}
+
+-- no checking on writeability here but not faster either
+--
+-- local xmlfilehandler = newhandlers {
+-- initialize = function(name)
+-- io.output(name,"wb")
+-- return true
+-- end,
+-- finalize = function()
+-- io.close()
+-- return true
+-- end,
+-- handle = io.write,
+-- }
+
+function xml.save(root,name)
+ serialize(root,xmlfilehandler,name)
+end
+
+local result
+
+local xmlstringhandler = newhandlers {
+ name = "string",
+ initialize = function()
+ result = { }
+ return result
+ end,
+ finalize = function()
+ return concat(result)
+ end,
+ handle = function(...)
+ result[#result+1] = concat { ... }
+ end,
+}
+
+local function xmltostring(root) -- 25% overhead due to collecting
+ if not root then
+ return ""
+ elseif type(root) == "string" then
+ return root
+ else -- if next(root) then -- next is faster than type (and >0 test)
+ return serialize(root,xmlstringhandler) or ""
+ end
+end
+
+local function __tostring(root) -- inline
+ return (root and xmltostring(root)) or ""
+end
+
+initialize_mt = function(root) -- redefinition
+ mt = { __tostring = __tostring, __index = root }
+end
+
+xml.defaulthandlers = handlers
+xml.newhandlers = newhandlers
+xml.serialize = serialize
+xml.tostring = xmltostring
+
+--[[ldx--
+<p>The next function operated on the content only and needs a handle function
+that accepts a string.</p>
+--ldx]]--
+
+local function xmlstring(e,handle)
+ if not handle or (e.special and e.tg ~= "@rt@") then
+ -- nothing
+ elseif e.tg then
+ local edt = e.dt
+ if edt then
+ for i=1,#edt do
+ xmlstring(edt[i],handle)
+ end
+ end
+ else
+ handle(e)
+ end
+end
+
+xml.string = xmlstring
+
+--[[ldx--
+<p>A few helpers:</p>
+--ldx]]--
+
+--~ xmlsetproperty(root,"settings",settings)
+
+function xml.settings(e)
+ while e do
+ local s = e.settings
+ if s then
+ return s
+ else
+ e = e.__p__
+ end
+ end
+ return nil
+end
+
+function xml.root(e)
+ local r = e
+ while e do
+ e = e.__p__
+ if e then
+ r = e
+ end
+ end
+ return r
+end
+
+function xml.parent(root)
+ return root.__p__
+end
+
+function xml.body(root)
+ return root.ri and root.dt[root.ri] or root -- not ok yet
+end
+
+function xml.name(root)
+ if not root then
+ return ""
+ end
+ local ns = root.ns
+ local tg = root.tg
+ if ns == "" then
+ return tg
+ else
+ return ns .. ":" .. tg
+ end
+end
+
+--[[ldx--
+<p>The next helper erases an element but keeps the table as it is,
+and since empty strings are not serialized (effectively) it does
+not harm. Copying the table would take more time. Usage:</p>
+--ldx]]--
+
+function xml.erase(dt,k)
+ if dt then
+ if k then
+ dt[k] = ""
+ else for k=1,#dt do
+ dt[1] = { "" }
+ end end
+ end
+end
+
+--[[ldx--
+<p>The next helper assigns a tree (or string). Usage:</p>
+
+<typing>
+dt[k] = xml.assign(root) or xml.assign(dt,k,root)
+</typing>
+--ldx]]--
+
+function xml.assign(dt,k,root)
+ if dt and k then
+ dt[k] = type(root) == "table" and xml.body(root) or root
+ return dt[k]
+ else
+ return xml.body(root)
+ end
+end
+
+-- the following helpers may move
+
+--[[ldx--
+<p>The next helper assigns a tree (or string). Usage:</p>
+<typing>
+xml.tocdata(e)
+xml.tocdata(e,"error")
+</typing>
+--ldx]]--
+
+function xml.tocdata(e,wrapper) -- a few more in the aux module
+ local whatever = type(e) == "table" and xmltostring(e.dt) or e or ""
+ if wrapper then
+ whatever = formatters["<%s>%s</%s>"](wrapper,whatever,wrapper)
+ end
+ local t = { special = true, ns = "", tg = "@cd@", at = { }, rn = "", dt = { whatever }, __p__ = e }
+ setmetatable(t,getmetatable(e))
+ e.dt = { t }
+end
+
+function xml.makestandalone(root)
+ if root.ri then
+ local dt = root.dt
+ for k=1,#dt do
+ local v = dt[k]
+ if type(v) == "table" and v.special and v.tg == "@pi@" then
+ local txt = v.dt[1]
+ if find(txt,"xml.*version=") then
+ v.dt[1] = txt .. " standalone='yes'"
+ break
+ end
+ end
+ end
+ end
+ return root
+end
+
+function xml.kind(e)
+ local dt = e and e.dt
+ if dt then
+ local n = #dt
+ if n == 1 then
+ local d = dt[1]
+ if d.special then
+ local tg = d.tg
+ if tg == "@cd@" then
+ return "cdata"
+ elseif tg == "@cm" then
+ return "comment"
+ elseif tg == "@pi@" then
+ return "instruction"
+ elseif tg == "@dt@" then
+ return "declaration"
+ end
+ elseif type(d) == "string" then
+ return "text"
+ end
+ return "element"
+ elseif n > 0 then
+ return "mixed"
+ end
+ end
+ return "empty"
+end
diff --git a/tex/context/base/lxml-tex.lua b/tex/context/base/lxml-tex.lua
index 936a96041..112f62751 100644
--- a/tex/context/base/lxml-tex.lua
+++ b/tex/context/base/lxml-tex.lua
@@ -1,1686 +1,1686 @@
-if not modules then modules = { } end modules ['lxml-tex'] = {
- version = 1.001,
- comment = "companion to lxml-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- Because we split and resolve entities we use the direct printing
--- interface and not the context one. If we ever do that there will
--- be an cldf-xml helper library.
-
-local utfchar = utf.char
-local concat, insert, remove = table.concat, table.insert, table.remove
-local format, sub, gsub, find, gmatch, match = string.format, string.sub, string.gsub, string.find, string.gmatch, string.match
-local type, next, tonumber, tostring, select = type, next, tonumber, tostring, select
-local lpegmatch = lpeg.match
-local P, S, C, Cc = lpeg.P, lpeg.S, lpeg.C, lpeg.Cc
-
-local tex, xml = tex, xml
-local lowerchars, upperchars, lettered = characters.lower, characters.upper, characters.lettered
-
-lxml = lxml or { }
-local lxml = lxml
-
-local catcodenumbers = catcodes.numbers
-local ctxcatcodes = catcodenumbers.ctxcatcodes -- todo: use different method
-local notcatcodes = catcodenumbers.notcatcodes -- todo: use different method
-
-local context = context
-local contextsprint = context.sprint -- with catcodes (here we use fast variants, but with option for tracing)
-
-local xmlelements, xmlcollected, xmlsetproperty = xml.elements, xml.collected, xml.setproperty
-local xmlwithelements = xml.withelements
-local xmlserialize, xmlcollect, xmltext, xmltostring = xml.serialize, xml.collect, xml.text, xml.tostring
-local xmlapplylpath = xml.applylpath
-local xmlunprivatized, xmlprivatetoken, xmlprivatecodes = xml.unprivatized, xml.privatetoken, xml.privatecodes
-
-local variables = (interfaces and interfaces.variables) or { }
-
-local insertbeforevalue, insertaftervalue = utilities.tables.insertbeforevalue, utilities.tables.insertaftervalue
-
-local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming
-
-local trace_setups = false trackers.register("lxml.setups", function(v) trace_setups = v end)
-local trace_loading = false trackers.register("lxml.loading", function(v) trace_loading = v end)
-local trace_access = false trackers.register("lxml.access", function(v) trace_access = v end)
-local trace_comments = false trackers.register("lxml.comments", function(v) trace_comments = v end)
-local trace_entities = false trackers.register("xml.entities", function(v) trace_entities = v end)
-
-local report_lxml = logs.reporter("xml","tex")
-local report_xml = logs.reporter("xml","tex")
-
-local forceraw, rawroot = false, nil
-
--- tex entities
---
--- todo: unprivatize attributes
-
-lxml.entities = lxml.entities or { }
-
-storage.register("lxml/entities",lxml.entities,"lxml.entities")
-
---~ xml.placeholders.unknown_any_entity = nil -- has to be per xml
-
-local xmlentities = xml.entities
-local texentities = lxml.entities
-local parsedentity = xml.parsedentitylpeg
-
-function lxml.registerentity(key,value)
- texentities[key] = value
- if trace_entities then
- report_xml("registering tex entity %a as %a",key,value)
- end
-end
-
-function lxml.resolvedentity(str)
- if forceraw then
- if trace_entities then
- report_xml("passing entity %a as &%s;",str,str)
- end
- context("&%s;",str)
- else
- local e = texentities[str]
- if e then
- local te = type(e)
- if te == "function" then
- if trace_entities then
- report_xml("passing entity %a using function",str)
- end
- e(str)
- elseif e then
- if trace_entities then
- report_xml("passing entity %a as %a using %a",str,e,"ctxcatcodes")
- end
- context(e)
- end
- return
- end
- local e = xmlentities[str]
- if e then
- local te = type(e)
- if te == "function" then
- e = e(str)
- end
- if e then
- if trace_entities then
- report_xml("passing entity %a as %a using %a",str,e,"notcatcodes")
- end
- contextsprint(notcatcodes,e)
- return
- end
- end
- -- resolve hex and dec, todo: escape # & etc for ctxcatcodes
- -- normally this is already solved while loading the file
- local chr, err = lpegmatch(parsedentity,str)
- if chr then
- if trace_entities then
- report_xml("passing entity %a as %a using %a",str,chr,"ctxcatcodes")
- end
- context(chr)
- elseif err then
- if trace_entities then
- report_xml("passing faulty entity %a as %a",str,err)
- end
- context(err)
- else
- local tag = upperchars(str)
- if trace_entities then
- report_xml("passing entity %a to \\xmle using tag %a",str,tag)
- end
- context.xmle(str,tag) -- we need to use our own upper
- end
- end
-end
-
--- tex interface
-
-lxml.loaded = lxml.loaded or { }
-local loaded = lxml.loaded
-
--- print(contextdirective("context-mathml-directive function reduction yes "))
--- print(contextdirective("context-mathml-directive function "))
-
-xml.defaultprotocol = "tex"
-
-local finalizers = xml.finalizers
-
-finalizers.xml = finalizers.xml or { }
-finalizers.tex = finalizers.tex or { }
-
-local xmlfinalizers = finalizers.xml
-local texfinalizers = finalizers.tex
-
--- serialization with entity handling
-
-local ampersand = P("&")
-local semicolon = P(";")
-local entity = ampersand * C((1-semicolon)^1) * semicolon / lxml.resolvedentity -- context.bold
-
-local _, xmltextcapture = context.newtexthandler {
- exception = entity,
- catcodes = notcatcodes,
-}
-
-local _, xmlspacecapture = context.newtexthandler {
- endofline = context.xmlcdataobeyedline,
- emptyline = context.xmlcdataobeyedline,
- simpleline = context.xmlcdataobeyedline,
- space = context.xmlcdataobeyedspace,
- exception = entity,
- catcodes = notcatcodes,
-}
-
-local _, xmllinecapture = context.newtexthandler {
- endofline = context.xmlcdataobeyedline,
- emptyline = context.xmlcdataobeyedline,
- simpleline = context.xmlcdataobeyedline,
- exception = entity,
- catcodes = notcatcodes,
-}
-
-local _, ctxtextcapture = context.newtexthandler {
- exception = entity,
- catcodes = ctxcatcodes,
-}
-
--- cdata
-
-local toverbatim = context.newverbosehandler {
- line = context.xmlcdataobeyedline,
- space = context.xmlcdataobeyedspace,
- before = context.xmlcdatabefore,
- after = context.xmlcdataafter,
-}
-
-lxml.toverbatim = context.newverbosehandler {
- line = context.xmlcdataobeyedline,
- space = context.xmlcdataobeyedspace,
- before = context.xmlcdatabefore,
- after = context.xmlcdataafter,
- strip = true,
-}
-
--- raw flushing
-
-function lxml.startraw()
- forceraw = true
-end
-
-function lxml.stopraw()
- forceraw = false
-end
-
-function lxml.rawroot()
- return rawroot
-end
-
--- storage
-
-function lxml.store(id,root,filename)
- loaded[id] = root
- xmlsetproperty(root,"name",id)
- if filename then
- xmlsetproperty(root,"filename",filename)
- end
-end
-
-local splitter = lpeg.splitat("::")
-
-lxml.idsplitter = splitter
-
-function lxml.splitid(id)
- local d, i = lpegmatch(splitter,id)
- if d then
- return d, i
- else
- return "", id
- end
-end
-
-local function getid(id, qualified)
- if id then
- local lid = loaded[id]
- if lid then
- return lid
- elseif type(id) == "table" then
- return id
- else
- local d, i = lpegmatch(splitter,id)
- if d then
- local ld = loaded[d]
- if ld then
- local ldi = ld.index
- if ldi then
- local root = ldi[tonumber(i)]
- if root then
- if qualified then -- we need this else two args that confuse others
- return root, d
- else
- return root
- end
- elseif trace_access then
- report_lxml("%a has no index entry %a",d,i)
- end
- elseif trace_access then
- report_lxml("%a has no index",d)
- end
- elseif trace_access then
- report_lxml("%a is not loaded",d)
- end
- elseif trace_access then
- report_lxml("%a is not loaded",i)
- end
- end
- elseif trace_access then
- report_lxml("invalid id (nil)")
- end
-end
-
-lxml.id = getid -- we provide two names as locals can already use such
-lxml.getid = getid -- names and we don't want clashes
-
-function lxml.root(id)
- return loaded[id]
-end
-
--- index
-
-local nofindices = 0
-
-local function addindex(name,check_sum,force)
- local root = getid(name)
- if root and (not root.index or force) then -- weird, only called once
- local n, index, maxindex, check = 0, root.index or { }, root.maxindex or 0, root.check or { }
- local function nest(root)
- local dt = root.dt
- if not root.ix then
- maxindex = maxindex + 1
- root.ix = maxindex
- check[maxindex] = root.tg -- still needed ?
- index[maxindex] = root
- n = n + 1
- end
- if dt then
- for k=1,#dt do
- local dk = dt[k]
- if type(dk) == "table" then
- nest(dk)
- end
- end
- end
- end
- nest(root)
- nofindices = nofindices + n
- --
- if type(name) ~= "string" then
- name = "unknown"
- end
- root.index = index
- root.maxindex = maxindex
- if trace_access then
- report_lxml("indexed entries %a, found nodes %a",tostring(name),maxindex)
- end
- end
-end
-
-lxml.addindex = addindex
-
--- another cache
-
-local function lxmlapplylpath(id,pattern) -- better inline, saves call
- return xmlapplylpath(getid(id),pattern)
-end
-
-lxml.filter = lxmlapplylpath
-
-function lxml.filterlist(list,pattern)
- for s in gmatch(list,"[^, ]+") do -- we could cache a table
- xmlapplylpath(getid(s),pattern)
- end
-end
-
-function lxml.applyfunction(id,name)
- local f = xml.functions[name]
- return f and f(getid(id))
-end
-
--- rather new, indexed storage (backward refs), maybe i will merge this
-
-function lxml.checkindex(name)
- local root = getid(name)
- return (root and root.index) or 0
-end
-
-function lxml.withindex(name,n,command) -- will change as name is always there now
- local i, p = lpegmatch(splitter,n)
- if p then
- contextsprint(ctxcatcodes,"\\xmlw{",command,"}{",n,"}")
- else
- contextsprint(ctxcatcodes,"\\xmlw{",command,"}{",name,"::",n,"}")
- end
-end
-
-function lxml.getindex(name,n) -- will change as name is always there now
- local i, p = lpegmatch(splitter,n)
- if p then
- contextsprint(ctxcatcodes,n)
- else
- contextsprint(ctxcatcodes,name,"::",n)
- end
-end
-
--- loading (to be redone, no overload) .. best use different methods and
--- keep raw xml (at least as option)
-
-xml.originalload = xml.originalload or xml.load
-
-local noffiles, nofconverted = 0, 0
-
-function xml.load(filename,settings)
- noffiles, nofconverted = noffiles + 1, nofconverted + 1
- starttiming(xml)
- local ok, data = resolvers.loadbinfile(filename)
- settings = settings or { }
- settings.currentresource = filename
- local xmltable = xml.convert((ok and data) or "",settings)
- settings.currentresource = nil
- stoptiming(xml)
- return xmltable
-end
-
-local function entityconverter(id,str)
- return xmlentities[str] or xmlprivatetoken(str) or "" -- roundtrip handler
-end
-
-function lxml.convert(id,data,entities,compress,currentresource)
- local settings = { -- we're now roundtrip anyway
- unify_predefined_entities = true,
- utfize_entities = true,
- resolve_predefined_entities = true,
- resolve_entities = function(str) return entityconverter(id,str) end, -- needed for mathml
- currentresource = tostring(currentresource or id),
- }
- if compress and compress == variables.yes then
- settings.strip_cm_and_dt = true
- end
- -- if entities and entities == variables.yes then
- -- settings.utfize_entities = true
- -- -- settings.resolve_entities = function (str) return entityconverter(id,str) end
- -- end
- return xml.convert(data,settings)
-end
-
-function lxml.load(id,filename,compress,entities)
- filename = commands.preparedfile(filename) -- not commands!
- if trace_loading then
- report_lxml("loading file %a as %a",filename,id)
- end
- noffiles, nofconverted = noffiles + 1, nofconverted + 1
- -- local xmltable = xml.load(filename)
- starttiming(xml)
- local ok, data = resolvers.loadbinfile(filename)
- local xmltable = lxml.convert(id,(ok and data) or "",compress,entities,format("id: %s, file: %s",id,filename))
- stoptiming(xml)
- lxml.store(id,xmltable,filename)
- return xmltable, filename
-end
-
-function lxml.register(id,xmltable,filename)
- lxml.store(id,xmltable,filename)
- return xmltable
-end
-
-function lxml.include(id,pattern,attribute,recurse)
- starttiming(xml)
- local root = getid(id)
- xml.include(root,pattern,attribute,recurse,function(filename)
- if filename then
- filename = commands.preparedfile(filename)
- if file.dirname(filename) == "" and root.filename then
- local dn = file.dirname(root.filename)
- if dn ~= "" then
- filename = file.join(dn,filename)
- end
- end
- if trace_loading then
- report_lxml("including file %a",filename)
- end
- noffiles, nofconverted = noffiles + 1, nofconverted + 1
- return resolvers.loadtexfile(filename) or ""
- else
- return ""
- end
- end)
- stoptiming(xml)
-end
-
-function xml.getbuffer(name,compress,entities) -- we need to make sure that commands are processed
- if not name or name == "" then
- name = tex.jobname
- end
- nofconverted = nofconverted + 1
- local data = buffers.getcontent(name)
- xmltostring(lxml.convert(name,data,compress,entities,format("buffer: %s",tostring(name or "?")))) -- one buffer
-end
-
-function lxml.loadbuffer(id,name,compress,entities)
- starttiming(xml)
- nofconverted = nofconverted + 1
- local data = buffers.collectcontent(name or id) -- name can be list
- local xmltable = lxml.convert(id,data,compress,entities,format("buffer: %s",tostring(name or id or "?")))
- lxml.store(id,xmltable)
- stoptiming(xml)
- return xmltable, name or id
-end
-
-function lxml.loaddata(id,str,compress,entities)
- starttiming(xml)
- nofconverted = nofconverted + 1
- local xmltable = lxml.convert(id,str or "",compress,entities,format("id: %s",id))
- lxml.store(id,xmltable)
- stoptiming(xml)
- return xmltable, id
-end
-
-function lxml.loadregistered(id)
- return loaded[id], id
-end
-
--- e.command:
---
--- string : setup
--- true : text (no <self></self>)
--- false : ignore
--- function : call
-
-local function tex_doctype(e,handlers)
- -- ignore
-end
-
-local function tex_comment(e,handlers)
- if trace_comments then
- report_lxml("comment %a",e.dt[1])
- end
-end
-
-local default_element_handler = xml.gethandlers("verbose").functions["@el@"]
-
-local function tex_element(e,handlers)
- local command = e.command
- if command == nil then
- default_element_handler(e,handlers)
- elseif command == true then
- -- text (no <self></self>) / so, no mkii fallback then
- handlers.serialize(e.dt,handlers)
- elseif command == false then
- -- ignore
- else
- local tc = type(command)
- if tc == "string" then
- local rootname, ix = e.name, e.ix
- if rootname then
- if not ix then
- addindex(rootname,false,true)
- ix = e.ix
- end
- -- faster than context.xmlw
- contextsprint(ctxcatcodes,"\\xmlw{",command,"}{",rootname,"::",ix,"}")
- else
- report_lxml("fatal error: no index for %a",command)
- contextsprint(ctxcatcodes,"\\xmlw{",command,"}{",ix or 0,"}")
- end
- elseif tc == "function" then
- command(e)
- end
- end
-end
-
-local pihandlers = { } xml.pihandlers = pihandlers
-
-local category = P("context-") * C((1-P("-"))^1) * P("-directive")
-local space = S(" \n\r")
-local spaces = space^0
-local class = C((1-space)^0)
-local key = class
-local value = C(P(1-(space * -1))^0)
-
-local parser = category * spaces * class * spaces * key * spaces * value
-
-pihandlers[#pihandlers+1] = function(str)
- if str then
- local a, b, c, d = lpegmatch(parser,str)
- if d then
- contextsprint(ctxcatcodes,"\\xmlcontextdirective{",a,"}{",b,"}{",c,"}{",d,"}")
- end
- end
-end
-
-local function tex_pi(e,handlers)
- local str = e.dt[1]
- for i=1,#pihandlers do
- pihandlers[i](str)
- end
-end
-
-local obeycdata = true
-
-function lxml.setcdata()
- obeycdata = true
-end
-
-function lxml.resetcdata()
- obeycdata = false
-end
-
-local function tex_cdata(e,handlers)
- if obeycdata then
- toverbatim(e.dt[1])
- end
-end
-
-local function tex_text(e)
- e = xmlunprivatized(e)
- lpegmatch(xmltextcapture,e)
-end
-
-local function ctx_text(e) -- can be just context(e) as we split there
- lpegmatch(ctxtextcapture,e)
-end
-
-local function tex_handle(...)
- contextsprint(ctxcatcodes,...) -- notcatcodes is active anyway
-end
-
-local xmltexhandler = xml.newhandlers {
- name = "tex",
- handle = tex_handle,
- functions = {
- -- ["@dc@"] = tex_document,
- ["@dt@"] = tex_doctype,
- -- ["@rt@"] = tex_root,
- ["@el@"] = tex_element,
- ["@pi@"] = tex_pi,
- ["@cm@"] = tex_comment,
- ["@cd@"] = tex_cdata,
- ["@tx@"] = tex_text,
- }
-}
-
-lxml.xmltexhandler = xmltexhandler
-
--- begin of test
-
-local function tex_space(e)
- e = xmlunprivatized(e)
- lpegmatch(xmlspacecapture,e)
-end
-
-local xmltexspacehandler = xml.newhandlers {
- name = "texspace",
- handle = tex_handle,
- functions = {
- ["@dt@"] = tex_doctype,
- ["@el@"] = tex_element,
- ["@pi@"] = tex_pi,
- ["@cm@"] = tex_comment,
- ["@cd@"] = tex_cdata,
- ["@tx@"] = tex_space,
- }
-}
-
-local function tex_line(e)
- e = xmlunprivatized(e)
- lpegmatch(xmllinecapture,e)
-end
-
-local xmltexlinehandler = xml.newhandlers {
- name = "texline",
- handle = tex_handle,
- functions = {
- ["@dt@"] = tex_doctype,
- ["@el@"] = tex_element,
- ["@pi@"] = tex_pi,
- ["@cm@"] = tex_comment,
- ["@cd@"] = tex_cdata,
- ["@tx@"] = tex_line,
- }
-}
-
-function lxml.flushspacewise(id) -- keeps spaces and lines
- id = getid(id)
- local dt = id and id.dt
- if dt then
- xmlserialize(dt,xmltexspacehandler)
- end
-end
-
-function lxml.flushlinewise(id) -- keeps lines
- id = getid(id)
- local dt = id and id.dt
- if dt then
- xmlserialize(dt,xmltexlinehandler)
- end
-end
-
--- end of test
-
-function lxml.serialize(root)
- xmlserialize(root,xmltexhandler)
-end
-
-function lxml.setaction(id,pattern,action)
- local collected = xmlapplylpath(getid(id),pattern)
- if collected then
- local nc = #collected
- if nc > 0 then
- for c=1,nc do
- collected[c].command = action
- end
- end
- end
-end
-
-local function sprint(root) -- check rawroot usage
- if root then
- local tr = type(root)
- if tr == "string" then -- can also be result of lpath
- -- rawroot = false -- ?
- root = xmlunprivatized(root)
- lpegmatch(xmltextcapture,root)
- elseif tr == "table" then
- if forceraw then
- rawroot = root
- -- contextsprint(ctxcatcodes,xmltostring(root)) -- goes wrong with % etc
- root = xmlunprivatized(xmltostring(root))
- lpegmatch(xmltextcapture,root) -- goes to toc
- else
- xmlserialize(root,xmltexhandler)
- end
- end
- end
-end
-
-local function tprint(root) -- we can move sprint inline
- local tr = type(root)
- if tr == "table" then
- local n = #root
- if n == 0 then
- -- skip
- else
- for i=1,n do
- sprint(root[i])
- end
- end
- elseif tr == "string" then
- root = xmlunprivatized(root)
- lpegmatch(xmltextcapture,root)
- end
-end
-
-local function cprint(root) -- content
- if not root then
- -- rawroot = false
- -- quit
- elseif type(root) == 'string' then
- -- rawroot = false
- root = xmlunprivatized(root)
- lpegmatch(xmltextcapture,root)
- else
- local rootdt = root.dt
- if forceraw then
- rawroot = root
- -- contextsprint(ctxcatcodes,xmltostring(rootdt or root))
- root = xmlunprivatized(xmltostring(root))
- lpegmatch(xmltextcapture,root) -- goes to toc
- else
- xmlserialize(rootdt or root,xmltexhandler)
- end
- end
-end
-
-xml.sprint = sprint local xmlsprint = sprint -- calls ct mathml -> will be replaced
-xml.tprint = tprint local xmltprint = tprint -- only used here
-xml.cprint = cprint local xmlcprint = cprint -- calls ct mathml -> will be replaced
-
--- now we can flush
-
-function lxml.main(id)
- xmlserialize(getid(id),xmltexhandler) -- the real root (@rt@)
-end
-
--- -- lines (untested)
---
--- local buffer = { }
---
--- local xmllinescapture = (
--- newline^2 / function() buffer[#buffer+1] = "" end +
--- newline / function() buffer[#buffer] = buffer[#buffer] .. " " end +
--- content / function(s) buffer[#buffer] = buffer[#buffer] .. s end
--- )^0
---
--- local xmllineshandler = table.copy(xmltexhandler)
---
--- xmllineshandler.handle = function(...) lpegmatch(xmllinescapture,concat{ ... }) end
---
--- function lines(root)
--- if not root then
--- -- rawroot = false
--- -- quit
--- elseif type(root) == 'string' then
--- -- rawroot = false
--- lpegmatch(xmllinescapture,root)
--- elseif next(root) then -- tr == 'table'
--- xmlserialize(root,xmllineshandler)
--- end
--- end
---
--- function xml.lines(root) -- used at all?
--- buffer = { "" }
--- lines(root)
--- return result
--- end
-
-local function to_text(e)
- if e.command == nil then
- local etg = e.tg
- if etg and e.special and etg ~= "@rt@" then
- e.command = false -- i.e. skip
- else
- e.command = true -- i.e. no <self></self>
- end
- end
-end
-
-local function to_none(e)
- if e.command == nil then
- e.command = false -- i.e. skip
- end
-end
-
--- setups
-
-local setups = { }
-
-function lxml.setcommandtotext(id)
- xmlwithelements(getid(id),to_text)
-end
-
-function lxml.setcommandtonone(id)
- xmlwithelements(getid(id),to_none)
-end
-
-function lxml.installsetup(what,document,setup,where)
- document = document or "*"
- local sd = setups[document]
- if not sd then sd = { } setups[document] = sd end
- for k=1,#sd do
- if sd[k] == setup then sd[k] = nil break end
- end
- if what == 1 then
- if trace_loading then
- report_lxml("prepending setup %a for %a",setup,document)
- end
- insert(sd,1,setup)
- elseif what == 2 then
- if trace_loading then
- report_lxml("appending setup %a for %a",setup,document)
- end
- insert(sd,setup)
- elseif what == 3 then
- if trace_loading then
- report_lxml("inserting setup %a for %a before %a",setup,document,where)
- end
- insertbeforevalue(sd,setup,where)
- elseif what == 4 then
- if trace_loading then
- report_lxml("inserting setup %a for %a after %a",setup,document,where)
- end
- insertaftervalue(sd,setup,where)
- end
-end
-
-function lxml.flushsetups(id,...)
- local done = { }
- for i=1,select("#",...) do
- local document = select(i,...)
- local sd = setups[document]
- if sd then
- for k=1,#sd do
- local v= sd[k]
- if not done[v] then
- if trace_loading then
- report_lxml("applying setup %02i : %a to %a",k,v,document)
- end
- contextsprint(ctxcatcodes,"\\xmlsetup{",id,"}{",v,"}")
- done[v] = true
- end
- end
- elseif trace_loading then
- report_lxml("no setups for %a",document)
- end
- end
-end
-
-function lxml.resetsetups(document)
- if trace_loading then
- report_lxml("resetting all setups for %a",document)
- end
- setups[document] = { }
-end
-
-function lxml.removesetup(document,setup)
- local s = setups[document]
- if s then
- for i=1,#s do
- if s[i] == setup then
- if trace_loading then
- report_lxml("removing setup %a for %a",setup,document)
- end
- remove(t,i)
- break
- end
- end
- end
-end
-
-function lxml.setsetup(id,pattern,setup)
- if not setup or setup == "" or setup == "*" or setup == "-" or setup == "+" then
- local collected = xmlapplylpath(getid(id),pattern)
- if collected then
- local nc = #collected
- if nc > 0 then
- if trace_setups then
- for c=1,nc do
- local e = collected[c]
- local ix = e.ix or 0
- if setup == "-" then
- e.command = false
- report_lxml("lpath matched (a) %5i: %s = %s -> skipped",c,ix,setup)
- elseif setup == "+" then
- e.command = true
- report_lxml("lpath matched (b) %5i: %s = %s -> text",c,ix,setup)
- else
- local tg = e.tg
- if tg then -- to be sure
- e.command = tg
- local ns = e.rn or e.ns
- if ns == "" then
- report_lxml("lpath matched (c) %5i: %s = %s -> %s",c,ix,tg,tg)
- else
- report_lxml("lpath matched (d) %5i: %s = %s:%s -> %s",c,ix,ns,tg,tg)
- end
- end
- end
- end
- else
- for c=1,nc do
- local e = collected[c]
- if setup == "-" then
- e.command = false
- elseif setup == "+" then
- e.command = true
- else
- e.command = e.tg
- end
- end
- end
- elseif trace_setups then
- report_lxml("%s lpath matches for pattern: %s","zero",pattern)
- end
- elseif trace_setups then
- report_lxml("%s lpath matches for pattern: %s","no",pattern)
- end
- else
- local a, b = match(setup,"^(.+:)([%*%-])$")
- if a and b then
- local collected = xmlapplylpath(getid(id),pattern)
- if collected then
- local nc = #collected
- if nc > 0 then
- if trace_setups then
- for c=1,nc do
- local e = collected[c]
- local ns, tg, ix = e.rn or e.ns, e.tg, e.ix or 0
- if b == "-" then
- e.command = false
- if ns == "" then
- report_lxml("lpath matched (e) %5i: %s = %s -> skipped",c,ix,tg)
- else
- report_lxml("lpath matched (f) %5i: %s = %s:%s -> skipped",c,ix,ns,tg)
- end
- elseif b == "+" then
- e.command = true
- if ns == "" then
- report_lxml("lpath matched (g) %5i: %s = %s -> text",c,ix,tg)
- else
- report_lxml("lpath matched (h) %5i: %s = %s:%s -> text",c,ix,ns,tg)
- end
- else
- e.command = a .. tg
- if ns == "" then
- report_lxml("lpath matched (i) %5i: %s = %s -> %s",c,ix,tg,e.command)
- else
- report_lxml("lpath matched (j) %5i: %s = %s:%s -> %s",c,ix,ns,tg,e.command)
- end
- end
- end
- else
- for c=1,nc do
- local e = collected[c]
- if b == "-" then
- e.command = false
- elseif b == "+" then
- e.command = true
- else
- e.command = a .. e.tg
- end
- end
- end
- elseif trace_setups then
- report_lxml("%s lpath matches for pattern: %s","zero",pattern)
- end
- elseif trace_setups then
- report_lxml("%s lpath matches for pattern: %s","no",pattern)
- end
- else
- local collected = xmlapplylpath(getid(id),pattern)
- if collected then
- local nc = #collected
- if nc > 0 then
- if trace_setups then
- for c=1,nc do
- local e = collected[c]
- e.command = setup
- local ns, tg, ix = e.rn or e.ns, e.tg, e.ix or 0
- if ns == "" then
- report_lxml("lpath matched (k) %5i: %s = %s -> %s",c,ix,tg,setup)
- else
- report_lxml("lpath matched (l) %5i: %s = %s:%s -> %s",c,ix,ns,tg,setup)
- end
- end
- else
- for c=1,nc do
- collected[c].command = setup
- end
- end
- elseif trace_setups then
- report_lxml("%s lpath matches for pattern: %s","zero",pattern)
- end
- elseif trace_setups then
- report_lxml("%s lpath matches for pattern: %s","no",pattern)
- end
- end
- end
-end
-
--- finalizers
-
-local function first(collected)
- if collected and #collected > 0 then
- xmlsprint(collected[1])
- end
-end
-
-local function last(collected)
- if collected then
- local nc = #collected
- if nc > 0 then
- xmlsprint(collected[nc])
- end
- end
-end
-
-local function all(collected)
- if collected then
- local nc = #collected
- if nc > 0 then
- for c=1,nc do
- xmlsprint(collected[c])
- end
- end
- end
-end
-
-local function reverse(collected)
- if collected then
- local nc = #collected
- if nc >0 then
- for c=nc,1,-1 do
- xmlsprint(collected[c])
- end
- end
- end
-end
-
-local function count(collected)
- contextsprint(ctxcatcodes,(collected and #collected) or 0) -- why ctxcatcodes
-end
-
-local function position(collected,n)
- -- todo: if not n then == match
- if collected then
- local nc = #collected
- if nc > 0 then
- n = tonumber(n) or 0
- if n < 0 then
- n = nc + n + 1
- end
- if n > 0 then
- local cn = collected[n]
- if cn then
- xmlsprint(cn)
- return
- end
- end
- end
- end
-end
-
-local function match(collected) -- is match in preceding collected, never change, see bibxml
- local m = collected and collected[1]
- contextsprint(ctxcatcodes,m and m.mi or 0) -- why ctxcatcodes
-end
-
-local function index(collected,n)
- if collected then
- local nc = #collected
- if nc > 0 then
- n = tonumber(n) or 0
- if n < 0 then
- n = nc + n + 1 -- brrr
- end
- if n > 0 then
- local cn = collected[n]
- if cn then
- contextsprint(ctxcatcodes,cn.ni or 0) -- why ctxcatcodes
- return
- end
- end
- end
- end
- contextsprint(ctxcatcodes,0) -- why ctxcatcodes
-end
-
-local function command(collected,cmd,otherwise)
- local n = collected and #collected
- if n and n > 0 then
- local wildcard = find(cmd,"%*")
- for c=1,n do -- maybe optimize for n=1
- local e = collected[c]
- local ix = e.ix
- local name = e.name
- if not ix then
- lxml.addindex(name,false,true)
- ix = e.ix
- end
- if wildcard then
- contextsprint(ctxcatcodes,"\\xmlw{",(gsub(cmd,"%*",e.tg)),"}{",name,"::",ix,"}")
- else
- contextsprint(ctxcatcodes,"\\xmlw{",cmd,"}{",name,"::",ix,"}")
- end
- end
- elseif otherwise then
- contextsprint(ctxcatcodes,"\\xmlw{",otherwise,"}{#1}")
- end
-end
-
-local function attribute(collected,a,default)
- if collected and #collected > 0 then
- local at = collected[1].at
- local str = (at and at[a]) or default
- if str and str ~= "" then
- contextsprint(notcatcodes,str)
- end
- elseif default then
- contextsprint(notcatcodes,default)
- end
-end
-
-local function chainattribute(collected,arguments) -- todo: optional levels
- if collected and #collected > 0 then
- local e = collected[1]
- while e do
- local at = e.at
- if at then
- local a = at[arguments]
- if a then
- contextsprint(notcatcodes,a)
- end
- else
- break -- error
- end
- e = e.__p__
- end
- end
-end
-
-local function text(collected)
- if collected then
- local nc = #collected
- if nc == 0 then
- -- nothing
- elseif nc == 1 then -- hardly any gain so this will go
- cprint(collected[1])
- else for c=1,nc do
- cprint(collected[c])
- end end
- end
-end
-
-local function ctxtext(collected)
- if collected then
- local nc = #collected
- if nc > 0 then
- for c=1,nc do
- contextsprint(ctxcatcodes,collected[c].dt)
- end
- end
- end
-end
-
-local function stripped(collected) -- tricky as we strip in place
- if collected then
- local nc = #collected
- if nc > 0 then
- for c=1,nc do
- cprint(xml.stripelement(collected[c]))
- end
- end
- end
-end
-
-local function lower(collected)
- if not collected then
- local nc = #collected
- if nc > 0 then
- for c=1,nc do
- contextsprint(ctxcatcodes,lowerchars(collected[c].dt[1]))
- end
- end
- end
-end
-
-local function upper(collected)
- if collected then
- local nc = #collected
- if nc > 0 then
- for c=1,nc do
- contextsprint(ctxcatcodes,upperchars(collected[c].dt[1]))
- end
- end
- end
-end
-
-local function number(collected)
- local nc = collected and #collected or 0
- local n = 0
- if nc > 0 then
- for c=1,nc do
- n = n + tonumber(collected[c].dt[1] or 0)
- end
- end
- contextsprint(ctxcatcodes,n)
-end
-
-local function concatrange(collected,start,stop,separator,lastseparator,textonly) -- test this on mml
- if collected then
- local nofcollected = #collected
- if nofcollected > 0 then
- local separator = separator or ""
- local lastseparator = lastseparator or separator or ""
- start, stop = (start == "" and 1) or tonumber(start) or 1, (stop == "" and nofcollected) or tonumber(stop) or nofcollected
- if stop < 0 then stop = nofcollected + stop end -- -1 == last-1
- for i=start,stop do
- if textonly then
- xmlcprint(collected[i])
- else
- xmlsprint(collected[i])
- end
- if i == nofcollected then
- -- nothing
- elseif i == nofcollected-1 and lastseparator ~= "" then
- contextsprint(ctxcatcodes,lastseparator)
- elseif separator ~= "" then
- contextsprint(ctxcatcodes,separator)
- end
- end
- end
- end
-end
-
-local function concat(collected,separator,lastseparator,textonly) -- test this on mml
- concatrange(collected,false,false,separator,lastseparator,textonly)
-end
-
-texfinalizers.first = first
-texfinalizers.last = last
-texfinalizers.all = all
-texfinalizers.reverse = reverse
-texfinalizers.count = count
-texfinalizers.command = command
-texfinalizers.attribute = attribute
-texfinalizers.text = text
-texfinalizers.stripped = stripped
-texfinalizers.lower = lower
-texfinalizers.upper = upper
-texfinalizers.ctxtext = ctxtext
-texfinalizers.context = ctxtext
-texfinalizers.position = position
-texfinalizers.match = match
-texfinalizers.index = index
-texfinalizers.concat = concat
-texfinalizers.concatrange = concatrange
-texfinalizers.chainattribute = chainattribute
-texfinalizers.default = all -- !!
-
-local concat = table.concat
-
-function texfinalizers.tag(collected,n)
- if collected then
- local nc = #collected
- if nc > 0 then
- n = tonumber(n) or 0
- local c
- if n == 0 then
- c = collected[1]
- elseif n > 1 then
- c = collected[n]
- else
- c = collected[nc-n+1]
- end
- if c then
- contextsprint(ctxcatcodes,c.tg)
- end
- end
- end
-end
-
-function texfinalizers.name(collected,n)
- if collected then
- local nc = #collected
- if nc > 0 then
- local c
- if n == 0 or not n then
- c = collected[1]
- elseif n > 1 then
- c = collected[n]
- else
- c = collected[nc-n+1]
- end
- if c then
- if c.ns == "" then
- contextsprint(ctxcatcodes,c.tg)
- else
- contextsprint(ctxcatcodes,c.ns,":",c.tg)
- end
- end
- end
- end
-end
-
-function texfinalizers.tags(collected,nonamespace)
- if collected then
- local nc = #collected
- if nc > 0 then
- for c=1,nc do
- local e = collected[c]
- local ns, tg = e.ns, e.tg
- if nonamespace or ns == "" then
- contextsprint(ctxcatcodes,tg)
- else
- contextsprint(ctxcatcodes,ns,":",tg)
- end
- end
- end
- end
-end
-
---
-
-local function verbatim(id,before,after)
- local root = getid(id)
- if root then
- if before then contextsprint(ctxcatcodes,before,"[",root.tg or "?","]") end
- lxml.toverbatim(xmltostring(root.dt))
---~ lxml.toverbatim(xml.totext(root.dt))
- if after then contextsprint(ctxcatcodes,after) end
- end
-end
-
-function lxml.inlineverbatim(id)
- verbatim(id,"\\startxmlinlineverbatim","\\stopxmlinlineverbatim")
-end
-
-function lxml.displayverbatim(id)
- verbatim(id,"\\startxmldisplayverbatim","\\stopxmldisplayverbatim")
-end
-
-lxml.verbatim = verbatim
-
--- helpers
-
-function lxml.first(id,pattern)
- local collected = xmlapplylpath(getid(id),pattern)
- if collected then
- first(collected)
- end
-end
-
-function lxml.last(id,pattern)
- local collected = xmlapplylpath(getid(id),pattern)
- if collected then
- last(collected)
- end
-end
-
-function lxml.all(id,pattern)
- local collected = xmlapplylpath(getid(id),pattern)
- if collected then
- all(collected)
- end
-end
-
-function lxml.count(id,pattern)
- -- always needs to produce a result so no test here
- count(xmlapplylpath(getid(id),pattern))
-end
-
-function lxml.attribute(id,pattern,a,default)
- local collected = xmlapplylpath(getid(id),pattern)
- if collected then
- attribute(collected,a,default)
- end
-end
-
-function lxml.raw(id,pattern) -- the content, untouched by commands
- local collected = (pattern and xmlapplylpath(getid(id),pattern)) or getid(id)
- if collected and #collected > 0 then
- contextsprint(notcatcodes,xmltostring(collected[1].dt))
- end
-end
-
-function lxml.context(id,pattern) -- the content, untouched by commands
- if pattern then
- local collected = xmlapplylpath(getid(id),pattern) or getid(id)
- if collected and #collected > 0 then
- contextsprint(ctxcatcodes,collected[1].dt)
- end
- else
- local collected = getid(id)
- if collected then
- local dt = collected.dt
- if #dt > 0 then
- ctx_text(dt[1])
- end
- end
- end
-end
-
-function lxml.text(id,pattern)
- local collected = (pattern and xmlapplylpath(getid(id),pattern)) or getid(id)
- if collected and #collected > 0 then
- text(collected)
- end
-end
-
-lxml.content = text
-
-function lxml.position(id,pattern,n)
- position(xmlapplylpath(getid(id),pattern),n)
-end
-
-function lxml.chainattribute(id,pattern,a,default)
- chainattribute(xmlapplylpath(getid(id),pattern),a,default)
-end
-
-function lxml.concatrange(id,pattern,start,stop,separator,lastseparator,textonly) -- test this on mml
- concatrange(xmlapplylpath(getid(id),pattern),start,stop,separator,lastseparator,textonly)
-end
-
-function lxml.concat(id,pattern,separator,lastseparator,textonly)
- concatrange(xmlapplylpath(getid(id),pattern),false,false,separator,lastseparator,textonly)
-end
-
-function lxml.element(id,n)
- position(xmlapplylpath(getid(id),"/*"),n)
-end
-
-lxml.index = lxml.position
-
-function lxml.pos(id)
- local root = getid(id)
- contextsprint(ctxcatcodes,(root and root.ni) or 0)
-end
-
-function lxml.att(id,a,default)
- local root = getid(id)
- if root then
- local at = root.at
- local str = (at and at[a]) or default
- if str and str ~= "" then
- contextsprint(notcatcodes,str)
- end
- elseif default then
- contextsprint(notcatcodes,default)
- end
-end
-
-function lxml.name(id) -- or remapped name? -> lxml.info, combine
- local r = getid(id)
- local ns = r.rn or r.ns or ""
- if ns ~= "" then
- contextsprint(ctxcatcodes,ns,":",r.tg)
- else
- contextsprint(ctxcatcodes,r.tg)
- end
-end
-
-function lxml.match(id) -- or remapped name? -> lxml.info, combine
- contextsprint(ctxcatcodes,getid(id).mi or 0)
-end
-
-function lxml.tag(id) -- tag vs name -> also in l-xml tag->name
- contextsprint(ctxcatcodes,getid(id).tg or "")
-end
-
-function lxml.namespace(id) -- or remapped name?
- local root = getid(id)
- contextsprint(ctxcatcodes,root.rn or root.ns or "")
-end
-
-function lxml.flush(id)
- id = getid(id)
- local dt = id and id.dt
- if dt then
- xmlsprint(dt)
- end
-end
-
-function lxml.snippet(id,i)
- local e = getid(id)
- if e then
- local edt = e.dt
- if edt then
- xmlsprint(edt[i])
- end
- end
-end
-
-function lxml.direct(id)
- xmlsprint(getid(id))
-end
-
-function lxml.command(id,pattern,cmd)
- local i, p = getid(id,true)
- local collected = xmlapplylpath(getid(i),pattern)
- if collected then
- local nc = #collected
- if nc > 0 then
- local rootname = p or i.name
- for c=1,nc do
- local e = collected[c]
- local ix = e.ix
- if not ix then
- addindex(rootname,false,true)
- ix = e.ix
- end
- contextsprint(ctxcatcodes,"\\xmlw{",cmd,"}{",rootname,"::",ix,"}")
- end
- end
- end
-end
-
--- loops
-
-function lxml.collected(id,pattern,reverse)
- return xmlcollected(getid(id),pattern,reverse)
-end
-
-function lxml.elements(id,pattern,reverse)
- return xmlelements(getid(id),pattern,reverse)
-end
-
--- obscure ones
-
-lxml.info = lxml.name
-
--- testers
-
-local found, empty = xml.found, xml.empty
-
-local doif, doifnot, doifelse = commands.doif, commands.doifnot, commands.doifelse
-
-function lxml.doif (id,pattern) doif (found(getid(id),pattern)) end
-function lxml.doifnot (id,pattern) doifnot (found(getid(id),pattern)) end
-function lxml.doifelse (id,pattern) doifelse(found(getid(id),pattern)) end
-function lxml.doiftext (id,pattern) doif (not empty(getid(id),pattern)) end
-function lxml.doifnottext (id,pattern) doifnot (not empty(getid(id),pattern)) end
-function lxml.doifelsetext (id,pattern) doifelse(not empty(getid(id),pattern)) end
-
--- special case: "*" and "" -> self else lpath lookup
-
---~ function lxml.doifelseempty(id,pattern) doifelse(isempty(getid(id),pattern ~= "" and pattern ~= nil)) end -- not yet done, pattern
-
--- status info
-
-statistics.register("xml load time", function()
- if noffiles > 0 or nofconverted > 0 then
- return format("%s seconds, %s files, %s converted", statistics.elapsedtime(xml), noffiles, nofconverted)
- else
- return nil
- end
-end)
-
-statistics.register("lxml preparation time", function()
- local calls, cached = xml.lpathcalls(), xml.lpathcached()
- if calls > 0 or cached > 0 then
- return format("%s seconds, %s nodes, %s lpath calls, %s cached calls",
- statistics.elapsedtime(lxml), nofindices, calls, cached)
- else
- return nil
- end
-end)
-
-statistics.register("lxml lpath profile", function()
- local p = xml.profiled
- if p and next(p) then
- local s = table.sortedkeys(p)
- local tested, matched, finalized = 0, 0, 0
- logs.pushtarget("logfile")
- logs.writer("\nbegin of lxml profile\n")
- logs.writer("\n tested matched finalized pattern\n\n")
- for i=1,#s do
- local pattern = s[i]
- local pp = p[pattern]
- local t, m, f = pp.tested, pp.matched, pp.finalized
- tested, matched, finalized = tested + t, matched + m, finalized + f
- logs.writer(format("%9i %9i %9i %s",t,m,f,pattern))
- end
- logs.writer("\nend of lxml profile\n")
- logs.poptarget()
- return format("%s patterns, %s tested, %s matched, %s finalized (see log for details)",#s,tested,matched,finalized)
- else
- return nil
- end
-end)
-
--- misc
-
-function lxml.nonspace(id,pattern) -- slow, todo loop
- xmltprint(xmlcollect(getid(id),pattern,true))
-end
-
-function lxml.strip(id,pattern,nolines,anywhere)
- xml.strip(getid(id),pattern,nolines,anywhere)
-end
-
-function lxml.stripped(id,pattern,nolines)
- local str = xmltext(getid(id),pattern) or ""
- str = gsub(str,"^%s*(.-)%s*$","%1")
- if nolines then
- str = gsub(str,"%s+"," ")
- end
- xmlsprint(str)
-end
-
-function lxml.delete(id,pattern)
- xml.delete(getid(id),pattern)
-end
-
-lxml.obsolete = { }
-
-lxml.get_id = getid lxml.obsolete.get_id = getid
-
--- goodies:
-
-function texfinalizers.lettered(collected)
- if collected then
- local nc = #collected
- if nc > 0 then
- for c=1,nc do
- contextsprint(ctxcatcodes,lettered(collected[c].dt[1]))
- end
- end
- end
-end
-
---~ function texfinalizers.apply(collected,what) -- to be tested
---~ if collected then
---~ for c=1,#collected do
---~ contextsprint(ctxcatcodes,what(collected[c].dt[1]))
---~ end
---~ end
---~ end
-
-function lxml.toparameters(id)
- local e = getid(id)
- if e then
- local a = e.at
- if a and next(a) then
- local setups, s = { }, 0
- for k, v in next, a do
- s = s + 1
- setups[s] = k .. "=" .. v
- end
- setups = concat(setups,",")
- -- tracing
- context(setups)
- end
- end
-end
-
-local template = '<?xml version="1.0" ?>\n\n<!-- %s -->\n\n%s'
-
-function lxml.tofile(id,pattern,filename,comment)
- local collected = xmlapplylpath(getid(id),pattern)
- if collected then
- io.savedata(filename,format(template,comment or "exported fragment",tostring(collected[1])))
- else
- os.remove(filename) -- get rid of old content
- end
-end
-
-texfinalizers.upperall = xmlfinalizers.upperall
-texfinalizers.lowerall = xmlfinalizers.lowerall
+if not modules then modules = { } end modules ['lxml-tex'] = {
+ version = 1.001,
+ comment = "companion to lxml-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Because we split and resolve entities we use the direct printing
+-- interface and not the context one. If we ever do that there will
+-- be an cldf-xml helper library.
+
+local utfchar = utf.char
+local concat, insert, remove = table.concat, table.insert, table.remove
+local format, sub, gsub, find, gmatch, match = string.format, string.sub, string.gsub, string.find, string.gmatch, string.match
+local type, next, tonumber, tostring, select = type, next, tonumber, tostring, select
+local lpegmatch = lpeg.match
+local P, S, C, Cc = lpeg.P, lpeg.S, lpeg.C, lpeg.Cc
+
+local tex, xml = tex, xml
+local lowerchars, upperchars, lettered = characters.lower, characters.upper, characters.lettered
+
+lxml = lxml or { }
+local lxml = lxml
+
+local catcodenumbers = catcodes.numbers
+local ctxcatcodes = catcodenumbers.ctxcatcodes -- todo: use different method
+local notcatcodes = catcodenumbers.notcatcodes -- todo: use different method
+
+local context = context
+local contextsprint = context.sprint -- with catcodes (here we use fast variants, but with option for tracing)
+
+local xmlelements, xmlcollected, xmlsetproperty = xml.elements, xml.collected, xml.setproperty
+local xmlwithelements = xml.withelements
+local xmlserialize, xmlcollect, xmltext, xmltostring = xml.serialize, xml.collect, xml.text, xml.tostring
+local xmlapplylpath = xml.applylpath
+local xmlunprivatized, xmlprivatetoken, xmlprivatecodes = xml.unprivatized, xml.privatetoken, xml.privatecodes
+
+local variables = (interfaces and interfaces.variables) or { }
+
+local insertbeforevalue, insertaftervalue = utilities.tables.insertbeforevalue, utilities.tables.insertaftervalue
+
+local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming
+
+local trace_setups = false trackers.register("lxml.setups", function(v) trace_setups = v end)
+local trace_loading = false trackers.register("lxml.loading", function(v) trace_loading = v end)
+local trace_access = false trackers.register("lxml.access", function(v) trace_access = v end)
+local trace_comments = false trackers.register("lxml.comments", function(v) trace_comments = v end)
+local trace_entities = false trackers.register("xml.entities", function(v) trace_entities = v end)
+
+local report_lxml = logs.reporter("xml","tex")
+local report_xml = logs.reporter("xml","tex")
+
+local forceraw, rawroot = false, nil
+
+-- tex entities
+--
+-- todo: unprivatize attributes
+
+lxml.entities = lxml.entities or { }
+
+storage.register("lxml/entities",lxml.entities,"lxml.entities")
+
+--~ xml.placeholders.unknown_any_entity = nil -- has to be per xml
+
+local xmlentities = xml.entities
+local texentities = lxml.entities
+local parsedentity = xml.parsedentitylpeg
+
+function lxml.registerentity(key,value)
+ texentities[key] = value
+ if trace_entities then
+ report_xml("registering tex entity %a as %a",key,value)
+ end
+end
+
+function lxml.resolvedentity(str)
+ if forceraw then
+ if trace_entities then
+ report_xml("passing entity %a as &%s;",str,str)
+ end
+ context("&%s;",str)
+ else
+ local e = texentities[str]
+ if e then
+ local te = type(e)
+ if te == "function" then
+ if trace_entities then
+ report_xml("passing entity %a using function",str)
+ end
+ e(str)
+ elseif e then
+ if trace_entities then
+ report_xml("passing entity %a as %a using %a",str,e,"ctxcatcodes")
+ end
+ context(e)
+ end
+ return
+ end
+ local e = xmlentities[str]
+ if e then
+ local te = type(e)
+ if te == "function" then
+ e = e(str)
+ end
+ if e then
+ if trace_entities then
+ report_xml("passing entity %a as %a using %a",str,e,"notcatcodes")
+ end
+ contextsprint(notcatcodes,e)
+ return
+ end
+ end
+ -- resolve hex and dec, todo: escape # & etc for ctxcatcodes
+ -- normally this is already solved while loading the file
+ local chr, err = lpegmatch(parsedentity,str)
+ if chr then
+ if trace_entities then
+ report_xml("passing entity %a as %a using %a",str,chr,"ctxcatcodes")
+ end
+ context(chr)
+ elseif err then
+ if trace_entities then
+ report_xml("passing faulty entity %a as %a",str,err)
+ end
+ context(err)
+ else
+ local tag = upperchars(str)
+ if trace_entities then
+ report_xml("passing entity %a to \\xmle using tag %a",str,tag)
+ end
+ context.xmle(str,tag) -- we need to use our own upper
+ end
+ end
+end
+
+-- tex interface
+
+lxml.loaded = lxml.loaded or { }
+local loaded = lxml.loaded
+
+-- print(contextdirective("context-mathml-directive function reduction yes "))
+-- print(contextdirective("context-mathml-directive function "))
+
+xml.defaultprotocol = "tex"
+
+local finalizers = xml.finalizers
+
+finalizers.xml = finalizers.xml or { }
+finalizers.tex = finalizers.tex or { }
+
+local xmlfinalizers = finalizers.xml
+local texfinalizers = finalizers.tex
+
+-- serialization with entity handling
+
+local ampersand = P("&")
+local semicolon = P(";")
+local entity = ampersand * C((1-semicolon)^1) * semicolon / lxml.resolvedentity -- context.bold
+
+local _, xmltextcapture = context.newtexthandler {
+ exception = entity,
+ catcodes = notcatcodes,
+}
+
+local _, xmlspacecapture = context.newtexthandler {
+ endofline = context.xmlcdataobeyedline,
+ emptyline = context.xmlcdataobeyedline,
+ simpleline = context.xmlcdataobeyedline,
+ space = context.xmlcdataobeyedspace,
+ exception = entity,
+ catcodes = notcatcodes,
+}
+
+local _, xmllinecapture = context.newtexthandler {
+ endofline = context.xmlcdataobeyedline,
+ emptyline = context.xmlcdataobeyedline,
+ simpleline = context.xmlcdataobeyedline,
+ exception = entity,
+ catcodes = notcatcodes,
+}
+
+local _, ctxtextcapture = context.newtexthandler {
+ exception = entity,
+ catcodes = ctxcatcodes,
+}
+
+-- cdata
+
+local toverbatim = context.newverbosehandler {
+ line = context.xmlcdataobeyedline,
+ space = context.xmlcdataobeyedspace,
+ before = context.xmlcdatabefore,
+ after = context.xmlcdataafter,
+}
+
+lxml.toverbatim = context.newverbosehandler {
+ line = context.xmlcdataobeyedline,
+ space = context.xmlcdataobeyedspace,
+ before = context.xmlcdatabefore,
+ after = context.xmlcdataafter,
+ strip = true,
+}
+
+-- raw flushing
+
+function lxml.startraw()
+ forceraw = true
+end
+
+function lxml.stopraw()
+ forceraw = false
+end
+
+function lxml.rawroot()
+ return rawroot
+end
+
+-- storage
+
+function lxml.store(id,root,filename)
+ loaded[id] = root
+ xmlsetproperty(root,"name",id)
+ if filename then
+ xmlsetproperty(root,"filename",filename)
+ end
+end
+
+local splitter = lpeg.splitat("::")
+
+lxml.idsplitter = splitter
+
+function lxml.splitid(id)
+ local d, i = lpegmatch(splitter,id)
+ if d then
+ return d, i
+ else
+ return "", id
+ end
+end
+
+local function getid(id, qualified)
+ if id then
+ local lid = loaded[id]
+ if lid then
+ return lid
+ elseif type(id) == "table" then
+ return id
+ else
+ local d, i = lpegmatch(splitter,id)
+ if d then
+ local ld = loaded[d]
+ if ld then
+ local ldi = ld.index
+ if ldi then
+ local root = ldi[tonumber(i)]
+ if root then
+ if qualified then -- we need this else two args that confuse others
+ return root, d
+ else
+ return root
+ end
+ elseif trace_access then
+ report_lxml("%a has no index entry %a",d,i)
+ end
+ elseif trace_access then
+ report_lxml("%a has no index",d)
+ end
+ elseif trace_access then
+ report_lxml("%a is not loaded",d)
+ end
+ elseif trace_access then
+ report_lxml("%a is not loaded",i)
+ end
+ end
+ elseif trace_access then
+ report_lxml("invalid id (nil)")
+ end
+end
+
+lxml.id = getid -- we provide two names as locals can already use such
+lxml.getid = getid -- names and we don't want clashes
+
+function lxml.root(id)
+ return loaded[id]
+end
+
+-- index
+
+local nofindices = 0
+
+local function addindex(name,check_sum,force)
+ local root = getid(name)
+ if root and (not root.index or force) then -- weird, only called once
+ local n, index, maxindex, check = 0, root.index or { }, root.maxindex or 0, root.check or { }
+ local function nest(root)
+ local dt = root.dt
+ if not root.ix then
+ maxindex = maxindex + 1
+ root.ix = maxindex
+ check[maxindex] = root.tg -- still needed ?
+ index[maxindex] = root
+ n = n + 1
+ end
+ if dt then
+ for k=1,#dt do
+ local dk = dt[k]
+ if type(dk) == "table" then
+ nest(dk)
+ end
+ end
+ end
+ end
+ nest(root)
+ nofindices = nofindices + n
+ --
+ if type(name) ~= "string" then
+ name = "unknown"
+ end
+ root.index = index
+ root.maxindex = maxindex
+ if trace_access then
+ report_lxml("indexed entries %a, found nodes %a",tostring(name),maxindex)
+ end
+ end
+end
+
+lxml.addindex = addindex
+
+-- another cache
+
+local function lxmlapplylpath(id,pattern) -- better inline, saves call
+ return xmlapplylpath(getid(id),pattern)
+end
+
+lxml.filter = lxmlapplylpath
+
+function lxml.filterlist(list,pattern)
+ for s in gmatch(list,"[^, ]+") do -- we could cache a table
+ xmlapplylpath(getid(s),pattern)
+ end
+end
+
+function lxml.applyfunction(id,name)
+ local f = xml.functions[name]
+ return f and f(getid(id))
+end
+
+-- rather new, indexed storage (backward refs), maybe i will merge this
+
+function lxml.checkindex(name)
+ local root = getid(name)
+ return (root and root.index) or 0
+end
+
+function lxml.withindex(name,n,command) -- will change as name is always there now
+ local i, p = lpegmatch(splitter,n)
+ if p then
+ contextsprint(ctxcatcodes,"\\xmlw{",command,"}{",n,"}")
+ else
+ contextsprint(ctxcatcodes,"\\xmlw{",command,"}{",name,"::",n,"}")
+ end
+end
+
+function lxml.getindex(name,n) -- will change as name is always there now
+ local i, p = lpegmatch(splitter,n)
+ if p then
+ contextsprint(ctxcatcodes,n)
+ else
+ contextsprint(ctxcatcodes,name,"::",n)
+ end
+end
+
+-- loading (to be redone, no overload) .. best use different methods and
+-- keep raw xml (at least as option)
+
+xml.originalload = xml.originalload or xml.load
+
+local noffiles, nofconverted = 0, 0
+
+function xml.load(filename,settings)
+ noffiles, nofconverted = noffiles + 1, nofconverted + 1
+ starttiming(xml)
+ local ok, data = resolvers.loadbinfile(filename)
+ settings = settings or { }
+ settings.currentresource = filename
+ local xmltable = xml.convert((ok and data) or "",settings)
+ settings.currentresource = nil
+ stoptiming(xml)
+ return xmltable
+end
+
+local function entityconverter(id,str)
+ return xmlentities[str] or xmlprivatetoken(str) or "" -- roundtrip handler
+end
+
+function lxml.convert(id,data,entities,compress,currentresource)
+ local settings = { -- we're now roundtrip anyway
+ unify_predefined_entities = true,
+ utfize_entities = true,
+ resolve_predefined_entities = true,
+ resolve_entities = function(str) return entityconverter(id,str) end, -- needed for mathml
+ currentresource = tostring(currentresource or id),
+ }
+ if compress and compress == variables.yes then
+ settings.strip_cm_and_dt = true
+ end
+ -- if entities and entities == variables.yes then
+ -- settings.utfize_entities = true
+ -- -- settings.resolve_entities = function (str) return entityconverter(id,str) end
+ -- end
+ return xml.convert(data,settings)
+end
+
+function lxml.load(id,filename,compress,entities)
+ filename = commands.preparedfile(filename) -- not commands!
+ if trace_loading then
+ report_lxml("loading file %a as %a",filename,id)
+ end
+ noffiles, nofconverted = noffiles + 1, nofconverted + 1
+ -- local xmltable = xml.load(filename)
+ starttiming(xml)
+ local ok, data = resolvers.loadbinfile(filename)
+ local xmltable = lxml.convert(id,(ok and data) or "",compress,entities,format("id: %s, file: %s",id,filename))
+ stoptiming(xml)
+ lxml.store(id,xmltable,filename)
+ return xmltable, filename
+end
+
+function lxml.register(id,xmltable,filename)
+ lxml.store(id,xmltable,filename)
+ return xmltable
+end
+
+function lxml.include(id,pattern,attribute,recurse)
+ starttiming(xml)
+ local root = getid(id)
+ xml.include(root,pattern,attribute,recurse,function(filename)
+ if filename then
+ filename = commands.preparedfile(filename)
+ if file.dirname(filename) == "" and root.filename then
+ local dn = file.dirname(root.filename)
+ if dn ~= "" then
+ filename = file.join(dn,filename)
+ end
+ end
+ if trace_loading then
+ report_lxml("including file %a",filename)
+ end
+ noffiles, nofconverted = noffiles + 1, nofconverted + 1
+ return resolvers.loadtexfile(filename) or ""
+ else
+ return ""
+ end
+ end)
+ stoptiming(xml)
+end
+
+function xml.getbuffer(name,compress,entities) -- we need to make sure that commands are processed
+ if not name or name == "" then
+ name = tex.jobname
+ end
+ nofconverted = nofconverted + 1
+ local data = buffers.getcontent(name)
+ xmltostring(lxml.convert(name,data,compress,entities,format("buffer: %s",tostring(name or "?")))) -- one buffer
+end
+
+function lxml.loadbuffer(id,name,compress,entities)
+ starttiming(xml)
+ nofconverted = nofconverted + 1
+ local data = buffers.collectcontent(name or id) -- name can be list
+ local xmltable = lxml.convert(id,data,compress,entities,format("buffer: %s",tostring(name or id or "?")))
+ lxml.store(id,xmltable)
+ stoptiming(xml)
+ return xmltable, name or id
+end
+
+function lxml.loaddata(id,str,compress,entities)
+ starttiming(xml)
+ nofconverted = nofconverted + 1
+ local xmltable = lxml.convert(id,str or "",compress,entities,format("id: %s",id))
+ lxml.store(id,xmltable)
+ stoptiming(xml)
+ return xmltable, id
+end
+
+function lxml.loadregistered(id)
+ return loaded[id], id
+end
+
+-- e.command:
+--
+-- string : setup
+-- true : text (no <self></self>)
+-- false : ignore
+-- function : call
+
+local function tex_doctype(e,handlers)
+ -- ignore
+end
+
+local function tex_comment(e,handlers)
+ if trace_comments then
+ report_lxml("comment %a",e.dt[1])
+ end
+end
+
+local default_element_handler = xml.gethandlers("verbose").functions["@el@"]
+
+local function tex_element(e,handlers)
+ local command = e.command
+ if command == nil then
+ default_element_handler(e,handlers)
+ elseif command == true then
+ -- text (no <self></self>) / so, no mkii fallback then
+ handlers.serialize(e.dt,handlers)
+ elseif command == false then
+ -- ignore
+ else
+ local tc = type(command)
+ if tc == "string" then
+ local rootname, ix = e.name, e.ix
+ if rootname then
+ if not ix then
+ addindex(rootname,false,true)
+ ix = e.ix
+ end
+ -- faster than context.xmlw
+ contextsprint(ctxcatcodes,"\\xmlw{",command,"}{",rootname,"::",ix,"}")
+ else
+ report_lxml("fatal error: no index for %a",command)
+ contextsprint(ctxcatcodes,"\\xmlw{",command,"}{",ix or 0,"}")
+ end
+ elseif tc == "function" then
+ command(e)
+ end
+ end
+end
+
+local pihandlers = { } xml.pihandlers = pihandlers
+
+local category = P("context-") * C((1-P("-"))^1) * P("-directive")
+local space = S(" \n\r")
+local spaces = space^0
+local class = C((1-space)^0)
+local key = class
+local value = C(P(1-(space * -1))^0)
+
+local parser = category * spaces * class * spaces * key * spaces * value
+
+pihandlers[#pihandlers+1] = function(str)
+ if str then
+ local a, b, c, d = lpegmatch(parser,str)
+ if d then
+ contextsprint(ctxcatcodes,"\\xmlcontextdirective{",a,"}{",b,"}{",c,"}{",d,"}")
+ end
+ end
+end
+
+local function tex_pi(e,handlers)
+ local str = e.dt[1]
+ for i=1,#pihandlers do
+ pihandlers[i](str)
+ end
+end
+
+local obeycdata = true
+
+function lxml.setcdata()
+ obeycdata = true
+end
+
+function lxml.resetcdata()
+ obeycdata = false
+end
+
+local function tex_cdata(e,handlers)
+ if obeycdata then
+ toverbatim(e.dt[1])
+ end
+end
+
+local function tex_text(e)
+ e = xmlunprivatized(e)
+ lpegmatch(xmltextcapture,e)
+end
+
+local function ctx_text(e) -- can be just context(e) as we split there
+ lpegmatch(ctxtextcapture,e)
+end
+
+local function tex_handle(...)
+ contextsprint(ctxcatcodes,...) -- notcatcodes is active anyway
+end
+
+local xmltexhandler = xml.newhandlers {
+ name = "tex",
+ handle = tex_handle,
+ functions = {
+ -- ["@dc@"] = tex_document,
+ ["@dt@"] = tex_doctype,
+ -- ["@rt@"] = tex_root,
+ ["@el@"] = tex_element,
+ ["@pi@"] = tex_pi,
+ ["@cm@"] = tex_comment,
+ ["@cd@"] = tex_cdata,
+ ["@tx@"] = tex_text,
+ }
+}
+
+lxml.xmltexhandler = xmltexhandler
+
+-- begin of test
+
+local function tex_space(e)
+ e = xmlunprivatized(e)
+ lpegmatch(xmlspacecapture,e)
+end
+
+local xmltexspacehandler = xml.newhandlers {
+ name = "texspace",
+ handle = tex_handle,
+ functions = {
+ ["@dt@"] = tex_doctype,
+ ["@el@"] = tex_element,
+ ["@pi@"] = tex_pi,
+ ["@cm@"] = tex_comment,
+ ["@cd@"] = tex_cdata,
+ ["@tx@"] = tex_space,
+ }
+}
+
+local function tex_line(e)
+ e = xmlunprivatized(e)
+ lpegmatch(xmllinecapture,e)
+end
+
+local xmltexlinehandler = xml.newhandlers {
+ name = "texline",
+ handle = tex_handle,
+ functions = {
+ ["@dt@"] = tex_doctype,
+ ["@el@"] = tex_element,
+ ["@pi@"] = tex_pi,
+ ["@cm@"] = tex_comment,
+ ["@cd@"] = tex_cdata,
+ ["@tx@"] = tex_line,
+ }
+}
+
+function lxml.flushspacewise(id) -- keeps spaces and lines
+ id = getid(id)
+ local dt = id and id.dt
+ if dt then
+ xmlserialize(dt,xmltexspacehandler)
+ end
+end
+
+function lxml.flushlinewise(id) -- keeps lines
+ id = getid(id)
+ local dt = id and id.dt
+ if dt then
+ xmlserialize(dt,xmltexlinehandler)
+ end
+end
+
+-- end of test
+
+function lxml.serialize(root)
+ xmlserialize(root,xmltexhandler)
+end
+
+function lxml.setaction(id,pattern,action)
+ local collected = xmlapplylpath(getid(id),pattern)
+ if collected then
+ local nc = #collected
+ if nc > 0 then
+ for c=1,nc do
+ collected[c].command = action
+ end
+ end
+ end
+end
+
+local function sprint(root) -- check rawroot usage
+ if root then
+ local tr = type(root)
+ if tr == "string" then -- can also be result of lpath
+ -- rawroot = false -- ?
+ root = xmlunprivatized(root)
+ lpegmatch(xmltextcapture,root)
+ elseif tr == "table" then
+ if forceraw then
+ rawroot = root
+ -- contextsprint(ctxcatcodes,xmltostring(root)) -- goes wrong with % etc
+ root = xmlunprivatized(xmltostring(root))
+ lpegmatch(xmltextcapture,root) -- goes to toc
+ else
+ xmlserialize(root,xmltexhandler)
+ end
+ end
+ end
+end
+
+local function tprint(root) -- we can move sprint inline
+ local tr = type(root)
+ if tr == "table" then
+ local n = #root
+ if n == 0 then
+ -- skip
+ else
+ for i=1,n do
+ sprint(root[i])
+ end
+ end
+ elseif tr == "string" then
+ root = xmlunprivatized(root)
+ lpegmatch(xmltextcapture,root)
+ end
+end
+
+local function cprint(root) -- content
+ if not root then
+ -- rawroot = false
+ -- quit
+ elseif type(root) == 'string' then
+ -- rawroot = false
+ root = xmlunprivatized(root)
+ lpegmatch(xmltextcapture,root)
+ else
+ local rootdt = root.dt
+ if forceraw then
+ rawroot = root
+ -- contextsprint(ctxcatcodes,xmltostring(rootdt or root))
+ root = xmlunprivatized(xmltostring(root))
+ lpegmatch(xmltextcapture,root) -- goes to toc
+ else
+ xmlserialize(rootdt or root,xmltexhandler)
+ end
+ end
+end
+
+xml.sprint = sprint local xmlsprint = sprint -- calls ct mathml -> will be replaced
+xml.tprint = tprint local xmltprint = tprint -- only used here
+xml.cprint = cprint local xmlcprint = cprint -- calls ct mathml -> will be replaced
+
+-- now we can flush
+
+function lxml.main(id)
+ xmlserialize(getid(id),xmltexhandler) -- the real root (@rt@)
+end
+
+-- -- lines (untested)
+--
+-- local buffer = { }
+--
+-- local xmllinescapture = (
+-- newline^2 / function() buffer[#buffer+1] = "" end +
+-- newline / function() buffer[#buffer] = buffer[#buffer] .. " " end +
+-- content / function(s) buffer[#buffer] = buffer[#buffer] .. s end
+-- )^0
+--
+-- local xmllineshandler = table.copy(xmltexhandler)
+--
+-- xmllineshandler.handle = function(...) lpegmatch(xmllinescapture,concat{ ... }) end
+--
+-- function lines(root)
+-- if not root then
+-- -- rawroot = false
+-- -- quit
+-- elseif type(root) == 'string' then
+-- -- rawroot = false
+-- lpegmatch(xmllinescapture,root)
+-- elseif next(root) then -- tr == 'table'
+-- xmlserialize(root,xmllineshandler)
+-- end
+-- end
+--
+-- function xml.lines(root) -- used at all?
+-- buffer = { "" }
+-- lines(root)
+-- return result
+-- end
+
+local function to_text(e)
+ if e.command == nil then
+ local etg = e.tg
+ if etg and e.special and etg ~= "@rt@" then
+ e.command = false -- i.e. skip
+ else
+ e.command = true -- i.e. no <self></self>
+ end
+ end
+end
+
+local function to_none(e)
+ if e.command == nil then
+ e.command = false -- i.e. skip
+ end
+end
+
+-- setups
+
+local setups = { }
+
+function lxml.setcommandtotext(id)
+ xmlwithelements(getid(id),to_text)
+end
+
+function lxml.setcommandtonone(id)
+ xmlwithelements(getid(id),to_none)
+end
+
+function lxml.installsetup(what,document,setup,where)
+ document = document or "*"
+ local sd = setups[document]
+ if not sd then sd = { } setups[document] = sd end
+ for k=1,#sd do
+ if sd[k] == setup then sd[k] = nil break end
+ end
+ if what == 1 then
+ if trace_loading then
+ report_lxml("prepending setup %a for %a",setup,document)
+ end
+ insert(sd,1,setup)
+ elseif what == 2 then
+ if trace_loading then
+ report_lxml("appending setup %a for %a",setup,document)
+ end
+ insert(sd,setup)
+ elseif what == 3 then
+ if trace_loading then
+ report_lxml("inserting setup %a for %a before %a",setup,document,where)
+ end
+ insertbeforevalue(sd,setup,where)
+ elseif what == 4 then
+ if trace_loading then
+ report_lxml("inserting setup %a for %a after %a",setup,document,where)
+ end
+ insertaftervalue(sd,setup,where)
+ end
+end
+
+function lxml.flushsetups(id,...)
+ local done = { }
+ for i=1,select("#",...) do
+ local document = select(i,...)
+ local sd = setups[document]
+ if sd then
+ for k=1,#sd do
+ local v= sd[k]
+ if not done[v] then
+ if trace_loading then
+ report_lxml("applying setup %02i : %a to %a",k,v,document)
+ end
+ contextsprint(ctxcatcodes,"\\xmlsetup{",id,"}{",v,"}")
+ done[v] = true
+ end
+ end
+ elseif trace_loading then
+ report_lxml("no setups for %a",document)
+ end
+ end
+end
+
+function lxml.resetsetups(document)
+ if trace_loading then
+ report_lxml("resetting all setups for %a",document)
+ end
+ setups[document] = { }
+end
+
+function lxml.removesetup(document,setup)
+ local s = setups[document]
+ if s then
+ for i=1,#s do
+ if s[i] == setup then
+ if trace_loading then
+ report_lxml("removing setup %a for %a",setup,document)
+ end
+ remove(t,i)
+ break
+ end
+ end
+ end
+end
+
+function lxml.setsetup(id,pattern,setup)
+ if not setup or setup == "" or setup == "*" or setup == "-" or setup == "+" then
+ local collected = xmlapplylpath(getid(id),pattern)
+ if collected then
+ local nc = #collected
+ if nc > 0 then
+ if trace_setups then
+ for c=1,nc do
+ local e = collected[c]
+ local ix = e.ix or 0
+ if setup == "-" then
+ e.command = false
+ report_lxml("lpath matched (a) %5i: %s = %s -> skipped",c,ix,setup)
+ elseif setup == "+" then
+ e.command = true
+ report_lxml("lpath matched (b) %5i: %s = %s -> text",c,ix,setup)
+ else
+ local tg = e.tg
+ if tg then -- to be sure
+ e.command = tg
+ local ns = e.rn or e.ns
+ if ns == "" then
+ report_lxml("lpath matched (c) %5i: %s = %s -> %s",c,ix,tg,tg)
+ else
+ report_lxml("lpath matched (d) %5i: %s = %s:%s -> %s",c,ix,ns,tg,tg)
+ end
+ end
+ end
+ end
+ else
+ for c=1,nc do
+ local e = collected[c]
+ if setup == "-" then
+ e.command = false
+ elseif setup == "+" then
+ e.command = true
+ else
+ e.command = e.tg
+ end
+ end
+ end
+ elseif trace_setups then
+ report_lxml("%s lpath matches for pattern: %s","zero",pattern)
+ end
+ elseif trace_setups then
+ report_lxml("%s lpath matches for pattern: %s","no",pattern)
+ end
+ else
+ local a, b = match(setup,"^(.+:)([%*%-])$")
+ if a and b then
+ local collected = xmlapplylpath(getid(id),pattern)
+ if collected then
+ local nc = #collected
+ if nc > 0 then
+ if trace_setups then
+ for c=1,nc do
+ local e = collected[c]
+ local ns, tg, ix = e.rn or e.ns, e.tg, e.ix or 0
+ if b == "-" then
+ e.command = false
+ if ns == "" then
+ report_lxml("lpath matched (e) %5i: %s = %s -> skipped",c,ix,tg)
+ else
+ report_lxml("lpath matched (f) %5i: %s = %s:%s -> skipped",c,ix,ns,tg)
+ end
+ elseif b == "+" then
+ e.command = true
+ if ns == "" then
+ report_lxml("lpath matched (g) %5i: %s = %s -> text",c,ix,tg)
+ else
+ report_lxml("lpath matched (h) %5i: %s = %s:%s -> text",c,ix,ns,tg)
+ end
+ else
+ e.command = a .. tg
+ if ns == "" then
+ report_lxml("lpath matched (i) %5i: %s = %s -> %s",c,ix,tg,e.command)
+ else
+ report_lxml("lpath matched (j) %5i: %s = %s:%s -> %s",c,ix,ns,tg,e.command)
+ end
+ end
+ end
+ else
+ for c=1,nc do
+ local e = collected[c]
+ if b == "-" then
+ e.command = false
+ elseif b == "+" then
+ e.command = true
+ else
+ e.command = a .. e.tg
+ end
+ end
+ end
+ elseif trace_setups then
+ report_lxml("%s lpath matches for pattern: %s","zero",pattern)
+ end
+ elseif trace_setups then
+ report_lxml("%s lpath matches for pattern: %s","no",pattern)
+ end
+ else
+ local collected = xmlapplylpath(getid(id),pattern)
+ if collected then
+ local nc = #collected
+ if nc > 0 then
+ if trace_setups then
+ for c=1,nc do
+ local e = collected[c]
+ e.command = setup
+ local ns, tg, ix = e.rn or e.ns, e.tg, e.ix or 0
+ if ns == "" then
+ report_lxml("lpath matched (k) %5i: %s = %s -> %s",c,ix,tg,setup)
+ else
+ report_lxml("lpath matched (l) %5i: %s = %s:%s -> %s",c,ix,ns,tg,setup)
+ end
+ end
+ else
+ for c=1,nc do
+ collected[c].command = setup
+ end
+ end
+ elseif trace_setups then
+ report_lxml("%s lpath matches for pattern: %s","zero",pattern)
+ end
+ elseif trace_setups then
+ report_lxml("%s lpath matches for pattern: %s","no",pattern)
+ end
+ end
+ end
+end
+
+-- finalizers
+
+local function first(collected)
+ if collected and #collected > 0 then
+ xmlsprint(collected[1])
+ end
+end
+
+local function last(collected)
+ if collected then
+ local nc = #collected
+ if nc > 0 then
+ xmlsprint(collected[nc])
+ end
+ end
+end
+
+local function all(collected)
+ if collected then
+ local nc = #collected
+ if nc > 0 then
+ for c=1,nc do
+ xmlsprint(collected[c])
+ end
+ end
+ end
+end
+
+local function reverse(collected)
+ if collected then
+ local nc = #collected
+ if nc >0 then
+ for c=nc,1,-1 do
+ xmlsprint(collected[c])
+ end
+ end
+ end
+end
+
+local function count(collected)
+ contextsprint(ctxcatcodes,(collected and #collected) or 0) -- why ctxcatcodes
+end
+
+local function position(collected,n)
+ -- todo: if not n then == match
+ if collected then
+ local nc = #collected
+ if nc > 0 then
+ n = tonumber(n) or 0
+ if n < 0 then
+ n = nc + n + 1
+ end
+ if n > 0 then
+ local cn = collected[n]
+ if cn then
+ xmlsprint(cn)
+ return
+ end
+ end
+ end
+ end
+end
+
+local function match(collected) -- is match in preceding collected, never change, see bibxml
+ local m = collected and collected[1]
+ contextsprint(ctxcatcodes,m and m.mi or 0) -- why ctxcatcodes
+end
+
+local function index(collected,n)
+ if collected then
+ local nc = #collected
+ if nc > 0 then
+ n = tonumber(n) or 0
+ if n < 0 then
+ n = nc + n + 1 -- brrr
+ end
+ if n > 0 then
+ local cn = collected[n]
+ if cn then
+ contextsprint(ctxcatcodes,cn.ni or 0) -- why ctxcatcodes
+ return
+ end
+ end
+ end
+ end
+ contextsprint(ctxcatcodes,0) -- why ctxcatcodes
+end
+
+local function command(collected,cmd,otherwise)
+ local n = collected and #collected
+ if n and n > 0 then
+ local wildcard = find(cmd,"%*")
+ for c=1,n do -- maybe optimize for n=1
+ local e = collected[c]
+ local ix = e.ix
+ local name = e.name
+ if not ix then
+ lxml.addindex(name,false,true)
+ ix = e.ix
+ end
+ if wildcard then
+ contextsprint(ctxcatcodes,"\\xmlw{",(gsub(cmd,"%*",e.tg)),"}{",name,"::",ix,"}")
+ else
+ contextsprint(ctxcatcodes,"\\xmlw{",cmd,"}{",name,"::",ix,"}")
+ end
+ end
+ elseif otherwise then
+ contextsprint(ctxcatcodes,"\\xmlw{",otherwise,"}{#1}")
+ end
+end
+
+local function attribute(collected,a,default)
+ if collected and #collected > 0 then
+ local at = collected[1].at
+ local str = (at and at[a]) or default
+ if str and str ~= "" then
+ contextsprint(notcatcodes,str)
+ end
+ elseif default then
+ contextsprint(notcatcodes,default)
+ end
+end
+
+local function chainattribute(collected,arguments) -- todo: optional levels
+ if collected and #collected > 0 then
+ local e = collected[1]
+ while e do
+ local at = e.at
+ if at then
+ local a = at[arguments]
+ if a then
+ contextsprint(notcatcodes,a)
+ end
+ else
+ break -- error
+ end
+ e = e.__p__
+ end
+ end
+end
+
+local function text(collected)
+ if collected then
+ local nc = #collected
+ if nc == 0 then
+ -- nothing
+ elseif nc == 1 then -- hardly any gain so this will go
+ cprint(collected[1])
+ else for c=1,nc do
+ cprint(collected[c])
+ end end
+ end
+end
+
+local function ctxtext(collected)
+ if collected then
+ local nc = #collected
+ if nc > 0 then
+ for c=1,nc do
+ contextsprint(ctxcatcodes,collected[c].dt)
+ end
+ end
+ end
+end
+
+local function stripped(collected) -- tricky as we strip in place
+ if collected then
+ local nc = #collected
+ if nc > 0 then
+ for c=1,nc do
+ cprint(xml.stripelement(collected[c]))
+ end
+ end
+ end
+end
+
+local function lower(collected)
+ if not collected then
+ local nc = #collected
+ if nc > 0 then
+ for c=1,nc do
+ contextsprint(ctxcatcodes,lowerchars(collected[c].dt[1]))
+ end
+ end
+ end
+end
+
+local function upper(collected)
+ if collected then
+ local nc = #collected
+ if nc > 0 then
+ for c=1,nc do
+ contextsprint(ctxcatcodes,upperchars(collected[c].dt[1]))
+ end
+ end
+ end
+end
+
+local function number(collected)
+ local nc = collected and #collected or 0
+ local n = 0
+ if nc > 0 then
+ for c=1,nc do
+ n = n + tonumber(collected[c].dt[1] or 0)
+ end
+ end
+ contextsprint(ctxcatcodes,n)
+end
+
+local function concatrange(collected,start,stop,separator,lastseparator,textonly) -- test this on mml
+ if collected then
+ local nofcollected = #collected
+ if nofcollected > 0 then
+ local separator = separator or ""
+ local lastseparator = lastseparator or separator or ""
+ start, stop = (start == "" and 1) or tonumber(start) or 1, (stop == "" and nofcollected) or tonumber(stop) or nofcollected
+ if stop < 0 then stop = nofcollected + stop end -- -1 == last-1
+ for i=start,stop do
+ if textonly then
+ xmlcprint(collected[i])
+ else
+ xmlsprint(collected[i])
+ end
+ if i == nofcollected then
+ -- nothing
+ elseif i == nofcollected-1 and lastseparator ~= "" then
+ contextsprint(ctxcatcodes,lastseparator)
+ elseif separator ~= "" then
+ contextsprint(ctxcatcodes,separator)
+ end
+ end
+ end
+ end
+end
+
+local function concat(collected,separator,lastseparator,textonly) -- test this on mml
+ concatrange(collected,false,false,separator,lastseparator,textonly)
+end
+
+texfinalizers.first = first
+texfinalizers.last = last
+texfinalizers.all = all
+texfinalizers.reverse = reverse
+texfinalizers.count = count
+texfinalizers.command = command
+texfinalizers.attribute = attribute
+texfinalizers.text = text
+texfinalizers.stripped = stripped
+texfinalizers.lower = lower
+texfinalizers.upper = upper
+texfinalizers.ctxtext = ctxtext
+texfinalizers.context = ctxtext
+texfinalizers.position = position
+texfinalizers.match = match
+texfinalizers.index = index
+texfinalizers.concat = concat
+texfinalizers.concatrange = concatrange
+texfinalizers.chainattribute = chainattribute
+texfinalizers.default = all -- !!
+
+local concat = table.concat
+
+function texfinalizers.tag(collected,n)
+ if collected then
+ local nc = #collected
+ if nc > 0 then
+ n = tonumber(n) or 0
+ local c
+ if n == 0 then
+ c = collected[1]
+ elseif n > 1 then
+ c = collected[n]
+ else
+ c = collected[nc-n+1]
+ end
+ if c then
+ contextsprint(ctxcatcodes,c.tg)
+ end
+ end
+ end
+end
+
+function texfinalizers.name(collected,n)
+ if collected then
+ local nc = #collected
+ if nc > 0 then
+ local c
+ if n == 0 or not n then
+ c = collected[1]
+ elseif n > 1 then
+ c = collected[n]
+ else
+ c = collected[nc-n+1]
+ end
+ if c then
+ if c.ns == "" then
+ contextsprint(ctxcatcodes,c.tg)
+ else
+ contextsprint(ctxcatcodes,c.ns,":",c.tg)
+ end
+ end
+ end
+ end
+end
+
+function texfinalizers.tags(collected,nonamespace)
+ if collected then
+ local nc = #collected
+ if nc > 0 then
+ for c=1,nc do
+ local e = collected[c]
+ local ns, tg = e.ns, e.tg
+ if nonamespace or ns == "" then
+ contextsprint(ctxcatcodes,tg)
+ else
+ contextsprint(ctxcatcodes,ns,":",tg)
+ end
+ end
+ end
+ end
+end
+
+--
+
+local function verbatim(id,before,after)
+ local root = getid(id)
+ if root then
+ if before then contextsprint(ctxcatcodes,before,"[",root.tg or "?","]") end
+ lxml.toverbatim(xmltostring(root.dt))
+--~ lxml.toverbatim(xml.totext(root.dt))
+ if after then contextsprint(ctxcatcodes,after) end
+ end
+end
+
+function lxml.inlineverbatim(id)
+ verbatim(id,"\\startxmlinlineverbatim","\\stopxmlinlineverbatim")
+end
+
+function lxml.displayverbatim(id)
+ verbatim(id,"\\startxmldisplayverbatim","\\stopxmldisplayverbatim")
+end
+
+lxml.verbatim = verbatim
+
+-- helpers
+
+function lxml.first(id,pattern)
+ local collected = xmlapplylpath(getid(id),pattern)
+ if collected then
+ first(collected)
+ end
+end
+
+function lxml.last(id,pattern)
+ local collected = xmlapplylpath(getid(id),pattern)
+ if collected then
+ last(collected)
+ end
+end
+
+function lxml.all(id,pattern)
+ local collected = xmlapplylpath(getid(id),pattern)
+ if collected then
+ all(collected)
+ end
+end
+
+function lxml.count(id,pattern)
+ -- always needs to produce a result so no test here
+ count(xmlapplylpath(getid(id),pattern))
+end
+
+function lxml.attribute(id,pattern,a,default)
+ local collected = xmlapplylpath(getid(id),pattern)
+ if collected then
+ attribute(collected,a,default)
+ end
+end
+
+function lxml.raw(id,pattern) -- the content, untouched by commands
+ local collected = (pattern and xmlapplylpath(getid(id),pattern)) or getid(id)
+ if collected and #collected > 0 then
+ contextsprint(notcatcodes,xmltostring(collected[1].dt))
+ end
+end
+
+function lxml.context(id,pattern) -- the content, untouched by commands
+ if pattern then
+ local collected = xmlapplylpath(getid(id),pattern) or getid(id)
+ if collected and #collected > 0 then
+ contextsprint(ctxcatcodes,collected[1].dt)
+ end
+ else
+ local collected = getid(id)
+ if collected then
+ local dt = collected.dt
+ if #dt > 0 then
+ ctx_text(dt[1])
+ end
+ end
+ end
+end
+
+function lxml.text(id,pattern)
+ local collected = (pattern and xmlapplylpath(getid(id),pattern)) or getid(id)
+ if collected and #collected > 0 then
+ text(collected)
+ end
+end
+
+lxml.content = text
+
+function lxml.position(id,pattern,n)
+ position(xmlapplylpath(getid(id),pattern),n)
+end
+
+function lxml.chainattribute(id,pattern,a,default)
+ chainattribute(xmlapplylpath(getid(id),pattern),a,default)
+end
+
+function lxml.concatrange(id,pattern,start,stop,separator,lastseparator,textonly) -- test this on mml
+ concatrange(xmlapplylpath(getid(id),pattern),start,stop,separator,lastseparator,textonly)
+end
+
+function lxml.concat(id,pattern,separator,lastseparator,textonly)
+ concatrange(xmlapplylpath(getid(id),pattern),false,false,separator,lastseparator,textonly)
+end
+
+function lxml.element(id,n)
+ position(xmlapplylpath(getid(id),"/*"),n)
+end
+
+lxml.index = lxml.position
+
+function lxml.pos(id)
+ local root = getid(id)
+ contextsprint(ctxcatcodes,(root and root.ni) or 0)
+end
+
+function lxml.att(id,a,default)
+ local root = getid(id)
+ if root then
+ local at = root.at
+ local str = (at and at[a]) or default
+ if str and str ~= "" then
+ contextsprint(notcatcodes,str)
+ end
+ elseif default then
+ contextsprint(notcatcodes,default)
+ end
+end
+
+function lxml.name(id) -- or remapped name? -> lxml.info, combine
+ local r = getid(id)
+ local ns = r.rn or r.ns or ""
+ if ns ~= "" then
+ contextsprint(ctxcatcodes,ns,":",r.tg)
+ else
+ contextsprint(ctxcatcodes,r.tg)
+ end
+end
+
+function lxml.match(id) -- or remapped name? -> lxml.info, combine
+ contextsprint(ctxcatcodes,getid(id).mi or 0)
+end
+
+function lxml.tag(id) -- tag vs name -> also in l-xml tag->name
+ contextsprint(ctxcatcodes,getid(id).tg or "")
+end
+
+function lxml.namespace(id) -- or remapped name?
+ local root = getid(id)
+ contextsprint(ctxcatcodes,root.rn or root.ns or "")
+end
+
+function lxml.flush(id)
+ id = getid(id)
+ local dt = id and id.dt
+ if dt then
+ xmlsprint(dt)
+ end
+end
+
+function lxml.snippet(id,i)
+ local e = getid(id)
+ if e then
+ local edt = e.dt
+ if edt then
+ xmlsprint(edt[i])
+ end
+ end
+end
+
+function lxml.direct(id)
+ xmlsprint(getid(id))
+end
+
+function lxml.command(id,pattern,cmd)
+ local i, p = getid(id,true)
+ local collected = xmlapplylpath(getid(i),pattern)
+ if collected then
+ local nc = #collected
+ if nc > 0 then
+ local rootname = p or i.name
+ for c=1,nc do
+ local e = collected[c]
+ local ix = e.ix
+ if not ix then
+ addindex(rootname,false,true)
+ ix = e.ix
+ end
+ contextsprint(ctxcatcodes,"\\xmlw{",cmd,"}{",rootname,"::",ix,"}")
+ end
+ end
+ end
+end
+
+-- loops
+
+function lxml.collected(id,pattern,reverse)
+ return xmlcollected(getid(id),pattern,reverse)
+end
+
+function lxml.elements(id,pattern,reverse)
+ return xmlelements(getid(id),pattern,reverse)
+end
+
+-- obscure ones
+
+lxml.info = lxml.name
+
+-- testers
+
+local found, empty = xml.found, xml.empty
+
+local doif, doifnot, doifelse = commands.doif, commands.doifnot, commands.doifelse
+
+function lxml.doif (id,pattern) doif (found(getid(id),pattern)) end
+function lxml.doifnot (id,pattern) doifnot (found(getid(id),pattern)) end
+function lxml.doifelse (id,pattern) doifelse(found(getid(id),pattern)) end
+function lxml.doiftext (id,pattern) doif (not empty(getid(id),pattern)) end
+function lxml.doifnottext (id,pattern) doifnot (not empty(getid(id),pattern)) end
+function lxml.doifelsetext (id,pattern) doifelse(not empty(getid(id),pattern)) end
+
+-- special case: "*" and "" -> self else lpath lookup
+
+--~ function lxml.doifelseempty(id,pattern) doifelse(isempty(getid(id),pattern ~= "" and pattern ~= nil)) end -- not yet done, pattern
+
+-- status info
+
+statistics.register("xml load time", function()
+ if noffiles > 0 or nofconverted > 0 then
+ return format("%s seconds, %s files, %s converted", statistics.elapsedtime(xml), noffiles, nofconverted)
+ else
+ return nil
+ end
+end)
+
+statistics.register("lxml preparation time", function()
+ local calls, cached = xml.lpathcalls(), xml.lpathcached()
+ if calls > 0 or cached > 0 then
+ return format("%s seconds, %s nodes, %s lpath calls, %s cached calls",
+ statistics.elapsedtime(lxml), nofindices, calls, cached)
+ else
+ return nil
+ end
+end)
+
+statistics.register("lxml lpath profile", function()
+ local p = xml.profiled
+ if p and next(p) then
+ local s = table.sortedkeys(p)
+ local tested, matched, finalized = 0, 0, 0
+ logs.pushtarget("logfile")
+ logs.writer("\nbegin of lxml profile\n")
+ logs.writer("\n tested matched finalized pattern\n\n")
+ for i=1,#s do
+ local pattern = s[i]
+ local pp = p[pattern]
+ local t, m, f = pp.tested, pp.matched, pp.finalized
+ tested, matched, finalized = tested + t, matched + m, finalized + f
+ logs.writer(format("%9i %9i %9i %s",t,m,f,pattern))
+ end
+ logs.writer("\nend of lxml profile\n")
+ logs.poptarget()
+ return format("%s patterns, %s tested, %s matched, %s finalized (see log for details)",#s,tested,matched,finalized)
+ else
+ return nil
+ end
+end)
+
+-- misc
+
+function lxml.nonspace(id,pattern) -- slow, todo loop
+ xmltprint(xmlcollect(getid(id),pattern,true))
+end
+
+function lxml.strip(id,pattern,nolines,anywhere)
+ xml.strip(getid(id),pattern,nolines,anywhere)
+end
+
+function lxml.stripped(id,pattern,nolines)
+ local str = xmltext(getid(id),pattern) or ""
+ str = gsub(str,"^%s*(.-)%s*$","%1")
+ if nolines then
+ str = gsub(str,"%s+"," ")
+ end
+ xmlsprint(str)
+end
+
+function lxml.delete(id,pattern)
+ xml.delete(getid(id),pattern)
+end
+
+lxml.obsolete = { }
+
+lxml.get_id = getid lxml.obsolete.get_id = getid
+
+-- goodies:
+
+function texfinalizers.lettered(collected)
+ if collected then
+ local nc = #collected
+ if nc > 0 then
+ for c=1,nc do
+ contextsprint(ctxcatcodes,lettered(collected[c].dt[1]))
+ end
+ end
+ end
+end
+
+--~ function texfinalizers.apply(collected,what) -- to be tested
+--~ if collected then
+--~ for c=1,#collected do
+--~ contextsprint(ctxcatcodes,what(collected[c].dt[1]))
+--~ end
+--~ end
+--~ end
+
+function lxml.toparameters(id)
+ local e = getid(id)
+ if e then
+ local a = e.at
+ if a and next(a) then
+ local setups, s = { }, 0
+ for k, v in next, a do
+ s = s + 1
+ setups[s] = k .. "=" .. v
+ end
+ setups = concat(setups,",")
+ -- tracing
+ context(setups)
+ end
+ end
+end
+
+local template = '<?xml version="1.0" ?>\n\n<!-- %s -->\n\n%s'
+
+function lxml.tofile(id,pattern,filename,comment)
+ local collected = xmlapplylpath(getid(id),pattern)
+ if collected then
+ io.savedata(filename,format(template,comment or "exported fragment",tostring(collected[1])))
+ else
+ os.remove(filename) -- get rid of old content
+ end
+end
+
+texfinalizers.upperall = xmlfinalizers.upperall
+texfinalizers.lowerall = xmlfinalizers.lowerall
diff --git a/tex/context/base/lxml-xml.lua b/tex/context/base/lxml-xml.lua
index d4e103206..d0e256078 100644
--- a/tex/context/base/lxml-xml.lua
+++ b/tex/context/base/lxml-xml.lua
@@ -1,445 +1,445 @@
-if not modules then modules = { } end modules ['lxml-xml'] = {
- version = 1.001,
- comment = "this module is the basis for the lxml-* ones",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local concat = table.concat
-local find, lower, upper = string.find, string.lower, string.upper
-
-local xml = xml
-
-local finalizers = xml.finalizers.xml
-local xmlfilter = xml.filter -- we could inline this one for speed
-local xmltostring = xml.tostring
-local xmlserialize = xml.serialize
-local xmlcollected = xml.collected
-local xmlnewhandlers = xml.newhandlers
-
-local function first(collected) -- wrong ?
- return collected and collected[1]
-end
-
-local function last(collected)
- return collected and collected[#collected]
-end
-
-local function all(collected)
- return collected
-end
-
--- local function reverse(collected)
--- if collected then
--- local nc = #collected
--- if nc > 0 then
--- local reversed, r = { }, 0
--- for c=nc,1,-1 do
--- r = r + 1
--- reversed[r] = collected[c]
--- end
--- return reversed
--- else
--- return collected
--- end
--- end
--- end
-
-local reverse = table.reversed
-
-local function attribute(collected,name)
- if collected and #collected > 0 then
- local at = collected[1].at
- return at and at[name]
- end
-end
-
-local function att(id,name)
- local at = id.at
- return at and at[name]
-end
-
-local function count(collected)
- return collected and #collected or 0
-end
-
-local function position(collected,n)
- if not collected then
- return 0
- end
- local nc = #collected
- if nc == 0 then
- return 0
- end
- n = tonumber(n) or 0
- if n < 0 then
- return collected[nc + n + 1]
- elseif n > 0 then
- return collected[n]
- else
- return collected[1].mi or 0
- end
-end
-
-local function match(collected)
- return collected and #collected > 0 and collected[1].mi or 0 -- match
-end
-
-local function index(collected)
- return collected and #collected > 0 and collected[1].ni or 0 -- 0 is new
-end
-
-local function attributes(collected,arguments)
- if collected and #collected > 0 then
- local at = collected[1].at
- if arguments then
- return at[arguments]
- elseif next(at) then
- return at -- all of them
- end
- end
-end
-
-local function chainattribute(collected,arguments) -- todo: optional levels
- if collected and #collected > 0 then
- local e = collected[1]
- while e do
- local at = e.at
- if at then
- local a = at[arguments]
- if a then
- return a
- end
- else
- break -- error
- end
- e = e.__p__
- end
- end
- return ""
-end
-
-local function raw(collected) -- hybrid (not much different from text so it might go)
- if collected and #collected > 0 then
- local e = collected[1] or collected
- return e and xmltostring(e) or "" -- only first as we cannot concat function
- else
- return ""
- end
-end
-
---
-
-local xmltexthandler = xmlnewhandlers {
- name = "string",
- initialize = function()
- result = { }
- return result
- end,
- finalize = function()
- return concat(result)
- end,
- handle = function(...)
- result[#result+1] = concat { ... }
- end,
- escape = false,
-}
-
-local function xmltotext(root)
- local dt = root.dt
- if not dt then
- return ""
- end
- local nt = #dt -- string or table
- if nt == 0 then
- return ""
- elseif nt == 1 and type(dt[1]) == "string" then
- return dt[1] -- no escaping of " ' < > &
- else
- return xmlserialize(root,xmltexthandler) or ""
- end
-end
-
---
-
-local function text(collected) -- hybrid
- if collected then -- no # test here !
- local e = collected[1] or collected -- why fallback to element, how about cdata
- return e and xmltotext(e) or ""
- else
- return ""
- end
-end
-
-local function texts(collected)
- if not collected then
- return { } -- why no nil
- end
- local nc = #collected
- if nc == 0 then
- return { } -- why no nil
- end
- local t, n = { }, 0
- for c=1,nc do
- local e = collected[c]
- if e and e.dt then
- n = n + 1
- t[n] = e.dt
- end
- end
- return t
-end
-
-local function tag(collected,n)
- if not collected then
- return
- end
- local nc = #collected
- if nc == 0 then
- return
- end
- local c
- if n == 0 or not n then
- c = collected[1]
- elseif n > 1 then
- c = collected[n]
- else
- c = collected[nc-n+1]
- end
- return c and c.tg
-end
-
-local function name(collected,n)
- if not collected then
- return
- end
- local nc = #collected
- if nc == 0 then
- return
- end
- local c
- if n == 0 or not n then
- c = collected[1]
- elseif n > 1 then
- c = collected[n]
- else
- c = collected[nc-n+1]
- end
- if not c then
- -- sorry
- elseif c.ns == "" then
- return c.tg
- else
- return c.ns .. ":" .. c.tg
- end
-end
-
-local function tags(collected,nonamespace)
- if not collected then
- return
- end
- local nc = #collected
- if nc == 0 then
- return
- end
- local t, n = { }, 0
- for c=1,nc do
- local e = collected[c]
- local ns, tg = e.ns, e.tg
- n = n + 1
- if nonamespace or ns == "" then
- t[n] = tg
- else
- t[n] = ns .. ":" .. tg
- end
- end
- return t
-end
-
-local function empty(collected,spacesonly)
- if not collected then
- return true
- end
- local nc = #collected
- if nc == 0 then
- return true
- end
- for c=1,nc do
- local e = collected[c]
- if e then
- local edt = e.dt
- if edt then
- local n = #edt
- if n == 1 then
- local edk = edt[1]
- local typ = type(edk)
- if typ == "table" then
- return false
- elseif edk ~= "" then
- return false
- elseif spacesonly and not find(edk,"%S") then
- return false
- end
- elseif n > 1 then
- return false
- end
- end
- end
- end
- return true
-end
-
-finalizers.first = first
-finalizers.last = last
-finalizers.all = all
-finalizers.reverse = reverse
-finalizers.elements = all
-finalizers.default = all
-finalizers.attribute = attribute
-finalizers.att = att
-finalizers.count = count
-finalizers.position = position
-finalizers.match = match
-finalizers.index = index
-finalizers.attributes = attributes
-finalizers.chainattribute = chainattribute
-finalizers.text = text
-finalizers.texts = texts
-finalizers.tag = tag
-finalizers.name = name
-finalizers.tags = tags
-finalizers.empty = empty
-
--- shortcuts -- we could support xmlfilter(id,pattern,first)
-
-function xml.first(id,pattern)
- return first(xmlfilter(id,pattern))
-end
-
-function xml.last(id,pattern)
- return last(xmlfilter(id,pattern))
-end
-
-function xml.count(id,pattern)
- return count(xmlfilter(id,pattern))
-end
-
-function xml.attribute(id,pattern,a,default)
- return attribute(xmlfilter(id,pattern),a,default)
-end
-
-function xml.raw(id,pattern)
- if pattern then
- return raw(xmlfilter(id,pattern))
- else
- return raw(id)
- end
-end
-
-function xml.text(id,pattern) -- brrr either content or element (when cdata)
- if pattern then
- -- return text(xmlfilter(id,pattern))
- local collected = xmlfilter(id,pattern)
- return collected and #collected > 0 and xmltotext(collected[1]) or ""
- elseif id then
- -- return text(id)
- return xmltotext(id) or ""
- else
- return ""
- end
-end
-
-xml.content = text
-
---
-
-function xml.position(id,pattern,n) -- element
- return position(xmlfilter(id,pattern),n)
-end
-
-function xml.match(id,pattern) -- number
- return match(xmlfilter(id,pattern))
-end
-
-function xml.empty(id,pattern,spacesonly)
- return empty(xmlfilter(id,pattern),spacesonly)
-end
-
-xml.all = xml.filter
-xml.index = xml.position
-xml.found = xml.filter
-
--- a nice one:
-
-local function totable(x)
- local t = { }
- for e in xmlcollected(x[1] or x,"/*") do
- t[e.tg] = xmltostring(e.dt) or ""
- end
- return next(t) and t or nil
-end
-
-xml.table = totable
-finalizers.table = totable
-
-local function textonly(e,t)
- if e then
- local edt = e.dt
- if edt then
- for i=1,#edt do
- local e = edt[i]
- if type(e) == "table" then
- textonly(e,t)
- else
- t[#t+1] = e
- end
- end
- end
- end
- return t
-end
-
-function xml.textonly(e) -- no pattern
- return concat(textonly(e,{}))
-end
-
---
-
--- local x = xml.convert("<x><a x='+'>1<B>2</B>3</a></x>")
--- xml.filter(x,"**/lowerall()") print(x)
--- xml.filter(x,"**/upperall()") print(x)
-
-function finalizers.lowerall(collected)
- for c=1,#collected do
- local e = collected[c]
- if not e.special then
- e.tg = lower(e.tg)
- local eat = e.at
- if eat then
- local t = { }
- for k,v in next, eat do
- t[lower(k)] = v
- end
- e.at = t
- end
- end
- end
-end
-
-function finalizers.upperall(collected)
- for c=1,#collected do
- local e = collected[c]
- if not e.special then
- e.tg = upper(e.tg)
- local eat = e.at
- if eat then
- local t = { }
- for k,v in next, eat do
- t[upper(k)] = v
- end
- e.at = t
- end
- end
- end
-end
+if not modules then modules = { } end modules ['lxml-xml'] = {
+ version = 1.001,
+ comment = "this module is the basis for the lxml-* ones",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local concat = table.concat
+local find, lower, upper = string.find, string.lower, string.upper
+
+local xml = xml
+
+local finalizers = xml.finalizers.xml
+local xmlfilter = xml.filter -- we could inline this one for speed
+local xmltostring = xml.tostring
+local xmlserialize = xml.serialize
+local xmlcollected = xml.collected
+local xmlnewhandlers = xml.newhandlers
+
+local function first(collected) -- wrong ?
+ return collected and collected[1]
+end
+
+local function last(collected)
+ return collected and collected[#collected]
+end
+
+local function all(collected)
+ return collected
+end
+
+-- local function reverse(collected)
+-- if collected then
+-- local nc = #collected
+-- if nc > 0 then
+-- local reversed, r = { }, 0
+-- for c=nc,1,-1 do
+-- r = r + 1
+-- reversed[r] = collected[c]
+-- end
+-- return reversed
+-- else
+-- return collected
+-- end
+-- end
+-- end
+
+local reverse = table.reversed
+
+local function attribute(collected,name)
+ if collected and #collected > 0 then
+ local at = collected[1].at
+ return at and at[name]
+ end
+end
+
+local function att(id,name)
+ local at = id.at
+ return at and at[name]
+end
+
+local function count(collected)
+ return collected and #collected or 0
+end
+
+local function position(collected,n)
+ if not collected then
+ return 0
+ end
+ local nc = #collected
+ if nc == 0 then
+ return 0
+ end
+ n = tonumber(n) or 0
+ if n < 0 then
+ return collected[nc + n + 1]
+ elseif n > 0 then
+ return collected[n]
+ else
+ return collected[1].mi or 0
+ end
+end
+
+local function match(collected)
+ return collected and #collected > 0 and collected[1].mi or 0 -- match
+end
+
+local function index(collected)
+ return collected and #collected > 0 and collected[1].ni or 0 -- 0 is new
+end
+
+local function attributes(collected,arguments)
+ if collected and #collected > 0 then
+ local at = collected[1].at
+ if arguments then
+ return at[arguments]
+ elseif next(at) then
+ return at -- all of them
+ end
+ end
+end
+
+local function chainattribute(collected,arguments) -- todo: optional levels
+ if collected and #collected > 0 then
+ local e = collected[1]
+ while e do
+ local at = e.at
+ if at then
+ local a = at[arguments]
+ if a then
+ return a
+ end
+ else
+ break -- error
+ end
+ e = e.__p__
+ end
+ end
+ return ""
+end
+
+local function raw(collected) -- hybrid (not much different from text so it might go)
+ if collected and #collected > 0 then
+ local e = collected[1] or collected
+ return e and xmltostring(e) or "" -- only first as we cannot concat function
+ else
+ return ""
+ end
+end
+
+--
+
+local xmltexthandler = xmlnewhandlers {
+ name = "string",
+ initialize = function()
+ result = { }
+ return result
+ end,
+ finalize = function()
+ return concat(result)
+ end,
+ handle = function(...)
+ result[#result+1] = concat { ... }
+ end,
+ escape = false,
+}
+
+local function xmltotext(root)
+ local dt = root.dt
+ if not dt then
+ return ""
+ end
+ local nt = #dt -- string or table
+ if nt == 0 then
+ return ""
+ elseif nt == 1 and type(dt[1]) == "string" then
+ return dt[1] -- no escaping of " ' < > &
+ else
+ return xmlserialize(root,xmltexthandler) or ""
+ end
+end
+
+--
+
+local function text(collected) -- hybrid
+ if collected then -- no # test here !
+ local e = collected[1] or collected -- why fallback to element, how about cdata
+ return e and xmltotext(e) or ""
+ else
+ return ""
+ end
+end
+
+local function texts(collected)
+ if not collected then
+ return { } -- why no nil
+ end
+ local nc = #collected
+ if nc == 0 then
+ return { } -- why no nil
+ end
+ local t, n = { }, 0
+ for c=1,nc do
+ local e = collected[c]
+ if e and e.dt then
+ n = n + 1
+ t[n] = e.dt
+ end
+ end
+ return t
+end
+
+local function tag(collected,n)
+ if not collected then
+ return
+ end
+ local nc = #collected
+ if nc == 0 then
+ return
+ end
+ local c
+ if n == 0 or not n then
+ c = collected[1]
+ elseif n > 1 then
+ c = collected[n]
+ else
+ c = collected[nc-n+1]
+ end
+ return c and c.tg
+end
+
+local function name(collected,n)
+ if not collected then
+ return
+ end
+ local nc = #collected
+ if nc == 0 then
+ return
+ end
+ local c
+ if n == 0 or not n then
+ c = collected[1]
+ elseif n > 1 then
+ c = collected[n]
+ else
+ c = collected[nc-n+1]
+ end
+ if not c then
+ -- sorry
+ elseif c.ns == "" then
+ return c.tg
+ else
+ return c.ns .. ":" .. c.tg
+ end
+end
+
+local function tags(collected,nonamespace)
+ if not collected then
+ return
+ end
+ local nc = #collected
+ if nc == 0 then
+ return
+ end
+ local t, n = { }, 0
+ for c=1,nc do
+ local e = collected[c]
+ local ns, tg = e.ns, e.tg
+ n = n + 1
+ if nonamespace or ns == "" then
+ t[n] = tg
+ else
+ t[n] = ns .. ":" .. tg
+ end
+ end
+ return t
+end
+
+local function empty(collected,spacesonly)
+ if not collected then
+ return true
+ end
+ local nc = #collected
+ if nc == 0 then
+ return true
+ end
+ for c=1,nc do
+ local e = collected[c]
+ if e then
+ local edt = e.dt
+ if edt then
+ local n = #edt
+ if n == 1 then
+ local edk = edt[1]
+ local typ = type(edk)
+ if typ == "table" then
+ return false
+ elseif edk ~= "" then
+ return false
+ elseif spacesonly and not find(edk,"%S") then
+ return false
+ end
+ elseif n > 1 then
+ return false
+ end
+ end
+ end
+ end
+ return true
+end
+
+finalizers.first = first
+finalizers.last = last
+finalizers.all = all
+finalizers.reverse = reverse
+finalizers.elements = all
+finalizers.default = all
+finalizers.attribute = attribute
+finalizers.att = att
+finalizers.count = count
+finalizers.position = position
+finalizers.match = match
+finalizers.index = index
+finalizers.attributes = attributes
+finalizers.chainattribute = chainattribute
+finalizers.text = text
+finalizers.texts = texts
+finalizers.tag = tag
+finalizers.name = name
+finalizers.tags = tags
+finalizers.empty = empty
+
+-- shortcuts -- we could support xmlfilter(id,pattern,first)
+
+function xml.first(id,pattern)
+ return first(xmlfilter(id,pattern))
+end
+
+function xml.last(id,pattern)
+ return last(xmlfilter(id,pattern))
+end
+
+function xml.count(id,pattern)
+ return count(xmlfilter(id,pattern))
+end
+
+function xml.attribute(id,pattern,a,default)
+ return attribute(xmlfilter(id,pattern),a,default)
+end
+
+function xml.raw(id,pattern)
+ if pattern then
+ return raw(xmlfilter(id,pattern))
+ else
+ return raw(id)
+ end
+end
+
+function xml.text(id,pattern) -- brrr either content or element (when cdata)
+ if pattern then
+ -- return text(xmlfilter(id,pattern))
+ local collected = xmlfilter(id,pattern)
+ return collected and #collected > 0 and xmltotext(collected[1]) or ""
+ elseif id then
+ -- return text(id)
+ return xmltotext(id) or ""
+ else
+ return ""
+ end
+end
+
+xml.content = text
+
+--
+
+function xml.position(id,pattern,n) -- element
+ return position(xmlfilter(id,pattern),n)
+end
+
+function xml.match(id,pattern) -- number
+ return match(xmlfilter(id,pattern))
+end
+
+function xml.empty(id,pattern,spacesonly)
+ return empty(xmlfilter(id,pattern),spacesonly)
+end
+
+xml.all = xml.filter
+xml.index = xml.position
+xml.found = xml.filter
+
+-- a nice one:
+
+local function totable(x)
+ local t = { }
+ for e in xmlcollected(x[1] or x,"/*") do
+ t[e.tg] = xmltostring(e.dt) or ""
+ end
+ return next(t) and t or nil
+end
+
+xml.table = totable
+finalizers.table = totable
+
+local function textonly(e,t)
+ if e then
+ local edt = e.dt
+ if edt then
+ for i=1,#edt do
+ local e = edt[i]
+ if type(e) == "table" then
+ textonly(e,t)
+ else
+ t[#t+1] = e
+ end
+ end
+ end
+ end
+ return t
+end
+
+function xml.textonly(e) -- no pattern
+ return concat(textonly(e,{}))
+end
+
+--
+
+-- local x = xml.convert("<x><a x='+'>1<B>2</B>3</a></x>")
+-- xml.filter(x,"**/lowerall()") print(x)
+-- xml.filter(x,"**/upperall()") print(x)
+
+function finalizers.lowerall(collected)
+ for c=1,#collected do
+ local e = collected[c]
+ if not e.special then
+ e.tg = lower(e.tg)
+ local eat = e.at
+ if eat then
+ local t = { }
+ for k,v in next, eat do
+ t[lower(k)] = v
+ end
+ e.at = t
+ end
+ end
+ end
+end
+
+function finalizers.upperall(collected)
+ for c=1,#collected do
+ local e = collected[c]
+ if not e.special then
+ e.tg = upper(e.tg)
+ local eat = e.at
+ if eat then
+ local t = { }
+ for k,v in next, eat do
+ t[upper(k)] = v
+ end
+ e.at = t
+ end
+ end
+ end
+end
diff --git a/tex/context/base/m-chart.lua b/tex/context/base/m-chart.lua
index 34f77c074..c4da2eb63 100644
--- a/tex/context/base/m-chart.lua
+++ b/tex/context/base/m-chart.lua
@@ -1,916 +1,916 @@
-if not modules then modules = { } end modules ['x-flow'] = {
- version = 1.001,
- comment = "companion to m-flow.mkvi",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- when we can resolve mpcolor at the lua end we will
--- use metapost.graphic(....) directly
-
--- todo: labels
-
-moduledata.charts = moduledata.charts or { }
-
-local gsub, match, find, format, lower = string.gsub, string.match, string.find, string.format, string.lower
-local setmetatableindex = table.setmetatableindex
-local P, S, C, Cc, lpegmatch = lpeg.P, lpeg.S, lpeg.C, lpeg.Cc, lpeg.match
-
-local report_chart = logs.reporter("chart")
-
-local points = number.points
-
-local variables = interfaces.variables
-
-local v_yes = variables.yes
-local v_no = variables.no
-local v_none = variables.none
-local v_standard = variables.standard
-local v_overlay = variables.overlay
-local v_round = variables.round
-local v_test = variables.test
-
-local defaults = {
- chart = {
- name = "",
- option = "",
- backgroundcolor = "",
- width = 100*65536,
- height = 50*65536,
- dx = 30*65536,
- dy = 30*65536,
- offset = 0,
- bodyfont = "",
- dot = "",
- hcompact = variables_no,
- vcompact = variables_no,
- autofocus = "",
- focus = "",
- labeloffset = 5*65536,
- commentoffset = 5*65536,
- exitoffset = 0,
-
- },
- shape = { -- FLOS
- rulethickness = 65536,
- default = "",
- framecolor = "darkblue",
- backgroundcolor = "lightgray",
- },
- focus = { -- FLOF
- rulethickness = 65536,
- framecolor = "darkred",
- backgroundcolor = "gray",
- },
- line = { -- FLOL
- rulethickness = 65536,
- radius = 10*65536,
- color = "darkgreen",
- corner = "",
- dash = "",
- arrow = "",
- offset = "",
- },
- set = { -- FLOX
- },
- split = {
- nx = 3,
- ny = 3,
- command = "",
- marking = "",
- before = "",
- after = "",
- }
-}
-
-local validshapes = {
- ["node"] = { kind = "shape", number = 0 },
- ["action"] = { kind = "shape", number = 24 },
- ["procedure"] = { kind = "shape", number = 5 },
- ["product"] = { kind = "shape", number = 12 },
- ["decision"] = { kind = "shape", number = 14 },
- ["archive"] = { kind = "shape", number = 19 },
- ["loop"] = { kind = "shape", number = 35 },
- ["wait"] = { kind = "shape", number = 6 },
- ["subprocedure"] = { kind = "shape", number = 20 },
- ["singledocument"] = { kind = "shape", number = 32 },
- ["multidocument"] = { kind = "shape", number = 33 },
-
- ["right"] = { kind = "line", number = 66 },
- ["left"] = { kind = "line", number = 67 },
- ["up"] = { kind = "line", number = 68 },
- ["down"] = { kind = "line", number = 69 },
-}
-
-local validlabellocations = {
- l = "l", left = "l",
- r = "r", right = "r",
- t = "t", top = "t",
- b = "b", bottom = "b",
- lt = "lt",
- rt = "rt",
- lb = "lb",
- rb = "rb",
- tl = "tl",
- tr = "tr",
- bl = "bl",
- br = "br",
-}
-
-local validcommentlocations = {
- l = "l", left = "l",
- r = "r", right = "r",
- t = "t", top = "t",
- b = "b", bottom = "b",
- lt = "lt",
- rt = "rt",
- lb = "lb",
- rb = "rb",
- tl = "tl",
- tr = "tr",
- bl = "bl",
- br = "br",
-}
-
-local validtextlocations = {
- l = "l", left = "l",
- r = "r", right = "r",
- t = "t", top = "t",
- b = "b", bottom = "b",
- c = "c", center = "c",
- m = "c", middle = "m",
- lt = "lt",
- rt = "rt",
- lb = "lb",
- rb = "rb",
- tl = "lt",
- tr = "rt",
- bl = "lb",
- br = "rb",
-}
-
-setmetatableindex(validshapes,function(t,k)
- local l = gsub(lower(k)," ","")
- local v = rawget(t,l)
- if not v then
- local n = tonumber(k)
- if n then
- v = { kind = "shape", number = n }
- else
- v = rawget(t,"action")
- end
- end
- t[k] = v
- return v
-end)
-
-local charts = { }
-
-local data, hash, temp, last_x, last_y, name
-
-function commands.flow_start_chart(chartname)
- data = { }
- hash = { }
- last_x, last_y = 0, 0
- name = chartname
-end
-
-function commands.flow_stop_chart()
- charts[name] = {
- data = data,
- hash = hash,
- last_x = last_x,
- last_y = last_y,
- }
- data, hash, temp = nil, nil, nil
-end
-
--- function commands.flow_set(chartname,chartdata)
--- local hash = { }
--- local data = { }
--- charts[name] = {
--- data = data,
--- hash = hash,
--- }
--- for i=1,#chartdata do
--- local di = data[i]
--- local name = di.name or ""
--- if name then
--- data[#data+1] = {
--- name = name,
--- labels = di.labels or { },
--- comments = di.comments or { },
--- exits = di.exits or { },
--- connections = di.connections or { },
--- settings = di.settings or { },
--- x = di.x or 1,
--- y = di.y or 1,
--- }
--- hash[name] = i
--- end
--- end
--- end
-
-function commands.flow_reset(chartname)
- charts[name] = nil
-end
-
-function commands.flow_set_current_cell(n)
- temp = data[tonumber(n)] or { }
-end
-
-function commands.flow_start_cell(settings)
- temp = {
- texts = { },
- labels = { },
- exits = { },
- connections = { },
- settings = settings,
- x = 1,
- y = 1,
- name = "",
- }
-end
-
-function commands.flow_stop_cell()
- data[#data+1] = temp
- hash[temp.name or #data] = temp
-end
-
-function commands.flow_set_name(str)
- temp.name = str
-end
-
-function commands.flow_set_shape(str)
- temp.shape = str
-end
-
-function commands.flow_set_destination(str)
- temp.destination = str
-end
-
-function commands.flow_set_text(align,str)
- temp.texts[#temp.texts+1] = {
- location = align,
- text = str,
- }
-end
-
-function commands.flow_set_overlay(str)
- temp.overlay = str
-end
-
-function commands.flow_set_focus(str)
- temp.focus = str
-end
-
-function commands.flow_set_figure(str)
- temp.figure = str
-end
-
-function commands.flow_set_label(location,text)
- temp.labels[#temp.labels+1] = {
- location = location,
- text = text,
- }
-end
-
-function commands.flow_set_comment(location,text)
- local connections = temp.connections
- if connections then
- local connection = connections[#connections]
- if connection then
- local comments = connection.comments
- if comments then
- comments[#comments+1] = {
- location = location,
- text = text,
- }
- end
- end
- end
-end
-
-function commands.flow_set_exit(location,text)
- temp.exits[#temp.exits+1] = {
- location = location,
- text = text,
- }
-end
-
-function commands.flow_set_include(name,x,y,settings)
- data[#data+1] = {
- include = name,
- x = x,
- y = y,
- -- settings = settings,
- }
-end
-
-local function inject(includedata,data,hash)
- local subchart = charts[includedata.include]
- if not subchart then
- return
- end
- local subdata = subchart.data
- if not subdata then
- return
- end
- local xoffset = (includedata.x or 1) - 1
- local yoffset = (includedata.y or 1) - 1
- local settings = includedata.settings
- for i=1,#subdata do
- local si = subdata[i]
- if si.include then
- inject(si,data,hash)
- else
- local t = {
- x = si.x + xoffset,
- y = si.y + yoffset,
- settings = settings,
- }
- setmetatableindex(t,si)
- data[#data+1] = t
- hash[si.name or #data] = t
- end
- end
-end
-
-local function pack(data,field)
- local list, max = { }, 0
- for e=1,#data do
- local d = data[e]
- local f = d[field]
- list[f] = true
- if f > max then
- max = f
- end
- end
- for i=1,max do
- if not list[i] then
- for e=1,#data do
- local d = data[e]
- local f = d[field]
- if f > i then
- d[field] = f - 1
- end
- end
- end
- end
-end
-
-local function expanded(chart,chartsettings)
- local expandeddata = { }
- local expandedhash = { }
- local expandedchart = {
- data = expandeddata,
- hash = expandedhash,
- }
- setmetatableindex(expandedchart,chart)
- local data = chart.data
- local hash = chart.hash
- for i=1,#data do
- local di = data[i]
- if di.include then
- inject(di,expandeddata,expandedhash)
- else
- expandeddata[#expandeddata+1] = di
- expandedhash[di.name or #expandeddata] = di
- end
- end
- --
- expandedchart.settings = chartsettings or { }
- -- make locals
- chartsettings.shape = chartsettings.shape or { }
- chartsettings.focus = chartsettings.focus or { }
- chartsettings.line = chartsettings.line or { }
- chartsettings.set = chartsettings.set or { }
- chartsettings.split = chartsettings.split or { }
- chartsettings.chart = chartsettings.chart or { }
- setmetatableindex(chartsettings.shape,defaults.shape)
- setmetatableindex(chartsettings.focus,defaults.focus)
- setmetatableindex(chartsettings.line ,defaults.line )
- setmetatableindex(chartsettings.set ,defaults.set )
- setmetatableindex(chartsettings.split,defaults.split)
- setmetatableindex(chartsettings.chart,defaults.chart)
- --
- if chartsettings.chart.vcompact == v_yes then
- pack(expandeddata,"y")
- end
- if chartsettings.chart.hcompact == v_yes then
- pack(expandeddata,"x")
- end
- --
- for i=1,#expandeddata do
- local cell = expandeddata[i]
- local settings = cell.settings
- if not settings then
- cell.settings = chartsettings
- else
- settings.shape = settings.shape or { }
- settings.focus = settings.focus or { }
- settings.line = settings.line or { }
- setmetatableindex(settings.shape,chartsettings.shape)
- setmetatableindex(settings.focus,chartsettings.focus)
- setmetatableindex(settings.line ,chartsettings.line)
- end
- end
- return expandedchart
-end
-
-local splitter = lpeg.splitat(",")
-
-function commands.flow_set_location(x,y)
- if type(x) == "string" and not y then
- x, y = lpegmatch(splitter,x)
- end
- if not x or x == "" then
- x = last_x
- elseif type(x) == "number" then
- -- ok
- elseif x == "+" then
- x = last_x + 1
- elseif x == "-" then
- x = last_x - 1
- elseif find(x,"^[%+%-]") then
- x = last_x + (tonumber(x) or 0)
- else
- x = tonumber(x)
- end
- if not y or y == "" then
- y = last_y
- elseif type(y) == "number" then
- -- ok
- elseif y == "+" then
- y = last_y + 1
- elseif x == "-" then
- y = last_y - 1
- elseif find(y,"^[%+%-]") then
- y = last_y + (tonumber(y) or 0)
- else
- y = tonumber(y)
- end
- temp.x = x or 1
- temp.y = y or 1
- last_x = x or last_x
- last_y = y or last_y
-end
-
-function commands.flow_set_connection(location,displacement,name)
- local dx, dy = lpegmatch(splitter,displacement)
- dx = tonumber(dx)
- dy = tonumber(dy)
- temp.connections[#temp.connections+1] = {
- location = location,
- dx = dx or 0,
- dy = dy or 0,
- name = name,
- comments = { },
- }
-end
-
-local function visible(chart,cell)
- local x, y = cell.x, cell.y
- return
- x >= chart.from_x and x <= chart.to_x and
- y >= chart.from_y and y <= chart.to_y and cell
-end
-
-local function process_cells(chart,xoffset,yoffset)
- local data = chart.data
- if not data then
- return
- end
- local focus = utilities.parsers.settings_to_hash(chart.settings.chart.focus or "")
- for i=1,#data do
- local cell = visible(chart,data[i])
- if cell then
- local settings = cell.settings
- local shapesettings = settings.shape
- local shape = cell.shape
- if not shape or shape == "" then
- shape = shapesettings.default or "none"
- end
- if shape ~= v_none then
- local shapedata = validshapes[shape]
- context("flow_begin_sub_chart ;") -- when is this needed
- if shapedata.kind == "line" then
- local linesettings = settings.line
- context("flow_shape_line_color := \\MPcolor{%s} ;", linesettings.color)
- context("flow_shape_fill_color := \\MPcolor{%s} ;", linesettings.backgroundcolor)
- context("flow_shape_line_width := %s ; ", points(linesettingsrulethickness))
- elseif focus[cell.focus] or focus[cell.name] then
- local focussettings = settings.focus
- context("flow_shape_line_color := \\MPcolor{%s} ;", focussettings.framecolor)
- context("flow_shape_fill_color := \\MPcolor{%s} ;", focussettings.backgroundcolor)
- context("flow_shape_line_width := %s ; ", points(focussettings.rulethickness))
- else
- local shapesettings = settings.shape
- context("flow_shape_line_color := \\MPcolor{%s} ;", shapesettings.framecolor)
- context("flow_shape_fill_color := \\MPcolor{%s} ;", shapesettings.backgroundcolor)
- context("flow_shape_line_width := %s ; " , points(shapesettings.rulethickness))
- end
- context("flow_peepshape := false ;") -- todo
- context("flow_new_shape(%s,%s,%s) ;",cell.x+xoffset,cell.y+yoffset,shapedata.number)
- context("flow_end_sub_chart ;")
- end
- end
- end
-end
-
--- todo : make lpeg for splitter
-
-local sign = S("+p") / "1"
- + S("-m") / "-1"
-
-local full = C(P("left"))
- + C(P("right"))
- + C(P("top"))
- + C(P("bottom"))
-
-local char = P("l") / "left"
- + P("r") / "right"
- + P("t") / "top"
- + P("b") / "bottom"
-
-local space = P(" ")^0
-
-local what = space
- * (sign + Cc("0"))
- * space
- * (full + char)
- * space
- * (sign + Cc("0"))
- * space
- * (full + char)
- * space
- * P(-1)
-
--- print(lpegmatch(what,"lr"))
--- print(lpegmatch(what,"+l+r"))
--- print(lpegmatch(what,"+l"))
--- print(lpegmatch(what,"+ left+r "))
-
-local function process_connections(chart,xoffset,yoffset)
- local data = chart.data
- local hash = chart.hash
- if not data then
- return
- end
- local settings = chart.settings
- for i=1,#data do
- local cell = visible(chart,data[i])
- if cell then
- local connections = cell.connections
- for j=1,#connections do
- local connection = connections[j]
- local othername = connection.name
- local othercell = hash[othername]
- if othercell then -- and visible(chart,data[i]) then
- local cellx, celly = cell.x, cell.y
- local otherx, othery, location = othercell.x, othercell.y, connection.location
- if otherx > 0 and othery > 0 and cellx > 0 and celly > 0 and connection.location then
- local what_cell, where_cell, what_other, where_other = lpegmatch(what,location)
- if what_cell and where_cell and what_other and where_other then
- local linesettings = settings.line
- context("flow_smooth := %s ;", linesettings.corner == v_round and "true" or "false")
- context("flow_dashline := %s ;", linesettings.dash == v_yes and "true" or "false")
- context("flow_arrowtip := %s ;", linesettings.arrow == v_yes and "true" or "false")
- context("flow_touchshape := %s ;", linesettings.offset == v_none and "true" or "false")
- context("flow_dsp_x := %s ; flow_dsp_y := %s ;",connection.dx or 0, connection.dy or 0)
- context("flow_connection_line_color := \\MPcolor{%s} ;",linesettings.color)
- context("flow_connection_line_width := 2pt ;",points(linesettings.rulethickness))
- context("flow_connect_%s_%s (%s) (%s,%s,%s) (%s,%s,%s) ;",where_cell,where_other,j,cellx,celly,what_cell,otherx,othery,what_other)
- context("flow_dsp_x := 0 ; flow_dsp_y := 0 ;")
- end
- end
- end
- end
- end
- end
-end
-
-local texttemplate = "\\setvariables[flowcell:text][x=%s,y=%s,text={%s},align={%s},figure={%s},destination={%s}]"
-
-local splitter = lpeg.splitat(":")
-
-local function process_texts(chart,xoffset,yoffset)
- local data = chart.data
- local hash = chart.hash
- if not data then
- return
- end
- for i=1,#data do
- local cell = visible(chart,data[i])
- if cell then
- local x = cell.x or 1
- local y = cell.y or 1
- local texts = cell.texts
- for i=1,#texts do
- local text = texts[i]
- local data = text.text
- local align = validlabellocations[text.align or ""] or text.align or ""
- local figure = i == 1 and cell.figure or ""
- local destination = i == 1 and cell.destination or ""
- context('flow_chart_draw_text(%s,%s,textext("%s")) ;',x,y,format(texttemplate,x,y,data,align,figure,destination))
- end
- local labels = cell.labels
- for i=1,#labels do
- local label = labels[i]
- local text = label.text
- local location = validlabellocations[label.location or ""] or label.location or ""
- if text and location then
- context('flow_chart_draw_label(%s,%s,"%s",textext("\\strut %s")) ;',x,y,location,text)
- end
- end
- local exits = cell.exits
- for i=1,#exits do
- local exit = exits[i]
- local text = exit.text
- local location = validlabellocations[exit.location or ""]
- if text and location then
- -- maybe make autoexit an option
- if location == "l" and x == chart.from_x + 1 or
- location == "r" and x == chart.to_x - 1 or
- location == "t" and y == chart.to_y - 1 or
- location == "b" and y == chart.from_y + 1 then
- context('flow_chart_draw_exit(%s,%s,"%s",textext("\\strut %s")) ;',x,y,location,text)
- end
- end
- end
- local connections = cell.connections
- for i=1,#connections do
- local comments = connections[i].comments
- for j=1,#comments do
- local comment = comments[j]
- local text = comment.text
- local location = comment.location or ""
- local length = 0
- -- "tl" "tl:*" "tl:0.5"
- local loc, len = lpegmatch(splitter,location) -- do the following in lpeg
- if len == "*" then
- location = validcommentlocations[loc] or ""
- if location == "" then
- location = "*"
- else
- location = location .. ":*"
- end
- elseif loc then
- location = validcommentlocations[loc] or "*"
- length = tonumber(len) or 0
- else
- location = validcommentlocations[location] or ""
- end
- if text and location then
- context('flow_chart_draw_comment(%s,%s,%s,"%s",%s,textext("\\strut %s")) ;',x,y,i,location,length,text)
- end
- end
- end
- end
- end
-end
-
-local function getchart(settings,forced_x,forced_y,forced_nx,forced_ny)
- if not settings then
- print("no settings given")
- return
- end
- local chartname = settings.chart.name
- if not chartname then
- print("no name given")
- return
- end
- local chart = charts[chartname]
- if not chart then
- print("no such chart",chartname)
- return
- end
- chart = expanded(chart,settings)
- local chartsettings = chart.settings.chart
- local autofocus = chart.settings.chart.autofocus
- if autofocus then
- autofocus = utilities.parsers.settings_to_hash(autofocus)
- if not next(autofocus) then
- autofocus = false
- end
- end
- -- check natural window
- local x = forced_x or tonumber(chartsettings.x)
- local y = forced_y or tonumber(chartsettings.y)
- local nx = forced_nx or tonumber(chartsettings.nx)
- local ny = forced_ny or tonumber(chartsettings.ny)
- --
- local minx, miny, maxx, maxy = 0, 0, 0, 0
- local data = chart.data
- for i=1,#data do
- local cell = data[i]
- if not autofocus or autofocus[cell.name] then -- offsets probably interfere with autofocus
- local x = cell.x
- local y = cell.y
- if minx == 0 or x < minx then minx = x end
- if miny == 0 or y < miny then miny = y end
- if minx == 0 or x > maxx then maxx = x end
- if miny == 0 or y > maxy then maxy = y end
- end
- end
- -- print("1>",x,y,nx,ny)
- -- print("2>",minx, miny, maxx, maxy)
- -- check of window should be larger (maybe autofocus + nx/ny?)
- if autofocus then
- -- x and y are ignored
- if nx and nx > 0 then
- maxx = minx + nx - 1
- end
- if ny and ny > 0 then
- maxy = miny + ny - 1
- end
- else
- if x and x > 0 then
- minx = x
- end
- if y and y > 0 then
- miny = y
- end
- if nx and nx > 0 then
- maxx = minx + nx - 1
- end
- if ny and ny > 0 then
- maxy = miny + ny - 1
- end
- end
--- print("3>",minx, miny, maxx, maxy)
- --
- local nx = maxx - minx + 1
- local ny = maxy - miny + 1
- -- relocate cells
- for i=1,#data do
- local cell = data[i]
- cell.x = cell.x - minx + 1
- cell.y = cell.y - miny + 1
- end
- chart.from_x = 1
- chart.from_y = 1
- chart.to_x = nx
- chart.to_y = ny
- chart.nx = nx
- chart.ny = ny
- --
- -- inspect(chart)
- return chart
-end
-
-local function makechart(chart)
- local settings = chart.settings
- local chartsettings = settings.chart
- --
- context.begingroup()
- context.forgetall()
- --
- context.startMPcode()
- context("if unknown context_flow : input mp-char.mpiv ; fi ;")
- context("flow_begin_chart(0,%s,%s);",chart.nx,chart.ny)
- --
- if chartsettings.option == v_test or chartsettings.dot == v_yes then
- context("flow_show_con_points := true ;")
- context("flow_show_mid_points := true ;")
- context("flow_show_all_points := true ;")
- elseif chartsettings.dot ~= "" then -- no checking done, private option
- context("flow_show_%s_points := true ;",chartsettings.dot)
- end
- --
- local backgroundcolor = chartsettings.backgroundcolor
- if backgroundcolor and backgroundcolor ~= "" then
- context("flow_chart_background_color := \\MPcolor{%s} ;",backgroundcolor)
- end
- --
- local shapewidth = chartsettings.width
- local gridwidth = shapewidth + 2*chartsettings.dx
- local shapeheight = chartsettings.height
- local gridheight = shapeheight + 2*chartsettings.dy
- local chartoffset = chartsettings.offset
- local labeloffset = chartsettings.labeloffset
- local exitoffset = chartsettings.exitoffset
- local commentoffset = chartsettings.commentoffset
- context("flow_grid_width := %s ;", points(gridwidth))
- context("flow_grid_height := %s ;", points(gridheight))
- context("flow_shape_width := %s ;", points(shapewidth))
- context("flow_shape_height := %s ;", points(shapeheight))
- context("flow_chart_offset := %s ;", points(chartoffset))
- context("flow_label_offset := %s ;", points(labeloffset))
- context("flow_exit_offset := %s ;", points(exitoffset))
- context("flow_comment_offset := %s ;", points(commentoffset))
- --
- local radius = settings.line.radius
- local rulethickness = settings.line.rulethickness
- local dx = chartsettings.dx
- local dy = chartsettings.dy
- if radius < rulethickness then
- radius = 2.5*rulethickness
- if radius > dx then
- radius = dx
- end
- if radius > dy then
- radius = dy
- end
- end
- context("flow_connection_line_width := %s ;", points(rulethickness))
- context("flow_connection_smooth_size := %s ;", points(radius))
- context("flow_connection_arrow_size := %s ;", points(radius))
- context("flow_connection_dash_size := %s ;", points(radius))
- --
- local offset = chartsettings.offset -- todo: pass string
- if offset == v_none or offset == v_overlay or offset == "" then
- offset = -2.5 * radius -- or rulethickness?
- elseif offset == v_standard then
- offset = radius -- or rulethickness?
- end
- context("flow_chart_offset := %s ;",points(offset))
- --
- context("flow_reverse_y := true ;")
- process_cells(chart,0,0)
- process_connections(chart,0,0)
- process_texts(chart,0,0)
- -- context("clip_chart(%s,%s,%s,%s) ;",x,y,nx,ny) -- todo: draw lines but not shapes
- context("flow_end_chart ;")
- context.stopMPcode()
- context.endgroup()
-end
-
-local function splitchart(chart)
- local settings = chart.settings
- local splitsettings = settings.split
- local chartsettings = settings.chart
- --
- local name = chartsettings.name
- --
- local from_x = chart.from_x
- local from_y = chart.from_y
- local to_x = chart.to_x
- local to_y = chart.to_y
- --
- local step_x = splitsettings.nx or to_x
- local step_y = splitsettings.ny or to_y
- local delta_x = splitsettings.dx or 0
- local delta_y = splitsettings.dy or 0
- --
- report_chart("spliting %a from (%s,%s) upto (%s,%s) into (%s,%s) with overlap (%s,%s)",
- name,from_x,from_y,to_x,to_y,step_x,step_y,delta_x,delta_y)
- --
- local part_x = 0
- local first_x = from_x
- while true do
- part_x = part_x + 1
- local last_x = first_x + step_x - 1
- local done = last_x >= to_x
- if done then
- last_x = to_x
- end
- local part_y = 0
- local first_y = from_y
- while true do
- part_y = part_y + 1
- local last_y = first_y + step_y - 1
- local done = last_y >= to_y
- if done then
- last_y = to_y
- end
- --
- report_chart("part (%s,%s) of %a is split from (%s,%s) -> (%s,%s)",part_x,part_y,name,first_x,first_y,last_x,last_y)
- local x, y, nx, ny = first_x, first_y, last_x - first_x + 1,last_y - first_y + 1
- context.beforeFLOWsplit()
- context.handleFLOWsplit(function()
- makechart(getchart(settings,x,y,nx,ny)) -- we need to pass frozen settings !
- end)
- context.afterFLOWsplit()
- --
- if done then
- break
- else
- first_y = last_y + 1 - delta_y
- end
- end
- if done then
- break
- else
- first_x = last_x + 1 - delta_x
- end
- end
-end
-
-function commands.flow_make_chart(settings)
- local chart = getchart(settings)
- if chart then
- local settings = chart.settings
- if settings then
- local chartsettings = settings.chart
- if chartsettings and chartsettings.split == v_yes then
- splitchart(chart)
- else
- makechart(chart)
- end
- else
- makechart(chart)
- end
- end
-end
+if not modules then modules = { } end modules ['x-flow'] = {
+ version = 1.001,
+ comment = "companion to m-flow.mkvi",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- when we can resolve mpcolor at the lua end we will
+-- use metapost.graphic(....) directly
+
+-- todo: labels
+
+moduledata.charts = moduledata.charts or { }
+
+local gsub, match, find, format, lower = string.gsub, string.match, string.find, string.format, string.lower
+local setmetatableindex = table.setmetatableindex
+local P, S, C, Cc, lpegmatch = lpeg.P, lpeg.S, lpeg.C, lpeg.Cc, lpeg.match
+
+local report_chart = logs.reporter("chart")
+
+local points = number.points
+
+local variables = interfaces.variables
+
+local v_yes = variables.yes
+local v_no = variables.no
+local v_none = variables.none
+local v_standard = variables.standard
+local v_overlay = variables.overlay
+local v_round = variables.round
+local v_test = variables.test
+
+local defaults = {
+ chart = {
+ name = "",
+ option = "",
+ backgroundcolor = "",
+ width = 100*65536,
+ height = 50*65536,
+ dx = 30*65536,
+ dy = 30*65536,
+ offset = 0,
+ bodyfont = "",
+ dot = "",
+ hcompact = variables_no,
+ vcompact = variables_no,
+ autofocus = "",
+ focus = "",
+ labeloffset = 5*65536,
+ commentoffset = 5*65536,
+ exitoffset = 0,
+
+ },
+ shape = { -- FLOS
+ rulethickness = 65536,
+ default = "",
+ framecolor = "darkblue",
+ backgroundcolor = "lightgray",
+ },
+ focus = { -- FLOF
+ rulethickness = 65536,
+ framecolor = "darkred",
+ backgroundcolor = "gray",
+ },
+ line = { -- FLOL
+ rulethickness = 65536,
+ radius = 10*65536,
+ color = "darkgreen",
+ corner = "",
+ dash = "",
+ arrow = "",
+ offset = "",
+ },
+ set = { -- FLOX
+ },
+ split = {
+ nx = 3,
+ ny = 3,
+ command = "",
+ marking = "",
+ before = "",
+ after = "",
+ }
+}
+
+local validshapes = {
+ ["node"] = { kind = "shape", number = 0 },
+ ["action"] = { kind = "shape", number = 24 },
+ ["procedure"] = { kind = "shape", number = 5 },
+ ["product"] = { kind = "shape", number = 12 },
+ ["decision"] = { kind = "shape", number = 14 },
+ ["archive"] = { kind = "shape", number = 19 },
+ ["loop"] = { kind = "shape", number = 35 },
+ ["wait"] = { kind = "shape", number = 6 },
+ ["subprocedure"] = { kind = "shape", number = 20 },
+ ["singledocument"] = { kind = "shape", number = 32 },
+ ["multidocument"] = { kind = "shape", number = 33 },
+
+ ["right"] = { kind = "line", number = 66 },
+ ["left"] = { kind = "line", number = 67 },
+ ["up"] = { kind = "line", number = 68 },
+ ["down"] = { kind = "line", number = 69 },
+}
+
+local validlabellocations = {
+ l = "l", left = "l",
+ r = "r", right = "r",
+ t = "t", top = "t",
+ b = "b", bottom = "b",
+ lt = "lt",
+ rt = "rt",
+ lb = "lb",
+ rb = "rb",
+ tl = "tl",
+ tr = "tr",
+ bl = "bl",
+ br = "br",
+}
+
+local validcommentlocations = {
+ l = "l", left = "l",
+ r = "r", right = "r",
+ t = "t", top = "t",
+ b = "b", bottom = "b",
+ lt = "lt",
+ rt = "rt",
+ lb = "lb",
+ rb = "rb",
+ tl = "tl",
+ tr = "tr",
+ bl = "bl",
+ br = "br",
+}
+
+local validtextlocations = {
+ l = "l", left = "l",
+ r = "r", right = "r",
+ t = "t", top = "t",
+ b = "b", bottom = "b",
+ c = "c", center = "c",
+ m = "c", middle = "m",
+ lt = "lt",
+ rt = "rt",
+ lb = "lb",
+ rb = "rb",
+ tl = "lt",
+ tr = "rt",
+ bl = "lb",
+ br = "rb",
+}
+
+setmetatableindex(validshapes,function(t,k)
+ local l = gsub(lower(k)," ","")
+ local v = rawget(t,l)
+ if not v then
+ local n = tonumber(k)
+ if n then
+ v = { kind = "shape", number = n }
+ else
+ v = rawget(t,"action")
+ end
+ end
+ t[k] = v
+ return v
+end)
+
+local charts = { }
+
+local data, hash, temp, last_x, last_y, name
+
+function commands.flow_start_chart(chartname)
+ data = { }
+ hash = { }
+ last_x, last_y = 0, 0
+ name = chartname
+end
+
+function commands.flow_stop_chart()
+ charts[name] = {
+ data = data,
+ hash = hash,
+ last_x = last_x,
+ last_y = last_y,
+ }
+ data, hash, temp = nil, nil, nil
+end
+
+-- function commands.flow_set(chartname,chartdata)
+-- local hash = { }
+-- local data = { }
+-- charts[name] = {
+-- data = data,
+-- hash = hash,
+-- }
+-- for i=1,#chartdata do
+-- local di = data[i]
+-- local name = di.name or ""
+-- if name then
+-- data[#data+1] = {
+-- name = name,
+-- labels = di.labels or { },
+-- comments = di.comments or { },
+-- exits = di.exits or { },
+-- connections = di.connections or { },
+-- settings = di.settings or { },
+-- x = di.x or 1,
+-- y = di.y or 1,
+-- }
+-- hash[name] = i
+-- end
+-- end
+-- end
+
+function commands.flow_reset(chartname)
+ charts[name] = nil
+end
+
+function commands.flow_set_current_cell(n)
+ temp = data[tonumber(n)] or { }
+end
+
+function commands.flow_start_cell(settings)
+ temp = {
+ texts = { },
+ labels = { },
+ exits = { },
+ connections = { },
+ settings = settings,
+ x = 1,
+ y = 1,
+ name = "",
+ }
+end
+
+function commands.flow_stop_cell()
+ data[#data+1] = temp
+ hash[temp.name or #data] = temp
+end
+
+function commands.flow_set_name(str)
+ temp.name = str
+end
+
+function commands.flow_set_shape(str)
+ temp.shape = str
+end
+
+function commands.flow_set_destination(str)
+ temp.destination = str
+end
+
+function commands.flow_set_text(align,str)
+ temp.texts[#temp.texts+1] = {
+ location = align,
+ text = str,
+ }
+end
+
+function commands.flow_set_overlay(str)
+ temp.overlay = str
+end
+
+function commands.flow_set_focus(str)
+ temp.focus = str
+end
+
+function commands.flow_set_figure(str)
+ temp.figure = str
+end
+
+function commands.flow_set_label(location,text)
+ temp.labels[#temp.labels+1] = {
+ location = location,
+ text = text,
+ }
+end
+
+function commands.flow_set_comment(location,text)
+ local connections = temp.connections
+ if connections then
+ local connection = connections[#connections]
+ if connection then
+ local comments = connection.comments
+ if comments then
+ comments[#comments+1] = {
+ location = location,
+ text = text,
+ }
+ end
+ end
+ end
+end
+
+function commands.flow_set_exit(location,text)
+ temp.exits[#temp.exits+1] = {
+ location = location,
+ text = text,
+ }
+end
+
+function commands.flow_set_include(name,x,y,settings)
+ data[#data+1] = {
+ include = name,
+ x = x,
+ y = y,
+ -- settings = settings,
+ }
+end
+
+local function inject(includedata,data,hash)
+ local subchart = charts[includedata.include]
+ if not subchart then
+ return
+ end
+ local subdata = subchart.data
+ if not subdata then
+ return
+ end
+ local xoffset = (includedata.x or 1) - 1
+ local yoffset = (includedata.y or 1) - 1
+ local settings = includedata.settings
+ for i=1,#subdata do
+ local si = subdata[i]
+ if si.include then
+ inject(si,data,hash)
+ else
+ local t = {
+ x = si.x + xoffset,
+ y = si.y + yoffset,
+ settings = settings,
+ }
+ setmetatableindex(t,si)
+ data[#data+1] = t
+ hash[si.name or #data] = t
+ end
+ end
+end
+
+local function pack(data,field)
+ local list, max = { }, 0
+ for e=1,#data do
+ local d = data[e]
+ local f = d[field]
+ list[f] = true
+ if f > max then
+ max = f
+ end
+ end
+ for i=1,max do
+ if not list[i] then
+ for e=1,#data do
+ local d = data[e]
+ local f = d[field]
+ if f > i then
+ d[field] = f - 1
+ end
+ end
+ end
+ end
+end
+
+local function expanded(chart,chartsettings)
+ local expandeddata = { }
+ local expandedhash = { }
+ local expandedchart = {
+ data = expandeddata,
+ hash = expandedhash,
+ }
+ setmetatableindex(expandedchart,chart)
+ local data = chart.data
+ local hash = chart.hash
+ for i=1,#data do
+ local di = data[i]
+ if di.include then
+ inject(di,expandeddata,expandedhash)
+ else
+ expandeddata[#expandeddata+1] = di
+ expandedhash[di.name or #expandeddata] = di
+ end
+ end
+ --
+ expandedchart.settings = chartsettings or { }
+ -- make locals
+ chartsettings.shape = chartsettings.shape or { }
+ chartsettings.focus = chartsettings.focus or { }
+ chartsettings.line = chartsettings.line or { }
+ chartsettings.set = chartsettings.set or { }
+ chartsettings.split = chartsettings.split or { }
+ chartsettings.chart = chartsettings.chart or { }
+ setmetatableindex(chartsettings.shape,defaults.shape)
+ setmetatableindex(chartsettings.focus,defaults.focus)
+ setmetatableindex(chartsettings.line ,defaults.line )
+ setmetatableindex(chartsettings.set ,defaults.set )
+ setmetatableindex(chartsettings.split,defaults.split)
+ setmetatableindex(chartsettings.chart,defaults.chart)
+ --
+ if chartsettings.chart.vcompact == v_yes then
+ pack(expandeddata,"y")
+ end
+ if chartsettings.chart.hcompact == v_yes then
+ pack(expandeddata,"x")
+ end
+ --
+ for i=1,#expandeddata do
+ local cell = expandeddata[i]
+ local settings = cell.settings
+ if not settings then
+ cell.settings = chartsettings
+ else
+ settings.shape = settings.shape or { }
+ settings.focus = settings.focus or { }
+ settings.line = settings.line or { }
+ setmetatableindex(settings.shape,chartsettings.shape)
+ setmetatableindex(settings.focus,chartsettings.focus)
+ setmetatableindex(settings.line ,chartsettings.line)
+ end
+ end
+ return expandedchart
+end
+
+local splitter = lpeg.splitat(",")
+
+function commands.flow_set_location(x,y)
+ if type(x) == "string" and not y then
+ x, y = lpegmatch(splitter,x)
+ end
+ if not x or x == "" then
+ x = last_x
+ elseif type(x) == "number" then
+ -- ok
+ elseif x == "+" then
+ x = last_x + 1
+ elseif x == "-" then
+ x = last_x - 1
+ elseif find(x,"^[%+%-]") then
+ x = last_x + (tonumber(x) or 0)
+ else
+ x = tonumber(x)
+ end
+ if not y or y == "" then
+ y = last_y
+ elseif type(y) == "number" then
+ -- ok
+ elseif y == "+" then
+ y = last_y + 1
+ elseif x == "-" then
+ y = last_y - 1
+ elseif find(y,"^[%+%-]") then
+ y = last_y + (tonumber(y) or 0)
+ else
+ y = tonumber(y)
+ end
+ temp.x = x or 1
+ temp.y = y or 1
+ last_x = x or last_x
+ last_y = y or last_y
+end
+
+function commands.flow_set_connection(location,displacement,name)
+ local dx, dy = lpegmatch(splitter,displacement)
+ dx = tonumber(dx)
+ dy = tonumber(dy)
+ temp.connections[#temp.connections+1] = {
+ location = location,
+ dx = dx or 0,
+ dy = dy or 0,
+ name = name,
+ comments = { },
+ }
+end
+
+local function visible(chart,cell)
+ local x, y = cell.x, cell.y
+ return
+ x >= chart.from_x and x <= chart.to_x and
+ y >= chart.from_y and y <= chart.to_y and cell
+end
+
+local function process_cells(chart,xoffset,yoffset)
+ local data = chart.data
+ if not data then
+ return
+ end
+ local focus = utilities.parsers.settings_to_hash(chart.settings.chart.focus or "")
+ for i=1,#data do
+ local cell = visible(chart,data[i])
+ if cell then
+ local settings = cell.settings
+ local shapesettings = settings.shape
+ local shape = cell.shape
+ if not shape or shape == "" then
+ shape = shapesettings.default or "none"
+ end
+ if shape ~= v_none then
+ local shapedata = validshapes[shape]
+ context("flow_begin_sub_chart ;") -- when is this needed
+ if shapedata.kind == "line" then
+ local linesettings = settings.line
+ context("flow_shape_line_color := \\MPcolor{%s} ;", linesettings.color)
+ context("flow_shape_fill_color := \\MPcolor{%s} ;", linesettings.backgroundcolor)
+ context("flow_shape_line_width := %s ; ", points(linesettingsrulethickness))
+ elseif focus[cell.focus] or focus[cell.name] then
+ local focussettings = settings.focus
+ context("flow_shape_line_color := \\MPcolor{%s} ;", focussettings.framecolor)
+ context("flow_shape_fill_color := \\MPcolor{%s} ;", focussettings.backgroundcolor)
+ context("flow_shape_line_width := %s ; ", points(focussettings.rulethickness))
+ else
+ local shapesettings = settings.shape
+ context("flow_shape_line_color := \\MPcolor{%s} ;", shapesettings.framecolor)
+ context("flow_shape_fill_color := \\MPcolor{%s} ;", shapesettings.backgroundcolor)
+ context("flow_shape_line_width := %s ; " , points(shapesettings.rulethickness))
+ end
+ context("flow_peepshape := false ;") -- todo
+ context("flow_new_shape(%s,%s,%s) ;",cell.x+xoffset,cell.y+yoffset,shapedata.number)
+ context("flow_end_sub_chart ;")
+ end
+ end
+ end
+end
+
+-- todo : make lpeg for splitter
+
+local sign = S("+p") / "1"
+ + S("-m") / "-1"
+
+local full = C(P("left"))
+ + C(P("right"))
+ + C(P("top"))
+ + C(P("bottom"))
+
+local char = P("l") / "left"
+ + P("r") / "right"
+ + P("t") / "top"
+ + P("b") / "bottom"
+
+local space = P(" ")^0
+
+local what = space
+ * (sign + Cc("0"))
+ * space
+ * (full + char)
+ * space
+ * (sign + Cc("0"))
+ * space
+ * (full + char)
+ * space
+ * P(-1)
+
+-- print(lpegmatch(what,"lr"))
+-- print(lpegmatch(what,"+l+r"))
+-- print(lpegmatch(what,"+l"))
+-- print(lpegmatch(what,"+ left+r "))
+
+local function process_connections(chart,xoffset,yoffset)
+ local data = chart.data
+ local hash = chart.hash
+ if not data then
+ return
+ end
+ local settings = chart.settings
+ for i=1,#data do
+ local cell = visible(chart,data[i])
+ if cell then
+ local connections = cell.connections
+ for j=1,#connections do
+ local connection = connections[j]
+ local othername = connection.name
+ local othercell = hash[othername]
+ if othercell then -- and visible(chart,data[i]) then
+ local cellx, celly = cell.x, cell.y
+ local otherx, othery, location = othercell.x, othercell.y, connection.location
+ if otherx > 0 and othery > 0 and cellx > 0 and celly > 0 and connection.location then
+ local what_cell, where_cell, what_other, where_other = lpegmatch(what,location)
+ if what_cell and where_cell and what_other and where_other then
+ local linesettings = settings.line
+ context("flow_smooth := %s ;", linesettings.corner == v_round and "true" or "false")
+ context("flow_dashline := %s ;", linesettings.dash == v_yes and "true" or "false")
+ context("flow_arrowtip := %s ;", linesettings.arrow == v_yes and "true" or "false")
+ context("flow_touchshape := %s ;", linesettings.offset == v_none and "true" or "false")
+ context("flow_dsp_x := %s ; flow_dsp_y := %s ;",connection.dx or 0, connection.dy or 0)
+ context("flow_connection_line_color := \\MPcolor{%s} ;",linesettings.color)
+ context("flow_connection_line_width := 2pt ;",points(linesettings.rulethickness))
+ context("flow_connect_%s_%s (%s) (%s,%s,%s) (%s,%s,%s) ;",where_cell,where_other,j,cellx,celly,what_cell,otherx,othery,what_other)
+ context("flow_dsp_x := 0 ; flow_dsp_y := 0 ;")
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+local texttemplate = "\\setvariables[flowcell:text][x=%s,y=%s,text={%s},align={%s},figure={%s},destination={%s}]"
+
+local splitter = lpeg.splitat(":")
+
+local function process_texts(chart,xoffset,yoffset)
+ local data = chart.data
+ local hash = chart.hash
+ if not data then
+ return
+ end
+ for i=1,#data do
+ local cell = visible(chart,data[i])
+ if cell then
+ local x = cell.x or 1
+ local y = cell.y or 1
+ local texts = cell.texts
+ for i=1,#texts do
+ local text = texts[i]
+ local data = text.text
+ local align = validlabellocations[text.align or ""] or text.align or ""
+ local figure = i == 1 and cell.figure or ""
+ local destination = i == 1 and cell.destination or ""
+ context('flow_chart_draw_text(%s,%s,textext("%s")) ;',x,y,format(texttemplate,x,y,data,align,figure,destination))
+ end
+ local labels = cell.labels
+ for i=1,#labels do
+ local label = labels[i]
+ local text = label.text
+ local location = validlabellocations[label.location or ""] or label.location or ""
+ if text and location then
+ context('flow_chart_draw_label(%s,%s,"%s",textext("\\strut %s")) ;',x,y,location,text)
+ end
+ end
+ local exits = cell.exits
+ for i=1,#exits do
+ local exit = exits[i]
+ local text = exit.text
+ local location = validlabellocations[exit.location or ""]
+ if text and location then
+ -- maybe make autoexit an option
+ if location == "l" and x == chart.from_x + 1 or
+ location == "r" and x == chart.to_x - 1 or
+ location == "t" and y == chart.to_y - 1 or
+ location == "b" and y == chart.from_y + 1 then
+ context('flow_chart_draw_exit(%s,%s,"%s",textext("\\strut %s")) ;',x,y,location,text)
+ end
+ end
+ end
+ local connections = cell.connections
+ for i=1,#connections do
+ local comments = connections[i].comments
+ for j=1,#comments do
+ local comment = comments[j]
+ local text = comment.text
+ local location = comment.location or ""
+ local length = 0
+ -- "tl" "tl:*" "tl:0.5"
+ local loc, len = lpegmatch(splitter,location) -- do the following in lpeg
+ if len == "*" then
+ location = validcommentlocations[loc] or ""
+ if location == "" then
+ location = "*"
+ else
+ location = location .. ":*"
+ end
+ elseif loc then
+ location = validcommentlocations[loc] or "*"
+ length = tonumber(len) or 0
+ else
+ location = validcommentlocations[location] or ""
+ end
+ if text and location then
+ context('flow_chart_draw_comment(%s,%s,%s,"%s",%s,textext("\\strut %s")) ;',x,y,i,location,length,text)
+ end
+ end
+ end
+ end
+ end
+end
+
+local function getchart(settings,forced_x,forced_y,forced_nx,forced_ny)
+ if not settings then
+ print("no settings given")
+ return
+ end
+ local chartname = settings.chart.name
+ if not chartname then
+ print("no name given")
+ return
+ end
+ local chart = charts[chartname]
+ if not chart then
+ print("no such chart",chartname)
+ return
+ end
+ chart = expanded(chart,settings)
+ local chartsettings = chart.settings.chart
+ local autofocus = chart.settings.chart.autofocus
+ if autofocus then
+ autofocus = utilities.parsers.settings_to_hash(autofocus)
+ if not next(autofocus) then
+ autofocus = false
+ end
+ end
+ -- check natural window
+ local x = forced_x or tonumber(chartsettings.x)
+ local y = forced_y or tonumber(chartsettings.y)
+ local nx = forced_nx or tonumber(chartsettings.nx)
+ local ny = forced_ny or tonumber(chartsettings.ny)
+ --
+ local minx, miny, maxx, maxy = 0, 0, 0, 0
+ local data = chart.data
+ for i=1,#data do
+ local cell = data[i]
+ if not autofocus or autofocus[cell.name] then -- offsets probably interfere with autofocus
+ local x = cell.x
+ local y = cell.y
+ if minx == 0 or x < minx then minx = x end
+ if miny == 0 or y < miny then miny = y end
+ if minx == 0 or x > maxx then maxx = x end
+ if miny == 0 or y > maxy then maxy = y end
+ end
+ end
+ -- print("1>",x,y,nx,ny)
+ -- print("2>",minx, miny, maxx, maxy)
+ -- check of window should be larger (maybe autofocus + nx/ny?)
+ if autofocus then
+ -- x and y are ignored
+ if nx and nx > 0 then
+ maxx = minx + nx - 1
+ end
+ if ny and ny > 0 then
+ maxy = miny + ny - 1
+ end
+ else
+ if x and x > 0 then
+ minx = x
+ end
+ if y and y > 0 then
+ miny = y
+ end
+ if nx and nx > 0 then
+ maxx = minx + nx - 1
+ end
+ if ny and ny > 0 then
+ maxy = miny + ny - 1
+ end
+ end
+-- print("3>",minx, miny, maxx, maxy)
+ --
+ local nx = maxx - minx + 1
+ local ny = maxy - miny + 1
+ -- relocate cells
+ for i=1,#data do
+ local cell = data[i]
+ cell.x = cell.x - minx + 1
+ cell.y = cell.y - miny + 1
+ end
+ chart.from_x = 1
+ chart.from_y = 1
+ chart.to_x = nx
+ chart.to_y = ny
+ chart.nx = nx
+ chart.ny = ny
+ --
+ -- inspect(chart)
+ return chart
+end
+
+local function makechart(chart)
+ local settings = chart.settings
+ local chartsettings = settings.chart
+ --
+ context.begingroup()
+ context.forgetall()
+ --
+ context.startMPcode()
+ context("if unknown context_flow : input mp-char.mpiv ; fi ;")
+ context("flow_begin_chart(0,%s,%s);",chart.nx,chart.ny)
+ --
+ if chartsettings.option == v_test or chartsettings.dot == v_yes then
+ context("flow_show_con_points := true ;")
+ context("flow_show_mid_points := true ;")
+ context("flow_show_all_points := true ;")
+ elseif chartsettings.dot ~= "" then -- no checking done, private option
+ context("flow_show_%s_points := true ;",chartsettings.dot)
+ end
+ --
+ local backgroundcolor = chartsettings.backgroundcolor
+ if backgroundcolor and backgroundcolor ~= "" then
+ context("flow_chart_background_color := \\MPcolor{%s} ;",backgroundcolor)
+ end
+ --
+ local shapewidth = chartsettings.width
+ local gridwidth = shapewidth + 2*chartsettings.dx
+ local shapeheight = chartsettings.height
+ local gridheight = shapeheight + 2*chartsettings.dy
+ local chartoffset = chartsettings.offset
+ local labeloffset = chartsettings.labeloffset
+ local exitoffset = chartsettings.exitoffset
+ local commentoffset = chartsettings.commentoffset
+ context("flow_grid_width := %s ;", points(gridwidth))
+ context("flow_grid_height := %s ;", points(gridheight))
+ context("flow_shape_width := %s ;", points(shapewidth))
+ context("flow_shape_height := %s ;", points(shapeheight))
+ context("flow_chart_offset := %s ;", points(chartoffset))
+ context("flow_label_offset := %s ;", points(labeloffset))
+ context("flow_exit_offset := %s ;", points(exitoffset))
+ context("flow_comment_offset := %s ;", points(commentoffset))
+ --
+ local radius = settings.line.radius
+ local rulethickness = settings.line.rulethickness
+ local dx = chartsettings.dx
+ local dy = chartsettings.dy
+ if radius < rulethickness then
+ radius = 2.5*rulethickness
+ if radius > dx then
+ radius = dx
+ end
+ if radius > dy then
+ radius = dy
+ end
+ end
+ context("flow_connection_line_width := %s ;", points(rulethickness))
+ context("flow_connection_smooth_size := %s ;", points(radius))
+ context("flow_connection_arrow_size := %s ;", points(radius))
+ context("flow_connection_dash_size := %s ;", points(radius))
+ --
+ local offset = chartsettings.offset -- todo: pass string
+ if offset == v_none or offset == v_overlay or offset == "" then
+ offset = -2.5 * radius -- or rulethickness?
+ elseif offset == v_standard then
+ offset = radius -- or rulethickness?
+ end
+ context("flow_chart_offset := %s ;",points(offset))
+ --
+ context("flow_reverse_y := true ;")
+ process_cells(chart,0,0)
+ process_connections(chart,0,0)
+ process_texts(chart,0,0)
+ -- context("clip_chart(%s,%s,%s,%s) ;",x,y,nx,ny) -- todo: draw lines but not shapes
+ context("flow_end_chart ;")
+ context.stopMPcode()
+ context.endgroup()
+end
+
+local function splitchart(chart)
+ local settings = chart.settings
+ local splitsettings = settings.split
+ local chartsettings = settings.chart
+ --
+ local name = chartsettings.name
+ --
+ local from_x = chart.from_x
+ local from_y = chart.from_y
+ local to_x = chart.to_x
+ local to_y = chart.to_y
+ --
+ local step_x = splitsettings.nx or to_x
+ local step_y = splitsettings.ny or to_y
+ local delta_x = splitsettings.dx or 0
+ local delta_y = splitsettings.dy or 0
+ --
+ report_chart("spliting %a from (%s,%s) upto (%s,%s) into (%s,%s) with overlap (%s,%s)",
+ name,from_x,from_y,to_x,to_y,step_x,step_y,delta_x,delta_y)
+ --
+ local part_x = 0
+ local first_x = from_x
+ while true do
+ part_x = part_x + 1
+ local last_x = first_x + step_x - 1
+ local done = last_x >= to_x
+ if done then
+ last_x = to_x
+ end
+ local part_y = 0
+ local first_y = from_y
+ while true do
+ part_y = part_y + 1
+ local last_y = first_y + step_y - 1
+ local done = last_y >= to_y
+ if done then
+ last_y = to_y
+ end
+ --
+ report_chart("part (%s,%s) of %a is split from (%s,%s) -> (%s,%s)",part_x,part_y,name,first_x,first_y,last_x,last_y)
+ local x, y, nx, ny = first_x, first_y, last_x - first_x + 1,last_y - first_y + 1
+ context.beforeFLOWsplit()
+ context.handleFLOWsplit(function()
+ makechart(getchart(settings,x,y,nx,ny)) -- we need to pass frozen settings !
+ end)
+ context.afterFLOWsplit()
+ --
+ if done then
+ break
+ else
+ first_y = last_y + 1 - delta_y
+ end
+ end
+ if done then
+ break
+ else
+ first_x = last_x + 1 - delta_x
+ end
+ end
+end
+
+function commands.flow_make_chart(settings)
+ local chart = getchart(settings)
+ if chart then
+ local settings = chart.settings
+ if settings then
+ local chartsettings = settings.chart
+ if chartsettings and chartsettings.split == v_yes then
+ splitchart(chart)
+ else
+ makechart(chart)
+ end
+ else
+ makechart(chart)
+ end
+ end
+end
diff --git a/tex/context/base/m-database.lua b/tex/context/base/m-database.lua
index c287f4926..47854daa0 100644
--- a/tex/context/base/m-database.lua
+++ b/tex/context/base/m-database.lua
@@ -1,137 +1,137 @@
-if not modules then modules = { } end modules ['m-database'] = {
- version = 1.001,
- comment = "companion to m-database.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local sub, gmatch, format = string.sub, string.gmatch, string.format
-local concat = table.concat
-local lpegpatterns, lpegmatch, lpegsplitat = lpeg.patterns, lpeg.match, lpeg.splitat
-local lpegP, lpegC, lpegS, lpegCt = lpeg.P, lpeg.C, lpeg.S, lpeg.Ct
-local stripstring = string.strip
-
--- One also needs to enable context.trace, here we only plug in some code (maybe
--- some day this tracker will also toggle the main context tracer.
-
-local trace_flush = false trackers.register("module.database.flush", function(v) trace_flush = v end)
-
-local report_database = logs.reporter("database")
-
-buffers.database = buffers.database or { }
-
-local l_tab = lpegpatterns.tab
-local l_space = lpegpatterns.space
-local l_comma = lpegpatterns.comma
-local l_empty = lpegS("\t\n\r ")^0 * lpegP(-1)
-
-local v_yes = interfaces.variables.yes
-
-local separators = { -- not interfaced
- tab = l_tab,
- tabs = l_tab^1,
- comma = l_comma,
- space = l_space,
- spaces = l_space^1,
-}
-
-function buffers.database.process(settings)
- local data
- if settings.type == "file" then
- local filename = resolvers.finders.byscheme("any",settings.database)
- data = filename ~= "" and io.loaddata(filename)
- data = data and string.splitlines(data)
- else
- data = buffers.getlines(settings.database)
- end
- if data and #data > 0 then
- if trace_flush then
- context.pushlogger(report_database)
- end
- local separatorchar, quotechar, commentchar = settings.separator, settings.quotechar, settings.commentchar
- local before, after = settings.before or "", settings.after or ""
- local first, last = settings.first or "", settings.last or ""
- local left, right = settings.left or "", settings.right or ""
- local setups = settings.setups or ""
- local strip = settings.strip == v_yes or false
- local command = settings.command
- separatorchar = (not separatorchar and ",") or separators[separatorchar] or separatorchar
- local separator = type(separatorchar) == "string" and lpegS(separatorchar) or separatorchar
- local whatever = lpegC((1 - separator)^0)
- if quotechar and quotechar ~= "" then
- local quotedata = nil
- for chr in gmatch(quotechar,".") do
- local quotechar = lpegP(chr)
- local quoteword = l_space^0 * quotechar * lpegC((1 - quotechar)^0) * quotechar * l_space^0
- if quotedata then
- quotedata = quotedata + quoteword
- else
- quotedata = quoteword
- end
- end
- whatever = quotedata + whatever
- end
- local checker = commentchar ~= "" and lpegS(commentchar)
- local splitter = lpegCt(whatever * (separator * whatever)^0)
- local found = false
- for i=1,#data do
- local line = data[i]
- if not lpegmatch(l_empty,line) and (not checker or not lpegmatch(checker,line)) then
- local list = lpegmatch(splitter,line)
- if not found then
- if setups ~= "" then
- context.begingroup()
- context.setups { setups }
- end
- context(before)
- found = true
- end
- if trace_flush then
- local result, r = { }, 0
- r = r + 1 ; result[r] = first
- for j=1,#list do
- local str = strip and stripstring(list[j]) or list[j]
- r = r + 1 ; result[r] = left
- if command == "" then
- r = r + 1 ; result[r] = str
- else
- r = r + 1 ; result[r] = command
- r = r + 1 ; result[r] = "{"
- r = r + 1 ; result[r] = str
- r = r + 1 ; result[r] = "}"
- end
- r = r + 1 ; result[r] = right
- end
- r = r + 1 ; result[r] = last
- context(concat(result))
- else
- context(first)
- for j=1,#list do
- local str = strip and stripstring(list[j]) or list[j]
- context(left)
- if command == "" then
- context(str)
- else
- context(command)
- context(false,str)
- end
- context(right)
- end
- context(last)
- end
- end
- end
- if found then
- context(after)
- if setups ~= "" then
- context.endgroup()
- end
- end
- if trace_flush then
- context.poplogger()
- end
- else
- -- message
- end
-end
+if not modules then modules = { } end modules ['m-database'] = {
+ version = 1.001,
+ comment = "companion to m-database.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local sub, gmatch, format = string.sub, string.gmatch, string.format
+local concat = table.concat
+local lpegpatterns, lpegmatch, lpegsplitat = lpeg.patterns, lpeg.match, lpeg.splitat
+local lpegP, lpegC, lpegS, lpegCt = lpeg.P, lpeg.C, lpeg.S, lpeg.Ct
+local stripstring = string.strip
+
+-- One also needs to enable context.trace, here we only plug in some code (maybe
+-- some day this tracker will also toggle the main context tracer.
+
+local trace_flush = false trackers.register("module.database.flush", function(v) trace_flush = v end)
+
+local report_database = logs.reporter("database")
+
+buffers.database = buffers.database or { }
+
+local l_tab = lpegpatterns.tab
+local l_space = lpegpatterns.space
+local l_comma = lpegpatterns.comma
+local l_empty = lpegS("\t\n\r ")^0 * lpegP(-1)
+
+local v_yes = interfaces.variables.yes
+
+local separators = { -- not interfaced
+ tab = l_tab,
+ tabs = l_tab^1,
+ comma = l_comma,
+ space = l_space,
+ spaces = l_space^1,
+}
+
+function buffers.database.process(settings)
+ local data
+ if settings.type == "file" then
+ local filename = resolvers.finders.byscheme("any",settings.database)
+ data = filename ~= "" and io.loaddata(filename)
+ data = data and string.splitlines(data)
+ else
+ data = buffers.getlines(settings.database)
+ end
+ if data and #data > 0 then
+ if trace_flush then
+ context.pushlogger(report_database)
+ end
+ local separatorchar, quotechar, commentchar = settings.separator, settings.quotechar, settings.commentchar
+ local before, after = settings.before or "", settings.after or ""
+ local first, last = settings.first or "", settings.last or ""
+ local left, right = settings.left or "", settings.right or ""
+ local setups = settings.setups or ""
+ local strip = settings.strip == v_yes or false
+ local command = settings.command
+ separatorchar = (not separatorchar and ",") or separators[separatorchar] or separatorchar
+ local separator = type(separatorchar) == "string" and lpegS(separatorchar) or separatorchar
+ local whatever = lpegC((1 - separator)^0)
+ if quotechar and quotechar ~= "" then
+ local quotedata = nil
+ for chr in gmatch(quotechar,".") do
+ local quotechar = lpegP(chr)
+ local quoteword = l_space^0 * quotechar * lpegC((1 - quotechar)^0) * quotechar * l_space^0
+ if quotedata then
+ quotedata = quotedata + quoteword
+ else
+ quotedata = quoteword
+ end
+ end
+ whatever = quotedata + whatever
+ end
+ local checker = commentchar ~= "" and lpegS(commentchar)
+ local splitter = lpegCt(whatever * (separator * whatever)^0)
+ local found = false
+ for i=1,#data do
+ local line = data[i]
+ if not lpegmatch(l_empty,line) and (not checker or not lpegmatch(checker,line)) then
+ local list = lpegmatch(splitter,line)
+ if not found then
+ if setups ~= "" then
+ context.begingroup()
+ context.setups { setups }
+ end
+ context(before)
+ found = true
+ end
+ if trace_flush then
+ local result, r = { }, 0
+ r = r + 1 ; result[r] = first
+ for j=1,#list do
+ local str = strip and stripstring(list[j]) or list[j]
+ r = r + 1 ; result[r] = left
+ if command == "" then
+ r = r + 1 ; result[r] = str
+ else
+ r = r + 1 ; result[r] = command
+ r = r + 1 ; result[r] = "{"
+ r = r + 1 ; result[r] = str
+ r = r + 1 ; result[r] = "}"
+ end
+ r = r + 1 ; result[r] = right
+ end
+ r = r + 1 ; result[r] = last
+ context(concat(result))
+ else
+ context(first)
+ for j=1,#list do
+ local str = strip and stripstring(list[j]) or list[j]
+ context(left)
+ if command == "" then
+ context(str)
+ else
+ context(command)
+ context(false,str)
+ end
+ context(right)
+ end
+ context(last)
+ end
+ end
+ end
+ if found then
+ context(after)
+ if setups ~= "" then
+ context.endgroup()
+ end
+ end
+ if trace_flush then
+ context.poplogger()
+ end
+ else
+ -- message
+ end
+end
diff --git a/tex/context/base/m-markdown.lua b/tex/context/base/m-markdown.lua
index 6c9c44d78..1f9402f60 100644
--- a/tex/context/base/m-markdown.lua
+++ b/tex/context/base/m-markdown.lua
@@ -1,824 +1,824 @@
-if not modules then modules = { } end modules ['m-markdown'] = {
- version = 1.002,
- comment = "companion to m-markdown.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "see below",
- license = "see context related readme files"
-}
-
---[[
-Copyright (C) 2009 John MacFarlane / Khaled Hosny / Hans Hagen
-
-The main parser is derived from the lunamark parser written by John MacFarlane. You
-can download lunamark from:
-
- http://github.com/jgm/lunamark.git
-
-Khaled Hosny provided the context writer for lunamark and that was used as starting
-point for the mapping. The original code can be fetched from the above location.
-
-While playing with the original code I got the feeling that lpeg could perform better.
-The slowdown was due to the fact that the parser's lpeg was reconstructed each time a
-nested parse was needed. After changing that code a bit I could bring down parsing of
-some test code from 2 seconds to less than 0.1 second so I decided to stick to this
-parser instead of writing my own. After all, the peg code looks pretty impressive and
-visiting Johns pandoc pages is worth the effort:
-
- http://johnmacfarlane.net/pandoc/
-
-The code here is mostly meant for processing snippets embedded in a context
-documents and is no replacement for pandoc at all. Therefore an alternative is to use
-pandoc in combination with Aditya's filter module.
-
-As I changed (and optimized) the original code, it will be clear that all errors
-are mine. Eventually I might also adapt the parser code a bit more. When I ran into of
-closure stack limitations I decided to flatten the code. The following implementation
-seems to be a couple of hundred times faster than what I started with which is not that
-bad.
-
-This is a second rewrite. The mentioned speed gain largely depended on the kind of
-content: blocks, references and items can be rather demanding. Also, There were
-some limitations with respect to the captures. So, table storage has been removed in
-favor of strings, and nesting has been simplified. The first example at the end of this
-file now takes .33 seconds for 567KB code (resulting in over 1MB) so we're getting there.
-
-There will be a third rewrite eventually.
-]]--
-
--- todo: we have better quote and tag scanners in ctx
--- todo: provide an xhtml mapping
--- todo: add a couple of extensions
--- todo: check patches to the real peg
-
-local type, next, tonumber = type, next, tonumber
-local lower, upper, gsub, rep, gmatch, format, length = string.lower, string.upper, string.gsub, string.rep, string.gmatch, string.format, string.len
-local concat = table.concat
-local P, R, S, V, C, Ct, Cg, Cb, Cmt, Cc, Cf, Cs = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Ct, lpeg.Cg, lpeg.Cb, lpeg.Cmt, lpeg.Cc, lpeg.Cf, lpeg.Cs
-local lpegmatch = lpeg.match
-local utfbyte, utfchar = utf.byte, utf.char
-
-moduledata = moduledata or { }
-moduledata.markdown = moduledata.markdown or { }
-local markdown = moduledata.markdown
-
-local nofruns, nofbytes, nofhtmlblobs = 0, 0, 0
-
----------------------------------------------------------------------------------------------
-
-local nestedparser
-local syntax
-
-nestedparser = function(str) return lpegmatch(syntax,str) end
-
----------------------------------------------------------------------------------------------
-
-local asterisk = P("*")
-local dash = P("-")
-local plus = P("+")
-local underscore = P("_")
-local period = P(".")
-local hash = P("#")
-local ampersand = P("&")
-local backtick = P("`")
-local less = P("<")
-local more = P(">")
-local space = P(" ")
-local squote = P("'")
-local dquote = P('"')
-local lparent = P("(")
-local rparent = P(")")
-local lbracket = P("[")
-local rbracket = P("]")
-local slash = P("/")
-local equal = P("=")
-local colon = P(":")
-local semicolon = P(";")
-local exclamation = P("!")
-
-local digit = R("09")
-local hexdigit = R("09","af","AF")
-local alphanumeric = R("AZ","az","09")
-
-local doubleasterisks = P("**")
-local doubleunderscores = P("__")
-local fourspaces = P(" ")
-
-local any = P(1)
-local always = P("")
-
-local tab = P("\t")
-local spacechar = S("\t ")
-local spacing = S(" \n\r\t")
-local newline = P("\r")^-1 * P("\n")
-local spaceornewline = spacechar + newline
-local nonspacechar = any - spaceornewline
-local optionalspace = spacechar^0
-local spaces = spacechar^1
-local eof = - any
-local nonindentspace = space^-3
-local blankline = optionalspace * C(newline)
-local blanklines = blankline^0
-local skipblanklines = (optionalspace * newline)^0
-local linechar = P(1 - newline)
-local indent = fourspaces + (nonindentspace * tab) / ""
-local indentedline = indent /"" * C(linechar^1 * (newline + eof))
-local optionallyindentedline = indent^-1 /"" * C(linechar^1 * (newline + eof))
-local spnl = optionalspace * (newline * optionalspace)^-1
-local specialchar = S("*_`*&[]<!\\")
-local normalchar = any - (specialchar + spaceornewline)
-local line = C((any - newline)^0 * newline)
- + C(any^1 * eof)
-local nonemptyline = (any - newline)^1 * newline
-
----------------------------------------------------------------------------------------------
-
-local function lineof(c)
- return (nonindentspace * (P(c) * optionalspace)^3 * newline * blankline^1)
-end
-
-local lineof_asterisks = lineof(asterisk)
-local lineof_dashes = lineof(dash)
-local lineof_underscores = lineof(underscore)
-
-local bullet = nonindentspace * (plus + (asterisk - lineof_asterisks) + (dash - lineof_dashes)) * spaces
-local enumerator = nonindentspace * digit^1 * period * spaces
-
----------------------------------------------------------------------------------------------
-
-local openticks = Cg(backtick^1, "ticks")
-local closeticks = space^-1 * Cmt(C(backtick^1) * Cb("ticks"), function(s,i,a,b) return #a == #b and i end)
-local intickschar = (any - S(" \n\r`"))
- + (newline * -blankline)
- + (space - closeticks)
- + (backtick^1 - closeticks)
-local inticks = openticks * space^-1 * C(intickschar^1) * closeticks
-
----------------------------------------------------------------------------------------------
-
-local leader = space^-3
-local nestedbrackets = P { lbracket * ((1 - lbracket - rbracket) + V(1))^0 * rbracket }
-local tag = lbracket * C((nestedbrackets + 1 - rbracket)^0) * rbracket
-local url = less * C((1-more)^0) * more
- + C((1-spacing- rparent)^1) -- sneaky: ) for resolver
-local title_s = squote * lpeg.C((1-squote )^0) * squote
-local title_d = dquote * lpeg.C((1-dquote )^0) * dquote
-local title_p = lparent * lpeg.C((1-rparent)^0) * rparent
-local title = title_s + title_d + title_p
-local optionaltitle = ((spacing^0 * title * spacechar^0) + lpeg.Cc(""))
-
-local references = { }
-
-local function register_link(tag,url,title)
- tag = lower(gsub(tag, "[ \n\r\t]+", " "))
- references[tag] = { url, title }
-end
-
-local function direct_link(label,url,title) -- title is typical html thing
- return label, url, title
-end
-
-local function indirect_link(label,tag)
- if tag == "" then
- tag = label
- end
- tag = lower(gsub(tag, "[ \n\r\t]+", " "))
- local r = references[tag]
- if r then
- return label, r[1], r[2]
- else
- return label, tag, ""
- end
-end
-
-local define_reference_parser = (leader * tag * colon * spacechar^0 * url * optionaltitle) / register_link
-local direct_link_parser = tag * spacechar^0 * lparent * (url + Cc("")) * optionaltitle * rparent / direct_link
-local indirect_link_parser = tag * spacechar^0 * tag / indirect_link
-
-local rparser = (define_reference_parser+1)^0
-
-local function referenceparser(str)
- references = { }
- lpegmatch(rparser,str)
-end
-
--- local reftest = [[
--- [1]: <http://example.com/>
--- [3]:http://example.com/ (Optional Title Here)
--- [2]: http://example.com/ 'Optional Title Here'
--- [a]: http://example.com/ "Optional *oeps* Title Here"
--- ]]
---
--- local linktest = [[
--- [This link] (http://example.net/)
--- [an example] (http://example.com/ "Title")
--- [an example][1]
--- [an example] [2]
--- ]]
---
--- lpeg.match((define_reference_parser+1)^0,reftest)
---
--- inspect(references)
---
--- lpeg.match((direct_link_parser/print + indirect_link_parser/print + 1)^0,linktest)
-
----------------------------------------------------------------------------------------------
-
-local blocktags = table.tohash {
- "address", "blockquote" , "center", "dir", "div", "p", "pre",
- "li", "ol", "ul", "dl", "dd",
- "form", "fieldset", "isindex", "menu", "noframes", "frameset",
- "h1", "h2", "h3", "h4", "h5", "h6",
- "hr", "ht", "script", "noscript",
- "table", "tbody", "tfoot", "thead", "th", "td", "tr",
-}
-
------ htmlattributevalue = squote * C((any - (blankline + squote))^0) * squote
------ + dquote * C((any - (blankline + dquote))^0) * dquote
------ + (any - S("\t >"))^1 -- any - tab - space - more
------ htmlattribute = (alphanumeric + S("_-"))^1 * spnl * (equal * spnl * htmlattributevalue)^-1 * spnl
------ htmlcomment = P("<!--") * (any - P("-->"))^0 * P("-->")
-
------ htmltag = less * spnl * slash^-1 * alphanumeric^1 * spnl * htmlattribute^0 * slash^-1 * spnl * more
------
------ blocktag = Cmt(C(alphanumeric^1), function(s,i,a) return blocktags[lower(a)] and i, a end)
------
------ openblocktag = less * Cg(blocktag, "opentag") * spnl * htmlattribute^0 * more
------ closeblocktag = less * slash * Cmt(C(alphanumeric^1) * Cb("opentag"), function(s,i,a,b) return lower(a) == lower(b) and i end) * spnl * more
------ selfclosingblocktag = less * blocktag * spnl * htmlattribute^0 * slash * more
------
------ displayhtml = Cs { "HtmlBlock",
------ InBlockTags = openblocktag * (V("HtmlBlock") + (any - closeblocktag))^0 * closeblocktag,
------ HtmlBlock = C(V("InBlockTags") + selfclosingblocktag + htmlcomment),
------ }
------
------ inlinehtml = Cs(htmlcomment + htmltag)
-
--- There is no reason to support crappy html, so we expect proper attributes.
-
-local htmlattributevalue = squote * C((any - (blankline + squote))^0) * squote
- + dquote * C((any - (blankline + dquote))^0) * dquote
-local htmlattribute = (alphanumeric + S("_-"))^1 * spnl * equal * spnl * htmlattributevalue * spnl
-
-local htmlcomment = P("<!--") * (any - P("-->"))^0 * P("-->")
-local htmlinstruction = P("<?") * (any - P("?>" ))^0 * P("?>" )
-
--- We don't care too much about matching elements and there is no reason why display elements could not
--- have inline elements so the above should be patched then. Well, markdown mixed with html is not meant
--- for anything else than webpages anyway.
-
-local blocktag = Cmt(C(alphanumeric^1), function(s,i,a) return blocktags[lower(a)] and i, a end)
-
-local openelement = less * alphanumeric^1 * spnl * htmlattribute^0 * more
-local closeelement = less * slash * alphanumeric^1 * spnl * more
-local emptyelement = less * alphanumeric^1 * spnl * htmlattribute^0 * slash * more
-
-local displaytext = (any - less)^1
-local inlinetext = displaytext / nestedparser
-
-local displayhtml = #(less * blocktag * spnl * htmlattribute^0 * more)
- * Cs { "HtmlBlock",
- InBlockTags = openelement * (V("HtmlBlock") + displaytext)^0 * closeelement,
- HtmlBlock = (V("InBlockTags") + emptyelement + htmlcomment + htmlinstruction),
- }
-
-local inlinehtml = Cs { "HtmlBlock",
- InBlockTags = openelement * (V("HtmlBlock") + inlinetext)^0 * closeelement,
- HtmlBlock = (V("InBlockTags") + emptyelement + htmlcomment + htmlinstruction),
- }
-
----------------------------------------------------------------------------------------------
-
-local hexentity = ampersand * hash * S("Xx") * C(hexdigit ^1) * semicolon
-local decentity = ampersand * hash * C(digit ^1) * semicolon
-local tagentity = ampersand * C(alphanumeric^1) * semicolon
-
----------------------------------------------------------------------------------------------
-
--- --[[
-
-local escaped = {
- ["{" ] = "",
- ["}" ] = "",
- ["$" ] = "",
- ["&" ] = "",
- ["#" ] = "",
- ["~" ] = "",
- ["|" ] = "",
- ["%%"] = "",
- ["\\"] = "",
-}
-
-for k, v in next, escaped do
- escaped[k] = "\\char" .. utfbyte(k) .. "{}"
-end
-
-local function c_string(s) -- has to be done more often
- return (gsub(s,".",escaped))
-end
-
-local c_linebreak = "\\crlf\n" -- is this ok?
-local c_space = " "
-
-local function c_paragraph(c)
- return c .. "\n\n" -- { "\\startparagraph ", c, " \\stopparagraph\n" }
-end
-
-local function listitem(c)
- return format("\n\\startitem\n%s\n\\stopitem\n",nestedparser(c))
-end
-
-local function c_tightbulletlist(c)
- return format("\n\\startmarkdownitemize[packed]\n%s\\stopmarkdownitemize\n",c)
-end
-
-local function c_loosebulletlist(c)
- return format("\n\\startmarkdownitemize\n\\stopmarkdownitemize\n",c)
-end
-
-local function c_tightorderedlist(c)
- return format("\n\\startmarkdownitemize[n,packed]\n%s\\stopmarkdownitemize\n",c)
-end
-
-local function c_looseorderedlist(c)
- return format("\n\\startmarkdownitemize[n]\n%s\\stopmarkdownitemize\n",c)
-end
-
-local function c_inline_html(content)
- nofhtmlblobs = nofhtmlblobs + 1
- return format("\\markdowninlinehtml{%s}",content)
-end
-
-local function c_display_html(content)
- nofhtmlblobs = nofhtmlblobs + 1
- return format("\\startmarkdowndisplayhtml\n%s\n\\stopmarkdowndisplayhtml",content)
-end
-
-local function c_emphasis(c)
- return format("\\markdownemphasis{%s}",c)
-end
-
-local function c_strong(c)
- return format("\\markdownstrong{%s}",c)
-end
-
-local function c_blockquote(c)
- return format("\\startmarkdownblockquote\n%s\\stopmarkdownblockquote\n",nestedparser(c))
-end
-
-local function c_verbatim(c)
- return format("\\startmarkdowntyping\n%s\\stopmarkdowntyping\n",c)
-end
-
-local function c_code(c)
- return format("\\markdowntype{%s}",c)
-end
-
-local levels = { "", "", "", "", "", "" }
-
-local function c_start_document()
- levels = { "", "", "", "", "", "" }
- return ""
-end
-
-local function c_stop_document()
- return concat(levels,"\n") or ""
-end
-
-local function c_heading(level,c)
- if level > #levels then
- level = #levels
- end
- local finish = concat(levels,"\n",level) or ""
- for i=level+1,#levels do
- levels[i] = ""
- end
- levels[level] = "\\stopstructurelevel"
- return format("%s\\startstructurelevel[markdown][title={%s}]\n",finish,c)
-end
-
-local function c_hrule()
- return "\\markdownrule\n"
-end
-
-local function c_link(lab,src,tit)
- return format("\\goto{%s}[url(%s)]",nestedparser(lab),src)
-end
-
-local function c_image(lab,src,tit)
- return format("\\externalfigure[%s]",src)
-end
-
-local function c_email_link(address)
- return format("\\goto{%s}[url(mailto:%s)]",c_string(address),address)
-end
-
-local function c_url_link(url)
- return format("\\goto{%s}[url(%s)]",c_string(url),url)
-end
-
-local function f_heading(c,n)
- return c_heading(n,c)
-end
-
-local function c_hex_entity(s)
- return utfchar(tonumber(s,16))
-end
-
-local function c_dec_entity(s)
- return utfchar(tonumber(s))
-end
-
-local function c_tag_entity(s)
- return s -- we can use the default resolver
-end
-
---]]
-
----------------------------------------------------------------------------------------------
-
---[[
-
-local escaped = {
- ["<"] = "&lt;",
- [">"] = "&gt;",
- ["&"] = "&amp;",
- ['"'] = "&quot;",
-}
-
-local function c_string(s) -- has to be done more often
- return (gsub(s,".",escaped))
-end
-
-local c_linebreak = "<br/>"
-local c_space = " "
-
-local function c_paragraph(c)
- return format("<p>%s</p>\n", c)
-end
-
-local function listitem(c)
- return format("<li>%s</li>",nestedparser(c))
-end
-
-local function c_tightbulletlist(c)
- return format("<ul>\n%s\n</ul>\n",c)
-end
-
-local function c_loosebulletlist(c)
- return format("<ul>\n%s\n</ul>\n",c)
-end
-
-local function c_tightorderedlist(c)
- return format("<ol>\n%s\n</ol>\n",c)
-end
-
-local function c_looseorderedlist(c)
- return format("<ol>\n%s\n</ol>\n",c)
-end
-
-local function c_inline_html(content)
- nofhtmlblobs = nofhtmlblobs + 1
- return content
-end
-
-local function c_display_html(content)
- nofhtmlblobs = nofhtmlblobs + 1
- return format("\n%s\n",content)
-end
-
-local function c_emphasis(c)
- return format("<em>%s</em>",c)
-end
-
-local function c_strong(c)
- return format("<strong>%s</strong>",c)
-end
-
-local function c_blockquote(c)
- return format("<blockquote>\n%s\n</blockquote>",nestedparser(c))
-end
-
-local function c_verbatim(c)
- return format("<pre><code>%s</code></pre>",c)
-end
-
-local function c_code(c)
- return format("<code>%s</code>",c)
-end
-
-local c_start_document = ""
-local c_stop_document = ""
-
-local function c_heading(level,c)
- return format("<h%d>%s</h%d>\n",level,c,level)
-end
-
-local function c_hrule()
- return "<hr/>\n"
-end
-
-local function c_link(lab,src,tit)
- local titattr = #tit > 0 and format(" title=%q",tit) or ""
- return format("<a href=%q%s>%s</a>",src,titattr,nestedparser(lab))
-end
-
-local function c_image(lab,src,tit)
- return format("<img href=%q title=%q>%s</a>",src,tit,nestedparser(lab))
-end
-
-local function c_email_link(address)
- return format("<a href=%q>%s</a>","mailto:",address,c_escape(address))
-end
-
-local function c_url_link(url)
- return format("<a href=%q>%s</a>",url,c_string(url))
-end
-
-local function f_heading(c,n)
- return c_heading(n,c)
-end
-
-local function c_hex_entity(s)
- return utfchar(tonumber(s,16))
-end
-
-local function c_dec_entity(s)
- return utfchar(tonumber(s))
-end
-
-local function c_tag_entity(s)
- return format("&%s;",s)
-end
-
---]]
-
----------------------------------------------------------------------------------------------
-
-local Str = normalchar^1 / c_string
-local Space = spacechar^1 / c_space
-local Symbol = specialchar / c_string
-local Code = inticks / c_code
-
-local HeadingStart = C(hash * hash^-5) / length
-local HeadingStop = optionalspace * hash^0 * optionalspace * newline * blanklines
-local HeadingLevel = equal^3 * Cc(1)
- + dash ^3 * Cc(2)
-
-local NormalEndline = optionalspace * newline * -(
- blankline
- + more
- + HeadingStart
- + ( line * (P("===")^3 + P("---")^3) * newline )
- ) / c_space
-
-local LineBreak = P(" ") * NormalEndline / c_linebreak
-
-local TerminalEndline = optionalspace * newline * eof / ""
-
-local Endline = LineBreak
- + TerminalEndline
- + NormalEndline
-
-local AutoLinkUrl = less * C(alphanumeric^1 * P("://") * (any - (newline + more))^1) * more / c_url_link
-local AutoLinkEmail = less * C((alphanumeric + S("-_+"))^1 * P("@") * (any - (newline + more))^1) * more / c_email_link
-
-local DirectLink = direct_link_parser / c_link
-local IndirectLink = indirect_link_parser / c_link
-
-local ImageLink = exclamation * (direct_link_parser + indirect_link_parser) / c_image -- we can combine this with image ... smaller lpeg
-
-local UlOrStarLine = asterisk^4
- + underscore^4
- + (spaces * S("*_")^1 * #spaces) / c_string
-
-local EscapedChar = P("\\") * C(P(1 - newline)) / c_string
-
-local InlineHtml = inlinehtml / c_inline_html
-local DisplayHtml = displayhtml / c_display_html
-local HtmlEntity = hexentity / c_hex_entity
- + decentity / c_dec_entity
- + tagentity / c_tag_entity
-
-local NestedList = Cs(optionallyindentedline - (bullet + enumerator))^1 / nestedparser
-
-local ListBlockLine = -blankline * -(indent^-1 * (bullet + enumerator)) * optionallyindentedline
-
-local Verbatim = Cs(blanklines * (indentedline - blankline)^1) / c_verbatim
- * (blankline^1 + eof) -- not really needed, probably capture trailing? we can do that beforehand
-
-local Blockquote = Cs((
- ((nonindentspace * more * space^-1)/"" * linechar^0 * newline)^1
- * ((linechar - blankline)^1 * newline)^0
- * blankline^0
- )^1) / c_blockquote
-
-local HorizontalRule = (lineof_asterisks + lineof_dashes + lineof_underscores) / c_hrule
-
-local Reference = define_reference_parser / ""
-
--- could be a mini grammar
-
-local ListBlock = line * ListBlockLine^0
-local ListContinuationBlock = blanklines * indent * ListBlock
-local ListItem = Cs(ListBlock * (NestedList + ListContinuationBlock^0)) / listitem
-
----- LeadingLines = blankline^0 / ""
----- TrailingLines = blankline^1 * #(any) / "\n"
-
-syntax = Cs { "Document",
-
- Document = V("Display")^0,
-
- Display = blankline -- ^1/"\n"
- + Blockquote
- + Verbatim
- + Reference
- + HorizontalRule
- + HeadingStart * optionalspace * Cs((V("Inline") - HeadingStop)^1) * HeadingStop / c_heading
- + Cs((V("Inline") - Endline)^1) * newline * HeadingLevel * newline * blanklines / f_heading
- + Cs((bullet /"" * ListItem)^1) * blanklines * -bullet / c_tightbulletlist
- + Cs((bullet /"" * ListItem * C(blanklines))^1) / c_loosebulletlist
- + Cs((enumerator /"" * ListItem)^1) * blanklines * -enumerator / c_tightorderedlist
- + Cs((enumerator /"" * ListItem * C(blanklines))^1) / c_looseorderedlist
- + DisplayHtml
- + nonindentspace * Cs(V("Inline")^1)* newline * blankline^1 / c_paragraph
- + V("Inline")^1,
-
- Inline = Str
- + Space
- + Endline
- + UlOrStarLine -- still needed ?
- + doubleasterisks * -spaceornewline * Cs((V("Inline") - doubleasterisks )^1) * doubleasterisks / c_strong
- + doubleunderscores * -spaceornewline * Cs((V("Inline") - doubleunderscores)^1) * doubleunderscores / c_strong
- + asterisk * -spaceornewline * Cs((V("Inline") - asterisk )^1) * asterisk / c_emphasis
- + underscore * -spaceornewline * Cs((V("Inline") - underscore )^1) * underscore / c_emphasis
- + ImageLink
- + DirectLink
- + IndirectLink
- + AutoLinkUrl
- + AutoLinkEmail
- + Code
- + InlineHtml
- + HtmlEntity
- + EscapedChar
- + Symbol,
-
-}
-
----------------------------------------------------------------------------------------------
-
-local function convert(str)
- nofruns = nofruns + 1
- nofbytes = nofbytes + #str
- statistics.starttiming(markdown)
- referenceparser(str)
- local result = c_start_document() .. nestedparser(str) .. c_stop_document()
- statistics.stoptiming(markdown)
- return result
-end
-
-markdown.convert = convert
-
-function markdown.typesetstring(data)
- if data and data ~= "" then
- local result = convert(data)
- context.viafile(result)
- end
-end
-
-function markdown.typesetbuffer(name)
- markdown.typesetstring(buffers.getcontent(name))
-end
-
-function markdown.typesetfile(name)
- local fullname = resolvers.findctxfile(name)
- if fullname and fullname ~= "" then
- markdown.typesetstring(io.loaddata(fullname))
- end
-end
-
-statistics.register("markdown",function()
- if nofruns > 0 then
- return format("%s bytes converted, %s runs, %s html blobs, %s seconds used",
- nofbytes, nofruns, nofhtmlblobs, statistics.elapsedtime(markdown))
- end
-end)
-
----------------------------------------------------------------------------------------------
-
---~ context.starttext()
---~ moduledata.markdown.convert(str)
---~ context.stoptext()
-
-if not tex.jobname then
-
- local one = [[
-Test *123*
-==========
-
-<b>BOLD *BOLD* BOLD</b>
-
-<pre>PRE <b>PRE</b> PRE</pre>
-
-
-* Test
-** Test
-* Test1
- * Test2
-* Test
-
-Test
-====
-
-> test
-> test **123** *123*
-> test `code`
-
-test
-
-Test
-====
-
-> test
-> test
-> test
-
-test
-oeps
-
-more
-
- code
- code
-
-oeps
-
-[an example][a]
-
-[an example] [2]
-
-[a]: http://example.com/ "Optional *oeps* Title Here"
-[2]: http://example.com/ 'Optional Title Here'
-[3]: http://example.com/ (Optional Title Here)
-
-[an example][a]
-
-[an example] [2]
-
-[an [tricky] example](http://example.com/ "Title")
-
-[This **xx** link](http://example.net/)
- ]]
-
--- This snippet takes some 4 seconds in the original parser (the one that is
--- a bit clearer from the perspective of grammars but somewhat messy with
--- respect to the captures. In the above parser it takes .1 second. Also,
--- in the later case only memory is the limit.
-
- local two = [[
-Test
-====
-* Test
-** Test
-* Test
-** Test
-* Test
-
-Test
-====
-
-> test
-> test
-> test
-
-test
-
-Test
-====
-
-> test
-> test
-> test
-
-test
- ]]
-
- local function test(str)
- local n = 1 -- 000
- local t = os.clock()
- local one = convert(str)
- -- print("runtime",1,#str,#one,os.clock()-t)
- str = string.rep(str,n)
- local t = os.clock()
- local two = convert(str)
- print(two)
- -- print("runtime",n,#str,#two,os.clock()-t)
- -- print(format("==============\n%s\n==============",one))
- end
-
- -- test(one)
- -- test(two)
- -- test(io.read("*all"))
-
-
-end
+if not modules then modules = { } end modules ['m-markdown'] = {
+ version = 1.002,
+ comment = "companion to m-markdown.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "see below",
+ license = "see context related readme files"
+}
+
+--[[
+Copyright (C) 2009 John MacFarlane / Khaled Hosny / Hans Hagen
+
+The main parser is derived from the lunamark parser written by John MacFarlane. You
+can download lunamark from:
+
+ http://github.com/jgm/lunamark.git
+
+Khaled Hosny provided the context writer for lunamark and that was used as starting
+point for the mapping. The original code can be fetched from the above location.
+
+While playing with the original code I got the feeling that lpeg could perform better.
+The slowdown was due to the fact that the parser's lpeg was reconstructed each time a
+nested parse was needed. After changing that code a bit I could bring down parsing of
+some test code from 2 seconds to less than 0.1 second so I decided to stick to this
+parser instead of writing my own. After all, the peg code looks pretty impressive and
+visiting Johns pandoc pages is worth the effort:
+
+ http://johnmacfarlane.net/pandoc/
+
+The code here is mostly meant for processing snippets embedded in a context
+documents and is no replacement for pandoc at all. Therefore an alternative is to use
+pandoc in combination with Aditya's filter module.
+
+As I changed (and optimized) the original code, it will be clear that all errors
+are mine. Eventually I might also adapt the parser code a bit more. When I ran into of
+closure stack limitations I decided to flatten the code. The following implementation
+seems to be a couple of hundred times faster than what I started with which is not that
+bad.
+
+This is a second rewrite. The mentioned speed gain largely depended on the kind of
+content: blocks, references and items can be rather demanding. Also, There were
+some limitations with respect to the captures. So, table storage has been removed in
+favor of strings, and nesting has been simplified. The first example at the end of this
+file now takes .33 seconds for 567KB code (resulting in over 1MB) so we're getting there.
+
+There will be a third rewrite eventually.
+]]--
+
+-- todo: we have better quote and tag scanners in ctx
+-- todo: provide an xhtml mapping
+-- todo: add a couple of extensions
+-- todo: check patches to the real peg
+
+local type, next, tonumber = type, next, tonumber
+local lower, upper, gsub, rep, gmatch, format, length = string.lower, string.upper, string.gsub, string.rep, string.gmatch, string.format, string.len
+local concat = table.concat
+local P, R, S, V, C, Ct, Cg, Cb, Cmt, Cc, Cf, Cs = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Ct, lpeg.Cg, lpeg.Cb, lpeg.Cmt, lpeg.Cc, lpeg.Cf, lpeg.Cs
+local lpegmatch = lpeg.match
+local utfbyte, utfchar = utf.byte, utf.char
+
+moduledata = moduledata or { }
+moduledata.markdown = moduledata.markdown or { }
+local markdown = moduledata.markdown
+
+local nofruns, nofbytes, nofhtmlblobs = 0, 0, 0
+
+---------------------------------------------------------------------------------------------
+
+local nestedparser
+local syntax
+
+nestedparser = function(str) return lpegmatch(syntax,str) end
+
+---------------------------------------------------------------------------------------------
+
+local asterisk = P("*")
+local dash = P("-")
+local plus = P("+")
+local underscore = P("_")
+local period = P(".")
+local hash = P("#")
+local ampersand = P("&")
+local backtick = P("`")
+local less = P("<")
+local more = P(">")
+local space = P(" ")
+local squote = P("'")
+local dquote = P('"')
+local lparent = P("(")
+local rparent = P(")")
+local lbracket = P("[")
+local rbracket = P("]")
+local slash = P("/")
+local equal = P("=")
+local colon = P(":")
+local semicolon = P(";")
+local exclamation = P("!")
+
+local digit = R("09")
+local hexdigit = R("09","af","AF")
+local alphanumeric = R("AZ","az","09")
+
+local doubleasterisks = P("**")
+local doubleunderscores = P("__")
+local fourspaces = P(" ")
+
+local any = P(1)
+local always = P("")
+
+local tab = P("\t")
+local spacechar = S("\t ")
+local spacing = S(" \n\r\t")
+local newline = P("\r")^-1 * P("\n")
+local spaceornewline = spacechar + newline
+local nonspacechar = any - spaceornewline
+local optionalspace = spacechar^0
+local spaces = spacechar^1
+local eof = - any
+local nonindentspace = space^-3
+local blankline = optionalspace * C(newline)
+local blanklines = blankline^0
+local skipblanklines = (optionalspace * newline)^0
+local linechar = P(1 - newline)
+local indent = fourspaces + (nonindentspace * tab) / ""
+local indentedline = indent /"" * C(linechar^1 * (newline + eof))
+local optionallyindentedline = indent^-1 /"" * C(linechar^1 * (newline + eof))
+local spnl = optionalspace * (newline * optionalspace)^-1
+local specialchar = S("*_`*&[]<!\\")
+local normalchar = any - (specialchar + spaceornewline)
+local line = C((any - newline)^0 * newline)
+ + C(any^1 * eof)
+local nonemptyline = (any - newline)^1 * newline
+
+---------------------------------------------------------------------------------------------
+
+local function lineof(c)
+ return (nonindentspace * (P(c) * optionalspace)^3 * newline * blankline^1)
+end
+
+local lineof_asterisks = lineof(asterisk)
+local lineof_dashes = lineof(dash)
+local lineof_underscores = lineof(underscore)
+
+local bullet = nonindentspace * (plus + (asterisk - lineof_asterisks) + (dash - lineof_dashes)) * spaces
+local enumerator = nonindentspace * digit^1 * period * spaces
+
+---------------------------------------------------------------------------------------------
+
+local openticks = Cg(backtick^1, "ticks")
+local closeticks = space^-1 * Cmt(C(backtick^1) * Cb("ticks"), function(s,i,a,b) return #a == #b and i end)
+local intickschar = (any - S(" \n\r`"))
+ + (newline * -blankline)
+ + (space - closeticks)
+ + (backtick^1 - closeticks)
+local inticks = openticks * space^-1 * C(intickschar^1) * closeticks
+
+---------------------------------------------------------------------------------------------
+
+local leader = space^-3
+local nestedbrackets = P { lbracket * ((1 - lbracket - rbracket) + V(1))^0 * rbracket }
+local tag = lbracket * C((nestedbrackets + 1 - rbracket)^0) * rbracket
+local url = less * C((1-more)^0) * more
+ + C((1-spacing- rparent)^1) -- sneaky: ) for resolver
+local title_s = squote * lpeg.C((1-squote )^0) * squote
+local title_d = dquote * lpeg.C((1-dquote )^0) * dquote
+local title_p = lparent * lpeg.C((1-rparent)^0) * rparent
+local title = title_s + title_d + title_p
+local optionaltitle = ((spacing^0 * title * spacechar^0) + lpeg.Cc(""))
+
+local references = { }
+
+local function register_link(tag,url,title)
+ tag = lower(gsub(tag, "[ \n\r\t]+", " "))
+ references[tag] = { url, title }
+end
+
+local function direct_link(label,url,title) -- title is typical html thing
+ return label, url, title
+end
+
+local function indirect_link(label,tag)
+ if tag == "" then
+ tag = label
+ end
+ tag = lower(gsub(tag, "[ \n\r\t]+", " "))
+ local r = references[tag]
+ if r then
+ return label, r[1], r[2]
+ else
+ return label, tag, ""
+ end
+end
+
+local define_reference_parser = (leader * tag * colon * spacechar^0 * url * optionaltitle) / register_link
+local direct_link_parser = tag * spacechar^0 * lparent * (url + Cc("")) * optionaltitle * rparent / direct_link
+local indirect_link_parser = tag * spacechar^0 * tag / indirect_link
+
+local rparser = (define_reference_parser+1)^0
+
+local function referenceparser(str)
+ references = { }
+ lpegmatch(rparser,str)
+end
+
+-- local reftest = [[
+-- [1]: <http://example.com/>
+-- [3]:http://example.com/ (Optional Title Here)
+-- [2]: http://example.com/ 'Optional Title Here'
+-- [a]: http://example.com/ "Optional *oeps* Title Here"
+-- ]]
+--
+-- local linktest = [[
+-- [This link] (http://example.net/)
+-- [an example] (http://example.com/ "Title")
+-- [an example][1]
+-- [an example] [2]
+-- ]]
+--
+-- lpeg.match((define_reference_parser+1)^0,reftest)
+--
+-- inspect(references)
+--
+-- lpeg.match((direct_link_parser/print + indirect_link_parser/print + 1)^0,linktest)
+
+---------------------------------------------------------------------------------------------
+
+local blocktags = table.tohash {
+ "address", "blockquote" , "center", "dir", "div", "p", "pre",
+ "li", "ol", "ul", "dl", "dd",
+ "form", "fieldset", "isindex", "menu", "noframes", "frameset",
+ "h1", "h2", "h3", "h4", "h5", "h6",
+ "hr", "ht", "script", "noscript",
+ "table", "tbody", "tfoot", "thead", "th", "td", "tr",
+}
+
+----- htmlattributevalue = squote * C((any - (blankline + squote))^0) * squote
+----- + dquote * C((any - (blankline + dquote))^0) * dquote
+----- + (any - S("\t >"))^1 -- any - tab - space - more
+----- htmlattribute = (alphanumeric + S("_-"))^1 * spnl * (equal * spnl * htmlattributevalue)^-1 * spnl
+----- htmlcomment = P("<!--") * (any - P("-->"))^0 * P("-->")
+
+----- htmltag = less * spnl * slash^-1 * alphanumeric^1 * spnl * htmlattribute^0 * slash^-1 * spnl * more
+-----
+----- blocktag = Cmt(C(alphanumeric^1), function(s,i,a) return blocktags[lower(a)] and i, a end)
+-----
+----- openblocktag = less * Cg(blocktag, "opentag") * spnl * htmlattribute^0 * more
+----- closeblocktag = less * slash * Cmt(C(alphanumeric^1) * Cb("opentag"), function(s,i,a,b) return lower(a) == lower(b) and i end) * spnl * more
+----- selfclosingblocktag = less * blocktag * spnl * htmlattribute^0 * slash * more
+-----
+----- displayhtml = Cs { "HtmlBlock",
+----- InBlockTags = openblocktag * (V("HtmlBlock") + (any - closeblocktag))^0 * closeblocktag,
+----- HtmlBlock = C(V("InBlockTags") + selfclosingblocktag + htmlcomment),
+----- }
+-----
+----- inlinehtml = Cs(htmlcomment + htmltag)
+
+-- There is no reason to support crappy html, so we expect proper attributes.
+
+local htmlattributevalue = squote * C((any - (blankline + squote))^0) * squote
+ + dquote * C((any - (blankline + dquote))^0) * dquote
+local htmlattribute = (alphanumeric + S("_-"))^1 * spnl * equal * spnl * htmlattributevalue * spnl
+
+local htmlcomment = P("<!--") * (any - P("-->"))^0 * P("-->")
+local htmlinstruction = P("<?") * (any - P("?>" ))^0 * P("?>" )
+
+-- We don't care too much about matching elements and there is no reason why display elements could not
+-- have inline elements so the above should be patched then. Well, markdown mixed with html is not meant
+-- for anything else than webpages anyway.
+
+local blocktag = Cmt(C(alphanumeric^1), function(s,i,a) return blocktags[lower(a)] and i, a end)
+
+local openelement = less * alphanumeric^1 * spnl * htmlattribute^0 * more
+local closeelement = less * slash * alphanumeric^1 * spnl * more
+local emptyelement = less * alphanumeric^1 * spnl * htmlattribute^0 * slash * more
+
+local displaytext = (any - less)^1
+local inlinetext = displaytext / nestedparser
+
+local displayhtml = #(less * blocktag * spnl * htmlattribute^0 * more)
+ * Cs { "HtmlBlock",
+ InBlockTags = openelement * (V("HtmlBlock") + displaytext)^0 * closeelement,
+ HtmlBlock = (V("InBlockTags") + emptyelement + htmlcomment + htmlinstruction),
+ }
+
+local inlinehtml = Cs { "HtmlBlock",
+ InBlockTags = openelement * (V("HtmlBlock") + inlinetext)^0 * closeelement,
+ HtmlBlock = (V("InBlockTags") + emptyelement + htmlcomment + htmlinstruction),
+ }
+
+---------------------------------------------------------------------------------------------
+
+local hexentity = ampersand * hash * S("Xx") * C(hexdigit ^1) * semicolon
+local decentity = ampersand * hash * C(digit ^1) * semicolon
+local tagentity = ampersand * C(alphanumeric^1) * semicolon
+
+---------------------------------------------------------------------------------------------
+
+-- --[[
+
+local escaped = {
+ ["{" ] = "",
+ ["}" ] = "",
+ ["$" ] = "",
+ ["&" ] = "",
+ ["#" ] = "",
+ ["~" ] = "",
+ ["|" ] = "",
+ ["%%"] = "",
+ ["\\"] = "",
+}
+
+for k, v in next, escaped do
+ escaped[k] = "\\char" .. utfbyte(k) .. "{}"
+end
+
+local function c_string(s) -- has to be done more often
+ return (gsub(s,".",escaped))
+end
+
+local c_linebreak = "\\crlf\n" -- is this ok?
+local c_space = " "
+
+local function c_paragraph(c)
+ return c .. "\n\n" -- { "\\startparagraph ", c, " \\stopparagraph\n" }
+end
+
+local function listitem(c)
+ return format("\n\\startitem\n%s\n\\stopitem\n",nestedparser(c))
+end
+
+local function c_tightbulletlist(c)
+ return format("\n\\startmarkdownitemize[packed]\n%s\\stopmarkdownitemize\n",c)
+end
+
+local function c_loosebulletlist(c)
+ return format("\n\\startmarkdownitemize\n\\stopmarkdownitemize\n",c)
+end
+
+local function c_tightorderedlist(c)
+ return format("\n\\startmarkdownitemize[n,packed]\n%s\\stopmarkdownitemize\n",c)
+end
+
+local function c_looseorderedlist(c)
+ return format("\n\\startmarkdownitemize[n]\n%s\\stopmarkdownitemize\n",c)
+end
+
+local function c_inline_html(content)
+ nofhtmlblobs = nofhtmlblobs + 1
+ return format("\\markdowninlinehtml{%s}",content)
+end
+
+local function c_display_html(content)
+ nofhtmlblobs = nofhtmlblobs + 1
+ return format("\\startmarkdowndisplayhtml\n%s\n\\stopmarkdowndisplayhtml",content)
+end
+
+local function c_emphasis(c)
+ return format("\\markdownemphasis{%s}",c)
+end
+
+local function c_strong(c)
+ return format("\\markdownstrong{%s}",c)
+end
+
+local function c_blockquote(c)
+ return format("\\startmarkdownblockquote\n%s\\stopmarkdownblockquote\n",nestedparser(c))
+end
+
+local function c_verbatim(c)
+ return format("\\startmarkdowntyping\n%s\\stopmarkdowntyping\n",c)
+end
+
+local function c_code(c)
+ return format("\\markdowntype{%s}",c)
+end
+
+local levels = { "", "", "", "", "", "" }
+
+local function c_start_document()
+ levels = { "", "", "", "", "", "" }
+ return ""
+end
+
+local function c_stop_document()
+ return concat(levels,"\n") or ""
+end
+
+local function c_heading(level,c)
+ if level > #levels then
+ level = #levels
+ end
+ local finish = concat(levels,"\n",level) or ""
+ for i=level+1,#levels do
+ levels[i] = ""
+ end
+ levels[level] = "\\stopstructurelevel"
+ return format("%s\\startstructurelevel[markdown][title={%s}]\n",finish,c)
+end
+
+local function c_hrule()
+ return "\\markdownrule\n"
+end
+
+local function c_link(lab,src,tit)
+ return format("\\goto{%s}[url(%s)]",nestedparser(lab),src)
+end
+
+local function c_image(lab,src,tit)
+ return format("\\externalfigure[%s]",src)
+end
+
+local function c_email_link(address)
+ return format("\\goto{%s}[url(mailto:%s)]",c_string(address),address)
+end
+
+local function c_url_link(url)
+ return format("\\goto{%s}[url(%s)]",c_string(url),url)
+end
+
+local function f_heading(c,n)
+ return c_heading(n,c)
+end
+
+local function c_hex_entity(s)
+ return utfchar(tonumber(s,16))
+end
+
+local function c_dec_entity(s)
+ return utfchar(tonumber(s))
+end
+
+local function c_tag_entity(s)
+ return s -- we can use the default resolver
+end
+
+--]]
+
+---------------------------------------------------------------------------------------------
+
+--[[
+
+local escaped = {
+ ["<"] = "&lt;",
+ [">"] = "&gt;",
+ ["&"] = "&amp;",
+ ['"'] = "&quot;",
+}
+
+local function c_string(s) -- has to be done more often
+ return (gsub(s,".",escaped))
+end
+
+local c_linebreak = "<br/>"
+local c_space = " "
+
+local function c_paragraph(c)
+ return format("<p>%s</p>\n", c)
+end
+
+local function listitem(c)
+ return format("<li>%s</li>",nestedparser(c))
+end
+
+local function c_tightbulletlist(c)
+ return format("<ul>\n%s\n</ul>\n",c)
+end
+
+local function c_loosebulletlist(c)
+ return format("<ul>\n%s\n</ul>\n",c)
+end
+
+local function c_tightorderedlist(c)
+ return format("<ol>\n%s\n</ol>\n",c)
+end
+
+local function c_looseorderedlist(c)
+ return format("<ol>\n%s\n</ol>\n",c)
+end
+
+local function c_inline_html(content)
+ nofhtmlblobs = nofhtmlblobs + 1
+ return content
+end
+
+local function c_display_html(content)
+ nofhtmlblobs = nofhtmlblobs + 1
+ return format("\n%s\n",content)
+end
+
+local function c_emphasis(c)
+ return format("<em>%s</em>",c)
+end
+
+local function c_strong(c)
+ return format("<strong>%s</strong>",c)
+end
+
+local function c_blockquote(c)
+ return format("<blockquote>\n%s\n</blockquote>",nestedparser(c))
+end
+
+local function c_verbatim(c)
+ return format("<pre><code>%s</code></pre>",c)
+end
+
+local function c_code(c)
+ return format("<code>%s</code>",c)
+end
+
+local c_start_document = ""
+local c_stop_document = ""
+
+local function c_heading(level,c)
+ return format("<h%d>%s</h%d>\n",level,c,level)
+end
+
+local function c_hrule()
+ return "<hr/>\n"
+end
+
+local function c_link(lab,src,tit)
+ local titattr = #tit > 0 and format(" title=%q",tit) or ""
+ return format("<a href=%q%s>%s</a>",src,titattr,nestedparser(lab))
+end
+
+local function c_image(lab,src,tit)
+ return format("<img href=%q title=%q>%s</a>",src,tit,nestedparser(lab))
+end
+
+local function c_email_link(address)
+ return format("<a href=%q>%s</a>","mailto:",address,c_escape(address))
+end
+
+local function c_url_link(url)
+ return format("<a href=%q>%s</a>",url,c_string(url))
+end
+
+local function f_heading(c,n)
+ return c_heading(n,c)
+end
+
+local function c_hex_entity(s)
+ return utfchar(tonumber(s,16))
+end
+
+local function c_dec_entity(s)
+ return utfchar(tonumber(s))
+end
+
+local function c_tag_entity(s)
+ return format("&%s;",s)
+end
+
+--]]
+
+---------------------------------------------------------------------------------------------
+
+local Str = normalchar^1 / c_string
+local Space = spacechar^1 / c_space
+local Symbol = specialchar / c_string
+local Code = inticks / c_code
+
+local HeadingStart = C(hash * hash^-5) / length
+local HeadingStop = optionalspace * hash^0 * optionalspace * newline * blanklines
+local HeadingLevel = equal^3 * Cc(1)
+ + dash ^3 * Cc(2)
+
+local NormalEndline = optionalspace * newline * -(
+ blankline
+ + more
+ + HeadingStart
+ + ( line * (P("===")^3 + P("---")^3) * newline )
+ ) / c_space
+
+local LineBreak = P(" ") * NormalEndline / c_linebreak
+
+local TerminalEndline = optionalspace * newline * eof / ""
+
+local Endline = LineBreak
+ + TerminalEndline
+ + NormalEndline
+
+local AutoLinkUrl = less * C(alphanumeric^1 * P("://") * (any - (newline + more))^1) * more / c_url_link
+local AutoLinkEmail = less * C((alphanumeric + S("-_+"))^1 * P("@") * (any - (newline + more))^1) * more / c_email_link
+
+local DirectLink = direct_link_parser / c_link
+local IndirectLink = indirect_link_parser / c_link
+
+local ImageLink = exclamation * (direct_link_parser + indirect_link_parser) / c_image -- we can combine this with image ... smaller lpeg
+
+local UlOrStarLine = asterisk^4
+ + underscore^4
+ + (spaces * S("*_")^1 * #spaces) / c_string
+
+local EscapedChar = P("\\") * C(P(1 - newline)) / c_string
+
+local InlineHtml = inlinehtml / c_inline_html
+local DisplayHtml = displayhtml / c_display_html
+local HtmlEntity = hexentity / c_hex_entity
+ + decentity / c_dec_entity
+ + tagentity / c_tag_entity
+
+local NestedList = Cs(optionallyindentedline - (bullet + enumerator))^1 / nestedparser
+
+local ListBlockLine = -blankline * -(indent^-1 * (bullet + enumerator)) * optionallyindentedline
+
+local Verbatim = Cs(blanklines * (indentedline - blankline)^1) / c_verbatim
+ * (blankline^1 + eof) -- not really needed, probably capture trailing? we can do that beforehand
+
+local Blockquote = Cs((
+ ((nonindentspace * more * space^-1)/"" * linechar^0 * newline)^1
+ * ((linechar - blankline)^1 * newline)^0
+ * blankline^0
+ )^1) / c_blockquote
+
+local HorizontalRule = (lineof_asterisks + lineof_dashes + lineof_underscores) / c_hrule
+
+local Reference = define_reference_parser / ""
+
+-- could be a mini grammar
+
+local ListBlock = line * ListBlockLine^0
+local ListContinuationBlock = blanklines * indent * ListBlock
+local ListItem = Cs(ListBlock * (NestedList + ListContinuationBlock^0)) / listitem
+
+---- LeadingLines = blankline^0 / ""
+---- TrailingLines = blankline^1 * #(any) / "\n"
+
+syntax = Cs { "Document",
+
+ Document = V("Display")^0,
+
+ Display = blankline -- ^1/"\n"
+ + Blockquote
+ + Verbatim
+ + Reference
+ + HorizontalRule
+ + HeadingStart * optionalspace * Cs((V("Inline") - HeadingStop)^1) * HeadingStop / c_heading
+ + Cs((V("Inline") - Endline)^1) * newline * HeadingLevel * newline * blanklines / f_heading
+ + Cs((bullet /"" * ListItem)^1) * blanklines * -bullet / c_tightbulletlist
+ + Cs((bullet /"" * ListItem * C(blanklines))^1) / c_loosebulletlist
+ + Cs((enumerator /"" * ListItem)^1) * blanklines * -enumerator / c_tightorderedlist
+ + Cs((enumerator /"" * ListItem * C(blanklines))^1) / c_looseorderedlist
+ + DisplayHtml
+ + nonindentspace * Cs(V("Inline")^1)* newline * blankline^1 / c_paragraph
+ + V("Inline")^1,
+
+ Inline = Str
+ + Space
+ + Endline
+ + UlOrStarLine -- still needed ?
+ + doubleasterisks * -spaceornewline * Cs((V("Inline") - doubleasterisks )^1) * doubleasterisks / c_strong
+ + doubleunderscores * -spaceornewline * Cs((V("Inline") - doubleunderscores)^1) * doubleunderscores / c_strong
+ + asterisk * -spaceornewline * Cs((V("Inline") - asterisk )^1) * asterisk / c_emphasis
+ + underscore * -spaceornewline * Cs((V("Inline") - underscore )^1) * underscore / c_emphasis
+ + ImageLink
+ + DirectLink
+ + IndirectLink
+ + AutoLinkUrl
+ + AutoLinkEmail
+ + Code
+ + InlineHtml
+ + HtmlEntity
+ + EscapedChar
+ + Symbol,
+
+}
+
+---------------------------------------------------------------------------------------------
+
+local function convert(str)
+ nofruns = nofruns + 1
+ nofbytes = nofbytes + #str
+ statistics.starttiming(markdown)
+ referenceparser(str)
+ local result = c_start_document() .. nestedparser(str) .. c_stop_document()
+ statistics.stoptiming(markdown)
+ return result
+end
+
+markdown.convert = convert
+
+function markdown.typesetstring(data)
+ if data and data ~= "" then
+ local result = convert(data)
+ context.viafile(result)
+ end
+end
+
+function markdown.typesetbuffer(name)
+ markdown.typesetstring(buffers.getcontent(name))
+end
+
+function markdown.typesetfile(name)
+ local fullname = resolvers.findctxfile(name)
+ if fullname and fullname ~= "" then
+ markdown.typesetstring(io.loaddata(fullname))
+ end
+end
+
+statistics.register("markdown",function()
+ if nofruns > 0 then
+ return format("%s bytes converted, %s runs, %s html blobs, %s seconds used",
+ nofbytes, nofruns, nofhtmlblobs, statistics.elapsedtime(markdown))
+ end
+end)
+
+---------------------------------------------------------------------------------------------
+
+--~ context.starttext()
+--~ moduledata.markdown.convert(str)
+--~ context.stoptext()
+
+if not tex.jobname then
+
+ local one = [[
+Test *123*
+==========
+
+<b>BOLD *BOLD* BOLD</b>
+
+<pre>PRE <b>PRE</b> PRE</pre>
+
+
+* Test
+** Test
+* Test1
+ * Test2
+* Test
+
+Test
+====
+
+> test
+> test **123** *123*
+> test `code`
+
+test
+
+Test
+====
+
+> test
+> test
+> test
+
+test
+oeps
+
+more
+
+ code
+ code
+
+oeps
+
+[an example][a]
+
+[an example] [2]
+
+[a]: http://example.com/ "Optional *oeps* Title Here"
+[2]: http://example.com/ 'Optional Title Here'
+[3]: http://example.com/ (Optional Title Here)
+
+[an example][a]
+
+[an example] [2]
+
+[an [tricky] example](http://example.com/ "Title")
+
+[This **xx** link](http://example.net/)
+ ]]
+
+-- This snippet takes some 4 seconds in the original parser (the one that is
+-- a bit clearer from the perspective of grammars but somewhat messy with
+-- respect to the captures. In the above parser it takes .1 second. Also,
+-- in the later case only memory is the limit.
+
+ local two = [[
+Test
+====
+* Test
+** Test
+* Test
+** Test
+* Test
+
+Test
+====
+
+> test
+> test
+> test
+
+test
+
+Test
+====
+
+> test
+> test
+> test
+
+test
+ ]]
+
+ local function test(str)
+ local n = 1 -- 000
+ local t = os.clock()
+ local one = convert(str)
+ -- print("runtime",1,#str,#one,os.clock()-t)
+ str = string.rep(str,n)
+ local t = os.clock()
+ local two = convert(str)
+ print(two)
+ -- print("runtime",n,#str,#two,os.clock()-t)
+ -- print(format("==============\n%s\n==============",one))
+ end
+
+ -- test(one)
+ -- test(two)
+ -- test(io.read("*all"))
+
+
+end
diff --git a/tex/context/base/m-pstricks.lua b/tex/context/base/m-pstricks.lua
index 2c01ed898..b151e313a 100644
--- a/tex/context/base/m-pstricks.lua
+++ b/tex/context/base/m-pstricks.lua
@@ -1,74 +1,74 @@
-if not modules then modules = { } end modules ['m-pstricks'] = {
- version = 1.001,
- comment = "companion to m-pstricks.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- The following will be done when I need ps tricks figures
--- in large quantities:
---
--- + hash graphics and only process them once
--- + save md5 checksums in tuc file
---
--- It's no big deal but has a low priority.
-
-local format, lower, concat, gmatch = string.format, string.lower, table.concat, string.gmatch
-local variables = interfaces.variables
-
-moduledata.pstricks = moduledata.pstricks or { }
-
-local report_pstricks = logs.reporter("pstricks")
-
-local template = [[
-\starttext
- \pushcatcodetable
- \setcatcodetable\texcatcodes
- \usemodule[pstric]
- %s
- \popcatcodetable
- \startTEXpage
- \hbox\bgroup
- \ignorespaces
- %s
- \removeunwantedspaces
- \egroup
- \obeydepth %% temp hack as we need to figure this out
- \stopTEXpage
-\stoptext
-]]
-
-local loaded = { }
-local graphics = 0
-
-function moduledata.pstricks.usemodule(names)
- for name in gmatch(names,"([^%s,]+)") do
- loaded[#loaded+1] = format([[\readfile{%s}{}{}]],name)
- end
-end
-
-function moduledata.pstricks.process(n)
- graphics = graphics + 1
- local name = format("%s-pstricks-%04i",tex.jobname,graphics)
- local data = buffers.collectcontent("def-"..n)
- local tmpfile = name .. ".tmp"
- local epsfile = name .. ".ps"
- local pdffile = name .. ".pdf"
- local loaded = concat(loaded,"\n")
- os.remove(epsfile)
- os.remove(pdffile)
- io.savedata(tmpfile,format(template,loaded,data))
- os.execute(format("mtxrun --script texexec %s --once --dvips",tmpfile))
- if lfs.isfile(epsfile) then
- os.execute(format("ps2pdf %s %s",epsfile,pdffile))
- -- todo: direct call but not now
- if lfs.isfile(pdffile) then
- context.externalfigure( { pdffile }, { object = variables.no } )
- else
- report_pstricks("run failed, no pdf file")
- end
- else
- report_pstricks("run failed, no ps file")
- end
-end
+if not modules then modules = { } end modules ['m-pstricks'] = {
+ version = 1.001,
+ comment = "companion to m-pstricks.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- The following will be done when I need ps tricks figures
+-- in large quantities:
+--
+-- + hash graphics and only process them once
+-- + save md5 checksums in tuc file
+--
+-- It's no big deal but has a low priority.
+
+local format, lower, concat, gmatch = string.format, string.lower, table.concat, string.gmatch
+local variables = interfaces.variables
+
+moduledata.pstricks = moduledata.pstricks or { }
+
+local report_pstricks = logs.reporter("pstricks")
+
+local template = [[
+\starttext
+ \pushcatcodetable
+ \setcatcodetable\texcatcodes
+ \usemodule[pstric]
+ %s
+ \popcatcodetable
+ \startTEXpage
+ \hbox\bgroup
+ \ignorespaces
+ %s
+ \removeunwantedspaces
+ \egroup
+ \obeydepth %% temp hack as we need to figure this out
+ \stopTEXpage
+\stoptext
+]]
+
+local loaded = { }
+local graphics = 0
+
+function moduledata.pstricks.usemodule(names)
+ for name in gmatch(names,"([^%s,]+)") do
+ loaded[#loaded+1] = format([[\readfile{%s}{}{}]],name)
+ end
+end
+
+function moduledata.pstricks.process(n)
+ graphics = graphics + 1
+ local name = format("%s-pstricks-%04i",tex.jobname,graphics)
+ local data = buffers.collectcontent("def-"..n)
+ local tmpfile = name .. ".tmp"
+ local epsfile = name .. ".ps"
+ local pdffile = name .. ".pdf"
+ local loaded = concat(loaded,"\n")
+ os.remove(epsfile)
+ os.remove(pdffile)
+ io.savedata(tmpfile,format(template,loaded,data))
+ os.execute(format("mtxrun --script texexec %s --once --dvips",tmpfile))
+ if lfs.isfile(epsfile) then
+ os.execute(format("ps2pdf %s %s",epsfile,pdffile))
+ -- todo: direct call but not now
+ if lfs.isfile(pdffile) then
+ context.externalfigure( { pdffile }, { object = variables.no } )
+ else
+ report_pstricks("run failed, no pdf file")
+ end
+ else
+ report_pstricks("run failed, no ps file")
+ end
+end
diff --git a/tex/context/base/m-spreadsheet.lua b/tex/context/base/m-spreadsheet.lua
index dcd4ea1c4..9d5106e35 100644
--- a/tex/context/base/m-spreadsheet.lua
+++ b/tex/context/base/m-spreadsheet.lua
@@ -1,332 +1,332 @@
-if not modules then modules = { } end modules ['m-spreadsheet'] = {
- version = 1.001,
- comment = "companion to m-spreadsheet.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local byte, format, gsub, find = string.byte, string.format, string.gsub, string.find
-local R, P, S, C, V, Cs, Cc, Ct, Cg, Cf, Carg = lpeg.R, lpeg.P, lpeg.S, lpeg.C, lpeg.V, lpeg.Cs, lpeg.Cc, lpeg.Ct, lpeg.Cg, lpeg.Cf, lpeg.Carg
-local lpegmatch, patterns = lpeg.match, lpeg.patterns
-local setmetatable, loadstring, next, tostring, tonumber,rawget = setmetatable, loadstring, next, tostring, tonumber, rawget
-local formatters = string.formatters
-
-local context = context
-
-local splitthousands = utilities.parsers.splitthousands
-local variables = interfaces.variables
-
-local v_yes = variables.yes
-
-moduledata = moduledata or { }
-
-local spreadsheets = { }
-moduledata.spreadsheets = spreadsheets
-
-local data = {
- -- nothing yet
-}
-
-local settings = {
- period = ".",
- comma = ",",
-}
-
-spreadsheets.data = data
-spreadsheets.settings = settings
-
-local defaultname = "default"
-local stack = { }
-local current = defaultname
-
-local d_mt ; d_mt = {
- __index = function(t,k)
- local v = { }
- setmetatable(v,d_mt)
- t[k] = v
- return v
- end,
-}
-
-local s_mt ; s_mt = {
- __index = function(t,k)
- local v = settings[k]
- t[k] = v
- return v
- end,
-}
-
-function spreadsheets.setup(t)
- for k, v in next, t do
- settings[k] = v
- end
-end
-
-local function emptydata(name,settings)
- local data = { }
- local specifications = { }
- local settings = settings or { }
- setmetatable(data,d_mt)
- setmetatable(specifications,d_mt)
- setmetatable(settings,s_mt)
- return {
- name = name,
- data = data,
- maxcol = 0,
- maxrow = 0,
- settings = settings,
- temp = { }, -- for local usage
- specifications = specifications,
- }
-end
-
-function spreadsheets.reset(name)
- if not name or name == "" then name = defaultname end
- data[name] = emptydata(name,data[name] and data[name].settings)
-end
-
-function spreadsheets.start(name,s)
- if not name or name == "" then
- name = defaultname
- end
- if not s then
- s = { }
- end
- table.insert(stack,current)
- current = name
- if data[current] then
- setmetatable(s,s_mt)
- data[current].settings = s
- else
- data[current] = emptydata(name,s)
- end
-end
-
-function spreadsheets.stop()
- current = table.remove(stack)
-end
-
-spreadsheets.reset()
-
-local offset = byte("A") - 1
-
-local function assign(s,n)
- return formatters["moduledata.spreadsheets.data['%s'].data[%s]"](n,byte(s)-offset)
-end
-
-function datacell(a,b,...)
- local n = 0
- if b then
- local t = { a, b, ... }
- for i=1,#t do
- n = n * (i-1) * 26 + byte(t[i]) - offset
- end
- else
- n = byte(a) - offset
- end
- return formatters["dat[%s]"](n)
-end
-
-local function checktemplate(s)
- if find(s,"%%") then
- -- normal template
- return s
- elseif find(s,"@") then
- -- tex specific template
- return gsub(s,"@","%%")
- else
- -- tex specific quick template
- return "%" .. s
- end
-end
-
-local quoted = Cs(patterns.unquoted)
-local spaces = patterns.whitespace^0
-local cell = C(R("AZ"))^1 / datacell * (Cc("[") * (R("09")^1) * Cc("]") + #P(1))
-
--- A nasty aspect of lpeg: Cf ( spaces * Cc("") * { "start" ... this will create a table that will
--- be reused, so we accumulate!
-
-local pattern = Cf ( spaces * Ct("") * { "start",
- start = V("value") + V("set") + V("format") + V("string") + V("code"),
- value = Cg(P([[=]]) * spaces * Cc("kind") * Cc("value")) * V("code"),
- set = Cg(P([[!]]) * spaces * Cc("kind") * Cc("set")) * V("code"),
- format = Cg(P([[@]]) * spaces * Cc("kind") * Cc("format")) * spaces * Cg(Cc("template") * Cs(quoted/checktemplate)) * V("code"),
- string = Cg(#S([["']]) * Cc("kind") * Cc("string")) * Cg(Cc("content") * quoted),
- code = spaces * Cg(Cc("code") * Cs((cell + P(1))^0)),
-}, rawset)
-
-local functions = { }
-spreadsheets.functions = functions
-
-function functions._s_(row,col,c,f,t)
- local r = 0
- if f and t then -- f..t
- -- ok
- elseif f then -- 1..f
- f, t = 1, f
- else
- f, t = 1, row - 1
- end
- for i=f,t do
- local ci = c[i]
- if type(ci) == "number" then
- r = r + c[i]
- end
- end
- return r
-end
-
-functions.fmt = string.tformat
-
-local f_code = formatters [ [[
- local _m_ = moduledata.spreadsheets
- local dat = _m_.data['%s'].data
- local tmp = _m_.temp
- local fnc = _m_.functions
- local row = %s
- local col = %s
- function fnc.sum(...) return fnc._s_(row,col,...) end
- local sum = fnc.sum
- local fmt = fnc.fmt
- return %s
-]] ]
-
--- to be considered: a weak cache
-
-local function propername(name)
- if name ~= "" then
- return name
- elseif current ~= "" then
- return current
- else
- return defaultname
- end
-end
-
--- if name == "" then name = current if name == "" then name = defaultname end end
-
-local function execute(name,r,c,str)
- if str ~= "" then
- local d = data[name]
- if c > d.maxcol then
- d.maxcol = c
- end
- if r > d.maxrow then
- d.maxrow = r
- end
- local specification = lpegmatch(pattern,str,1,name)
- d.specifications[c][r] = specification
- local kind = specification.kind
- if kind == "string" then
- return specification.content or ""
- else
- local code = specification.code
- if code and code ~= "" then
- code = f_code(name,r,c,code or "")
- local result = loadstring(code) -- utilities.lua.strippedloadstring(code,true) -- when tracing
- result = result and result()
- if type(result) == "function" then
- result = result()
- end
- if type(result) == "number" then
- d.data[c][r] = result
- end
- if not result then
- -- nothing
- elseif kind == "set" then
- -- no return
- elseif kind == "format" then
- return formatters[specification.template](result)
- else
- return result
- end
- end
- end
- end
-end
-
-function spreadsheets.set(name,r,c,str)
- name = propername(name)
- execute(name,r,c,str)
-end
-
-function spreadsheets.get(name,r,c,str)
- name = propername(name)
- local dname = data[name]
- if not dname then
- -- nothing
- elseif not str or str == "" then
- context(dname.data[c][r] or 0)
- else
- local result = execute(name,r,c,str)
- if result then
--- if type(result) == "number" then
--- dname.data[c][r] = result
--- result = tostring(result)
--- end
- local settings = dname.settings
- local split = settings.split
- local period = settings.period
- local comma = settings.comma
- if split == v_yes then
- result = splitthousands(result)
- end
- if period == "" then period = nil end
- if comma == "" then comma = nil end
- result = gsub(result,".",{ ["."] = period, [","] = comma })
- context(result)
- end
- end
-end
-
-function spreadsheets.doifelsecell(name,r,c)
- name = propername(name)
- local d = data[name]
- local d = d and d.data
- local r = d and rawget(d,r)
- local c = r and rawget(r,c)
- commands.doifelse(c)
-end
-
-local function simplify(name)
- name = propername(name)
- local data = data[name]
- if data then
- data = data.data
- local temp = { }
- for k, v in next, data do
- local t = { }
- temp[k] = t
- for kk, vv in next, v do
- if type(vv) == "function" then
- t[kk] = "<function>"
- else
- t[kk] = vv
- end
- end
- end
- return temp
- end
-end
-
-local function serialize(name)
- local s = simplify(name)
- if s then
- return table.serialize(s,name)
- else
- return formatters["<unknown spreadsheet %a>"](name)
- end
-end
-
-spreadsheets.simplify = simplify
-spreadsheets.serialize = serialize
-
-function spreadsheets.inspect(name)
- inspect(serialize(name))
-end
-
-function spreadsheets.tocontext(name)
- context.tocontext(simplify(name))
-end
+if not modules then modules = { } end modules ['m-spreadsheet'] = {
+ version = 1.001,
+ comment = "companion to m-spreadsheet.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local byte, format, gsub, find = string.byte, string.format, string.gsub, string.find
+local R, P, S, C, V, Cs, Cc, Ct, Cg, Cf, Carg = lpeg.R, lpeg.P, lpeg.S, lpeg.C, lpeg.V, lpeg.Cs, lpeg.Cc, lpeg.Ct, lpeg.Cg, lpeg.Cf, lpeg.Carg
+local lpegmatch, patterns = lpeg.match, lpeg.patterns
+local setmetatable, loadstring, next, tostring, tonumber,rawget = setmetatable, loadstring, next, tostring, tonumber, rawget
+local formatters = string.formatters
+
+local context = context
+
+local splitthousands = utilities.parsers.splitthousands
+local variables = interfaces.variables
+
+local v_yes = variables.yes
+
+moduledata = moduledata or { }
+
+local spreadsheets = { }
+moduledata.spreadsheets = spreadsheets
+
+local data = {
+ -- nothing yet
+}
+
+local settings = {
+ period = ".",
+ comma = ",",
+}
+
+spreadsheets.data = data
+spreadsheets.settings = settings
+
+local defaultname = "default"
+local stack = { }
+local current = defaultname
+
+local d_mt ; d_mt = {
+ __index = function(t,k)
+ local v = { }
+ setmetatable(v,d_mt)
+ t[k] = v
+ return v
+ end,
+}
+
+local s_mt ; s_mt = {
+ __index = function(t,k)
+ local v = settings[k]
+ t[k] = v
+ return v
+ end,
+}
+
+function spreadsheets.setup(t)
+ for k, v in next, t do
+ settings[k] = v
+ end
+end
+
+local function emptydata(name,settings)
+ local data = { }
+ local specifications = { }
+ local settings = settings or { }
+ setmetatable(data,d_mt)
+ setmetatable(specifications,d_mt)
+ setmetatable(settings,s_mt)
+ return {
+ name = name,
+ data = data,
+ maxcol = 0,
+ maxrow = 0,
+ settings = settings,
+ temp = { }, -- for local usage
+ specifications = specifications,
+ }
+end
+
+function spreadsheets.reset(name)
+ if not name or name == "" then name = defaultname end
+ data[name] = emptydata(name,data[name] and data[name].settings)
+end
+
+function spreadsheets.start(name,s)
+ if not name or name == "" then
+ name = defaultname
+ end
+ if not s then
+ s = { }
+ end
+ table.insert(stack,current)
+ current = name
+ if data[current] then
+ setmetatable(s,s_mt)
+ data[current].settings = s
+ else
+ data[current] = emptydata(name,s)
+ end
+end
+
+function spreadsheets.stop()
+ current = table.remove(stack)
+end
+
+spreadsheets.reset()
+
+local offset = byte("A") - 1
+
+local function assign(s,n)
+ return formatters["moduledata.spreadsheets.data['%s'].data[%s]"](n,byte(s)-offset)
+end
+
+function datacell(a,b,...)
+ local n = 0
+ if b then
+ local t = { a, b, ... }
+ for i=1,#t do
+ n = n * (i-1) * 26 + byte(t[i]) - offset
+ end
+ else
+ n = byte(a) - offset
+ end
+ return formatters["dat[%s]"](n)
+end
+
+local function checktemplate(s)
+ if find(s,"%%") then
+ -- normal template
+ return s
+ elseif find(s,"@") then
+ -- tex specific template
+ return gsub(s,"@","%%")
+ else
+ -- tex specific quick template
+ return "%" .. s
+ end
+end
+
+local quoted = Cs(patterns.unquoted)
+local spaces = patterns.whitespace^0
+local cell = C(R("AZ"))^1 / datacell * (Cc("[") * (R("09")^1) * Cc("]") + #P(1))
+
+-- A nasty aspect of lpeg: Cf ( spaces * Cc("") * { "start" ... this will create a table that will
+-- be reused, so we accumulate!
+
+local pattern = Cf ( spaces * Ct("") * { "start",
+ start = V("value") + V("set") + V("format") + V("string") + V("code"),
+ value = Cg(P([[=]]) * spaces * Cc("kind") * Cc("value")) * V("code"),
+ set = Cg(P([[!]]) * spaces * Cc("kind") * Cc("set")) * V("code"),
+ format = Cg(P([[@]]) * spaces * Cc("kind") * Cc("format")) * spaces * Cg(Cc("template") * Cs(quoted/checktemplate)) * V("code"),
+ string = Cg(#S([["']]) * Cc("kind") * Cc("string")) * Cg(Cc("content") * quoted),
+ code = spaces * Cg(Cc("code") * Cs((cell + P(1))^0)),
+}, rawset)
+
+local functions = { }
+spreadsheets.functions = functions
+
+function functions._s_(row,col,c,f,t)
+ local r = 0
+ if f and t then -- f..t
+ -- ok
+ elseif f then -- 1..f
+ f, t = 1, f
+ else
+ f, t = 1, row - 1
+ end
+ for i=f,t do
+ local ci = c[i]
+ if type(ci) == "number" then
+ r = r + c[i]
+ end
+ end
+ return r
+end
+
+functions.fmt = string.tformat
+
+local f_code = formatters [ [[
+ local _m_ = moduledata.spreadsheets
+ local dat = _m_.data['%s'].data
+ local tmp = _m_.temp
+ local fnc = _m_.functions
+ local row = %s
+ local col = %s
+ function fnc.sum(...) return fnc._s_(row,col,...) end
+ local sum = fnc.sum
+ local fmt = fnc.fmt
+ return %s
+]] ]
+
+-- to be considered: a weak cache
+
+local function propername(name)
+ if name ~= "" then
+ return name
+ elseif current ~= "" then
+ return current
+ else
+ return defaultname
+ end
+end
+
+-- if name == "" then name = current if name == "" then name = defaultname end end
+
+local function execute(name,r,c,str)
+ if str ~= "" then
+ local d = data[name]
+ if c > d.maxcol then
+ d.maxcol = c
+ end
+ if r > d.maxrow then
+ d.maxrow = r
+ end
+ local specification = lpegmatch(pattern,str,1,name)
+ d.specifications[c][r] = specification
+ local kind = specification.kind
+ if kind == "string" then
+ return specification.content or ""
+ else
+ local code = specification.code
+ if code and code ~= "" then
+ code = f_code(name,r,c,code or "")
+ local result = loadstring(code) -- utilities.lua.strippedloadstring(code,true) -- when tracing
+ result = result and result()
+ if type(result) == "function" then
+ result = result()
+ end
+ if type(result) == "number" then
+ d.data[c][r] = result
+ end
+ if not result then
+ -- nothing
+ elseif kind == "set" then
+ -- no return
+ elseif kind == "format" then
+ return formatters[specification.template](result)
+ else
+ return result
+ end
+ end
+ end
+ end
+end
+
+function spreadsheets.set(name,r,c,str)
+ name = propername(name)
+ execute(name,r,c,str)
+end
+
+function spreadsheets.get(name,r,c,str)
+ name = propername(name)
+ local dname = data[name]
+ if not dname then
+ -- nothing
+ elseif not str or str == "" then
+ context(dname.data[c][r] or 0)
+ else
+ local result = execute(name,r,c,str)
+ if result then
+-- if type(result) == "number" then
+-- dname.data[c][r] = result
+-- result = tostring(result)
+-- end
+ local settings = dname.settings
+ local split = settings.split
+ local period = settings.period
+ local comma = settings.comma
+ if split == v_yes then
+ result = splitthousands(result)
+ end
+ if period == "" then period = nil end
+ if comma == "" then comma = nil end
+ result = gsub(result,".",{ ["."] = period, [","] = comma })
+ context(result)
+ end
+ end
+end
+
+function spreadsheets.doifelsecell(name,r,c)
+ name = propername(name)
+ local d = data[name]
+ local d = d and d.data
+ local r = d and rawget(d,r)
+ local c = r and rawget(r,c)
+ commands.doifelse(c)
+end
+
+local function simplify(name)
+ name = propername(name)
+ local data = data[name]
+ if data then
+ data = data.data
+ local temp = { }
+ for k, v in next, data do
+ local t = { }
+ temp[k] = t
+ for kk, vv in next, v do
+ if type(vv) == "function" then
+ t[kk] = "<function>"
+ else
+ t[kk] = vv
+ end
+ end
+ end
+ return temp
+ end
+end
+
+local function serialize(name)
+ local s = simplify(name)
+ if s then
+ return table.serialize(s,name)
+ else
+ return formatters["<unknown spreadsheet %a>"](name)
+ end
+end
+
+spreadsheets.simplify = simplify
+spreadsheets.serialize = serialize
+
+function spreadsheets.inspect(name)
+ inspect(serialize(name))
+end
+
+function spreadsheets.tocontext(name)
+ context.tocontext(simplify(name))
+end
diff --git a/tex/context/base/m-steps.lua b/tex/context/base/m-steps.lua
index caf765a56..97759b799 100644
--- a/tex/context/base/m-steps.lua
+++ b/tex/context/base/m-steps.lua
@@ -1,227 +1,227 @@
-if not modules then modules = { } end modules ['x-flow'] = {
- version = 1.001,
- comment = "companion to m-flow.mkvi",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- when we can resolve mpcolor at the lua end we will use metapost.graphic(....) directly
-
-moduledata.steps = moduledata.steps or { }
-
-local points = number.points -- number.pt
-local variables = interfaces.variables
-
-local trace_charts = false
-
-local defaults = {
- chart = {
- dx = 10*65436,
- dy = 10*65436,
- },
- cell = {
- alternative = 1,
- offset = 2*65436,
- rulethickness = 65436,
- framecolor = "blue",
- backgroundcolor = "gray",
- },
- text = {
- alternative = 1,
- offset = 2*65436,
- distance = 4*65436,
- rulethickness = 65436,
- framecolor = "red",
- backgroundcolor = "gray",
- },
- line = {
- alternative = 1,
- rulethickness = 65436,
- height = 30*65436,
- distance = 10*65436,
- offset = 5*65436,
- color = "green",
- },
-}
-
--- todo : name (no name then direct)
--- maybe: includes
--- maybe: flush ranges
-
-local charts = { }
-local steps = { }
-
-function commands.step_start_chart(name)
- name = name or ""
- steps = { }
- charts[name] = {
- steps = steps,
- }
-end
-
-function commands.step_stop_chart()
-end
-
-function commands.step_make_chart(settings)
- local chartsettings = settings.chart
- if not chartsettings then
- print("no chart")
- return
- end
- local chartname = chartsettings.name
- if not chartname then
- print("no name given")
- return
- end
- local chart = charts[chartname]
- if not chart then
- print("no such chart",chartname)
- return
- end
- local steps = chart.steps or { }
- --
- table.setmetatableindex(settings,defaults)
- --
- if trace_charts then
- inspect(steps)
- end
- --
- local textsettings = settings.text
- local cellsettings = settings.cell
- local linesettings = settings.line
- --
- context.startMPcode()
- context("if unknown context_cell : input mp-step.mpiv ; fi ;")
- context("step_begin_chart ;")
- --
- if chartsettings.alternative == variables.vertical then
- context("chart_vertical := true ;")
- end
- --
- context("text_line_color := \\MPcolor{%s} ;", textsettings.framecolor)
- context("text_line_width := %s ;", points(textsettings.rulethickness))
- context("text_fill_color := \\MPcolor{%s} ;", textsettings.backgroundcolor)
- context("text_offset := %s ;", points(textsettings.offset))
- context("text_distance_set := %s ;", points(textsettings.distance))
- --
- context("cell_line_color := \\MPcolor{%s} ;", cellsettings.framecolor)
- context("cell_line_width := %s ;", points(cellsettings.rulethickness))
- context("cell_fill_color := \\MPcolor{%s} ;", cellsettings.backgroundcolor)
- context("cell_offset := %s ;", points(cellsettings.offset))
- context("cell_distance_x := %s ;", points(cellsettings.dx))
- context("cell_distance_y := %s ;", points(cellsettings.dy))
- --
- context("line_line_color := \\MPcolor{%s} ;", linesettings.color)
- context("line_line_width := %s ;", points(linesettings.rulethickness))
- context("line_distance := %s ;", points(linesettings.distance))
- context("line_offset := %s ;", points(linesettings.offset))
- --
- for i=1,#steps do
- local step = steps[i]
- context("step_begin_cell ;")
- if step.cell_top ~= "" then
- context('step_cell_top("%s") ;',string.strip(step.cell_top))
- end
- if step.cell_bot ~= "" then
- context('step_cell_bot("%s") ;',string.strip(step.cell_bot))
- end
- if step.text_top ~= "" then
- context('step_text_top("%s") ;',string.strip(step.text_top))
- end
- if step.text_mid ~= "" then
- context('step_text_mid("%s") ;',string.strip(step.text_mid))
- end
- if step.text_bot ~= "" then
- context('step_text_bot("%s") ;',string.strip(step.text_bot))
- end
- context("step_end_cell ;")
- end
- --
- context("step_end_chart ;")
- context.stopMPcode()
-end
-
-function commands.step_cells(top,bot)
- steps[#steps+1] = {
- cell_top = top or "",
- cell_bot = bot or "",
- text_top = "",
- text_mid = "",
- text_bot = "",
- }
-end
-
-function commands.step_texts(top,bot)
- if #steps > 0 then
- steps[#steps].text_top = top or ""
- steps[#steps].text_bot = bot or ""
- end
-end
-
-function commands.step_cell(top)
- steps[#steps+1] = {
- cell_top = top or "",
- cell_bot = "",
- text_top = "",
- text_mid = "",
- text_bot = "",
- }
-end
-
-function commands.step_text(top)
- if #steps > 0 then
- steps[#steps].text_top = top or ""
- end
-end
-
-function commands.step_textset(left,middle,right)
- if #steps > 0 then
- steps[#steps].text_top = left or ""
- steps[#steps].text_mid = middle or ""
- steps[#steps].text_bot = right or ""
- end
-end
-
-function commands.step_start_cell()
- steps[#steps+1] = {
- cell_top = "",
- cell_bot = "",
- text_top = "",
- text_mid = "",
- text_bot = "",
- }
-end
-
-function commands.step_stop_cell()
-end
-
-function commands.step_text_top(str)
- if #steps > 0 then
- steps[#steps].text_top = str or ""
- end
-end
-
-function commands.step_text_mid(str)
- if #steps > 0 then
- steps[#steps].text_mid = str or ""
- end
-end
-
-function commands.step_text_bot(str)
- if #steps > 0 then
- steps[#steps].text_bot = str or ""
- end
-end
-
-function commands.step_cell_top(str)
- if #steps > 0 then
- steps[#steps].cell_top = str or ""
- end
-end
-
-function commands.step_cell_bot(str)
- if #steps > 0 then
- steps[#steps].cell_bot = str or ""
- end
-end
+if not modules then modules = { } end modules ['x-flow'] = {
+ version = 1.001,
+ comment = "companion to m-flow.mkvi",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- when we can resolve mpcolor at the lua end we will use metapost.graphic(....) directly
+
+moduledata.steps = moduledata.steps or { }
+
+local points = number.points -- number.pt
+local variables = interfaces.variables
+
+local trace_charts = false
+
+local defaults = {
+ chart = {
+ dx = 10*65436,
+ dy = 10*65436,
+ },
+ cell = {
+ alternative = 1,
+ offset = 2*65436,
+ rulethickness = 65436,
+ framecolor = "blue",
+ backgroundcolor = "gray",
+ },
+ text = {
+ alternative = 1,
+ offset = 2*65436,
+ distance = 4*65436,
+ rulethickness = 65436,
+ framecolor = "red",
+ backgroundcolor = "gray",
+ },
+ line = {
+ alternative = 1,
+ rulethickness = 65436,
+ height = 30*65436,
+ distance = 10*65436,
+ offset = 5*65436,
+ color = "green",
+ },
+}
+
+-- todo : name (no name then direct)
+-- maybe: includes
+-- maybe: flush ranges
+
+local charts = { }
+local steps = { }
+
+function commands.step_start_chart(name)
+ name = name or ""
+ steps = { }
+ charts[name] = {
+ steps = steps,
+ }
+end
+
+function commands.step_stop_chart()
+end
+
+function commands.step_make_chart(settings)
+ local chartsettings = settings.chart
+ if not chartsettings then
+ print("no chart")
+ return
+ end
+ local chartname = chartsettings.name
+ if not chartname then
+ print("no name given")
+ return
+ end
+ local chart = charts[chartname]
+ if not chart then
+ print("no such chart",chartname)
+ return
+ end
+ local steps = chart.steps or { }
+ --
+ table.setmetatableindex(settings,defaults)
+ --
+ if trace_charts then
+ inspect(steps)
+ end
+ --
+ local textsettings = settings.text
+ local cellsettings = settings.cell
+ local linesettings = settings.line
+ --
+ context.startMPcode()
+ context("if unknown context_cell : input mp-step.mpiv ; fi ;")
+ context("step_begin_chart ;")
+ --
+ if chartsettings.alternative == variables.vertical then
+ context("chart_vertical := true ;")
+ end
+ --
+ context("text_line_color := \\MPcolor{%s} ;", textsettings.framecolor)
+ context("text_line_width := %s ;", points(textsettings.rulethickness))
+ context("text_fill_color := \\MPcolor{%s} ;", textsettings.backgroundcolor)
+ context("text_offset := %s ;", points(textsettings.offset))
+ context("text_distance_set := %s ;", points(textsettings.distance))
+ --
+ context("cell_line_color := \\MPcolor{%s} ;", cellsettings.framecolor)
+ context("cell_line_width := %s ;", points(cellsettings.rulethickness))
+ context("cell_fill_color := \\MPcolor{%s} ;", cellsettings.backgroundcolor)
+ context("cell_offset := %s ;", points(cellsettings.offset))
+ context("cell_distance_x := %s ;", points(cellsettings.dx))
+ context("cell_distance_y := %s ;", points(cellsettings.dy))
+ --
+ context("line_line_color := \\MPcolor{%s} ;", linesettings.color)
+ context("line_line_width := %s ;", points(linesettings.rulethickness))
+ context("line_distance := %s ;", points(linesettings.distance))
+ context("line_offset := %s ;", points(linesettings.offset))
+ --
+ for i=1,#steps do
+ local step = steps[i]
+ context("step_begin_cell ;")
+ if step.cell_top ~= "" then
+ context('step_cell_top("%s") ;',string.strip(step.cell_top))
+ end
+ if step.cell_bot ~= "" then
+ context('step_cell_bot("%s") ;',string.strip(step.cell_bot))
+ end
+ if step.text_top ~= "" then
+ context('step_text_top("%s") ;',string.strip(step.text_top))
+ end
+ if step.text_mid ~= "" then
+ context('step_text_mid("%s") ;',string.strip(step.text_mid))
+ end
+ if step.text_bot ~= "" then
+ context('step_text_bot("%s") ;',string.strip(step.text_bot))
+ end
+ context("step_end_cell ;")
+ end
+ --
+ context("step_end_chart ;")
+ context.stopMPcode()
+end
+
+function commands.step_cells(top,bot)
+ steps[#steps+1] = {
+ cell_top = top or "",
+ cell_bot = bot or "",
+ text_top = "",
+ text_mid = "",
+ text_bot = "",
+ }
+end
+
+function commands.step_texts(top,bot)
+ if #steps > 0 then
+ steps[#steps].text_top = top or ""
+ steps[#steps].text_bot = bot or ""
+ end
+end
+
+function commands.step_cell(top)
+ steps[#steps+1] = {
+ cell_top = top or "",
+ cell_bot = "",
+ text_top = "",
+ text_mid = "",
+ text_bot = "",
+ }
+end
+
+function commands.step_text(top)
+ if #steps > 0 then
+ steps[#steps].text_top = top or ""
+ end
+end
+
+function commands.step_textset(left,middle,right)
+ if #steps > 0 then
+ steps[#steps].text_top = left or ""
+ steps[#steps].text_mid = middle or ""
+ steps[#steps].text_bot = right or ""
+ end
+end
+
+function commands.step_start_cell()
+ steps[#steps+1] = {
+ cell_top = "",
+ cell_bot = "",
+ text_top = "",
+ text_mid = "",
+ text_bot = "",
+ }
+end
+
+function commands.step_stop_cell()
+end
+
+function commands.step_text_top(str)
+ if #steps > 0 then
+ steps[#steps].text_top = str or ""
+ end
+end
+
+function commands.step_text_mid(str)
+ if #steps > 0 then
+ steps[#steps].text_mid = str or ""
+ end
+end
+
+function commands.step_text_bot(str)
+ if #steps > 0 then
+ steps[#steps].text_bot = str or ""
+ end
+end
+
+function commands.step_cell_top(str)
+ if #steps > 0 then
+ steps[#steps].cell_top = str or ""
+ end
+end
+
+function commands.step_cell_bot(str)
+ if #steps > 0 then
+ steps[#steps].cell_bot = str or ""
+ end
+end
diff --git a/tex/context/base/math-act.lua b/tex/context/base/math-act.lua
index 875e200c1..4f9b3b7e8 100644
--- a/tex/context/base/math-act.lua
+++ b/tex/context/base/math-act.lua
@@ -1,404 +1,404 @@
-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).
-
-local type, next = type, next
-local fastcopy = table.fastcopy
-
-local trace_defining = false trackers.register("math.defining", function(v) trace_defining = v end)
-local report_math = logs.reporter("mathematics","initializing")
-
-local context = context
-local commands = commands
-local mathematics = mathematics
-local texdimen = tex.dimen
-local abs = math.abs
-
-local sequencers = utilities.sequencers
-local appendgroup = sequencers.appendgroup
-local appendaction = sequencers.appendaction
-
-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
- target.mathparameters = mathematics.dimensions(mathparameters)
- end
-end
-
-sequencers.appendaction("mathparameters","system","mathematics.initializeparameters")
-
-local how = {
- -- RadicalKernBeforeDegree = "horizontal",
- -- RadicalKernAfterDegree = "horizontal",
- ScriptPercentScaleDown = "unscaled",
- ScriptScriptPercentScaleDown = "unscaled",
- RadicalDegreeBottomRaisePercent = "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
-
-sequencers.appendaction("mathparameters","system","mathematics.scaleparameters")
-
-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
-
-sequencers.appendaction("mathparameters","system","mathematics.checkaccentbaseheight") -- should go in lfg instead
-
-function mathematics.checkprivateparameters(target,original)
- local mathparameters = target.mathparameters
- if mathparameters then
- local parameters = target.parameters
- if parameters then
- if not mathparameters.FractionDelimiterSize then
- mathparameters.FractionDelimiterSize = 1.01 * parameters.size
- end
- if not mathparameters.FractionDelimiterDisplayStyleSize then
- mathparameters.FractionDelimiterDisplayStyleSize = 2.40 * parameters.size
- end
- elseif target.properties then
- report_math("no parameters in font %a",target.properties.fullname or "?")
- else
- report_math("no parameters and properties in font")
- end
- end
-end
-
-sequencers.appendaction("mathparameters","system","mathematics.checkprivateparameters")
-
-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
-
-sequencers.appendaction("mathparameters","system","mathematics.overloadparameters")
-
-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 tweaks then
- tweaks = tweaks[when]
- if tweaks 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("beforecopyingcharacters","system","mathematics.tweakbeforecopyingfont")
-sequencers.appendaction("aftercopyingcharacters", "system","mathematics.tweakaftercopyingfont")
-
-function mathematics.overloaddimensions(target,original,set)
- local goodies = target.goodies
- if goodies then
- 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",target.properties.fullname,target.parameters.size)
- end
- local characters = target.characters
- local parameters = target.parameters
- local factor = parameters.factor
- local hfactor = parameters.hfactor
- local vfactor = parameters.vfactor
- local addprivate = fonts.helpers.addprivate
- local function overload(dimensions)
- for unicode, data in next, dimensions do
- local character = characters[unicode]
- 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 %a, height %a, depth %a",unicode,width,height,depth)
- 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 then
- xoffset = { "right", xoffset * hfactor }
- end
- if yoffset then
- yoffset = { "down", -yoffset * vfactor }
- end
- if xoffset or yoffset then
- local slot = { "slot", 1, addprivate(target,nil,fastcopy(character)) }
- if xoffset and yoffset then
- character.commands = { xoffset, yoffset, slot }
- elseif xoffset then
- character.commands = { xoffset, slot }
- else
- character.commands = { yoffset, slot }
- end
- character.index = nil
- end
- elseif trace_defining then
- report_math("no overloading dimensions of %C, not in font",unicode)
- end
- end
- end
- if set == nil then
- set = { "default" }
- end
- if set == "all" or set == true then
- for name, set in next, dimensions do
- overload(set)
- end
- else
- if type(set) == "string" then
- set = utilities.parsers.settings_to_array(set)
- end
- if type(set) == "table" then
- for i=1,#set do
- local d = dimensions[set[i]]
- if d then
- overload(d)
- end
- end
- end
- end
- end
- end
- end
-end
-
-sequencers.appendaction("aftercopyingcharacters", "system","mathematics.overloaddimensions")
-
--- a couple of predefined tewaks:
-
-local tweaks = { }
-mathematics.tweaks = tweaks
-
-function tweaks.fixbadprime(target,original)
- target.characters[0xFE325] = target.characters[0x2032]
-end
-
--- helpers
-
-local setmetatableindex = table.setmetatableindex
-local family_font = node.family_font
-
-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_vertical = extensibles.vertical
-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 code = unicode
- local next = character.next
- while next do
- code = next
- character = characters[next]
- next = character.next
- end
- local char = chardata[unicode]
- local mathextensible = char and char.mathextensible
- if character.horiz_variants then
- if character.vert_variants then
- return { e_mixed, code, character }
- else
- local e = mathextensible and extensibles[mathextensible]
- return e and { e, code, character } or unknown
- end
- elseif character.vert_variants then
- local e = mathextensible and extensibles[mathextensible]
- 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)
-
-function mathematics.extensiblecode(family,unicode)
- return extensibles[family_font(family or 0)][unicode][1]
-end
-
-function commands.extensiblecode(family,unicode)
- context(extensibles[family_font(family or 0)][unicode][1])
-end
-
--- left : [head] ...
--- right : ... [head]
--- horizontal : [head] ... [head]
---
--- abs(right["start"] - right["end"]) | right.advance | characters[right.glyph].width
-
-function commands.horizontalcode(family,unicode)
- local font = family_font(family or 0)
- local data = extensibles[font][unicode]
- local kind = data[1]
- if kind == e_left then
- local charlist = data[3].horiz_variants
- local characters = fontcharacters[font]
- local left = charlist[1]
- texdimen.scratchleftoffset = abs((left["start"] or 0) - (left["end"] or 0))
- texdimen.scratchrightoffset = 0
- elseif kind == e_right then
- local charlist = data[3].horiz_variants
- local characters = fontcharacters[font]
- local right = charlist[#charlist]
- texdimen.scratchleftoffset = 0
- texdimen.scratchrightoffset = abs((right["start"] or 0) - (right["end"] or 0))
- elseif kind == e_horizontal then
- local charlist = data[3].horiz_variants
- local characters = fontcharacters[font]
- local left = charlist[1]
- local right = charlist[#charlist]
- texdimen.scratchleftoffset = abs((left["start"] or 0) - (left["end"] or 0))
- texdimen.scratchrightoffset = abs((right["start"] or 0) - (right["end"] or 0))
- else
- texdimen.scratchleftoffset = 0
- texdimen.scratchrightoffset = 0
- end
- context(kind)
-end
+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).
+
+local type, next = type, next
+local fastcopy = table.fastcopy
+
+local trace_defining = false trackers.register("math.defining", function(v) trace_defining = v end)
+local report_math = logs.reporter("mathematics","initializing")
+
+local context = context
+local commands = commands
+local mathematics = mathematics
+local texdimen = tex.dimen
+local abs = math.abs
+
+local sequencers = utilities.sequencers
+local appendgroup = sequencers.appendgroup
+local appendaction = sequencers.appendaction
+
+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
+ target.mathparameters = mathematics.dimensions(mathparameters)
+ end
+end
+
+sequencers.appendaction("mathparameters","system","mathematics.initializeparameters")
+
+local how = {
+ -- RadicalKernBeforeDegree = "horizontal",
+ -- RadicalKernAfterDegree = "horizontal",
+ ScriptPercentScaleDown = "unscaled",
+ ScriptScriptPercentScaleDown = "unscaled",
+ RadicalDegreeBottomRaisePercent = "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
+
+sequencers.appendaction("mathparameters","system","mathematics.scaleparameters")
+
+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
+
+sequencers.appendaction("mathparameters","system","mathematics.checkaccentbaseheight") -- should go in lfg instead
+
+function mathematics.checkprivateparameters(target,original)
+ local mathparameters = target.mathparameters
+ if mathparameters then
+ local parameters = target.parameters
+ if parameters then
+ if not mathparameters.FractionDelimiterSize then
+ mathparameters.FractionDelimiterSize = 1.01 * parameters.size
+ end
+ if not mathparameters.FractionDelimiterDisplayStyleSize then
+ mathparameters.FractionDelimiterDisplayStyleSize = 2.40 * parameters.size
+ end
+ elseif target.properties then
+ report_math("no parameters in font %a",target.properties.fullname or "?")
+ else
+ report_math("no parameters and properties in font")
+ end
+ end
+end
+
+sequencers.appendaction("mathparameters","system","mathematics.checkprivateparameters")
+
+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
+
+sequencers.appendaction("mathparameters","system","mathematics.overloadparameters")
+
+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 tweaks then
+ tweaks = tweaks[when]
+ if tweaks 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("beforecopyingcharacters","system","mathematics.tweakbeforecopyingfont")
+sequencers.appendaction("aftercopyingcharacters", "system","mathematics.tweakaftercopyingfont")
+
+function mathematics.overloaddimensions(target,original,set)
+ local goodies = target.goodies
+ if goodies then
+ 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",target.properties.fullname,target.parameters.size)
+ end
+ local characters = target.characters
+ local parameters = target.parameters
+ local factor = parameters.factor
+ local hfactor = parameters.hfactor
+ local vfactor = parameters.vfactor
+ local addprivate = fonts.helpers.addprivate
+ local function overload(dimensions)
+ for unicode, data in next, dimensions do
+ local character = characters[unicode]
+ 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 %a, height %a, depth %a",unicode,width,height,depth)
+ 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 then
+ xoffset = { "right", xoffset * hfactor }
+ end
+ if yoffset then
+ yoffset = { "down", -yoffset * vfactor }
+ end
+ if xoffset or yoffset then
+ local slot = { "slot", 1, addprivate(target,nil,fastcopy(character)) }
+ if xoffset and yoffset then
+ character.commands = { xoffset, yoffset, slot }
+ elseif xoffset then
+ character.commands = { xoffset, slot }
+ else
+ character.commands = { yoffset, slot }
+ end
+ character.index = nil
+ end
+ elseif trace_defining then
+ report_math("no overloading dimensions of %C, not in font",unicode)
+ end
+ end
+ end
+ if set == nil then
+ set = { "default" }
+ end
+ if set == "all" or set == true then
+ for name, set in next, dimensions do
+ overload(set)
+ end
+ else
+ if type(set) == "string" then
+ set = utilities.parsers.settings_to_array(set)
+ end
+ if type(set) == "table" then
+ for i=1,#set do
+ local d = dimensions[set[i]]
+ if d then
+ overload(d)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+sequencers.appendaction("aftercopyingcharacters", "system","mathematics.overloaddimensions")
+
+-- a couple of predefined tewaks:
+
+local tweaks = { }
+mathematics.tweaks = tweaks
+
+function tweaks.fixbadprime(target,original)
+ target.characters[0xFE325] = target.characters[0x2032]
+end
+
+-- helpers
+
+local setmetatableindex = table.setmetatableindex
+local family_font = node.family_font
+
+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_vertical = extensibles.vertical
+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 code = unicode
+ local next = character.next
+ while next do
+ code = next
+ character = characters[next]
+ next = character.next
+ end
+ local char = chardata[unicode]
+ local mathextensible = char and char.mathextensible
+ if character.horiz_variants then
+ if character.vert_variants then
+ return { e_mixed, code, character }
+ else
+ local e = mathextensible and extensibles[mathextensible]
+ return e and { e, code, character } or unknown
+ end
+ elseif character.vert_variants then
+ local e = mathextensible and extensibles[mathextensible]
+ 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)
+
+function mathematics.extensiblecode(family,unicode)
+ return extensibles[family_font(family or 0)][unicode][1]
+end
+
+function commands.extensiblecode(family,unicode)
+ context(extensibles[family_font(family or 0)][unicode][1])
+end
+
+-- left : [head] ...
+-- right : ... [head]
+-- horizontal : [head] ... [head]
+--
+-- abs(right["start"] - right["end"]) | right.advance | characters[right.glyph].width
+
+function commands.horizontalcode(family,unicode)
+ local font = family_font(family or 0)
+ local data = extensibles[font][unicode]
+ local kind = data[1]
+ if kind == e_left then
+ local charlist = data[3].horiz_variants
+ local characters = fontcharacters[font]
+ local left = charlist[1]
+ texdimen.scratchleftoffset = abs((left["start"] or 0) - (left["end"] or 0))
+ texdimen.scratchrightoffset = 0
+ elseif kind == e_right then
+ local charlist = data[3].horiz_variants
+ local characters = fontcharacters[font]
+ local right = charlist[#charlist]
+ texdimen.scratchleftoffset = 0
+ texdimen.scratchrightoffset = abs((right["start"] or 0) - (right["end"] or 0))
+ elseif kind == e_horizontal then
+ local charlist = data[3].horiz_variants
+ local characters = fontcharacters[font]
+ local left = charlist[1]
+ local right = charlist[#charlist]
+ texdimen.scratchleftoffset = abs((left["start"] or 0) - (left["end"] or 0))
+ texdimen.scratchrightoffset = abs((right["start"] or 0) - (right["end"] or 0))
+ else
+ texdimen.scratchleftoffset = 0
+ texdimen.scratchrightoffset = 0
+ end
+ context(kind)
+end
diff --git a/tex/context/base/math-dim.lua b/tex/context/base/math-dim.lua
index babed0afd..f4fc7905e 100644
--- a/tex/context/base/math-dim.lua
+++ b/tex/context/base/math-dim.lua
@@ -1,240 +1,240 @@
-if not modules then modules = { } end modules ['math-dim'] = {
- 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"
-}
-
--- Beware: only Taco and Ulrik really understands in depth what these dimensions
--- do so if you run into problems ask on the context list.
-
--- The radical_rule value is also used as a trigger. In luatex the accent
--- placement happens either the opentype way (using top_accent cum suis) or the
--- traditional way. In order to determine what method to use the \Umathradicalrule
--- setting is consulted to determine what method to use. This is more efficient
--- than analyzing the (potentially spread over multiple families) situation. For
--- this reason we need to set the radical_rule here. It used to be "<unset>" in
--- which case the engine takes the rulethickness. In c-speak:
---
--- int compat_mode = (radical_rule(cur_style) == undefined_math_parameter) ;
-
-local abs, next = math.abs, next
-
-local defaults = {
- axis = { default = { "AxisHeight", "axis_height" }, },
- accent_base_height = { default = { "AccentBaseHeight", "x_height" }, },
- fraction_del_size = { default = { "FractionDelimiterSize", "delim2" },
- cramped_display_style = { "FractionDelimiterDisplayStyleSize", "delim1" },
- display_style = { "FractionDelimiterDisplayStyleSize", "delim1" }, },
- fraction_denom_down = { default = { "FractionDenominatorShiftDown", "denom2" },
- cramped_display_style = { "FractionDenominatorDisplayStyleShiftDown", "denom1" },
- display_style = { "FractionDenominatorDisplayStyleShiftDown", "denom1" }, },
- fraction_denom_vgap = { default = { "FractionDenominatorGapMin", "default_rule_thickness" },
- cramped_display_style = { "FractionDenominatorDisplayStyleGapMin", "3*default_rule_thickness" },
- display_style = { "FractionDenominatorDisplayStyleGapMin", "3*default_rule_thickness" }, },
- fraction_num_up = { default = { "FractionNumeratorShiftUp", "num2" },
- cramped_display_style = { "FractionNumeratorDisplayStyleShiftUp", "num1" },
- display_style = { "FractionNumeratorDisplayStyleShiftUp", "num1" }, },
- fraction_num_vgap = { default = { "FractionNumeratorGapMin", "default_rule_thickness" },
- cramped_display_style = { "FractionNumeratorDisplayStyleGapMin", "3*default_rule_thickness" },
- display_style = { "FractionNumeratorDisplayStyleGapMin", "3*default_rule_thickness" }, },
- fraction_rule = { default = { "FractionRuleThickness", "default_rule_thickness" }, },
- limit_above_bgap = { default = { "UpperLimitBaselineRiseMin", "big_op_spacing3" }, },
- limit_above_vgap = { default = { "UpperLimitGapMin", "big_op_spacing1" }, },
- limit_above_kern = { default = { "0", "big_op_spacing5" }, },
- limit_below_bgap = { default = { "LowerLimitBaselineDropMin", "big_op_spacing4" }, },
- limit_below_vgap = { default = { "LowerLimitGapMin", "big_op_spacing2" }, },
- limit_below_kern = { default = { "0", "big_op_spacing5" }, },
- math_operator_size = { default = { "DisplayOperatorMinHeight", "math_x_height*3" }, }, -- 2
- overbar_kern = { default = { "OverbarExtraAscender", "default_rule_thickness" }, },
- overbar_rule = { default = { "OverbarRuleThickness", "default_rule_thickness" }, },
- overbar_vgap = { default = { "OverbarVerticalGap", "3*default_rule_thickness" }, },
- quad = { default = { "font_size(f)", "math_quad" }, },
- radical_kern = { default = { "RadicalExtraAscender", "default_rule_thickness" }, },
- radical_rule = { default = { "RadicalRuleThickness", "default_rule_thickness" }, },
- -- default = { "surd_height(f)", "default_rule_thickness" },
- radical_vgap = { default = { "RadicalVerticalGap", "default_rule_thickness+(abs(default_rule_thickness)/4)" },
- display_style = { "RadicalDisplayStyleVerticalGap", "default_rule_thickness+(abs(math_x_height)/4)" }, },
- space_after_script = { default = { "SpaceAfterScript", "script_space" }, },
- stack_denom_down = { default = { "StackBottomShiftDown", "denom2" },
- cramped_display_style = { "StackBottomDisplayStyleShiftDown", "denom1" },
- display_style = { "StackBottomDisplayStyleShiftDown", "denom1" }, },
- stack_num_up = { default = { "StackTopShiftUp", "num3" },
- cramped_display_style = { "StackTopDisplayStyleShiftUp", "num1" },
- display_style = { "StackTopDisplayStyleShiftUp", "num1" }, },
- stack_vgap = { default = { "StackGapMin", "3*default_rule_thickness" },
- cramped_display_style = { "StackDisplayStyleGapMin", "7*default_rule_thickness" },
- display_style = { "StackDisplayStyleGapMin", "7*default_rule_thickness" }, },
- sub_shift_down = { default = { "SubscriptShiftDown", "sub1" }, },
- sub_shift_drop = { default = { "SubscriptBaselineDropMin", "sub_drop" }, },
- sub_sup_shift_down = { default = { "SubscriptShiftDown", "sub2" }, },
- sub_top_max = { default = { "SubscriptTopMax", "abs(math_x_height*4)/5" }, },
- subsup_vgap = { default = { "SubSuperscriptGapMin", "4*default_rule_thickness" }, },
- sup_bottom_min = { default = { "SuperscriptBottomMin", "abs(math_x_height)/4" }, },
- sup_shift_drop = { default = { "SuperscriptBaselineDropMax", "sup_drop" }, },
- sup_shift_up = { cramped_display_style = { "SuperscriptShiftUpCramped", "sup3" },
- cramped_script_script_style = { "SuperscriptShiftUpCramped", "sup3" },
- cramped_script_style = { "SuperscriptShiftUpCramped", "sup3" },
- cramped_text_style = { "SuperscriptShiftUpCramped", "sup3" },
- display_style = { "SuperscriptShiftUp", "sup1" },
- script_script_style = { "SuperscriptShiftUp", "sup2" },
- script_style = { "SuperscriptShiftUp", "sup2" },
- text_style = { "SuperscriptShiftUp", "sup2" }, },
- sup_sub_bottom_max = { default = { "SuperscriptBottomMaxWithSubscript", "abs(math_x_height*4)/5" }, },
- underbar_kern = { default = { "UnderbarExtraDescender", "0" }, },
- underbar_rule = { default = { "UnderbarRuleThickness", "default_rule_thickness" }, },
- underbar_vgap = { default = { "UnderbarVerticalGap", "3*default_rule_thickness" }, },
- connector_overlap_min = { default = { "MinConnectorOverlap", "0.25*default_rule_thickness" }, },
- over_delimiter_vgap = { default = { "StretchStackGapBelowMin", "big_op_spacing1" }, },
- over_delimiter_bgap = { default = { "StretchStackTopShiftUp", "big_op_spacing3" }, },
- under_delimiter_vgap = { default = { "StretchStackGapAboveMin", "big_op_spacing2" }, },
- under_delimiter_bgap = { default = { "StretchStackBottomShiftDown", "big_op_spacing4" }, },
- radical_degree_before = { default = { "RadicalKernBeforeDegree", "(5/18)*quad" }, },
- radical_degree_after = { default = { "RadicalKernAfterDegree", "(-10/18)*quad" }, },
- radical_degree_raise = { default = { "RadicalDegreeBottomRaisePercent", "60" }, },
-}
-
-local styles = {
- 'cramped_display_style',
- 'cramped_script_script_style',
- 'cramped_script_style',
- 'cramped_text_style',
- 'display_style',
- 'script_script_style',
- 'script_style',
- 'text_style',
-}
-
-for k, v in next, defaults do
- for _, s in next, styles do
- if not v[s] then
- v[s] = v.default
- end
- end
-end
-
--- we cannot use a metatable because we do a copy (takes a bit more work)
---
--- local mt = { } setmetatable(defaults,mt)
---
--- mt.__index = function(t,s)
--- return t.default or t.text_style or 0
--- end
-
-function mathematics.dimensions(dimens) -- beware, dimens get spoiled
- if dimens.SpaceAfterScript then
- dimens.SubscriptShiftDownWithSuperscript = dimens.SubscriptShiftDown * 1.5 -- move this one
- return table.fastcopy(dimens), { }
- elseif dimens.AxisHeight or dimens.axis_height then
- local t = { }
- local math_x_height = dimens.x_height or 10*65536
- local math_quad = dimens.quad or 10*65536
- local default_rule_thickness = dimens.FractionDenominatorGapMin or dimens.default_rule_thickness or 0.4*65536
- dimens["0"] = 0
- dimens["60"] = 60
- dimens["0.25*default_rule_thickness"] = default_rule_thickness / 4
- dimens["3*default_rule_thickness"] = 3 * default_rule_thickness
- dimens["4*default_rule_thickness"] = 4 * default_rule_thickness
- dimens["7*default_rule_thickness"] = 7 * default_rule_thickness
- dimens["(5/18)*quad"] = (math_quad * 5) / 18
- dimens["(-10/18)*quad"] = - (math_quad * 10) / 18
- dimens["math_x_height*3"] = math_x_height * 3 -- needs checking
- dimens["abs(math_x_height*4)/5"] = abs(math_x_height * 4) / 5
- dimens["default_rule_thickness+(abs(default_rule_thickness)/4)"] = default_rule_thickness+(abs(default_rule_thickness) / 4)
- dimens["default_rule_thickness+(abs(math_x_height)/4)"] = default_rule_thickness+(abs(math_x_height) / 4)
- dimens["abs(math_x_height)/4"] = abs(math_x_height) / 4
- dimens["abs(math_x_height*4)/5"] = abs(math_x_height * 4) / 5
- dimens["<not set>"] = false
- dimens["script_space"] = false -- at macro level
- for variable, styles in next, defaults do
- local tt = { }
- for style, default in next, styles do
- local one, two = default[1], default[2]
- local value = dimens[one]
- if value then
- tt[style] = value
- else
- value = dimens[two]
- if value == false then
- tt[style] = nil
- else
- tt[style] = value or 0
- end
- end
- end
- t[variable] = tt
- end
- local d = {
- AccentBaseHeight = t . accent_base_height . text_style,
- AxisHeight = t . axis . text_style,
- -- DelimitedSubFormulaMinHeight
- DisplayOperatorMinHeight = t . math_operator_size . text_style, -- no longer let tex decide (weird values)
- -- FlattenedAccentBaseHeight
- FractionDenominatorDisplayStyleGapMin = t . fraction_denom_vgap . display_style,
- FractionDenominatorDisplayStyleShiftDown = t . fraction_denom_down . display_style,
- FractionDenominatorGapMin = t . fraction_denom_vgap . text_style,
- FractionDenominatorShiftDown = t . fraction_denom_down . text_style,
- FractionNumeratorDisplayStyleGapMin = t . fraction_num_vgap . display_style,
- FractionNumeratorDisplayStyleShiftUp = t . fraction_num_up . display_style,
- FractionNumeratorGapMin = t . fraction_num_vgap . text_style,
- FractionNumeratorShiftUp = t . fraction_num_up . text_style,
- FractionRuleThickness = t . fraction_rule . text_style,
- FractionDelimiterSize = t . fraction_del_size . text_style,
- FractionDelimiterDisplayStyleSize = t . fraction_del_size . display_style,
- LowerLimitBaselineDropMin = t . limit_below_bgap . text_style,
- LowerLimitGapMin = t . limit_below_vgap . text_style,
- -- MathLeading
- MinConnectorOverlap = t . connector_overlap_min . text_style,
- OverbarExtraAscender = t . overbar_kern . text_style,
- OverbarRuleThickness = t . overbar_rule . text_style,
- OverbarVerticalGap = t . overbar_vgap . text_style,
- RadicalDisplayStyleVerticalGap = t . radical_vgap . display_style,
- RadicalExtraAscender = t . radical_kern . text_style,
- RadicalRuleThickness = t . radical_rule . text_style,
- RadicalVerticalGap = t . radical_vgap . text_style,
- RadicalKernBeforeDegree = t . radical_degree_before . display_style,
- RadicalKernAfterDegree = t . radical_degree_after . display_style,
- RadicalDegreeBottomRaisePercent = t . radical_degree_raise . display_style,
- -- ScriptPercentScaleDown
- -- ScriptScriptPercentScaleDown
- -- SkewedFractionHorizontalGap
- -- SkewedFractionVerticalGap
- SpaceAfterScript = t . space_after_script . text_style,
- StackBottomDisplayStyleShiftDown = t . stack_denom_down . display_style,
- StackBottomShiftDown = t . stack_denom_down . text_style,
- StackDisplayStyleGapMin = t . stack_vgap . display_style,
- StackGapMin = t . stack_vgap . text_style,
- StackTopDisplayStyleShiftUp = t . stack_num_up . display_style,
- StackTopShiftUp = t . stack_num_up . text_style,
- StretchStackGapBelowMin = t . over_delimiter_vgap . text_style,
- StretchStackTopShiftUp = t . over_delimiter_bgap . text_style,
- StretchStackGapAboveMin = t . under_delimiter_vgap . text_style,
- StretchStackBottomShiftDown = t . under_delimiter_bgap . text_style,
- SubSuperscriptGapMin = t . subsup_vgap . text_style,
- SubscriptBaselineDropMin = t . sub_shift_drop . text_style,
- SubscriptShiftDown = t . sub_shift_down . text_style,
- SubscriptShiftDownWithSuperscript = t . sub_sup_shift_down . text_style,
- SubscriptTopMax = t . sub_top_max . text_style,
- SuperscriptBaselineDropMax = t . sup_shift_drop . text_style,
- SuperscriptBottomMaxWithSubscript = t . sup_sub_bottom_max . text_style,
- SuperscriptBottomMin = t . sup_bottom_min . text_style,
- SuperscriptShiftUp = t . sup_shift_up . text_style,
- SuperscriptShiftUpCramped = t . sup_shift_up . cramped_text_style,
- UnderbarExtraDescender = t . underbar_kern . text_style,
- UnderbarRuleThickness = t . underbar_rule . text_style,
- UnderbarVerticalGap = t . underbar_vgap . text_style,
- UpperLimitBaselineRiseMin = t . limit_above_bgap . text_style,
- UpperLimitGapMin = t . limit_above_vgap . text_style,
- }
-
- -- too fragile for tx/px ... even the same values give different results
- d.DisplayOperatorMinHeight = nil
- --
- d.AccentBaseHeight = 0 -- here? still?
- return d, t -- t only for diagnostics
- else
- return { }, { }
- end
-end
-
+if not modules then modules = { } end modules ['math-dim'] = {
+ 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"
+}
+
+-- Beware: only Taco and Ulrik really understands in depth what these dimensions
+-- do so if you run into problems ask on the context list.
+
+-- The radical_rule value is also used as a trigger. In luatex the accent
+-- placement happens either the opentype way (using top_accent cum suis) or the
+-- traditional way. In order to determine what method to use the \Umathradicalrule
+-- setting is consulted to determine what method to use. This is more efficient
+-- than analyzing the (potentially spread over multiple families) situation. For
+-- this reason we need to set the radical_rule here. It used to be "<unset>" in
+-- which case the engine takes the rulethickness. In c-speak:
+--
+-- int compat_mode = (radical_rule(cur_style) == undefined_math_parameter) ;
+
+local abs, next = math.abs, next
+
+local defaults = {
+ axis = { default = { "AxisHeight", "axis_height" }, },
+ accent_base_height = { default = { "AccentBaseHeight", "x_height" }, },
+ fraction_del_size = { default = { "FractionDelimiterSize", "delim2" },
+ cramped_display_style = { "FractionDelimiterDisplayStyleSize", "delim1" },
+ display_style = { "FractionDelimiterDisplayStyleSize", "delim1" }, },
+ fraction_denom_down = { default = { "FractionDenominatorShiftDown", "denom2" },
+ cramped_display_style = { "FractionDenominatorDisplayStyleShiftDown", "denom1" },
+ display_style = { "FractionDenominatorDisplayStyleShiftDown", "denom1" }, },
+ fraction_denom_vgap = { default = { "FractionDenominatorGapMin", "default_rule_thickness" },
+ cramped_display_style = { "FractionDenominatorDisplayStyleGapMin", "3*default_rule_thickness" },
+ display_style = { "FractionDenominatorDisplayStyleGapMin", "3*default_rule_thickness" }, },
+ fraction_num_up = { default = { "FractionNumeratorShiftUp", "num2" },
+ cramped_display_style = { "FractionNumeratorDisplayStyleShiftUp", "num1" },
+ display_style = { "FractionNumeratorDisplayStyleShiftUp", "num1" }, },
+ fraction_num_vgap = { default = { "FractionNumeratorGapMin", "default_rule_thickness" },
+ cramped_display_style = { "FractionNumeratorDisplayStyleGapMin", "3*default_rule_thickness" },
+ display_style = { "FractionNumeratorDisplayStyleGapMin", "3*default_rule_thickness" }, },
+ fraction_rule = { default = { "FractionRuleThickness", "default_rule_thickness" }, },
+ limit_above_bgap = { default = { "UpperLimitBaselineRiseMin", "big_op_spacing3" }, },
+ limit_above_vgap = { default = { "UpperLimitGapMin", "big_op_spacing1" }, },
+ limit_above_kern = { default = { "0", "big_op_spacing5" }, },
+ limit_below_bgap = { default = { "LowerLimitBaselineDropMin", "big_op_spacing4" }, },
+ limit_below_vgap = { default = { "LowerLimitGapMin", "big_op_spacing2" }, },
+ limit_below_kern = { default = { "0", "big_op_spacing5" }, },
+ math_operator_size = { default = { "DisplayOperatorMinHeight", "math_x_height*3" }, }, -- 2
+ overbar_kern = { default = { "OverbarExtraAscender", "default_rule_thickness" }, },
+ overbar_rule = { default = { "OverbarRuleThickness", "default_rule_thickness" }, },
+ overbar_vgap = { default = { "OverbarVerticalGap", "3*default_rule_thickness" }, },
+ quad = { default = { "font_size(f)", "math_quad" }, },
+ radical_kern = { default = { "RadicalExtraAscender", "default_rule_thickness" }, },
+ radical_rule = { default = { "RadicalRuleThickness", "default_rule_thickness" }, },
+ -- default = { "surd_height(f)", "default_rule_thickness" },
+ radical_vgap = { default = { "RadicalVerticalGap", "default_rule_thickness+(abs(default_rule_thickness)/4)" },
+ display_style = { "RadicalDisplayStyleVerticalGap", "default_rule_thickness+(abs(math_x_height)/4)" }, },
+ space_after_script = { default = { "SpaceAfterScript", "script_space" }, },
+ stack_denom_down = { default = { "StackBottomShiftDown", "denom2" },
+ cramped_display_style = { "StackBottomDisplayStyleShiftDown", "denom1" },
+ display_style = { "StackBottomDisplayStyleShiftDown", "denom1" }, },
+ stack_num_up = { default = { "StackTopShiftUp", "num3" },
+ cramped_display_style = { "StackTopDisplayStyleShiftUp", "num1" },
+ display_style = { "StackTopDisplayStyleShiftUp", "num1" }, },
+ stack_vgap = { default = { "StackGapMin", "3*default_rule_thickness" },
+ cramped_display_style = { "StackDisplayStyleGapMin", "7*default_rule_thickness" },
+ display_style = { "StackDisplayStyleGapMin", "7*default_rule_thickness" }, },
+ sub_shift_down = { default = { "SubscriptShiftDown", "sub1" }, },
+ sub_shift_drop = { default = { "SubscriptBaselineDropMin", "sub_drop" }, },
+ sub_sup_shift_down = { default = { "SubscriptShiftDown", "sub2" }, },
+ sub_top_max = { default = { "SubscriptTopMax", "abs(math_x_height*4)/5" }, },
+ subsup_vgap = { default = { "SubSuperscriptGapMin", "4*default_rule_thickness" }, },
+ sup_bottom_min = { default = { "SuperscriptBottomMin", "abs(math_x_height)/4" }, },
+ sup_shift_drop = { default = { "SuperscriptBaselineDropMax", "sup_drop" }, },
+ sup_shift_up = { cramped_display_style = { "SuperscriptShiftUpCramped", "sup3" },
+ cramped_script_script_style = { "SuperscriptShiftUpCramped", "sup3" },
+ cramped_script_style = { "SuperscriptShiftUpCramped", "sup3" },
+ cramped_text_style = { "SuperscriptShiftUpCramped", "sup3" },
+ display_style = { "SuperscriptShiftUp", "sup1" },
+ script_script_style = { "SuperscriptShiftUp", "sup2" },
+ script_style = { "SuperscriptShiftUp", "sup2" },
+ text_style = { "SuperscriptShiftUp", "sup2" }, },
+ sup_sub_bottom_max = { default = { "SuperscriptBottomMaxWithSubscript", "abs(math_x_height*4)/5" }, },
+ underbar_kern = { default = { "UnderbarExtraDescender", "0" }, },
+ underbar_rule = { default = { "UnderbarRuleThickness", "default_rule_thickness" }, },
+ underbar_vgap = { default = { "UnderbarVerticalGap", "3*default_rule_thickness" }, },
+ connector_overlap_min = { default = { "MinConnectorOverlap", "0.25*default_rule_thickness" }, },
+ over_delimiter_vgap = { default = { "StretchStackGapBelowMin", "big_op_spacing1" }, },
+ over_delimiter_bgap = { default = { "StretchStackTopShiftUp", "big_op_spacing3" }, },
+ under_delimiter_vgap = { default = { "StretchStackGapAboveMin", "big_op_spacing2" }, },
+ under_delimiter_bgap = { default = { "StretchStackBottomShiftDown", "big_op_spacing4" }, },
+ radical_degree_before = { default = { "RadicalKernBeforeDegree", "(5/18)*quad" }, },
+ radical_degree_after = { default = { "RadicalKernAfterDegree", "(-10/18)*quad" }, },
+ radical_degree_raise = { default = { "RadicalDegreeBottomRaisePercent", "60" }, },
+}
+
+local styles = {
+ 'cramped_display_style',
+ 'cramped_script_script_style',
+ 'cramped_script_style',
+ 'cramped_text_style',
+ 'display_style',
+ 'script_script_style',
+ 'script_style',
+ 'text_style',
+}
+
+for k, v in next, defaults do
+ for _, s in next, styles do
+ if not v[s] then
+ v[s] = v.default
+ end
+ end
+end
+
+-- we cannot use a metatable because we do a copy (takes a bit more work)
+--
+-- local mt = { } setmetatable(defaults,mt)
+--
+-- mt.__index = function(t,s)
+-- return t.default or t.text_style or 0
+-- end
+
+function mathematics.dimensions(dimens) -- beware, dimens get spoiled
+ if dimens.SpaceAfterScript then
+ dimens.SubscriptShiftDownWithSuperscript = dimens.SubscriptShiftDown * 1.5 -- move this one
+ return table.fastcopy(dimens), { }
+ elseif dimens.AxisHeight or dimens.axis_height then
+ local t = { }
+ local math_x_height = dimens.x_height or 10*65536
+ local math_quad = dimens.quad or 10*65536
+ local default_rule_thickness = dimens.FractionDenominatorGapMin or dimens.default_rule_thickness or 0.4*65536
+ dimens["0"] = 0
+ dimens["60"] = 60
+ dimens["0.25*default_rule_thickness"] = default_rule_thickness / 4
+ dimens["3*default_rule_thickness"] = 3 * default_rule_thickness
+ dimens["4*default_rule_thickness"] = 4 * default_rule_thickness
+ dimens["7*default_rule_thickness"] = 7 * default_rule_thickness
+ dimens["(5/18)*quad"] = (math_quad * 5) / 18
+ dimens["(-10/18)*quad"] = - (math_quad * 10) / 18
+ dimens["math_x_height*3"] = math_x_height * 3 -- needs checking
+ dimens["abs(math_x_height*4)/5"] = abs(math_x_height * 4) / 5
+ dimens["default_rule_thickness+(abs(default_rule_thickness)/4)"] = default_rule_thickness+(abs(default_rule_thickness) / 4)
+ dimens["default_rule_thickness+(abs(math_x_height)/4)"] = default_rule_thickness+(abs(math_x_height) / 4)
+ dimens["abs(math_x_height)/4"] = abs(math_x_height) / 4
+ dimens["abs(math_x_height*4)/5"] = abs(math_x_height * 4) / 5
+ dimens["<not set>"] = false
+ dimens["script_space"] = false -- at macro level
+ for variable, styles in next, defaults do
+ local tt = { }
+ for style, default in next, styles do
+ local one, two = default[1], default[2]
+ local value = dimens[one]
+ if value then
+ tt[style] = value
+ else
+ value = dimens[two]
+ if value == false then
+ tt[style] = nil
+ else
+ tt[style] = value or 0
+ end
+ end
+ end
+ t[variable] = tt
+ end
+ local d = {
+ AccentBaseHeight = t . accent_base_height . text_style,
+ AxisHeight = t . axis . text_style,
+ -- DelimitedSubFormulaMinHeight
+ DisplayOperatorMinHeight = t . math_operator_size . text_style, -- no longer let tex decide (weird values)
+ -- FlattenedAccentBaseHeight
+ FractionDenominatorDisplayStyleGapMin = t . fraction_denom_vgap . display_style,
+ FractionDenominatorDisplayStyleShiftDown = t . fraction_denom_down . display_style,
+ FractionDenominatorGapMin = t . fraction_denom_vgap . text_style,
+ FractionDenominatorShiftDown = t . fraction_denom_down . text_style,
+ FractionNumeratorDisplayStyleGapMin = t . fraction_num_vgap . display_style,
+ FractionNumeratorDisplayStyleShiftUp = t . fraction_num_up . display_style,
+ FractionNumeratorGapMin = t . fraction_num_vgap . text_style,
+ FractionNumeratorShiftUp = t . fraction_num_up . text_style,
+ FractionRuleThickness = t . fraction_rule . text_style,
+ FractionDelimiterSize = t . fraction_del_size . text_style,
+ FractionDelimiterDisplayStyleSize = t . fraction_del_size . display_style,
+ LowerLimitBaselineDropMin = t . limit_below_bgap . text_style,
+ LowerLimitGapMin = t . limit_below_vgap . text_style,
+ -- MathLeading
+ MinConnectorOverlap = t . connector_overlap_min . text_style,
+ OverbarExtraAscender = t . overbar_kern . text_style,
+ OverbarRuleThickness = t . overbar_rule . text_style,
+ OverbarVerticalGap = t . overbar_vgap . text_style,
+ RadicalDisplayStyleVerticalGap = t . radical_vgap . display_style,
+ RadicalExtraAscender = t . radical_kern . text_style,
+ RadicalRuleThickness = t . radical_rule . text_style,
+ RadicalVerticalGap = t . radical_vgap . text_style,
+ RadicalKernBeforeDegree = t . radical_degree_before . display_style,
+ RadicalKernAfterDegree = t . radical_degree_after . display_style,
+ RadicalDegreeBottomRaisePercent = t . radical_degree_raise . display_style,
+ -- ScriptPercentScaleDown
+ -- ScriptScriptPercentScaleDown
+ -- SkewedFractionHorizontalGap
+ -- SkewedFractionVerticalGap
+ SpaceAfterScript = t . space_after_script . text_style,
+ StackBottomDisplayStyleShiftDown = t . stack_denom_down . display_style,
+ StackBottomShiftDown = t . stack_denom_down . text_style,
+ StackDisplayStyleGapMin = t . stack_vgap . display_style,
+ StackGapMin = t . stack_vgap . text_style,
+ StackTopDisplayStyleShiftUp = t . stack_num_up . display_style,
+ StackTopShiftUp = t . stack_num_up . text_style,
+ StretchStackGapBelowMin = t . over_delimiter_vgap . text_style,
+ StretchStackTopShiftUp = t . over_delimiter_bgap . text_style,
+ StretchStackGapAboveMin = t . under_delimiter_vgap . text_style,
+ StretchStackBottomShiftDown = t . under_delimiter_bgap . text_style,
+ SubSuperscriptGapMin = t . subsup_vgap . text_style,
+ SubscriptBaselineDropMin = t . sub_shift_drop . text_style,
+ SubscriptShiftDown = t . sub_shift_down . text_style,
+ SubscriptShiftDownWithSuperscript = t . sub_sup_shift_down . text_style,
+ SubscriptTopMax = t . sub_top_max . text_style,
+ SuperscriptBaselineDropMax = t . sup_shift_drop . text_style,
+ SuperscriptBottomMaxWithSubscript = t . sup_sub_bottom_max . text_style,
+ SuperscriptBottomMin = t . sup_bottom_min . text_style,
+ SuperscriptShiftUp = t . sup_shift_up . text_style,
+ SuperscriptShiftUpCramped = t . sup_shift_up . cramped_text_style,
+ UnderbarExtraDescender = t . underbar_kern . text_style,
+ UnderbarRuleThickness = t . underbar_rule . text_style,
+ UnderbarVerticalGap = t . underbar_vgap . text_style,
+ UpperLimitBaselineRiseMin = t . limit_above_bgap . text_style,
+ UpperLimitGapMin = t . limit_above_vgap . text_style,
+ }
+
+ -- too fragile for tx/px ... even the same values give different results
+ d.DisplayOperatorMinHeight = nil
+ --
+ d.AccentBaseHeight = 0 -- here? still?
+ return d, t -- t only for diagnostics
+ else
+ return { }, { }
+ end
+end
+
diff --git a/tex/context/base/math-ext.lua b/tex/context/base/math-ext.lua
index 2b6860d75..b00d6cde2 100644
--- a/tex/context/base/math-ext.lua
+++ b/tex/context/base/math-ext.lua
@@ -1,197 +1,197 @@
-if not modules then modules = { } end modules ['math-ext'] = {
- 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"
-}
-
-local trace_virtual = false trackers.register("math.virtual", function(v) trace_virtual = v end)
-
-local basename = file.basename
-
-local mathematics = mathematics
-local characters = characters
-
-local report_math = logs.reporter("mathematics")
-
-mathematics.extras = mathematics.extras or { }
-local extras = mathematics.extras
-
-characters.math = characters.math or { }
-local mathdata = characters.math
-local chardata = characters.data
-
-function extras.add(unicode,t) -- todo: if already stored ...
- local min, max = mathematics.extrabase, mathematics.privatebase - 1
- -- if mathdata[unicode] or chardata[unicode] then
- -- report_math("extra %U overloads existing character",unicode)
- -- end
- if unicode >= min and unicode <= max then
- mathdata[unicode], chardata[unicode] = t, t
- else
- report_math("extra %U should be in range %U - %U",unicode,min,max)
- end
-end
-
-function extras.copy(target,original)
- local characters = target.characters
- local properties = target.properties
- local parameters = target.parameters
- for unicode, extradesc in next, mathdata do
- -- always, because in an intermediate step we can have a non math font
- local extrachar = characters[unicode]
- local nextinsize = extradesc.nextinsize
- if nextinsize then
- local first = 1
- local charused = unicode
- if not extrachar then
- for i=1,#nextinsize do
- local slot = nextinsize[i]
- extrachar = characters[slot]
- if extrachar then
- characters[unicode] = extrachar
- first = i + 1
- charused = slot
- break
- end
- end
- end
- if not extrachar then
- if trace_virtual then
- report_math("extra %U in %a at %p with class %a and name %a is not mapped",
- unicode,basename(properties.fullname),parameters.size,
- extradesc.mathclass,extradesc.mathname)
- end
- elseif not extrachar.next then
- local nextused = false
- for i=first,#nextinsize do
- local nextslot = nextinsize[i]
- local nextbase = characters[nextslot]
- if nextbase then
- local nextnext = nextbase and nextbase.next
- if nextnext then
- local nextchar = characters[nextnext]
- if nextchar then
- extrachar.next = nextchar
- nextused = nextslot
- break
- end
- end
- end
- end
- if trace_virtual then
- if nextused then
- report_math("extra %U in %a at %p with class %a and name %a maps onto %U with next %U",
- unicode,basename(properties.fullname),parameters.size,charused,
- extradesc.mathclass,extradesc.mathname,nextused)
- else
- report_math("extra %U in %a at %p with class %a and name %a maps onto %U with no next",
- unicode,basename(properties.fullname),parameters.size,charused,
- extradesc.mathclass,extradesc.mathname)
- end
- end
- else
- if trace_virtual then
- report_math("extra %U in %a at %p with class %a and name %a maps onto %U with no next", -- own next
- unicode,basename(properties.fullname),parameters.size,charused,
- extradesc.mathclass,extradesc.mathname)
- end
- end
- end
- end
-end
-
-utilities.sequencers.appendaction(mathactions,"system","mathematics.extras.copy")
-
--- 0xFE302 -- 0xFE320 for accents (gone with new lm/gyre)
---
--- extras.add(0xFE302, {
--- category="mn",
--- description="WIDE MATHEMATICAL HAT",
--- direction="nsm",
--- linebreak="cm",
--- mathclass="topaccent",
--- mathname="widehat",
--- mathstretch="h",
--- unicodeslot=0xFE302,
--- nextinsize={ 0x00302, 0x0005E },
--- } )
---
--- extras.add(0xFE303, {
--- category="mn",
--- cjkwd="a",
--- description="WIDE MATHEMATICAL TILDE",
--- direction="nsm",
--- linebreak="cm",
--- mathclass="topaccent",
--- mathname="widetilde",
--- mathstretch="h",
--- unicodeslot=0xFE303,
--- nextinsize={ 0x00303, 0x0007E },
--- } )
-
--- 0xFE321 -- 0xFE340 for missing characters
-
-extras.add(0xFE321, {
- category="sm",
- description="MATHEMATICAL SHORT BAR",
- -- direction="on",
- -- linebreak="nu",
- mathclass="relation",
- mathname="mapstochar",
- unicodeslot=0xFE321,
-} )
-
-extras.add(0xFE322, {
- category="sm",
- description="MATHEMATICAL LEFT HOOK",
- mathclass="relation",
- mathname="lhook",
- unicodeslot=0xFE322,
-} )
-
-extras.add(0xFE323, {
- category="sm",
- description="MATHEMATICAL RIGHT HOOK",
- mathclass="relation",
- mathname="rhook",
- unicodeslot=0xFE323,
-} )
-
-extras.add(0xFE324, {
- category="sm",
- description="MATHEMATICAL SHORT BAR MIRRORED",
--- direction="on",
--- linebreak="nu",
- mathclass="relation",
- mathname="mapsfromchar",
- unicodeslot=0xFE324,
-} )
-
---~ extras.add(0xFE304, {
---~ category="sm",
---~ description="TOP AND BOTTOM PARENTHESES",
---~ direction="on",
---~ linebreak="al",
---~ mathclass="doubleaccent",
---~ mathname="doubleparent",
---~ unicodeslot=0xFE304,
---~ accents={ 0x023DC, 0x023DD },
---~ } )
-
---~ extras.add(0xFE305, {
---~ category="sm",
---~ description="TOP AND BOTTOM BRACES",
---~ direction="on",
---~ linebreak="al",
---~ mathclass="doubleaccent",
---~ mathname="doublebrace",
---~ unicodeslot=0xFE305,
---~ accents={ 0x023DE, 0x023DF },
---~ } )
-
---~ \Umathchardef\braceld="0 "1 "FF07A
---~ \Umathchardef\bracerd="0 "1 "FF07B
---~ \Umathchardef\bracelu="0 "1 "FF07C
---~ \Umathchardef\braceru="0 "1 "FF07D
+if not modules then modules = { } end modules ['math-ext'] = {
+ 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"
+}
+
+local trace_virtual = false trackers.register("math.virtual", function(v) trace_virtual = v end)
+
+local basename = file.basename
+
+local mathematics = mathematics
+local characters = characters
+
+local report_math = logs.reporter("mathematics")
+
+mathematics.extras = mathematics.extras or { }
+local extras = mathematics.extras
+
+characters.math = characters.math or { }
+local mathdata = characters.math
+local chardata = characters.data
+
+function extras.add(unicode,t) -- todo: if already stored ...
+ local min, max = mathematics.extrabase, mathematics.privatebase - 1
+ -- if mathdata[unicode] or chardata[unicode] then
+ -- report_math("extra %U overloads existing character",unicode)
+ -- end
+ if unicode >= min and unicode <= max then
+ mathdata[unicode], chardata[unicode] = t, t
+ else
+ report_math("extra %U should be in range %U - %U",unicode,min,max)
+ end
+end
+
+function extras.copy(target,original)
+ local characters = target.characters
+ local properties = target.properties
+ local parameters = target.parameters
+ for unicode, extradesc in next, mathdata do
+ -- always, because in an intermediate step we can have a non math font
+ local extrachar = characters[unicode]
+ local nextinsize = extradesc.nextinsize
+ if nextinsize then
+ local first = 1
+ local charused = unicode
+ if not extrachar then
+ for i=1,#nextinsize do
+ local slot = nextinsize[i]
+ extrachar = characters[slot]
+ if extrachar then
+ characters[unicode] = extrachar
+ first = i + 1
+ charused = slot
+ break
+ end
+ end
+ end
+ if not extrachar then
+ if trace_virtual then
+ report_math("extra %U in %a at %p with class %a and name %a is not mapped",
+ unicode,basename(properties.fullname),parameters.size,
+ extradesc.mathclass,extradesc.mathname)
+ end
+ elseif not extrachar.next then
+ local nextused = false
+ for i=first,#nextinsize do
+ local nextslot = nextinsize[i]
+ local nextbase = characters[nextslot]
+ if nextbase then
+ local nextnext = nextbase and nextbase.next
+ if nextnext then
+ local nextchar = characters[nextnext]
+ if nextchar then
+ extrachar.next = nextchar
+ nextused = nextslot
+ break
+ end
+ end
+ end
+ end
+ if trace_virtual then
+ if nextused then
+ report_math("extra %U in %a at %p with class %a and name %a maps onto %U with next %U",
+ unicode,basename(properties.fullname),parameters.size,charused,
+ extradesc.mathclass,extradesc.mathname,nextused)
+ else
+ report_math("extra %U in %a at %p with class %a and name %a maps onto %U with no next",
+ unicode,basename(properties.fullname),parameters.size,charused,
+ extradesc.mathclass,extradesc.mathname)
+ end
+ end
+ else
+ if trace_virtual then
+ report_math("extra %U in %a at %p with class %a and name %a maps onto %U with no next", -- own next
+ unicode,basename(properties.fullname),parameters.size,charused,
+ extradesc.mathclass,extradesc.mathname)
+ end
+ end
+ end
+ end
+end
+
+utilities.sequencers.appendaction(mathactions,"system","mathematics.extras.copy")
+
+-- 0xFE302 -- 0xFE320 for accents (gone with new lm/gyre)
+--
+-- extras.add(0xFE302, {
+-- category="mn",
+-- description="WIDE MATHEMATICAL HAT",
+-- direction="nsm",
+-- linebreak="cm",
+-- mathclass="topaccent",
+-- mathname="widehat",
+-- mathstretch="h",
+-- unicodeslot=0xFE302,
+-- nextinsize={ 0x00302, 0x0005E },
+-- } )
+--
+-- extras.add(0xFE303, {
+-- category="mn",
+-- cjkwd="a",
+-- description="WIDE MATHEMATICAL TILDE",
+-- direction="nsm",
+-- linebreak="cm",
+-- mathclass="topaccent",
+-- mathname="widetilde",
+-- mathstretch="h",
+-- unicodeslot=0xFE303,
+-- nextinsize={ 0x00303, 0x0007E },
+-- } )
+
+-- 0xFE321 -- 0xFE340 for missing characters
+
+extras.add(0xFE321, {
+ category="sm",
+ description="MATHEMATICAL SHORT BAR",
+ -- direction="on",
+ -- linebreak="nu",
+ mathclass="relation",
+ mathname="mapstochar",
+ unicodeslot=0xFE321,
+} )
+
+extras.add(0xFE322, {
+ category="sm",
+ description="MATHEMATICAL LEFT HOOK",
+ mathclass="relation",
+ mathname="lhook",
+ unicodeslot=0xFE322,
+} )
+
+extras.add(0xFE323, {
+ category="sm",
+ description="MATHEMATICAL RIGHT HOOK",
+ mathclass="relation",
+ mathname="rhook",
+ unicodeslot=0xFE323,
+} )
+
+extras.add(0xFE324, {
+ category="sm",
+ description="MATHEMATICAL SHORT BAR MIRRORED",
+-- direction="on",
+-- linebreak="nu",
+ mathclass="relation",
+ mathname="mapsfromchar",
+ unicodeslot=0xFE324,
+} )
+
+--~ extras.add(0xFE304, {
+--~ category="sm",
+--~ description="TOP AND BOTTOM PARENTHESES",
+--~ direction="on",
+--~ linebreak="al",
+--~ mathclass="doubleaccent",
+--~ mathname="doubleparent",
+--~ unicodeslot=0xFE304,
+--~ accents={ 0x023DC, 0x023DD },
+--~ } )
+
+--~ extras.add(0xFE305, {
+--~ category="sm",
+--~ description="TOP AND BOTTOM BRACES",
+--~ direction="on",
+--~ linebreak="al",
+--~ mathclass="doubleaccent",
+--~ mathname="doublebrace",
+--~ unicodeslot=0xFE305,
+--~ accents={ 0x023DE, 0x023DF },
+--~ } )
+
+--~ \Umathchardef\braceld="0 "1 "FF07A
+--~ \Umathchardef\bracerd="0 "1 "FF07B
+--~ \Umathchardef\bracelu="0 "1 "FF07C
+--~ \Umathchardef\braceru="0 "1 "FF07D
diff --git a/tex/context/base/math-fbk.lua b/tex/context/base/math-fbk.lua
index f34019b6e..eebc4e4e7 100644
--- a/tex/context/base/math-fbk.lua
+++ b/tex/context/base/math-fbk.lua
@@ -1,312 +1,312 @@
-if not modules then modules = { } end modules ['math-fbk'] = {
- 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"
-}
-
-local trace_fallbacks = false trackers.register("math.fallbacks", function(v) trace_fallbacks = v end)
-
-local report_fallbacks = logs.reporter("math","fallbacks")
-
-local fallbacks = { }
-mathematics.fallbacks = fallbacks
-
-local virtualcharacters = { }
-
-local identifiers = fonts.hashes.identifiers
-local lastmathids = fonts.hashes.lastmathids
-
--- we need a trick (todo): if we define scriptscript, script and text in
--- that order we could use their id's .. i.e. we could always add a font
--- table with those id's .. in fact, we could also add a whole lot more
--- as it doesn't hurt
---
--- todo: use index 'true when luatex provides that feature (on the agenda)
-
-function fallbacks.apply(target,original)
- local mathparameters = target.mathparameters -- why not hasmath
- if mathparameters then
- local characters = target.characters
- local parameters = target.parameters
- local mathsize = parameters.mathsize
- local size = parameters.size
- local usedfonts = target.fonts
- if not usedfonts then
- usedfonts = { }
- target.fonts = usedfonts
- end
- -- This is not okay yet ... we have no proper way to refer to 'self'
- -- otherwise I will make my own id allocator).
-local self = #usedfonts == 0 and font.nextid() or nil -- will be true
- local textid, scriptid, scriptscriptid
- local textindex, scriptindex, scriptscriptindex
- local textdata, scriptdata, scriptscriptdata
- if mathsize == 3 then
- -- scriptscriptsize
- -- textid = nil -- self
- -- scriptid = nil -- no smaller
- -- scriptscriptid = nil -- no smaller
-textid = self
-scriptid = self
-scriptscriptid = self
- elseif mathsize == 2 then
- -- scriptsize
- -- textid = nil -- self
-textid = self
- scriptid = lastmathids[3]
- scriptscriptid = lastmathids[3]
- else
- -- textsize
- -- textid = nil -- self
-textid = self
- scriptid = lastmathids[2]
- scriptscriptid = lastmathids[3]
- end
- if textid then
- textindex = #usedfonts + 1
- usedfonts[textindex] = { id = textid }
- textdata = identifiers[textid]
- else
- textdata = target
- end
- if scriptid then
- scriptindex = #usedfonts + 1
- usedfonts[scriptindex] = { id = scriptid }
- scriptdata = identifiers[scriptid]
- else
- scriptindex = textindex
- scriptdata = textdata
- end
- if scriptscriptid then
- scriptscriptindex = #usedfonts + 1
- usedfonts[scriptscriptindex] = { id = scriptscriptid }
- scriptscriptdata = identifiers[scriptscriptid]
- else
- scriptscriptindex = scriptindex
- scriptscriptdata = scriptdata
- end
--- report_fallbacks("used textid: %s, used script id: %s, used scriptscript id: %s",
--- tostring(textid),tostring(scriptid),tostring(scriptscriptid))
- local data = {
- textdata = textdata,
- scriptdata = scriptdata,
- scriptscriptdata = scriptscriptdata,
- textindex = textindex,
- scriptindex = scriptindex,
- scriptscriptindex = scriptscriptindex,
- characters = characters,
- unicode = k,
- target = target,
- original = original,
- size = size,
- mathsize = mathsize,
- }
--- inspect(usedfonts)
- for k, v in next, virtualcharacters do
- if not characters[k] then
- local tv = type(v)
- if tv == "table" then
- characters[k] = v
- elseif tv == "number" then
- characters[k] = characters[v]
- elseif tv == "function" then
- characters[k] = v(data)
- end
- if trace_fallbacks then
- if characters[k] then
- report_fallbacks("extending font %a with %U",target.properties.fullname,k)
- end
- end
- end
- end
- end
-end
-
-utilities.sequencers.appendaction("aftercopyingcharacters","system","mathematics.fallbacks.apply")
-
-function fallbacks.install(unicode,value)
- virtualcharacters[unicode] = value
-end
-
--- a few examples:
-
-local function reference(index,char)
- if index then
- return { "slot", index, char }
- else
- return { "char", char }
- end
-end
-
-local function raised(data,down)
- local replacement = data.replacement
- local character = data.scriptdata.characters[replacement]
- if character then
- return {
- width = character.width,
- height = character.height,
- depth = character.depth,
- commands = {
- { "down", down and data.size/4 or -data.size/2 }, -- maybe exheight
- reference(data.scriptindex,replacement)
- }
- }
- end
-end
-
--- virtualcharacters[0x207A] = 0x2212
--- virtualcharacters[0x207B] = 0x002B
--- virtualcharacters[0x208A] = 0x2212
--- virtualcharacters[0x208B] = 0x002B
-
-virtualcharacters[0x207A] = function(data)
- data.replacement = 0x2212
- return raised(data)
-end
-
-virtualcharacters[0x207B] = function(data)
- data.replacement = 0x002B
- return raised(data)
-end
-
-virtualcharacters[0x208A] = function(data)
- data.replacement = 0x2212
- return raised(data,true)
-end
-
-virtualcharacters[0x208B] = function(data)
- data.replacement = 0x002B
- return raised(data,true)
-end
-
--- local function repeated(data,char,n,fraction)
--- local character = data.characters[char]
--- if character then
--- local width = character.width
--- local delta = width - character.italic -- width * fraction
--- local c = { "char", char }
--- local r = { "right", right }
--- local commands = { }
--- for i=1,n-1 do
--- width = width + delta
--- commands[#commands+1] = c
--- commands[#commands+1] = -delta
--- end
--- commands[#commands+1] = c
--- return {
--- width = width,
--- height = character.height,
--- depth = character.depth,
--- commands = commands,
--- }
--- end
--- end
-
--- virtualcharacters[0x222C] = function(data)
--- return repeated(data,0x222B,2,1/8)
--- end
-
--- virtualcharacters[0x222D] = function(data)
--- return repeated(data,0x222B,3,1/8)
--- end
-
-local addextra = mathematics.extras.add
-
-addextra(0xFE350, {
- category="sm",
- description="MATHEMATICAL DOUBLE ARROW LEFT END",
- mathclass="relation",
- mathname="ctxdoublearrowfillleftend",
- unicodeslot=0xFE350,
-} )
-
-addextra(0xFE351, {
- category="sm",
- description="MATHEMATICAL DOUBLE ARROW MIDDLE PART",
- mathclass="relation",
- mathname="ctxdoublearrowfillmiddlepart",
- unicodeslot=0xFE351,
-} )
-
-addextra(0xFE352, {
- category="sm",
- description="MATHEMATICAL DOUBLE ARROW RIGHT END",
- mathclass="relation",
- mathname="ctxdoublearrowfillrightend",
- unicodeslot=0xFE352,
-} )
-
-local push = { "push" }
-local pop = { "pop" }
-local leftarrow = { "char", 0x2190 }
-local relbar = { "char", 0x2212 }
-local rightarrow = { "char", 0x2192 }
-
-virtualcharacters[0xFE350] = function(data)
- -- return combined(data,0x2190,0x2212) -- leftarrow relbar
- local charone = data.characters[0x2190]
- local chartwo = data.characters[0x2212]
- if charone and chartwo then
- local size = data.size/2
- return {
- width = chartwo.width,
- height = size,
- depth = size,
- commands = {
- push,
- { "down", size/2 },
- leftarrow,
- pop,
- { "down", -size/2 },
- relbar,
- }
- }
- end
-end
-
-virtualcharacters[0xFE351] = function(data)
- -- return combined(data,0x2212,0x2212) -- relbar, relbar (isn't that just equal)
- local char = data.characters[0x2212]
- if char then
- local size = data.size/2
- return {
- width = char.width,
- height = size,
- depth = size,
- commands = {
- push,
- { "down", size/2 },
- relbar,
- pop,
- { "down", -size/2 },
- relbar,
- }
- }
- end
-end
-
-virtualcharacters[0xFE352] = function(data)
- -- return combined(data,0x2192,0x2212) -- rightarrow relbar
- local charone = data.characters[0x2192]
- local chartwo = data.characters[0x2212]
- if charone and chartwo then
- local size = data.size/2
- return {
- width = chartwo.width,
- height = size,
- depth = size,
- commands = {
- push,
- { "down", size/2 },
- relbar,
- pop,
- { "right", chartwo.width - charone.width },
- { "down", -size/2 },
- rightarrow,
- }
- }
- end
-end
-
+if not modules then modules = { } end modules ['math-fbk'] = {
+ 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"
+}
+
+local trace_fallbacks = false trackers.register("math.fallbacks", function(v) trace_fallbacks = v end)
+
+local report_fallbacks = logs.reporter("math","fallbacks")
+
+local fallbacks = { }
+mathematics.fallbacks = fallbacks
+
+local virtualcharacters = { }
+
+local identifiers = fonts.hashes.identifiers
+local lastmathids = fonts.hashes.lastmathids
+
+-- we need a trick (todo): if we define scriptscript, script and text in
+-- that order we could use their id's .. i.e. we could always add a font
+-- table with those id's .. in fact, we could also add a whole lot more
+-- as it doesn't hurt
+--
+-- todo: use index 'true when luatex provides that feature (on the agenda)
+
+function fallbacks.apply(target,original)
+ local mathparameters = target.mathparameters -- why not hasmath
+ if mathparameters then
+ local characters = target.characters
+ local parameters = target.parameters
+ local mathsize = parameters.mathsize
+ local size = parameters.size
+ local usedfonts = target.fonts
+ if not usedfonts then
+ usedfonts = { }
+ target.fonts = usedfonts
+ end
+ -- This is not okay yet ... we have no proper way to refer to 'self'
+ -- otherwise I will make my own id allocator).
+local self = #usedfonts == 0 and font.nextid() or nil -- will be true
+ local textid, scriptid, scriptscriptid
+ local textindex, scriptindex, scriptscriptindex
+ local textdata, scriptdata, scriptscriptdata
+ if mathsize == 3 then
+ -- scriptscriptsize
+ -- textid = nil -- self
+ -- scriptid = nil -- no smaller
+ -- scriptscriptid = nil -- no smaller
+textid = self
+scriptid = self
+scriptscriptid = self
+ elseif mathsize == 2 then
+ -- scriptsize
+ -- textid = nil -- self
+textid = self
+ scriptid = lastmathids[3]
+ scriptscriptid = lastmathids[3]
+ else
+ -- textsize
+ -- textid = nil -- self
+textid = self
+ scriptid = lastmathids[2]
+ scriptscriptid = lastmathids[3]
+ end
+ if textid then
+ textindex = #usedfonts + 1
+ usedfonts[textindex] = { id = textid }
+ textdata = identifiers[textid]
+ else
+ textdata = target
+ end
+ if scriptid then
+ scriptindex = #usedfonts + 1
+ usedfonts[scriptindex] = { id = scriptid }
+ scriptdata = identifiers[scriptid]
+ else
+ scriptindex = textindex
+ scriptdata = textdata
+ end
+ if scriptscriptid then
+ scriptscriptindex = #usedfonts + 1
+ usedfonts[scriptscriptindex] = { id = scriptscriptid }
+ scriptscriptdata = identifiers[scriptscriptid]
+ else
+ scriptscriptindex = scriptindex
+ scriptscriptdata = scriptdata
+ end
+-- report_fallbacks("used textid: %s, used script id: %s, used scriptscript id: %s",
+-- tostring(textid),tostring(scriptid),tostring(scriptscriptid))
+ local data = {
+ textdata = textdata,
+ scriptdata = scriptdata,
+ scriptscriptdata = scriptscriptdata,
+ textindex = textindex,
+ scriptindex = scriptindex,
+ scriptscriptindex = scriptscriptindex,
+ characters = characters,
+ unicode = k,
+ target = target,
+ original = original,
+ size = size,
+ mathsize = mathsize,
+ }
+-- inspect(usedfonts)
+ for k, v in next, virtualcharacters do
+ if not characters[k] then
+ local tv = type(v)
+ if tv == "table" then
+ characters[k] = v
+ elseif tv == "number" then
+ characters[k] = characters[v]
+ elseif tv == "function" then
+ characters[k] = v(data)
+ end
+ if trace_fallbacks then
+ if characters[k] then
+ report_fallbacks("extending font %a with %U",target.properties.fullname,k)
+ end
+ end
+ end
+ end
+ end
+end
+
+utilities.sequencers.appendaction("aftercopyingcharacters","system","mathematics.fallbacks.apply")
+
+function fallbacks.install(unicode,value)
+ virtualcharacters[unicode] = value
+end
+
+-- a few examples:
+
+local function reference(index,char)
+ if index then
+ return { "slot", index, char }
+ else
+ return { "char", char }
+ end
+end
+
+local function raised(data,down)
+ local replacement = data.replacement
+ local character = data.scriptdata.characters[replacement]
+ if character then
+ return {
+ width = character.width,
+ height = character.height,
+ depth = character.depth,
+ commands = {
+ { "down", down and data.size/4 or -data.size/2 }, -- maybe exheight
+ reference(data.scriptindex,replacement)
+ }
+ }
+ end
+end
+
+-- virtualcharacters[0x207A] = 0x2212
+-- virtualcharacters[0x207B] = 0x002B
+-- virtualcharacters[0x208A] = 0x2212
+-- virtualcharacters[0x208B] = 0x002B
+
+virtualcharacters[0x207A] = function(data)
+ data.replacement = 0x2212
+ return raised(data)
+end
+
+virtualcharacters[0x207B] = function(data)
+ data.replacement = 0x002B
+ return raised(data)
+end
+
+virtualcharacters[0x208A] = function(data)
+ data.replacement = 0x2212
+ return raised(data,true)
+end
+
+virtualcharacters[0x208B] = function(data)
+ data.replacement = 0x002B
+ return raised(data,true)
+end
+
+-- local function repeated(data,char,n,fraction)
+-- local character = data.characters[char]
+-- if character then
+-- local width = character.width
+-- local delta = width - character.italic -- width * fraction
+-- local c = { "char", char }
+-- local r = { "right", right }
+-- local commands = { }
+-- for i=1,n-1 do
+-- width = width + delta
+-- commands[#commands+1] = c
+-- commands[#commands+1] = -delta
+-- end
+-- commands[#commands+1] = c
+-- return {
+-- width = width,
+-- height = character.height,
+-- depth = character.depth,
+-- commands = commands,
+-- }
+-- end
+-- end
+
+-- virtualcharacters[0x222C] = function(data)
+-- return repeated(data,0x222B,2,1/8)
+-- end
+
+-- virtualcharacters[0x222D] = function(data)
+-- return repeated(data,0x222B,3,1/8)
+-- end
+
+local addextra = mathematics.extras.add
+
+addextra(0xFE350, {
+ category="sm",
+ description="MATHEMATICAL DOUBLE ARROW LEFT END",
+ mathclass="relation",
+ mathname="ctxdoublearrowfillleftend",
+ unicodeslot=0xFE350,
+} )
+
+addextra(0xFE351, {
+ category="sm",
+ description="MATHEMATICAL DOUBLE ARROW MIDDLE PART",
+ mathclass="relation",
+ mathname="ctxdoublearrowfillmiddlepart",
+ unicodeslot=0xFE351,
+} )
+
+addextra(0xFE352, {
+ category="sm",
+ description="MATHEMATICAL DOUBLE ARROW RIGHT END",
+ mathclass="relation",
+ mathname="ctxdoublearrowfillrightend",
+ unicodeslot=0xFE352,
+} )
+
+local push = { "push" }
+local pop = { "pop" }
+local leftarrow = { "char", 0x2190 }
+local relbar = { "char", 0x2212 }
+local rightarrow = { "char", 0x2192 }
+
+virtualcharacters[0xFE350] = function(data)
+ -- return combined(data,0x2190,0x2212) -- leftarrow relbar
+ local charone = data.characters[0x2190]
+ local chartwo = data.characters[0x2212]
+ if charone and chartwo then
+ local size = data.size/2
+ return {
+ width = chartwo.width,
+ height = size,
+ depth = size,
+ commands = {
+ push,
+ { "down", size/2 },
+ leftarrow,
+ pop,
+ { "down", -size/2 },
+ relbar,
+ }
+ }
+ end
+end
+
+virtualcharacters[0xFE351] = function(data)
+ -- return combined(data,0x2212,0x2212) -- relbar, relbar (isn't that just equal)
+ local char = data.characters[0x2212]
+ if char then
+ local size = data.size/2
+ return {
+ width = char.width,
+ height = size,
+ depth = size,
+ commands = {
+ push,
+ { "down", size/2 },
+ relbar,
+ pop,
+ { "down", -size/2 },
+ relbar,
+ }
+ }
+ end
+end
+
+virtualcharacters[0xFE352] = function(data)
+ -- return combined(data,0x2192,0x2212) -- rightarrow relbar
+ local charone = data.characters[0x2192]
+ local chartwo = data.characters[0x2212]
+ if charone and chartwo then
+ local size = data.size/2
+ return {
+ width = chartwo.width,
+ height = size,
+ depth = size,
+ commands = {
+ push,
+ { "down", size/2 },
+ relbar,
+ pop,
+ { "right", chartwo.width - charone.width },
+ { "down", -size/2 },
+ rightarrow,
+ }
+ }
+ end
+end
+
diff --git a/tex/context/base/math-frc.lua b/tex/context/base/math-frc.lua
index 077da643b..4f531a530 100644
--- a/tex/context/base/math-frc.lua
+++ b/tex/context/base/math-frc.lua
@@ -1,51 +1,51 @@
-if not modules then modules = { } end modules ['math-frc'] = {
- version = 1.001,
- comment = "companion to math-frc.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local utfchar = utf.char
-
-local context = context
-local variables = interfaces.variables
-
-local v_no = variables.no
-local v_yes = variables.yes
-
-local resolved = {
- [0x007B] = "\\{",
- [0x007D] = "\\}",
-}
-
-table.setmetatableindex(resolved, function(t,k)
- local v = utfchar(k)
- t[k] = v
- return v
-end)
-
-local normalatop = context.normalatop
-local normalover = context.normalover
-
-function commands.math_frac(how,left,right,width)
- if how == v_no then
- if left == 0x002E and right == 0x002E then
- normalatop()
- else
- context("\\atopwithdelims%s%s",resolved[left],resolved[right])
- end
- elseif how == v_yes then
- if left == 0x002E and right == 0x002E then
- context("\\normalabove%ssp",width)
- else
- context("\\abovewithdelims%s%s%ssp",resolved[left],resolved[right],width)
- end
- else -- v_auto
- if left == 0x002E and right == 0x002E then
- normalover()
- else
- context("\\overwithdelims%s%s",resolved[left],resolved[right])
- end
- end
-end
+if not modules then modules = { } end modules ['math-frc'] = {
+ version = 1.001,
+ comment = "companion to math-frc.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local utfchar = utf.char
+
+local context = context
+local variables = interfaces.variables
+
+local v_no = variables.no
+local v_yes = variables.yes
+
+local resolved = {
+ [0x007B] = "\\{",
+ [0x007D] = "\\}",
+}
+
+table.setmetatableindex(resolved, function(t,k)
+ local v = utfchar(k)
+ t[k] = v
+ return v
+end)
+
+local normalatop = context.normalatop
+local normalover = context.normalover
+
+function commands.math_frac(how,left,right,width)
+ if how == v_no then
+ if left == 0x002E and right == 0x002E then
+ normalatop()
+ else
+ context("\\atopwithdelims%s%s",resolved[left],resolved[right])
+ end
+ elseif how == v_yes then
+ if left == 0x002E and right == 0x002E then
+ context("\\normalabove%ssp",width)
+ else
+ context("\\abovewithdelims%s%s%ssp",resolved[left],resolved[right],width)
+ end
+ else -- v_auto
+ if left == 0x002E and right == 0x002E then
+ normalover()
+ else
+ context("\\overwithdelims%s%s",resolved[left],resolved[right])
+ end
+ end
+end
diff --git a/tex/context/base/math-map.lua b/tex/context/base/math-map.lua
index 51e0f6831..a0d7457d1 100644
--- a/tex/context/base/math-map.lua
+++ b/tex/context/base/math-map.lua
@@ -1,684 +1,684 @@
-if not modules then modules = { } end modules ['math-map'] = {
- 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: make sparse .. if self
-
---[[ldx--
-<p>Remapping mathematics alphabets.</p>
---ldx]]--
-
--- oldstyle: not really mathematics but happened to be part of
--- the mathematics fonts in cmr
---
--- persian: we will also provide mappers for other
--- scripts
-
--- todo: alphabets namespace
--- maybe: script/scriptscript dynamic,
-
--- to be looked into once the fonts are ready (will become font
--- goodie):
---
--- (U+2202,U+1D715) : upright
--- (U+2202,U+1D715) : italic
--- (U+2202,U+1D715) : upright
---
--- plus add them to the regular vectors below so that they honor \it etc
-
-local type, next = type, next
-local floor, div = math.floor, math.div
-local merged = table.merged
-local extract = bit32.extract
-
-local allocate = utilities.storage.allocate
-local texattribute = tex.attribute
-local otffeatures = fonts.constructors.newfeatures("otf")
-local registerotffeature = otffeatures.register
-local setmetatableindex = table.setmetatableindex
-
-local trace_greek = false trackers.register("math.greek", function(v) trace_greek = v end)
-local report_remapping = logs.reporter("mathematics","remapping")
-
-mathematics = mathematics or { }
-local mathematics = mathematics
-
--- Unfortunately some alphabets have gaps (thereby troubling all applications that
--- need to deal with math). Somewhat strange considering all those weird symbols that
--- were added afterwards. The following trickery (and data) is only to be used for
--- diagnostics and quick and dirty alphabet tracing (s-mat-10.mkiv) as we deal with
--- it otherwise.
-
-mathematics.gaps = {
- [0x1D455] = 0x0210E, -- H
- [0x1D49D] = 0x0212C, -- script B
- [0x1D4A0] = 0x02130, -- script E
- [0x1D4A1] = 0x02131, -- script F
- [0x1D4A3] = 0x0210B, -- script H
- [0x1D4A4] = 0x02110, -- script I
- [0x1D4A7] = 0x02112, -- script L
- [0x1D4A8] = 0x02133, -- script M
- [0x1D4AD] = 0x0211B, -- script R
- [0x1D4BA] = 0x0212F, -- script e
- [0x1D4BC] = 0x0210A, -- script g
- [0x1D4C4] = 0x02134, -- script o
- [0x1D506] = 0x0212D, -- fraktur C
- [0x1D50B] = 0x0210C, -- fraktur H
- [0x1D50C] = 0x02111, -- fraktur I
- [0x1D515] = 0x0211C, -- fraktur R
- [0x1D51D] = 0x02128, -- fraktur Z
- [0x1D53A] = 0x02102, -- bb C
- [0x1D53F] = 0x0210D, -- bb H
- [0x1D545] = 0x02115, -- bb N
- [0x1D547] = 0x02119, -- bb P
- [0x1D548] = 0x0211A, -- bb Q
- [0x1D549] = 0x0211D, -- bb R
- [0x1D551] = 0x02124, -- bb Z
-}
-
-local function fillinmathgaps(tfmdata,key,value)
- local mathgaps = mathematics.gaps
- local characters = tfmdata.characters
- local descriptions = tfmdata.descriptions
- for gap, original in next, mathgaps do
- if characters[original] and not characters[gap] then
- characters [gap] = characters [original]
- descriptions[gap] = descriptions[original]
- end
- end
-end
-
-registerotffeature {
- name = "mathgaps",
- description = "plug gaps in math alphabets",
- comment = "regular document sources should not depend on this",
- manipulators = {
- base = fillinmathgaps,
- node = fillinmathgaps,
- }
-}
-
--- we could use one level less and have tf etc be tables directly but the
--- following approach permits easier remapping of a-a, A-Z and 0-9 to
--- fallbacks; symbols is currently mostly greek
-
-local function todigit(n) local t = { } for i=0, 9 do t[0x00030+i] = n+i end return t end
-local function toupper(n) local t = { } for i=0,25 do t[0x00041+i] = n+i end return t end
-local function tolower(n) local t = { } for i=0,25 do t[0x00061+i] = n+i end return t end
-
-local regular_tf = {
- digits = todigit(0x00030),
- ucletters = toupper(0x00041),
- lcletters = tolower(0x00061),
- ucgreek = {
- [0x0391]=0x0391, [0x0392]=0x0392, [0x0393]=0x0393, [0x0394]=0x0394, [0x0395]=0x0395,
- [0x0396]=0x0396, [0x0397]=0x0397, [0x0398]=0x0398, [0x0399]=0x0399, [0x039A]=0x039A,
- [0x039B]=0x039B, [0x039C]=0x039C, [0x039D]=0x039D, [0x039E]=0x039E, [0x039F]=0x039F,
- [0x03A0]=0x03A0, [0x03A1]=0x03A1, [0x03A3]=0x03A3, [0x03A4]=0x03A4, [0x03A5]=0x03A5,
- [0x03A6]=0x03A6, [0x03A7]=0x03A7, [0x03A8]=0x03A8, [0x03A9]=0x03A9,
- },
- lcgreek = {
- [0x03B1]=0x03B1, [0x03B2]=0x03B2, [0x03B3]=0x03B3, [0x03B4]=0x03B4, [0x03B5]=0x03B5,
- [0x03B6]=0x03B6, [0x03B7]=0x03B7, [0x03B8]=0x03B8, [0x03B9]=0x03B9, [0x03BA]=0x03BA,
- [0x03BB]=0x03BB, [0x03BC]=0x03BC, [0x03BD]=0x03BD, [0x03BE]=0x03BE, [0x03BF]=0x03BF,
- [0x03C0]=0x03C0, [0x03C1]=0x03C1, [0x03C2]=0x03C2, [0x03C3]=0x03C3, [0x03C4]=0x03C4,
- [0x03C5]=0x03C5, [0x03C6]=0x03C6, [0x03C7]=0x03C7, [0x03C8]=0x03C8, [0x03C9]=0x03C9,
- [0x03D1]=0x03D1, [0x03D5]=0x03D5, [0x03D6]=0x03D6, [0x03F0]=0x03F0, [0x03F1]=0x03F1,
- [0x03F4]=0x03F4, [0x03F5]=0x03F5,
- },
- symbols = {
- [0x2202]=0x2202, [0x2207]=0x2207,
- },
-}
-
-local regular_it = {
- digits = regular_tf.digits,
- ucletters = toupper(0x1D434),
- lcletters = { -- H
- [0x00061]=0x1D44E, [0x00062]=0x1D44F, [0x00063]=0x1D450, [0x00064]=0x1D451, [0x00065]=0x1D452,
- [0x00066]=0x1D453, [0x00067]=0x1D454, [0x00068]=0x0210E, [0x00069]=0x1D456, [0x0006A]=0x1D457,
- [0x0006B]=0x1D458, [0x0006C]=0x1D459, [0x0006D]=0x1D45A, [0x0006E]=0x1D45B, [0x0006F]=0x1D45C,
- [0x00070]=0x1D45D, [0x00071]=0x1D45E, [0x00072]=0x1D45F, [0x00073]=0x1D460, [0x00074]=0x1D461,
- [0x00075]=0x1D462, [0x00076]=0x1D463, [0x00077]=0x1D464, [0x00078]=0x1D465, [0x00079]=0x1D466,
- [0x0007A]=0x1D467,
- },
- ucgreek = {
- [0x0391]=0x1D6E2, [0x0392]=0x1D6E3, [0x0393]=0x1D6E4, [0x0394]=0x1D6E5, [0x0395]=0x1D6E6,
- [0x0396]=0x1D6E7, [0x0397]=0x1D6E8, [0x0398]=0x1D6E9, [0x0399]=0x1D6EA, [0x039A]=0x1D6EB,
- [0x039B]=0x1D6EC, [0x039C]=0x1D6ED, [0x039D]=0x1D6EE, [0x039E]=0x1D6EF, [0x039F]=0x1D6F0,
- [0x03A0]=0x1D6F1, [0x03A1]=0x1D6F2, [0x03A3]=0x1D6F4, [0x03A4]=0x1D6F5, [0x03A5]=0x1D6F6,
- [0x03A6]=0x1D6F7, [0x03A7]=0x1D6F8, [0x03A8]=0x1D6F9, [0x03A9]=0x1D6FA,
- },
- lcgreek = {
- [0x03B1]=0x1D6FC, [0x03B2]=0x1D6FD, [0x03B3]=0x1D6FE, [0x03B4]=0x1D6FF, [0x03B5]=0x1D700,
- [0x03B6]=0x1D701, [0x03B7]=0x1D702, [0x03B8]=0x1D703, [0x03B9]=0x1D704, [0x03BA]=0x1D705,
- [0x03BB]=0x1D706, [0x03BC]=0x1D707, [0x03BD]=0x1D708, [0x03BE]=0x1D709, [0x03BF]=0x1D70A,
- [0x03C0]=0x1D70B, [0x03C1]=0x1D70C, [0x03C2]=0x1D70D, [0x03C3]=0x1D70E, [0x03C4]=0x1D70F,
- [0x03C5]=0x1D710, [0x03C6]=0x1D711, [0x03C7]=0x1D712, [0x03C8]=0x1D713, [0x03C9]=0x1D714,
- [0x03D1]=0x1D717, [0x03D5]=0x1D719, [0x03D6]=0x1D71B, [0x03F0]=0x1D718, [0x03F1]=0x1D71A,
- [0x03F4]=0x1D6F3, [0x03F5]=0x1D716,
- },
- symbols = {
- [0x2202]=0x1D715, [0x2207]=0x1D6FB,
- },
-}
-
-local regular_bf= {
- digits = todigit(0x1D7CE),
- ucletters = toupper(0x1D400),
- lcletters = tolower(0x1D41A),
- ucgreek = {
- [0x0391]=0x1D6A8, [0x0392]=0x1D6A9, [0x0393]=0x1D6AA, [0x0394]=0x1D6AB, [0x0395]=0x1D6AC,
- [0x0396]=0x1D6AD, [0x0397]=0x1D6AE, [0x0398]=0x1D6AF, [0x0399]=0x1D6B0, [0x039A]=0x1D6B1,
- [0x039B]=0x1D6B2, [0x039C]=0x1D6B3, [0x039D]=0x1D6B4, [0x039E]=0x1D6B5, [0x039F]=0x1D6B6,
- [0x03A0]=0x1D6B7, [0x03A1]=0x1D6B8, [0x03A3]=0x1D6BA, [0x03A4]=0x1D6BB, [0x03A5]=0x1D6BC,
- [0x03A6]=0x1D6BD, [0x03A7]=0x1D6BE, [0x03A8]=0x1D6BF, [0x03A9]=0x1D6C0,
- },
- lcgreek = {
- [0x03B1]=0x1D6C2, [0x03B2]=0x1D6C3, [0x03B3]=0x1D6C4, [0x03B4]=0x1D6C5, [0x03B5]=0x1D6C6,
- [0x03B6]=0x1D6C7, [0x03B7]=0x1D6C8, [0x03B8]=0x1D6C9, [0x03B9]=0x1D6CA, [0x03BA]=0x1D6CB,
- [0x03BB]=0x1D6CC, [0x03BC]=0x1D6CD, [0x03BD]=0x1D6CE, [0x03BE]=0x1D6CF, [0x03BF]=0x1D6D0,
- [0x03C0]=0x1D6D1, [0x03C1]=0x1D6D2, [0x03C2]=0x1D6D3, [0x03C3]=0x1D6D4, [0x03C4]=0x1D6D5,
- [0x03C5]=0x1D6D6, [0x03C6]=0x1D6D7, [0x03C7]=0x1D6D8, [0x03C8]=0x1D6D9, [0x03C9]=0x1D6DA,
- [0x03D1]=0x1D6DD, [0x03D5]=0x1D6DF, [0x03D6]=0x1D6E1, [0x03F0]=0x1D6DE, [0x03F1]=0x1D6E0,
- [0x03F4]=0x1D6B9, [0x03F5]=0x1D6DC,
- },
- symbols = {
- [0x2202]=0x1D6DB, [0x2207]=0x1D6C1,
- },
-}
-
-local regular_bi = {
- digits = regular_bf.digits,
- ucletters = toupper(0x1D468),
- lcletters = tolower(0x1D482),
- ucgreek = {
- [0x0391]=0x1D71C, [0x0392]=0x1D71D, [0x0393]=0x1D71E, [0x0394]=0x1D71F, [0x0395]=0x1D720,
- [0x0396]=0x1D721, [0x0397]=0x1D722, [0x0398]=0x1D723, [0x0399]=0x1D724, [0x039A]=0x1D725,
- [0x039B]=0x1D726, [0x039C]=0x1D727, [0x039D]=0x1D728, [0x039E]=0x1D729, [0x039F]=0x1D72A,
- [0x03A0]=0x1D72B, [0x03A1]=0x1D72C, [0x03A3]=0x1D72E, [0x03A4]=0x1D72F, [0x03A5]=0x1D730,
- [0x03A6]=0x1D731, [0x03A7]=0x1D732, [0x03A8]=0x1D733, [0x03A9]=0x1D734,
- },
- lcgreek = {
- [0x03B1]=0x1D736, [0x03B2]=0x1D737, [0x03B3]=0x1D738, [0x03B4]=0x1D739, [0x03B5]=0x1D73A,
- [0x03B6]=0x1D73B, [0x03B7]=0x1D73C, [0x03B8]=0x1D73D, [0x03B9]=0x1D73E, [0x03BA]=0x1D73F,
- [0x03BB]=0x1D740, [0x03BC]=0x1D741, [0x03BD]=0x1D742, [0x03BE]=0x1D743, [0x03BF]=0x1D744,
- [0x03C0]=0x1D745, [0x03C1]=0x1D746, [0x03C2]=0x1D747, [0x03C3]=0x1D748, [0x03C4]=0x1D749,
- [0x03C5]=0x1D74A, [0x03C6]=0x1D74B, [0x03C7]=0x1D74C, [0x03C8]=0x1D74D, [0x03C9]=0x1D74E,
- [0x03D1]=0x1D751, [0x03D5]=0x1D753, [0x03D6]=0x1D755, [0x03F0]=0x1D752, [0x03F1]=0x1D754,
- [0x03F4]=0x1D72D, [0x03F5]=0x1D750,
- },
- symbols = {
- [0x2202]=0x1D74F, [0x2207]=0x1D735,
- },
-}
-
-local regular = {
- tf = regular_tf,
- it = regular_it,
- bf = regular_bf,
- bi = regular_bi,
-}
-
-local sansserif_tf = {
- digits = todigit(0x1D7E2),
- ucletters = toupper(0x1D5A0),
- lcletters = tolower(0x1D5BA),
- lcgreek = regular_tf.lcgreek,
- ucgreek = regular_tf.ucgreek,
- symbols = regular_tf.symbols,
-}
-
-local sansserif_it = {
- digits = regular_tf.digits,
- ucletters = toupper(0x1D608),
- lcletters = tolower(0x1D622),
- lcgreek = regular_tf.lcgreek,
- ucgreek = regular_tf.ucgreek,
- symbols = regular_tf.symbols,
-}
-
-local sansserif_bf = {
- digits = todigit(0x1D7EC),
- ucletters = toupper(0x1D5D4),
- lcletters = tolower(0x1D5EE),
- ucgreek = {
- [0x0391]=0x1D756, [0x0392]=0x1D757, [0x0393]=0x1D758, [0x0394]=0x1D759, [0x0395]=0x1D75A,
- [0x0396]=0x1D75B, [0x0397]=0x1D75C, [0x0398]=0x1D75D, [0x0399]=0x1D75E, [0x039A]=0x1D75F,
- [0x039B]=0x1D760, [0x039C]=0x1D761, [0x039D]=0x1D762, [0x039E]=0x1D763, [0x039F]=0x1D764,
- [0x03A0]=0x1D765, [0x03A1]=0x1D766, [0x03A3]=0x1D768, [0x03A4]=0x1D769, [0x03A5]=0x1D76A,
- [0x03A6]=0x1D76B, [0x03A7]=0x1D76C, [0x03A8]=0x1D76D, [0x03A9]=0x1D76E,
- },
- lcgreek = {
- [0x03B1]=0x1D770, [0x03B2]=0x1D771, [0x03B3]=0x1D772, [0x03B4]=0x1D773, [0x03B5]=0x1D774,
- [0x03B6]=0x1D775, [0x03B7]=0x1D776, [0x03B8]=0x1D777, [0x03B9]=0x1D778, [0x03BA]=0x1D779,
- [0x03BB]=0x1D77A, [0x03BC]=0x1D77B, [0x03BD]=0x1D77C, [0x03BE]=0x1D77D, [0x03BF]=0x1D77E,
- [0x03C0]=0x1D77F, [0x03C1]=0x1D780, [0x03C2]=0x1D781, [0x03C3]=0x1D782, [0x03C4]=0x1D783,
- [0x03C5]=0x1D784, [0x03C6]=0x1D785, [0x03C7]=0x1D786, [0x03C8]=0x1D787, [0x03C9]=0x1D788,
- [0x03D1]=0x1D78B, [0x03D5]=0x1D78D, [0x03D6]=0x1D78F, [0x03F0]=0x1D78C, [0x03F1]=0x1D78E,
- [0x03F4]=0x1D767, [0x03F5]=0x1D78A,
- },
- symbols = {
- [0x2202]=0x1D789, [0x2207]=0x1D76F,
- },
-}
-
-local sansserif_bi = {
- digits = sansserif_bf.digits,
- ucletters = toupper(0x1D63C),
- lcletters = tolower(0x1D656),
- ucgreek = {
- [0x0391]=0x1D790, [0x0392]=0x1D791, [0x0393]=0x1D792, [0x0394]=0x1D793, [0x0395]=0x1D794,
- [0x0396]=0x1D795, [0x0397]=0x1D796, [0x0398]=0x1D797, [0x0399]=0x1D798, [0x039A]=0x1D799,
- [0x039B]=0x1D79A, [0x039C]=0x1D79B, [0x039D]=0x1D79C, [0x039E]=0x1D79D, [0x039F]=0x1D79E,
- [0x03A0]=0x1D79F, [0x03A1]=0x1D7A0, [0x03A3]=0x1D7A2, [0x03A4]=0x1D7A3, [0x03A5]=0x1D7A4,
- [0x03A6]=0x1D7A5, [0x03A7]=0x1D7A6, [0x03A8]=0x1D7A7, [0x03A9]=0x1D7A8,
- },
- lcgreek = {
- [0x03B1]=0x1D7AA, [0x03B2]=0x1D7AB, [0x03B3]=0x1D7AC, [0x03B4]=0x1D7AD, [0x03B5]=0x1D7AE,
- [0x03B6]=0x1D7AF, [0x03B7]=0x1D7B0, [0x03B8]=0x1D7B1, [0x03B9]=0x1D7B2, [0x03BA]=0x1D7B3,
- [0x03BB]=0x1D7B4, [0x03BC]=0x1D7B5, [0x03BD]=0x1D7B6, [0x03BE]=0x1D7B7, [0x03BF]=0x1D7B8,
- [0x03C0]=0x1D7B9, [0x03C1]=0x1D7BA, [0x03C2]=0x1D7BB, [0x03C3]=0x1D7BC, [0x03C4]=0x1D7BD,
- [0x03C5]=0x1D7BE, [0x03C6]=0x1D7BF, [0x03C7]=0x1D7C0, [0x03C8]=0x1D7C1, [0x03C9]=0x1D7C2,
- [0x03D1]=0x1D7C5, [0x03D5]=0x1D7C7, [0x03D6]=0x1D7C9, [0x03F0]=0x1D7C6, [0x03F1]=0x1D7C8,
- [0x03F4]=0x1D7A1, [0x03F5]=0x1D7C4,
- },
- symbols = {
- [0x2202]=0x1D7C3, [0x2207]=0x1D7A9,
- },
-}
-
-local sansserif = {
- tf = sansserif_tf,
- it = sansserif_it,
- bf = sansserif_bf,
- bi = sansserif_bi,
-}
-
-local monospaced_tf = {
- digits = todigit(0x1D7F6),
- ucletters = toupper(0x1D670),
- lcletters = tolower(0x1D68A),
- lcgreek = sansserif_tf.lcgreek,
- ucgreek = sansserif_tf.ucgreek,
- symbols = sansserif_tf.symbols,
-}
-
-local monospaced = {
- tf = monospaced_tf,
- it = sansserif_tf,
- bf = sansserif_tf,
- bi = sansserif_bf,
-}
-
-local blackboard_tf = {
- digits = todigit(0x1D7D8),
- ucletters = { -- C H N P Q R Z
- [0x00041]=0x1D538, [0x00042]=0x1D539, [0x00043]=0x02102, [0x00044]=0x1D53B, [0x00045]=0x1D53C,
- [0x00046]=0x1D53D, [0x00047]=0x1D53E, [0x00048]=0x0210D, [0x00049]=0x1D540, [0x0004A]=0x1D541,
- [0x0004B]=0x1D542, [0x0004C]=0x1D543, [0x0004D]=0x1D544, [0x0004E]=0x02115, [0x0004F]=0x1D546,
- [0x00050]=0x02119, [0x00051]=0x0211A, [0x00052]=0x0211D, [0x00053]=0x1D54A, [0x00054]=0x1D54B,
- [0x00055]=0x1D54C, [0x00056]=0x1D54D, [0x00057]=0x1D54E, [0x00058]=0x1D54F, [0x00059]=0x1D550,
- [0x0005A]=0x02124,
- },
- lcletters = tolower(0x1D552),
- lcgreek = { -- gamma pi
- [0x03B3]=0x0213C, [0x03C0]=0x0213D,
- },
- ucgreek = { -- Gamma pi
- [0x0393]=0x0213E, [0x03A0]=0x0213F,
- },
- symbols = { -- sum
- [0x2211]=0x02140,
- },
-}
-
-blackboard_tf.lcgreek = merged(regular_tf.lcgreek, blackboard_tf.lcgreek)
-blackboard_tf.ucgreek = merged(regular_tf.ucgreek, blackboard_tf.ucgreek)
-blackboard_tf.symbols = merged(regular_tf.symbols, blackboard_tf.symbols)
-
-local blackboard = {
- tf = blackboard_tf,
- it = blackboard_tf,
- bf = blackboard_tf,
- bi = blackboard_tf,
-}
-
-local fraktur_tf= {
- digits = regular_tf.digits,
- ucletters = { -- C H I R Z
- [0x00041]=0x1D504, [0x00042]=0x1D505, [0x00043]=0x0212D, [0x00044]=0x1D507, [0x00045]=0x1D508,
- [0x00046]=0x1D509, [0x00047]=0x1D50A, [0x00048]=0x0210C, [0x00049]=0x02111, [0x0004A]=0x1D50D,
- [0x0004B]=0x1D50E, [0x0004C]=0x1D50F, [0x0004D]=0x1D510, [0x0004E]=0x1D511, [0x0004F]=0x1D512,
- [0x00050]=0x1D513, [0x00051]=0x1D514, [0x00052]=0x0211C, [0x00053]=0x1D516, [0x00054]=0x1D517,
- [0x00055]=0x1D518, [0x00056]=0x1D519, [0x00057]=0x1D51A, [0x00058]=0x1D51B, [0x00059]=0x1D51C,
- [0x0005A]=0x02128,
- },
- lcletters = tolower(0x1D51E),
- lcgreek = regular_tf.lcgreek,
- ucgreek = regular_tf.ucgreek,
- symbols = regular_tf.symbols,
-}
-
-local fraktur_bf = {
- digits = regular_bf.digits,
- ucletters = toupper(0x1D56C),
- lcletters = tolower(0x1D586),
- lcgreek = regular_bf.lcgreek,
- ucgreek = regular_bf.ucgreek,
- symbols = regular_bf.symbols,
-}
-
-local fraktur = { -- ok
- tf = fraktur_tf,
- bf = fraktur_bf,
- it = fraktur_tf,
- bi = fraktur_bf,
-}
-
-local script_tf = {
- digits = regular_tf.digits,
- ucletters = { -- B E F H I L M R -- P 2118
- [0x00041]=0x1D49C, [0x00042]=0x0212C, [0x00043]=0x1D49E, [0x00044]=0x1D49F, [0x00045]=0x02130,
- [0x00046]=0x02131, [0x00047]=0x1D4A2, [0x00048]=0x0210B, [0x00049]=0x02110, [0x0004A]=0x1D4A5,
- [0x0004B]=0x1D4A6, [0x0004C]=0x02112, [0x0004D]=0x02133, [0x0004E]=0x1D4A9, [0x0004F]=0x1D4AA,
- [0x00050]=0x1D4AB, [0x00051]=0x1D4AC, [0x00052]=0x0211B, [0x00053]=0x1D4AE, [0x00054]=0x1D4AF,
- [0x00055]=0x1D4B0, [0x00056]=0x1D4B1, [0x00057]=0x1D4B2, [0x00058]=0x1D4B3, [0x00059]=0x1D4B4,
- [0x0005A]=0x1D4B5,
- },
- lcletters = { -- E G O -- L 2113
- [0x00061]=0x1D4B6, [0x00062]=0x1D4B7, [0x00063]=0x1D4B8, [0x00064]=0x1D4B9, [0x00065]=0x0212F,
- [0x00066]=0x1D4BB, [0x00067]=0x0210A, [0x00068]=0x1D4BD, [0x00069]=0x1D4BE, [0x0006A]=0x1D4BF,
- [0x0006B]=0x1D4C0, [0x0006C]=0x1D4C1, [0x0006D]=0x1D4C2, [0x0006E]=0x1D4C3, [0x0006F]=0x02134,
- [0x00070]=0x1D4C5, [0x00071]=0x1D4C6, [0x00072]=0x1D4C7, [0x00073]=0x1D4C8, [0x00074]=0x1D4C9,
- [0x00075]=0x1D4CA, [0x00076]=0x1D4CB, [0x00077]=0x1D4CC, [0x00078]=0x1D4CD, [0x00079]=0x1D4CE,
- [0x0007A]=0x1D4CF,
- },
- lcgreek = regular_tf.lcgreek,
- ucgreek = regular_tf.ucgreek,
- symbols = regular_tf.symbols,
-}
-
-local script_bf = {
- digits = regular_bf.digits,
- ucletters = toupper(0x1D4D0),
- lcletters = tolower(0x1D4EA),
- lcgreek = regular_bf.lcgreek,
- ucgreek = regular_bf.ucgreek,
- symbols = regular_bf.symbols,
-}
-
-local script = {
- tf = script_tf,
- bf = script_bf,
- it = script_tf,
- bi = script_bf,
-}
-
-local alphabets = allocate {
- regular = regular,
- sansserif = sansserif,
- monospaced = monospaced,
- blackboard = blackboard,
- fraktur = fraktur,
- script = script,
-}
-
-mathematics.alphabets = alphabets
-
-local boldmap = { }
-
-local function remap(tf,bf)
- for _, alphabet in next, alphabets do
- local tfdata = alphabet[tf]
- local bfdata = alphabet[bf]
- if tfdata then
- for k, tfd in next, tfdata do
- if type(tfd) == "table" then
- local bfd = bfdata[k]
- if bfd then
- for n, u in next, tfd do
- local bn = bfd[n]
- if bn then
- boldmap[u] = bn
- end
- end
- end
- end
- end
- end
- end
-end
-
-remap("tf","bf")
-remap("it","bi")
-
-mathematics.boldmap = boldmap
-
-local mathremap = allocate { }
-
-for alphabet, styles in next, alphabets do -- per 9/6/2011 we also have attr for missing
- for style, data in next, styles do
- -- let's keep the long names (for tracing)
- local n = #mathremap + 1
- data.attribute = n
- data.alphabet = alphabet
- data.style = style
- mathremap[n] = data
- end
-end
-
-mathematics.mapremap = mathremap
-
--- beware, these are shared tables (no problem since they're not
--- in unicode)
-
-alphabets.tt = monospaced
-alphabets.ss = sansserif
-alphabets.rm = regular
-alphabets.bb = blackboard
-alphabets.fr = fraktur
-alphabets.sr = script
-
-alphabets.serif = regular
-alphabets.type = monospaced
-alphabets.teletype = monospaced
-
-regular.normal = regular_tf
-regular.italic = regular_it
-regular.bold = regular_bf
-regular.bolditalic = regular_bi
-
-sansserif.normal = sansserif_tf
-sansserif.italic = sansserif_it
-sansserif.bold = sansserif_bf
-sansserif.bolditalic = sansserif_bi
-
-monospaced.normal = monospaced_tf
-monospaced.italic = monospaced_it
-monospaced.bold = monospaced_bf
-monospaced.bolditalic = monospaced_bi
-
-function mathematics.tostyle(attribute)
- local r = mathremap[attribute]
- return r and r.style or "tf"
-end
-
-function mathematics.toname(attribute)
- local r = mathremap[attribute]
- return r and r.alphabet or "regular"
-end
-
--- of course we could do some div/mod trickery instead
-
-local mathalphabet = attributes.private("mathalphabet")
-
-function mathematics.getboth(alphabet,style)
- local data = alphabet and alphabets[alphabet] or regular
- data = data[style or "tf"] or data.tf
- return data and data.attribute
-end
-
-function mathematics.getstyle(style)
- local r = mathremap[texattribute[mathalphabet]]
- local alphabet = r and r.alphabet or "regular"
- local data = alphabets[alphabet][style]
- return data and data.attribute
-end
-
-function mathematics.syncboth(alphabet,style)
- local data = alphabet and alphabets[alphabet] or regular
- data = style and data[style] or data.tf
- texattribute[mathalphabet] = data and data.attribute or texattribute[mathalphabet]
-end
-
-function mathematics.syncstyle(style)
- local r = mathremap[texattribute[mathalphabet]]
- local alphabet = r and r.alphabet or "regular"
- local data = alphabets[alphabet][style]
- texattribute[mathalphabet] = data and data.attribute or texattribute[mathalphabet]
-end
-
-function mathematics.syncname(alphabet)
- -- local r = mathremap[mathalphabet]
- local r = mathremap[texattribute[mathalphabet]]
- local style = r and r.style or "tf"
- local data = alphabets[alphabet][style]
- texattribute[mathalphabet] = data and data.attribute or texattribute[mathalphabet]
-end
-
-local islcgreek = regular_tf.lcgreek
-local isucgreek = regular_tf.ucgreek
-local issygreek = regular_tf.symbols
-local isgreek = merged(islcgreek,isucgreek,issygreek)
-
-local greekremapping = {
- [1] = { what = "unchanged" }, -- upright
- [2] = { what = "upright", it = "tf", bi = "bf" }, -- upright
- [3] = { what = "italic", tf = "it", bf = "bi" }, -- italic
-}
-
-local usedremap = { }
-
-local function resolver(map)
- return function (t,k)
- local v =
- map.digits [k] or
- map.lcletters[k] or map.ucletters[k] or
- map.lcgreek [k] or map.ucgreek [k] or
- map.symbols [k] or k
- t[k] = v
- return v
- end
-end
-
-for k, v in next, mathremap do
- local t = { }
- setmetatableindex(t,resolver(v))
- usedremap[k] = t
-end
-
-local function remapgreek(mathalphabet,how,detail,char)
- local r = mathremap[mathalphabet] -- what if 0
- local alphabet = r and r.alphabet or "regular"
- local style = r and r.style or "tf"
- local remapping = greekremapping[how]
- if trace_greek then
- report_remapping("greek %s, %s char %C, alphabet %a %a, method %a","before",detail,char,alphabet,style,remapping.what)
- end
- local newstyle = remapping[style]
- if newstyle then
- local data = alphabets[alphabet][newstyle] -- always something
- mathalphabet = data and data.attribute or mathalphabet
- style = newstyle
- end
- if trace_greek then
- report_remapping("greek %s, %s char %C, alphabet %a %a, method %a","after",detail,char,alphabet,style,remapping.what)
- end
- return mathalphabet, style
-end
-
-function mathematics.remapalphabets(char,mathalphabet,mathgreek)
- if not mathalphabet then
- return
- end
- if mathgreek and mathgreek > 0 then
- if not isgreek[char] then
- -- nothing needed
- elseif islcgreek[char] then
- local lc = extract(mathgreek,4,4)
- if lc > 1 then
- mathalphabet = remapgreek(mathalphabet,lc,"lowercase",char)
- end
- elseif isucgreek[char] then
- local uc = extract(mathgreek,0,4)
- if uc > 1 then
- mathalphabet = remapgreek(mathalphabet,uc,"uppercase",char)
- end
- elseif issygreek[char] then
- local sy = extract(mathgreek,8,4)
- if sy > 1 then
- mathalphabet = remapgreek(mathalphabet,sy,"symbol",char)
- end
- end
- end
- if mathalphabet > 0 then
- local remap = usedremap[mathalphabet] -- redundant check
- if remap then
- local newchar = remap[char]
- return newchar ~= char and newchar
- end
- end
- -- return nil
-end
-
--- begin of experiment
-
-local fallback = {
- tf = "bf",
- it = "bi",
- bf = "tf",
- bi = "it",
-}
-
-function mathematics.fallbackstyleattr(attribute)
- local r = mathremap[attribute]
- local alphabet = r.alphabet or "regular"
- local style = r.style or "tf"
- local fback = fallback[style]
- if fback then
- local data = alphabets[alphabet][fback]
- if data then
- local attr = data.attribute
- return attribute ~= attr and attr
- end
- end
-end
-
--- end of experiment
-
-local function checkedcopy(characters,child,parent)
- for k, v in next, child do
- if not characters[v] then
- characters[v] = characters[parent[k]]
- end
- end
-end
-
-function mathematics.addfallbacks(main)
- local characters = main.characters
- checkedcopy(characters,regular.bf.ucgreek,regular.tf.ucgreek)
- checkedcopy(characters,regular.bf.lcgreek,regular.tf.lcgreek)
- checkedcopy(characters,regular.bi.ucgreek,regular.it.ucgreek)
- checkedcopy(characters,regular.bi.lcgreek,regular.it.lcgreek)
-end
-
--- interface
-
-commands.setmathattribute = mathematics.syncboth
-commands.setmathalphabet = mathematics.syncname
-commands.setmathstyle = mathematics.syncstyle
+if not modules then modules = { } end modules ['math-map'] = {
+ 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: make sparse .. if self
+
+--[[ldx--
+<p>Remapping mathematics alphabets.</p>
+--ldx]]--
+
+-- oldstyle: not really mathematics but happened to be part of
+-- the mathematics fonts in cmr
+--
+-- persian: we will also provide mappers for other
+-- scripts
+
+-- todo: alphabets namespace
+-- maybe: script/scriptscript dynamic,
+
+-- to be looked into once the fonts are ready (will become font
+-- goodie):
+--
+-- (U+2202,U+1D715) : upright
+-- (U+2202,U+1D715) : italic
+-- (U+2202,U+1D715) : upright
+--
+-- plus add them to the regular vectors below so that they honor \it etc
+
+local type, next = type, next
+local floor, div = math.floor, math.div
+local merged = table.merged
+local extract = bit32.extract
+
+local allocate = utilities.storage.allocate
+local texattribute = tex.attribute
+local otffeatures = fonts.constructors.newfeatures("otf")
+local registerotffeature = otffeatures.register
+local setmetatableindex = table.setmetatableindex
+
+local trace_greek = false trackers.register("math.greek", function(v) trace_greek = v end)
+local report_remapping = logs.reporter("mathematics","remapping")
+
+mathematics = mathematics or { }
+local mathematics = mathematics
+
+-- Unfortunately some alphabets have gaps (thereby troubling all applications that
+-- need to deal with math). Somewhat strange considering all those weird symbols that
+-- were added afterwards. The following trickery (and data) is only to be used for
+-- diagnostics and quick and dirty alphabet tracing (s-mat-10.mkiv) as we deal with
+-- it otherwise.
+
+mathematics.gaps = {
+ [0x1D455] = 0x0210E, -- H
+ [0x1D49D] = 0x0212C, -- script B
+ [0x1D4A0] = 0x02130, -- script E
+ [0x1D4A1] = 0x02131, -- script F
+ [0x1D4A3] = 0x0210B, -- script H
+ [0x1D4A4] = 0x02110, -- script I
+ [0x1D4A7] = 0x02112, -- script L
+ [0x1D4A8] = 0x02133, -- script M
+ [0x1D4AD] = 0x0211B, -- script R
+ [0x1D4BA] = 0x0212F, -- script e
+ [0x1D4BC] = 0x0210A, -- script g
+ [0x1D4C4] = 0x02134, -- script o
+ [0x1D506] = 0x0212D, -- fraktur C
+ [0x1D50B] = 0x0210C, -- fraktur H
+ [0x1D50C] = 0x02111, -- fraktur I
+ [0x1D515] = 0x0211C, -- fraktur R
+ [0x1D51D] = 0x02128, -- fraktur Z
+ [0x1D53A] = 0x02102, -- bb C
+ [0x1D53F] = 0x0210D, -- bb H
+ [0x1D545] = 0x02115, -- bb N
+ [0x1D547] = 0x02119, -- bb P
+ [0x1D548] = 0x0211A, -- bb Q
+ [0x1D549] = 0x0211D, -- bb R
+ [0x1D551] = 0x02124, -- bb Z
+}
+
+local function fillinmathgaps(tfmdata,key,value)
+ local mathgaps = mathematics.gaps
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ for gap, original in next, mathgaps do
+ if characters[original] and not characters[gap] then
+ characters [gap] = characters [original]
+ descriptions[gap] = descriptions[original]
+ end
+ end
+end
+
+registerotffeature {
+ name = "mathgaps",
+ description = "plug gaps in math alphabets",
+ comment = "regular document sources should not depend on this",
+ manipulators = {
+ base = fillinmathgaps,
+ node = fillinmathgaps,
+ }
+}
+
+-- we could use one level less and have tf etc be tables directly but the
+-- following approach permits easier remapping of a-a, A-Z and 0-9 to
+-- fallbacks; symbols is currently mostly greek
+
+local function todigit(n) local t = { } for i=0, 9 do t[0x00030+i] = n+i end return t end
+local function toupper(n) local t = { } for i=0,25 do t[0x00041+i] = n+i end return t end
+local function tolower(n) local t = { } for i=0,25 do t[0x00061+i] = n+i end return t end
+
+local regular_tf = {
+ digits = todigit(0x00030),
+ ucletters = toupper(0x00041),
+ lcletters = tolower(0x00061),
+ ucgreek = {
+ [0x0391]=0x0391, [0x0392]=0x0392, [0x0393]=0x0393, [0x0394]=0x0394, [0x0395]=0x0395,
+ [0x0396]=0x0396, [0x0397]=0x0397, [0x0398]=0x0398, [0x0399]=0x0399, [0x039A]=0x039A,
+ [0x039B]=0x039B, [0x039C]=0x039C, [0x039D]=0x039D, [0x039E]=0x039E, [0x039F]=0x039F,
+ [0x03A0]=0x03A0, [0x03A1]=0x03A1, [0x03A3]=0x03A3, [0x03A4]=0x03A4, [0x03A5]=0x03A5,
+ [0x03A6]=0x03A6, [0x03A7]=0x03A7, [0x03A8]=0x03A8, [0x03A9]=0x03A9,
+ },
+ lcgreek = {
+ [0x03B1]=0x03B1, [0x03B2]=0x03B2, [0x03B3]=0x03B3, [0x03B4]=0x03B4, [0x03B5]=0x03B5,
+ [0x03B6]=0x03B6, [0x03B7]=0x03B7, [0x03B8]=0x03B8, [0x03B9]=0x03B9, [0x03BA]=0x03BA,
+ [0x03BB]=0x03BB, [0x03BC]=0x03BC, [0x03BD]=0x03BD, [0x03BE]=0x03BE, [0x03BF]=0x03BF,
+ [0x03C0]=0x03C0, [0x03C1]=0x03C1, [0x03C2]=0x03C2, [0x03C3]=0x03C3, [0x03C4]=0x03C4,
+ [0x03C5]=0x03C5, [0x03C6]=0x03C6, [0x03C7]=0x03C7, [0x03C8]=0x03C8, [0x03C9]=0x03C9,
+ [0x03D1]=0x03D1, [0x03D5]=0x03D5, [0x03D6]=0x03D6, [0x03F0]=0x03F0, [0x03F1]=0x03F1,
+ [0x03F4]=0x03F4, [0x03F5]=0x03F5,
+ },
+ symbols = {
+ [0x2202]=0x2202, [0x2207]=0x2207,
+ },
+}
+
+local regular_it = {
+ digits = regular_tf.digits,
+ ucletters = toupper(0x1D434),
+ lcletters = { -- H
+ [0x00061]=0x1D44E, [0x00062]=0x1D44F, [0x00063]=0x1D450, [0x00064]=0x1D451, [0x00065]=0x1D452,
+ [0x00066]=0x1D453, [0x00067]=0x1D454, [0x00068]=0x0210E, [0x00069]=0x1D456, [0x0006A]=0x1D457,
+ [0x0006B]=0x1D458, [0x0006C]=0x1D459, [0x0006D]=0x1D45A, [0x0006E]=0x1D45B, [0x0006F]=0x1D45C,
+ [0x00070]=0x1D45D, [0x00071]=0x1D45E, [0x00072]=0x1D45F, [0x00073]=0x1D460, [0x00074]=0x1D461,
+ [0x00075]=0x1D462, [0x00076]=0x1D463, [0x00077]=0x1D464, [0x00078]=0x1D465, [0x00079]=0x1D466,
+ [0x0007A]=0x1D467,
+ },
+ ucgreek = {
+ [0x0391]=0x1D6E2, [0x0392]=0x1D6E3, [0x0393]=0x1D6E4, [0x0394]=0x1D6E5, [0x0395]=0x1D6E6,
+ [0x0396]=0x1D6E7, [0x0397]=0x1D6E8, [0x0398]=0x1D6E9, [0x0399]=0x1D6EA, [0x039A]=0x1D6EB,
+ [0x039B]=0x1D6EC, [0x039C]=0x1D6ED, [0x039D]=0x1D6EE, [0x039E]=0x1D6EF, [0x039F]=0x1D6F0,
+ [0x03A0]=0x1D6F1, [0x03A1]=0x1D6F2, [0x03A3]=0x1D6F4, [0x03A4]=0x1D6F5, [0x03A5]=0x1D6F6,
+ [0x03A6]=0x1D6F7, [0x03A7]=0x1D6F8, [0x03A8]=0x1D6F9, [0x03A9]=0x1D6FA,
+ },
+ lcgreek = {
+ [0x03B1]=0x1D6FC, [0x03B2]=0x1D6FD, [0x03B3]=0x1D6FE, [0x03B4]=0x1D6FF, [0x03B5]=0x1D700,
+ [0x03B6]=0x1D701, [0x03B7]=0x1D702, [0x03B8]=0x1D703, [0x03B9]=0x1D704, [0x03BA]=0x1D705,
+ [0x03BB]=0x1D706, [0x03BC]=0x1D707, [0x03BD]=0x1D708, [0x03BE]=0x1D709, [0x03BF]=0x1D70A,
+ [0x03C0]=0x1D70B, [0x03C1]=0x1D70C, [0x03C2]=0x1D70D, [0x03C3]=0x1D70E, [0x03C4]=0x1D70F,
+ [0x03C5]=0x1D710, [0x03C6]=0x1D711, [0x03C7]=0x1D712, [0x03C8]=0x1D713, [0x03C9]=0x1D714,
+ [0x03D1]=0x1D717, [0x03D5]=0x1D719, [0x03D6]=0x1D71B, [0x03F0]=0x1D718, [0x03F1]=0x1D71A,
+ [0x03F4]=0x1D6F3, [0x03F5]=0x1D716,
+ },
+ symbols = {
+ [0x2202]=0x1D715, [0x2207]=0x1D6FB,
+ },
+}
+
+local regular_bf= {
+ digits = todigit(0x1D7CE),
+ ucletters = toupper(0x1D400),
+ lcletters = tolower(0x1D41A),
+ ucgreek = {
+ [0x0391]=0x1D6A8, [0x0392]=0x1D6A9, [0x0393]=0x1D6AA, [0x0394]=0x1D6AB, [0x0395]=0x1D6AC,
+ [0x0396]=0x1D6AD, [0x0397]=0x1D6AE, [0x0398]=0x1D6AF, [0x0399]=0x1D6B0, [0x039A]=0x1D6B1,
+ [0x039B]=0x1D6B2, [0x039C]=0x1D6B3, [0x039D]=0x1D6B4, [0x039E]=0x1D6B5, [0x039F]=0x1D6B6,
+ [0x03A0]=0x1D6B7, [0x03A1]=0x1D6B8, [0x03A3]=0x1D6BA, [0x03A4]=0x1D6BB, [0x03A5]=0x1D6BC,
+ [0x03A6]=0x1D6BD, [0x03A7]=0x1D6BE, [0x03A8]=0x1D6BF, [0x03A9]=0x1D6C0,
+ },
+ lcgreek = {
+ [0x03B1]=0x1D6C2, [0x03B2]=0x1D6C3, [0x03B3]=0x1D6C4, [0x03B4]=0x1D6C5, [0x03B5]=0x1D6C6,
+ [0x03B6]=0x1D6C7, [0x03B7]=0x1D6C8, [0x03B8]=0x1D6C9, [0x03B9]=0x1D6CA, [0x03BA]=0x1D6CB,
+ [0x03BB]=0x1D6CC, [0x03BC]=0x1D6CD, [0x03BD]=0x1D6CE, [0x03BE]=0x1D6CF, [0x03BF]=0x1D6D0,
+ [0x03C0]=0x1D6D1, [0x03C1]=0x1D6D2, [0x03C2]=0x1D6D3, [0x03C3]=0x1D6D4, [0x03C4]=0x1D6D5,
+ [0x03C5]=0x1D6D6, [0x03C6]=0x1D6D7, [0x03C7]=0x1D6D8, [0x03C8]=0x1D6D9, [0x03C9]=0x1D6DA,
+ [0x03D1]=0x1D6DD, [0x03D5]=0x1D6DF, [0x03D6]=0x1D6E1, [0x03F0]=0x1D6DE, [0x03F1]=0x1D6E0,
+ [0x03F4]=0x1D6B9, [0x03F5]=0x1D6DC,
+ },
+ symbols = {
+ [0x2202]=0x1D6DB, [0x2207]=0x1D6C1,
+ },
+}
+
+local regular_bi = {
+ digits = regular_bf.digits,
+ ucletters = toupper(0x1D468),
+ lcletters = tolower(0x1D482),
+ ucgreek = {
+ [0x0391]=0x1D71C, [0x0392]=0x1D71D, [0x0393]=0x1D71E, [0x0394]=0x1D71F, [0x0395]=0x1D720,
+ [0x0396]=0x1D721, [0x0397]=0x1D722, [0x0398]=0x1D723, [0x0399]=0x1D724, [0x039A]=0x1D725,
+ [0x039B]=0x1D726, [0x039C]=0x1D727, [0x039D]=0x1D728, [0x039E]=0x1D729, [0x039F]=0x1D72A,
+ [0x03A0]=0x1D72B, [0x03A1]=0x1D72C, [0x03A3]=0x1D72E, [0x03A4]=0x1D72F, [0x03A5]=0x1D730,
+ [0x03A6]=0x1D731, [0x03A7]=0x1D732, [0x03A8]=0x1D733, [0x03A9]=0x1D734,
+ },
+ lcgreek = {
+ [0x03B1]=0x1D736, [0x03B2]=0x1D737, [0x03B3]=0x1D738, [0x03B4]=0x1D739, [0x03B5]=0x1D73A,
+ [0x03B6]=0x1D73B, [0x03B7]=0x1D73C, [0x03B8]=0x1D73D, [0x03B9]=0x1D73E, [0x03BA]=0x1D73F,
+ [0x03BB]=0x1D740, [0x03BC]=0x1D741, [0x03BD]=0x1D742, [0x03BE]=0x1D743, [0x03BF]=0x1D744,
+ [0x03C0]=0x1D745, [0x03C1]=0x1D746, [0x03C2]=0x1D747, [0x03C3]=0x1D748, [0x03C4]=0x1D749,
+ [0x03C5]=0x1D74A, [0x03C6]=0x1D74B, [0x03C7]=0x1D74C, [0x03C8]=0x1D74D, [0x03C9]=0x1D74E,
+ [0x03D1]=0x1D751, [0x03D5]=0x1D753, [0x03D6]=0x1D755, [0x03F0]=0x1D752, [0x03F1]=0x1D754,
+ [0x03F4]=0x1D72D, [0x03F5]=0x1D750,
+ },
+ symbols = {
+ [0x2202]=0x1D74F, [0x2207]=0x1D735,
+ },
+}
+
+local regular = {
+ tf = regular_tf,
+ it = regular_it,
+ bf = regular_bf,
+ bi = regular_bi,
+}
+
+local sansserif_tf = {
+ digits = todigit(0x1D7E2),
+ ucletters = toupper(0x1D5A0),
+ lcletters = tolower(0x1D5BA),
+ lcgreek = regular_tf.lcgreek,
+ ucgreek = regular_tf.ucgreek,
+ symbols = regular_tf.symbols,
+}
+
+local sansserif_it = {
+ digits = regular_tf.digits,
+ ucletters = toupper(0x1D608),
+ lcletters = tolower(0x1D622),
+ lcgreek = regular_tf.lcgreek,
+ ucgreek = regular_tf.ucgreek,
+ symbols = regular_tf.symbols,
+}
+
+local sansserif_bf = {
+ digits = todigit(0x1D7EC),
+ ucletters = toupper(0x1D5D4),
+ lcletters = tolower(0x1D5EE),
+ ucgreek = {
+ [0x0391]=0x1D756, [0x0392]=0x1D757, [0x0393]=0x1D758, [0x0394]=0x1D759, [0x0395]=0x1D75A,
+ [0x0396]=0x1D75B, [0x0397]=0x1D75C, [0x0398]=0x1D75D, [0x0399]=0x1D75E, [0x039A]=0x1D75F,
+ [0x039B]=0x1D760, [0x039C]=0x1D761, [0x039D]=0x1D762, [0x039E]=0x1D763, [0x039F]=0x1D764,
+ [0x03A0]=0x1D765, [0x03A1]=0x1D766, [0x03A3]=0x1D768, [0x03A4]=0x1D769, [0x03A5]=0x1D76A,
+ [0x03A6]=0x1D76B, [0x03A7]=0x1D76C, [0x03A8]=0x1D76D, [0x03A9]=0x1D76E,
+ },
+ lcgreek = {
+ [0x03B1]=0x1D770, [0x03B2]=0x1D771, [0x03B3]=0x1D772, [0x03B4]=0x1D773, [0x03B5]=0x1D774,
+ [0x03B6]=0x1D775, [0x03B7]=0x1D776, [0x03B8]=0x1D777, [0x03B9]=0x1D778, [0x03BA]=0x1D779,
+ [0x03BB]=0x1D77A, [0x03BC]=0x1D77B, [0x03BD]=0x1D77C, [0x03BE]=0x1D77D, [0x03BF]=0x1D77E,
+ [0x03C0]=0x1D77F, [0x03C1]=0x1D780, [0x03C2]=0x1D781, [0x03C3]=0x1D782, [0x03C4]=0x1D783,
+ [0x03C5]=0x1D784, [0x03C6]=0x1D785, [0x03C7]=0x1D786, [0x03C8]=0x1D787, [0x03C9]=0x1D788,
+ [0x03D1]=0x1D78B, [0x03D5]=0x1D78D, [0x03D6]=0x1D78F, [0x03F0]=0x1D78C, [0x03F1]=0x1D78E,
+ [0x03F4]=0x1D767, [0x03F5]=0x1D78A,
+ },
+ symbols = {
+ [0x2202]=0x1D789, [0x2207]=0x1D76F,
+ },
+}
+
+local sansserif_bi = {
+ digits = sansserif_bf.digits,
+ ucletters = toupper(0x1D63C),
+ lcletters = tolower(0x1D656),
+ ucgreek = {
+ [0x0391]=0x1D790, [0x0392]=0x1D791, [0x0393]=0x1D792, [0x0394]=0x1D793, [0x0395]=0x1D794,
+ [0x0396]=0x1D795, [0x0397]=0x1D796, [0x0398]=0x1D797, [0x0399]=0x1D798, [0x039A]=0x1D799,
+ [0x039B]=0x1D79A, [0x039C]=0x1D79B, [0x039D]=0x1D79C, [0x039E]=0x1D79D, [0x039F]=0x1D79E,
+ [0x03A0]=0x1D79F, [0x03A1]=0x1D7A0, [0x03A3]=0x1D7A2, [0x03A4]=0x1D7A3, [0x03A5]=0x1D7A4,
+ [0x03A6]=0x1D7A5, [0x03A7]=0x1D7A6, [0x03A8]=0x1D7A7, [0x03A9]=0x1D7A8,
+ },
+ lcgreek = {
+ [0x03B1]=0x1D7AA, [0x03B2]=0x1D7AB, [0x03B3]=0x1D7AC, [0x03B4]=0x1D7AD, [0x03B5]=0x1D7AE,
+ [0x03B6]=0x1D7AF, [0x03B7]=0x1D7B0, [0x03B8]=0x1D7B1, [0x03B9]=0x1D7B2, [0x03BA]=0x1D7B3,
+ [0x03BB]=0x1D7B4, [0x03BC]=0x1D7B5, [0x03BD]=0x1D7B6, [0x03BE]=0x1D7B7, [0x03BF]=0x1D7B8,
+ [0x03C0]=0x1D7B9, [0x03C1]=0x1D7BA, [0x03C2]=0x1D7BB, [0x03C3]=0x1D7BC, [0x03C4]=0x1D7BD,
+ [0x03C5]=0x1D7BE, [0x03C6]=0x1D7BF, [0x03C7]=0x1D7C0, [0x03C8]=0x1D7C1, [0x03C9]=0x1D7C2,
+ [0x03D1]=0x1D7C5, [0x03D5]=0x1D7C7, [0x03D6]=0x1D7C9, [0x03F0]=0x1D7C6, [0x03F1]=0x1D7C8,
+ [0x03F4]=0x1D7A1, [0x03F5]=0x1D7C4,
+ },
+ symbols = {
+ [0x2202]=0x1D7C3, [0x2207]=0x1D7A9,
+ },
+}
+
+local sansserif = {
+ tf = sansserif_tf,
+ it = sansserif_it,
+ bf = sansserif_bf,
+ bi = sansserif_bi,
+}
+
+local monospaced_tf = {
+ digits = todigit(0x1D7F6),
+ ucletters = toupper(0x1D670),
+ lcletters = tolower(0x1D68A),
+ lcgreek = sansserif_tf.lcgreek,
+ ucgreek = sansserif_tf.ucgreek,
+ symbols = sansserif_tf.symbols,
+}
+
+local monospaced = {
+ tf = monospaced_tf,
+ it = sansserif_tf,
+ bf = sansserif_tf,
+ bi = sansserif_bf,
+}
+
+local blackboard_tf = {
+ digits = todigit(0x1D7D8),
+ ucletters = { -- C H N P Q R Z
+ [0x00041]=0x1D538, [0x00042]=0x1D539, [0x00043]=0x02102, [0x00044]=0x1D53B, [0x00045]=0x1D53C,
+ [0x00046]=0x1D53D, [0x00047]=0x1D53E, [0x00048]=0x0210D, [0x00049]=0x1D540, [0x0004A]=0x1D541,
+ [0x0004B]=0x1D542, [0x0004C]=0x1D543, [0x0004D]=0x1D544, [0x0004E]=0x02115, [0x0004F]=0x1D546,
+ [0x00050]=0x02119, [0x00051]=0x0211A, [0x00052]=0x0211D, [0x00053]=0x1D54A, [0x00054]=0x1D54B,
+ [0x00055]=0x1D54C, [0x00056]=0x1D54D, [0x00057]=0x1D54E, [0x00058]=0x1D54F, [0x00059]=0x1D550,
+ [0x0005A]=0x02124,
+ },
+ lcletters = tolower(0x1D552),
+ lcgreek = { -- gamma pi
+ [0x03B3]=0x0213C, [0x03C0]=0x0213D,
+ },
+ ucgreek = { -- Gamma pi
+ [0x0393]=0x0213E, [0x03A0]=0x0213F,
+ },
+ symbols = { -- sum
+ [0x2211]=0x02140,
+ },
+}
+
+blackboard_tf.lcgreek = merged(regular_tf.lcgreek, blackboard_tf.lcgreek)
+blackboard_tf.ucgreek = merged(regular_tf.ucgreek, blackboard_tf.ucgreek)
+blackboard_tf.symbols = merged(regular_tf.symbols, blackboard_tf.symbols)
+
+local blackboard = {
+ tf = blackboard_tf,
+ it = blackboard_tf,
+ bf = blackboard_tf,
+ bi = blackboard_tf,
+}
+
+local fraktur_tf= {
+ digits = regular_tf.digits,
+ ucletters = { -- C H I R Z
+ [0x00041]=0x1D504, [0x00042]=0x1D505, [0x00043]=0x0212D, [0x00044]=0x1D507, [0x00045]=0x1D508,
+ [0x00046]=0x1D509, [0x00047]=0x1D50A, [0x00048]=0x0210C, [0x00049]=0x02111, [0x0004A]=0x1D50D,
+ [0x0004B]=0x1D50E, [0x0004C]=0x1D50F, [0x0004D]=0x1D510, [0x0004E]=0x1D511, [0x0004F]=0x1D512,
+ [0x00050]=0x1D513, [0x00051]=0x1D514, [0x00052]=0x0211C, [0x00053]=0x1D516, [0x00054]=0x1D517,
+ [0x00055]=0x1D518, [0x00056]=0x1D519, [0x00057]=0x1D51A, [0x00058]=0x1D51B, [0x00059]=0x1D51C,
+ [0x0005A]=0x02128,
+ },
+ lcletters = tolower(0x1D51E),
+ lcgreek = regular_tf.lcgreek,
+ ucgreek = regular_tf.ucgreek,
+ symbols = regular_tf.symbols,
+}
+
+local fraktur_bf = {
+ digits = regular_bf.digits,
+ ucletters = toupper(0x1D56C),
+ lcletters = tolower(0x1D586),
+ lcgreek = regular_bf.lcgreek,
+ ucgreek = regular_bf.ucgreek,
+ symbols = regular_bf.symbols,
+}
+
+local fraktur = { -- ok
+ tf = fraktur_tf,
+ bf = fraktur_bf,
+ it = fraktur_tf,
+ bi = fraktur_bf,
+}
+
+local script_tf = {
+ digits = regular_tf.digits,
+ ucletters = { -- B E F H I L M R -- P 2118
+ [0x00041]=0x1D49C, [0x00042]=0x0212C, [0x00043]=0x1D49E, [0x00044]=0x1D49F, [0x00045]=0x02130,
+ [0x00046]=0x02131, [0x00047]=0x1D4A2, [0x00048]=0x0210B, [0x00049]=0x02110, [0x0004A]=0x1D4A5,
+ [0x0004B]=0x1D4A6, [0x0004C]=0x02112, [0x0004D]=0x02133, [0x0004E]=0x1D4A9, [0x0004F]=0x1D4AA,
+ [0x00050]=0x1D4AB, [0x00051]=0x1D4AC, [0x00052]=0x0211B, [0x00053]=0x1D4AE, [0x00054]=0x1D4AF,
+ [0x00055]=0x1D4B0, [0x00056]=0x1D4B1, [0x00057]=0x1D4B2, [0x00058]=0x1D4B3, [0x00059]=0x1D4B4,
+ [0x0005A]=0x1D4B5,
+ },
+ lcletters = { -- E G O -- L 2113
+ [0x00061]=0x1D4B6, [0x00062]=0x1D4B7, [0x00063]=0x1D4B8, [0x00064]=0x1D4B9, [0x00065]=0x0212F,
+ [0x00066]=0x1D4BB, [0x00067]=0x0210A, [0x00068]=0x1D4BD, [0x00069]=0x1D4BE, [0x0006A]=0x1D4BF,
+ [0x0006B]=0x1D4C0, [0x0006C]=0x1D4C1, [0x0006D]=0x1D4C2, [0x0006E]=0x1D4C3, [0x0006F]=0x02134,
+ [0x00070]=0x1D4C5, [0x00071]=0x1D4C6, [0x00072]=0x1D4C7, [0x00073]=0x1D4C8, [0x00074]=0x1D4C9,
+ [0x00075]=0x1D4CA, [0x00076]=0x1D4CB, [0x00077]=0x1D4CC, [0x00078]=0x1D4CD, [0x00079]=0x1D4CE,
+ [0x0007A]=0x1D4CF,
+ },
+ lcgreek = regular_tf.lcgreek,
+ ucgreek = regular_tf.ucgreek,
+ symbols = regular_tf.symbols,
+}
+
+local script_bf = {
+ digits = regular_bf.digits,
+ ucletters = toupper(0x1D4D0),
+ lcletters = tolower(0x1D4EA),
+ lcgreek = regular_bf.lcgreek,
+ ucgreek = regular_bf.ucgreek,
+ symbols = regular_bf.symbols,
+}
+
+local script = {
+ tf = script_tf,
+ bf = script_bf,
+ it = script_tf,
+ bi = script_bf,
+}
+
+local alphabets = allocate {
+ regular = regular,
+ sansserif = sansserif,
+ monospaced = monospaced,
+ blackboard = blackboard,
+ fraktur = fraktur,
+ script = script,
+}
+
+mathematics.alphabets = alphabets
+
+local boldmap = { }
+
+local function remap(tf,bf)
+ for _, alphabet in next, alphabets do
+ local tfdata = alphabet[tf]
+ local bfdata = alphabet[bf]
+ if tfdata then
+ for k, tfd in next, tfdata do
+ if type(tfd) == "table" then
+ local bfd = bfdata[k]
+ if bfd then
+ for n, u in next, tfd do
+ local bn = bfd[n]
+ if bn then
+ boldmap[u] = bn
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+remap("tf","bf")
+remap("it","bi")
+
+mathematics.boldmap = boldmap
+
+local mathremap = allocate { }
+
+for alphabet, styles in next, alphabets do -- per 9/6/2011 we also have attr for missing
+ for style, data in next, styles do
+ -- let's keep the long names (for tracing)
+ local n = #mathremap + 1
+ data.attribute = n
+ data.alphabet = alphabet
+ data.style = style
+ mathremap[n] = data
+ end
+end
+
+mathematics.mapremap = mathremap
+
+-- beware, these are shared tables (no problem since they're not
+-- in unicode)
+
+alphabets.tt = monospaced
+alphabets.ss = sansserif
+alphabets.rm = regular
+alphabets.bb = blackboard
+alphabets.fr = fraktur
+alphabets.sr = script
+
+alphabets.serif = regular
+alphabets.type = monospaced
+alphabets.teletype = monospaced
+
+regular.normal = regular_tf
+regular.italic = regular_it
+regular.bold = regular_bf
+regular.bolditalic = regular_bi
+
+sansserif.normal = sansserif_tf
+sansserif.italic = sansserif_it
+sansserif.bold = sansserif_bf
+sansserif.bolditalic = sansserif_bi
+
+monospaced.normal = monospaced_tf
+monospaced.italic = monospaced_it
+monospaced.bold = monospaced_bf
+monospaced.bolditalic = monospaced_bi
+
+function mathematics.tostyle(attribute)
+ local r = mathremap[attribute]
+ return r and r.style or "tf"
+end
+
+function mathematics.toname(attribute)
+ local r = mathremap[attribute]
+ return r and r.alphabet or "regular"
+end
+
+-- of course we could do some div/mod trickery instead
+
+local mathalphabet = attributes.private("mathalphabet")
+
+function mathematics.getboth(alphabet,style)
+ local data = alphabet and alphabets[alphabet] or regular
+ data = data[style or "tf"] or data.tf
+ return data and data.attribute
+end
+
+function mathematics.getstyle(style)
+ local r = mathremap[texattribute[mathalphabet]]
+ local alphabet = r and r.alphabet or "regular"
+ local data = alphabets[alphabet][style]
+ return data and data.attribute
+end
+
+function mathematics.syncboth(alphabet,style)
+ local data = alphabet and alphabets[alphabet] or regular
+ data = style and data[style] or data.tf
+ texattribute[mathalphabet] = data and data.attribute or texattribute[mathalphabet]
+end
+
+function mathematics.syncstyle(style)
+ local r = mathremap[texattribute[mathalphabet]]
+ local alphabet = r and r.alphabet or "regular"
+ local data = alphabets[alphabet][style]
+ texattribute[mathalphabet] = data and data.attribute or texattribute[mathalphabet]
+end
+
+function mathematics.syncname(alphabet)
+ -- local r = mathremap[mathalphabet]
+ local r = mathremap[texattribute[mathalphabet]]
+ local style = r and r.style or "tf"
+ local data = alphabets[alphabet][style]
+ texattribute[mathalphabet] = data and data.attribute or texattribute[mathalphabet]
+end
+
+local islcgreek = regular_tf.lcgreek
+local isucgreek = regular_tf.ucgreek
+local issygreek = regular_tf.symbols
+local isgreek = merged(islcgreek,isucgreek,issygreek)
+
+local greekremapping = {
+ [1] = { what = "unchanged" }, -- upright
+ [2] = { what = "upright", it = "tf", bi = "bf" }, -- upright
+ [3] = { what = "italic", tf = "it", bf = "bi" }, -- italic
+}
+
+local usedremap = { }
+
+local function resolver(map)
+ return function (t,k)
+ local v =
+ map.digits [k] or
+ map.lcletters[k] or map.ucletters[k] or
+ map.lcgreek [k] or map.ucgreek [k] or
+ map.symbols [k] or k
+ t[k] = v
+ return v
+ end
+end
+
+for k, v in next, mathremap do
+ local t = { }
+ setmetatableindex(t,resolver(v))
+ usedremap[k] = t
+end
+
+local function remapgreek(mathalphabet,how,detail,char)
+ local r = mathremap[mathalphabet] -- what if 0
+ local alphabet = r and r.alphabet or "regular"
+ local style = r and r.style or "tf"
+ local remapping = greekremapping[how]
+ if trace_greek then
+ report_remapping("greek %s, %s char %C, alphabet %a %a, method %a","before",detail,char,alphabet,style,remapping.what)
+ end
+ local newstyle = remapping[style]
+ if newstyle then
+ local data = alphabets[alphabet][newstyle] -- always something
+ mathalphabet = data and data.attribute or mathalphabet
+ style = newstyle
+ end
+ if trace_greek then
+ report_remapping("greek %s, %s char %C, alphabet %a %a, method %a","after",detail,char,alphabet,style,remapping.what)
+ end
+ return mathalphabet, style
+end
+
+function mathematics.remapalphabets(char,mathalphabet,mathgreek)
+ if not mathalphabet then
+ return
+ end
+ if mathgreek and mathgreek > 0 then
+ if not isgreek[char] then
+ -- nothing needed
+ elseif islcgreek[char] then
+ local lc = extract(mathgreek,4,4)
+ if lc > 1 then
+ mathalphabet = remapgreek(mathalphabet,lc,"lowercase",char)
+ end
+ elseif isucgreek[char] then
+ local uc = extract(mathgreek,0,4)
+ if uc > 1 then
+ mathalphabet = remapgreek(mathalphabet,uc,"uppercase",char)
+ end
+ elseif issygreek[char] then
+ local sy = extract(mathgreek,8,4)
+ if sy > 1 then
+ mathalphabet = remapgreek(mathalphabet,sy,"symbol",char)
+ end
+ end
+ end
+ if mathalphabet > 0 then
+ local remap = usedremap[mathalphabet] -- redundant check
+ if remap then
+ local newchar = remap[char]
+ return newchar ~= char and newchar
+ end
+ end
+ -- return nil
+end
+
+-- begin of experiment
+
+local fallback = {
+ tf = "bf",
+ it = "bi",
+ bf = "tf",
+ bi = "it",
+}
+
+function mathematics.fallbackstyleattr(attribute)
+ local r = mathremap[attribute]
+ local alphabet = r.alphabet or "regular"
+ local style = r.style or "tf"
+ local fback = fallback[style]
+ if fback then
+ local data = alphabets[alphabet][fback]
+ if data then
+ local attr = data.attribute
+ return attribute ~= attr and attr
+ end
+ end
+end
+
+-- end of experiment
+
+local function checkedcopy(characters,child,parent)
+ for k, v in next, child do
+ if not characters[v] then
+ characters[v] = characters[parent[k]]
+ end
+ end
+end
+
+function mathematics.addfallbacks(main)
+ local characters = main.characters
+ checkedcopy(characters,regular.bf.ucgreek,regular.tf.ucgreek)
+ checkedcopy(characters,regular.bf.lcgreek,regular.tf.lcgreek)
+ checkedcopy(characters,regular.bi.ucgreek,regular.it.ucgreek)
+ checkedcopy(characters,regular.bi.lcgreek,regular.it.lcgreek)
+end
+
+-- interface
+
+commands.setmathattribute = mathematics.syncboth
+commands.setmathalphabet = mathematics.syncname
+commands.setmathstyle = mathematics.syncstyle
diff --git a/tex/context/base/math-noa.lua b/tex/context/base/math-noa.lua
index 51c89ea77..b309ba077 100644
--- a/tex/context/base/math-noa.lua
+++ b/tex/context/base/math-noa.lua
@@ -1,1192 +1,1192 @@
-if not modules then modules = { } end modules ['math-noa'] = {
- 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"
-}
-
--- beware: this is experimental code and there will be a more
--- generic (attribute value driven) interface too but for the
--- moment this is ok
---
--- 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
-
-local utfchar, utfbyte = utf.char, utf.byte
-local formatters = string.formatters
-
-local fonts, nodes, node, mathematics = fonts, nodes, node, mathematics
-
-local otf = fonts.handlers.otf
-local otffeatures = fonts.constructors.newfeatures("otf")
-local registerotffeature = otffeatures.register
-
-local trace_remapping = false trackers.register("math.remapping", function(v) trace_remapping = v end)
-local trace_processing = false trackers.register("math.processing", function(v) trace_processing = v end)
-local trace_analyzing = false trackers.register("math.analyzing", function(v) trace_analyzing = v end)
-local trace_normalizing = false trackers.register("math.normalizing", function(v) trace_normalizing = v end)
-local trace_collapsing = false trackers.register("math.collapsing", function(v) trace_collapsing = v end)
-local trace_goodies = false trackers.register("math.goodies", function(v) trace_goodies = v end)
-local trace_variants = false trackers.register("math.variants", function(v) trace_variants = v end)
-local trace_alternates = false trackers.register("math.alternates", function(v) trace_alternates = v end)
-local trace_italics = false trackers.register("math.italics", function(v) trace_italics = v end)
-local trace_families = false trackers.register("math.families", function(v) trace_families = v end)
-
-local check_coverage = true directives.register("math.checkcoverage", function(v) check_coverage = v end)
-
-local report_processing = logs.reporter("mathematics","processing")
-local report_remapping = logs.reporter("mathematics","remapping")
-local report_normalizing = logs.reporter("mathematics","normalizing")
-local report_collapsing = logs.reporter("mathematics","collapsing")
-local report_goodies = logs.reporter("mathematics","goodies")
-local report_variants = logs.reporter("mathematics","variants")
-local report_alternates = logs.reporter("mathematics","alternates")
-local report_italics = logs.reporter("mathematics","italics")
-local report_families = logs.reporter("mathematics","families")
-
-local a_mathrendering = attributes.private("mathrendering")
-local a_exportstatus = attributes.private("exportstatus")
-
-local mlist_to_hlist = node.mlist_to_hlist
-local font_of_family = node.family_font
-local insert_node_after = node.insert_after
-local insert_node_before = node.insert_before
-local free_node = node.free
-local new_node = node.new -- todo: pool: math_noad math_sub
-
-local new_kern = nodes.pool.kern
-local new_rule = nodes.pool.rule
-local concat_nodes = nodes.concat
-
-local topoints = number.points
-
-local fonthashes = fonts.hashes
-local fontdata = fonthashes.identifiers
-local fontcharacters = fonthashes.characters
-local fontproperties = fonthashes.properties
-local fontitalics = fonthashes.italics
-local fontemwidths = fonthashes.emwidths
-local fontexheights = fonthashes.exheights
-
-local variables = interfaces.variables
-local texattribute = tex.attribute
-local unsetvalue = attributes.unsetvalue
-
-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 nodecodes = nodes.nodecodes
-local noadcodes = nodes.noadcodes
-
-local noad_ord = noadcodes.ord
-local noad_rel = noadcodes.rel
-local noad_punct = noadcodes.punct
-local noad_opdisplaylimits= noadcodes.opdisplaylimits
-local noad_oplimits = noadcodes.oplimits
-local noad_opnolimits = noadcodes.opnolimits
-
-local math_noad = nodecodes.noad -- attr nucleus sub sup
-local math_accent = nodecodes.accent -- attr nucleus sub sup accent
-local math_radical = nodecodes.radical -- attr nucleus sub sup left degree
-local math_fraction = nodecodes.fraction -- attr nucleus sub sup left right
-local math_box = nodecodes.subbox -- attr list
-local math_sub = nodecodes.submlist -- attr list
-local math_char = nodecodes.mathchar -- attr fam char
-local math_textchar = nodecodes.mathtextchar -- attr fam char
-local math_delim = nodecodes.delim -- attr small_fam small_char large_fam large_char
-local math_style = nodecodes.style -- attr style
-local math_choice = nodecodes.choice -- attr display text script scriptscript
-local math_fence = nodecodes.fence -- attr subtype
-
-local hlist_code = nodecodes.hlist
-local glyph_code = nodecodes.glyph
-
-local left_fence_code = 1
-
-local function process(start,what,n,parent)
- if n then n = n + 1 else n = 0 end
- while start do
- local id = start.id
- if trace_processing then
- if id == math_noad then
- report_processing("%w%S, class %a",n*2,start,noadcodes[start.subtype])
- elseif id == math_char then
- local char = start.char
- local fam = start.fam
- local font = font_of_family(fam)
- report_processing("%w%S, family %a, font %a, char %a, shape %c",n*2,start,fam,font,char,char)
- else
- report_processing("%w%S",n*2,start)
- end
- end
- local proc = what[id]
- if proc then
- -- report_processing("start processing")
- local done, newstart = proc(start,what,n,parent) -- prev is bugged: or start.prev
- if newstart then
- start = newstart
- -- report_processing("stop processing (new start)")
- else
- -- report_processing("stop processing")
- end
- elseif id == math_char or id == math_textchar or id == math_delim then
- break
- elseif id == math_noad then
- local noad = start.nucleus if noad then process(noad,what,n,start) end -- list
- noad = start.sup if noad then process(noad,what,n,start) end -- list
- noad = start.sub if noad then process(noad,what,n,start) end -- list
- elseif id == math_box or id == math_sub then
- -- local noad = start.list if noad then process(noad,what,n,start) end -- list
- local noad = start.head if noad then process(noad,what,n,start) end -- list
- elseif id == math_fraction then
- local noad = start.num if noad then process(noad,what,n,start) end -- list
- noad = start.denom if noad then process(noad,what,n,start) end -- list
- noad = start.left if noad then process(noad,what,n,start) end -- delimiter
- noad = start.right if noad then process(noad,what,n,start) end -- delimiter
- elseif id == math_choice then
- local noad = start.display if noad then process(noad,what,n,start) end -- list
- noad = start.text if noad then process(noad,what,n,start) end -- list
- noad = start.script if noad then process(noad,what,n,start) end -- list
- noad = start.scriptscript if noad then process(noad,what,n,start) end -- list
- elseif id == math_fence then
- local noad = start.delim if noad then process(noad,what,n,start) end -- delimiter
- elseif id == math_radical then
- local noad = start.nucleus if noad then process(noad,what,n,start) end -- list
- noad = start.sup if noad then process(noad,what,n,start) end -- list
- noad = start.sub if noad then process(noad,what,n,start) end -- list
- noad = start.left if noad then process(noad,what,n,start) end -- delimiter
- noad = start.degree if noad then process(noad,what,n,start) end -- list
- elseif id == math_accent then
- local noad = start.nucleus if noad then process(noad,what,n,start) end -- list
- noad = start.sup if noad then process(noad,what,n,start) end -- list
- noad = start.sub if noad then process(noad,what,n,start) end -- list
- noad = start.accent if noad then process(noad,what,n,start) end -- list
- noad = start.bot_accent if noad then process(noad,what,n,start) end -- list
- elseif id == math_style then
- -- has a next
- else
- -- glue, penalty, etc
- end
- start = start.next
- end
-end
-
-local function processnoads(head,actions,banner)
- if trace_processing then
- report_processing("start %a",banner)
- process(head,actions)
- report_processing("stop %a",banner)
- else
- process(head,actions)
- end
-end
-
-noads.process = processnoads
-
--- experiment (when not present fall back to fam 0) -- needs documentation
-
--- 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
-
-local families = { }
-local a_mathfamily = attributes.private("mathfamily")
-local boldmap = mathematics.boldmap
-
-local familymap = { [0] =
- "regular",
- "regular",
- "regular",
- "bold",
- "bold",
- "bold",
- "pseudobold",
- "pseudobold",
- "pseudobold",
-}
-
-families[math_char] = function(pointer)
- if pointer.fam == 0 then
- local a = pointer[a_mathfamily]
- if a and a > 0 then
- pointer[a_mathfamily] = 0
- if a > 5 then
- local char = pointer.char
- local bold = boldmap[char]
- local newa = a - 3
- if bold then
- pointer[a_exportstatus] = char
- pointer.char = 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
- else
- 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
- end
- pointer.fam = newa
- else
- if trace_families then
- local char = pointer.char
- report_families("family of %C becomes %s with remap %s",char,a,familymap[a])
- end
- pointer.fam = a
- end
- else
- -- pointer.fam = 0
- end
- end
-end
-
-families[math_delim] = function(pointer)
- if pointer.small_fam == 0 then
- local a = pointer[a_mathfamily]
- if a and a > 0 then
- pointer[a_mathfamily] = 0
- if a > 5 then
- -- no bold delimiters in unicode
- a = a - 3
- end
- pointer.small_fam = a
- pointer.large_fam = a
- else
- pointer.small_fam = 0
- pointer.large_fam = 0
- end
- end
-end
-
-families[math_textchar] = families[math_char]
-
-function handlers.families(head,style,penalties)
- processnoads(head,families,"families")
- return true
-end
-
--- character remapping
-
-local a_mathalphabet = attributes.private("mathalphabet")
-local a_mathgreek = attributes.private("mathgreek")
-
-processors.relocate = { }
-
-local function report_remap(tag,id,old,new,extra)
- report_remapping("remapping %s in font %s from %C to %C%s",tag,id,old,new,extra)
-end
-
-local remapalphabets = mathematics.remapalphabets
-local fallbackstyleattr = mathematics.fallbackstyleattr
-local setnodecolor = nodes.tracers.colors.set
-
-local function checked(pointer)
- local char = pointer.char
- local fam = pointer.fam
- local id = font_of_family(fam)
- local tc = fontcharacters[id]
- if not tc[char] then
- local specials = characters.data[char].specials
- if specials and (specials[1] == "char" or specials[1] == "font") then
- newchar = specials[#specials]
- if trace_remapping then
- report_remap("fallback",id,char,newchar)
- end
- if trace_analyzing then
- setnodecolor(pointer,"font:isol")
- end
- pointer[a_exportstatus] = char -- testcase: exponentiale
- pointer.char = newchar
- return true
- end
- end
-end
-
-processors.relocate[math_char] = function(pointer)
- local g = pointer[a_mathgreek] or 0
- local a = pointer[a_mathalphabet] or 0
- if a > 0 or g > 0 then
- if a > 0 then
- pointer[a_mathgreek] = 0
- end
- if g > 0 then
- pointer[a_mathalphabet] = 0
- end
- local char = pointer.char
- local newchar = remapalphabets(char,a,g)
- if newchar then
- local fam = pointer.fam
- local id = font_of_family(fam)
- local characters = fontcharacters[id]
- if characters[newchar] then
- if trace_remapping then
- report_remap("char",id,char,newchar)
- end
- if trace_analyzing then
- setnodecolor(pointer,"font:isol")
- end
- pointer.char = 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",id,char,newchar," (fallback remapping used)")
- end
- if trace_analyzing then
- setnodecolor(pointer,"font:isol")
- end
- pointer.char = newchar
- return true
- elseif trace_remapping then
- report_remap("char",id,char,newchar," fails (no fallback character)")
- end
- elseif trace_remapping then
- report_remap("char",id,char,newchar," fails (no fallback remap character)")
- end
- elseif trace_remapping then
- report_remap("char",id,char,newchar," fails (no fallback style)")
- end
- end
- end
- end
- if trace_analyzing then
- setnodecolor(pointer,"font:medi")
- end
- if check_coverage then
- return checked(pointer)
- end
-end
-
-processors.relocate[math_textchar] = function(pointer)
- if trace_analyzing then
- setnodecolor(pointer,"font:init")
- end
-end
-
-processors.relocate[math_delim] = function(pointer)
- if trace_analyzing then
- setnodecolor(pointer,"font:fina")
- end
-end
-
-function handlers.relocate(head,style,penalties)
- processnoads(head,processors.relocate,"relocate")
- return true
-end
-
--- rendering (beware, not exported)
-
-processors.render = { }
-
-local rendersets = mathematics.renderings.numbers or { } -- store
-
-processors.render[math_char] = function(pointer)
- local attr = pointer[a_mathrendering]
- if attr and attr > 0 then
- local char = pointer.char
- local renderset = rendersets[attr]
- if renderset then
- local newchar = renderset[char]
- if newchar then
- local fam = pointer.fam
- local id = font_of_family(fam)
- local characters = fontcharacters[id]
- if characters and characters[newchar] then
- pointer.char = newchar
- pointer[a_exportstatus] = char
- end
- end
- end
- end
-end
-
-function handlers.render(head,style,penalties)
- processnoads(head,processors.render,"render")
- return true
-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
-
-local mathsize = attributes.private("mathsize")
-
-local resize = { } processors.resize = resize
-
-resize[math_fence] = function(pointer)
- if pointer.subtype == left_fence_code then
- local a = pointer[mathsize]
- if a and a > 0 then
- pointer[mathsize] = 0
- local d = pointer.delim
- local df = d.small_fam
- local id = font_of_family(df)
- if id > 0 then
- local ch = d.small_char
- d.small_char = mathematics.big(fontdata[id],ch,a)
- end
- end
- end
-end
-
-function handlers.resize(head,style,penalties)
- processnoads(head,resize,"resize")
- return true
-end
-
--- respacing
-
--- local mathpunctuation = attributes.private("mathpunctuation")
---
--- local respace = { } processors.respace = respace
-
--- only [nd,ll,ul][po][nd,ll,ul]
-
--- respace[math_char] = function(pointer,what,n,parent) -- not math_noad .. math_char ... and then parent
--- pointer = parent
--- if pointer and pointer.subtype == noad_ord then
--- local a = pointer[mathpunctuation]
--- if a and a > 0 then
--- pointer[mathpunctuation] = 0
--- local current_nucleus = pointer.nucleus
--- if current_nucleus.id == math_char then
--- local current_char = current_nucleus.char
--- local fc = chardata[current_char]
--- fc = fc and fc.category
--- if fc == "nd" or fc == "ll" or fc == "lu" then
--- local next_noad = pointer.next
--- if next_noad and next_noad.id == math_noad and next_noad.subtype == noad_punct then
--- local next_nucleus = next_noad.nucleus
--- if next_nucleus.id == math_char then
--- local next_char = next_nucleus.char
--- local nc = chardata[next_char]
--- nc = nc and nc.category
--- if nc == "po" then
--- local last_noad = next_noad.next
--- if last_noad and last_noad.id == math_noad and last_noad.subtype == noad_ord then
--- local last_nucleus = last_noad.nucleus
--- if last_nucleus.id == math_char then
--- local last_char = last_nucleus.char
--- local lc = chardata[last_char]
--- lc = lc and lc.category
--- if lc == "nd" or lc == "ll" or lc == "lu" then
--- local ord = new_node(math_noad) -- todo: pool
--- ord.subtype, ord.nucleus, ord.sub, ord.sup, ord.attr = noad_ord, next_noad.nucleus, next_noad.sub, next_noad.sup, next_noad.attr
--- -- next_noad.nucleus, next_noad.sub, next_noad.sup, next_noad.attr = nil, nil, nil, nil
--- next_noad.nucleus, next_noad.sub, next_noad.sup = nil, nil, nil -- else crash with attributes ref count
--- --~ next_noad.attr = nil
--- ord.next = last_noad
--- pointer.next = ord
--- free_node(next_noad)
--- end
--- end
--- end
--- end
--- end
--- end
--- end
--- end
--- end
--- end
--- end
-
--- local comma = 0x002C
--- local period = 0x002E
---
--- respace[math_char] = function(pointer,what,n,parent)
--- pointer = parent
--- if pointer and pointer.subtype == noad_punct then
--- local current_nucleus = pointer.nucleus
--- if current_nucleus.id == math_char then
--- local current_nucleus = pointer.nucleus
--- if current_nucleus.id == math_char then
--- local current_char = current_nucleus.char
--- local a = pointer[mathpunctuation]
--- if not a or a == 0 then
--- if current_char == comma then
--- -- default tex: 2,5 or 2, 5 --> 2, 5
--- elseif current_char == period then
--- -- default tex: 2.5 or 2. 5 --> 2.5
--- pointer.subtype = noad_ord
--- end
--- elseif a == 1 then
--- local next_noad = pointer.next
--- if next_noad and next_noad.id == math_noad then
--- local next_nucleus = next_noad.nucleus
--- if next_nucleus.id == math_char and next_nucleus.char == 0 then
--- nodes.remove(pointer,next_noad,true)
--- end
--- if current_char == comma then
--- -- default tex: 2,5 or 2, 5 --> 2, 5
--- elseif current_char == period then
--- -- default tex: 2.5 or 2. 5 --> 2.5
--- pointer.subtype = noad_ord
--- end
--- end
--- elseif a == 2 then
--- if current_char == comma or current_char == period then
--- local next_noad = pointer.next
--- if next_noad and next_noad.id == math_noad then
--- local next_nucleus = next_noad.nucleus
--- if next_nucleus.id == math_char and next_nucleus.char == 0 then
--- if current_char == comma then
--- -- adaptive: 2, 5 --> 2, 5
--- elseif current_char == period then
--- -- adaptive: 2. 5 --> 2. 5
--- end
--- nodes.remove(pointer,next_noad,true)
--- else
--- if current_char == comma then
--- -- adaptive: 2,5 --> 2,5
--- pointer.subtype = noad_ord
--- elseif current_char == period then
--- -- adaptive: 2.5 --> 2.5
--- pointer.subtype = noad_ord
--- end
--- end
--- end
--- end
--- end
--- end
--- end
--- end
--- end
---
--- function handlers.respace(head,style,penalties)
--- processnoads(head,respace,"respace")
--- return true
--- end
-
--- The following code is dedicated to Luigi Scarso who pointed me
--- to the fact that \not= is not producing valid pdf-a code.
--- The code does not solve this for virtual characters but it does
--- a decent job on collapsing so that fonts that have the right
--- glyph will have a decent unicode point. In the meantime this code
--- has been moved elsewhere.
-
-local collapse = { } processors.collapse = collapse
-
-local mathpairs = characters.mathpairs
-
-mathpairs[0x2032] = { [0x2032] = 0x2033, [0x2033] = 0x2034 } -- (prime,prime) (prime,doubleprime)
-mathpairs[0x2033] = { [0x2032] = 0x2034 } -- (doubleprime,prime)
-
-mathpairs[0x222B] = { [0x222B] = 0x222C, [0x222C] = 0x222D }
-mathpairs[0x222C] = { [0x222B] = 0x222D }
-
-mathpairs[0x007C] = { [0x007C] = 0x2016 } -- double bars
-
-local validpair = {
- [noad_rel] = true,
- [noad_ord] = true,
- [noad_opdisplaylimits] = true,
- [noad_oplimits] = true,
- [noad_opnolimits] = true,
-}
-
-local function collapsepair(pointer,what,n,parent) -- todo: switch to turn in on and off
- if parent then
- if validpair[parent.subtype] then
- local current_nucleus = parent.nucleus
- if not parent.sub and not parent.sup and current_nucleus.id == math_char then
- local current_char = current_nucleus.char
- local mathpair = mathpairs[current_char]
- if mathpair then
- local next_noad = parent.next
- if next_noad and next_noad.id == math_noad then
- if validpair[next_noad.subtype] then
- local next_nucleus = next_noad.nucleus
- if next_nucleus.id == math_char then
- local next_char = next_nucleus.char
- local newchar = mathpair[next_char]
- if newchar then
- local fam = current_nucleus.fam
- local id = font_of_family(fam)
- local characters = fontcharacters[id]
- if characters and characters[newchar] then
- if trace_collapsing then
- report_collapsing("%U + %U => %U",current_char,next_char,newchar)
- end
- current_nucleus.char = newchar
- local next_next_noad = next_noad.next
- if next_next_noad then
- parent.next = next_next_noad
- next_next_noad.prev = parent
- else
- parent.next = nil
- end
- parent.sup = next_noad.sup
- parent.sub = next_noad.sub
- next_noad.sup = nil
- next_noad.sub = nil
- free_node(next_noad)
- collapsepair(pointer,what,n,parent)
- end
- end
- end
- end
- end
- end
- end
- end
- end
-end
-
-collapse[math_char] = collapsepair
-
-function noads.handlers.collapse(head,style,penalties)
- processnoads(head,collapse,"collapse")
- return true
-end
-
--- normalize scripts
-
-local unscript = { } noads.processors.unscript = unscript
-
-local superscripts = characters.superscripts
-local subscripts = characters.subscripts
-
-local replaced = { }
-
-local function replace(pointer,what,n,parent)
- pointer = parent -- we're following the parent list (chars trigger this)
- local next = pointer.next
- local start_super, stop_super, start_sub, stop_sub
- local mode = "unset"
- while next and next.id == math_noad do
- local nextnucleus = next.nucleus
- if nextnucleus and nextnucleus.id == math_char and not next.sub and not next.sup then
- local char = nextnucleus.char
- 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 = next.next
- nextnucleus.char = 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 = next.next
- nextnucleus.char = 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
- pointer.sup = start_super.nucleus
- else
- local list = new_node(math_sub) -- todo attr
- list.head = start_super
- pointer.sup = list
- end
- if mode == "super" then
- pointer.next = stop_super.next
- end
- stop_super.next = nil
- end
- if start_sub then
- if start_sub == stop_sub then
- pointer.sub = start_sub.nucleus
- else
- local list = new_node(math_sub) -- todo attr
- list.head = start_sub
- pointer.sub = list
- end
- if mode == "sub" then
- pointer.next = stop_sub.next
- end
- stop_sub.next = nil
- end
- -- we could return stop
-end
-
-unscript[math_char] = replace -- not noads as we need to recurse
-
-function handlers.unscript(head,style,penalties)
- processnoads(head,unscript,"unscript")
- return true
-end
-
-statistics.register("math script replacements", function()
- if next(replaced) then
- local n, t = 0, { }
- for k, v in table.sortedpairs(replaced) do
- n = n + v
- t[#t+1] = formatters["%C"](k)
- end
- return formatters["% t (n=%s)"](t,n)
- end
-end)
-
--- math alternates: (in xits lgf: $ABC$ $\cal ABC$ $\mathalternate{cal}\cal ABC$)
--- math alternates: (in lucidanova lgf: $ABC \mathalternate{italic} ABC$)
-
--- todo: set alternate for specific symbols
-
-local function initializemathalternates(tfmdata)
- local goodies = tfmdata.goodies
- if goodies then
- local shared = tfmdata.shared
- 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
- local lastattribute, attributes = 0, { }
- for k, v in next, alternates do
- lastattribute = lastattribute + 1
- v.attribute = lastattribute
- attributes[lastattribute] = v
- end
- shared.mathalternates = alternates -- to be checked if shared is ok here
- shared.mathalternatesattributes = attributes -- to be checked if shared is ok here
- return
- end
- end
- end
-end
-
-registerotffeature {
- name = "mathalternates",
- description = "additional math alternative shapes",
- initializers = {
- base = initializemathalternates,
- node = initializemathalternates,
- }
-}
-
-local getalternate = otf.getalternate
-
-local a_mathalternate = attributes.private("mathalternate")
-
-local alternate = { } -- processors.alternate = alternate
-
-function mathematics.setalternate(fam,tag)
- local id = font_of_family(fam)
- local tfmdata = fontdata[id]
- local mathalternates = tfmdata.shared and tfmdata.shared.mathalternates
- if mathalternates then
- local m = mathalternates[tag]
- tex.attribute[a_mathalternate] = m and m.attribute or unsetvalue
- end
-end
-
-alternate[math_char] = function(pointer)
- local a = pointer[a_mathalternate]
- if a and a > 0 then
- pointer[a_mathalternate] = 0
- local tfmdata = fontdata[font_of_family(pointer.fam)] -- we can also have a famdata
- local mathalternatesattributes = tfmdata.shared.mathalternatesattributes
- if mathalternatesattributes then
- local what = mathalternatesattributes[a]
- local alt = getalternate(tfmdata,pointer.char,what.feature,what.value)
- if alt then
- if trace_alternates then
- report_alternates("alternate %a, value %a, replacing glyph %U by glyph %U",
- tostring(what.feature),tostring(what.value),pointer.char,alt)
- end
- pointer.char = alt
- end
- end
- end
-end
-
-function handlers.check(head,style,penalties)
- processnoads(head,alternate,"check")
- return true
-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)
-
-local a_mathitalics = attributes.private("mathitalics")
-
-local italics = { }
-local default_factor = 1/20
-
-local function getcorrection(method,font,char) -- -- or character.italic -- (this one is for tex)
-
- local correction, fromvisual
-
- if method == 1 then
- -- only font data triggered by fontitalics
- local italics = fontitalics[font]
- if italics then
- local character = fontcharacters[font][char]
- if character then
- correction = character.italic_correction
- if correction and correction ~= 0 then
- return correction, false
- end
- end
- end
- elseif method == 2 then
- -- only font data triggered by fontdata
- local character = fontcharacters[font][char]
- if character then
- correction = character.italic_correction
- if correction and correction ~= 0 then
- return correction, false
- end
- end
- elseif method == 3 then
- -- only quad based by selective
- local visual = chardata[char].visual
- if not visual then
- -- skip
- elseif visual == "it" or visual == "bi" then
- correction = fontproperties[font].mathitalic_defaultvalue or default_factor*fontemwidths[font]
- if correction and correction ~= 0 then
- return correction, true
- end
- end
- elseif method == 4 then
- -- combination of 1 and 3
- local italics = fontitalics[font]
- if italics then
- local character = fontcharacters[font][char]
- if character then
- correction = character.italic_correction
- if correction and correction ~= 0 then
- return correction, false
- end
- end
- end
- if not correction then
- local visual = chardata[char].visual
- if not visual then
- -- skip
- elseif visual == "it" or visual == "bi" then
- correction = fontproperties[font].mathitalic_defaultvalue or default_factor*fontemwidths[font]
- if correction and correction ~= 0 then
- return correction, true
- end
- end
- end
- end
-
-end
-
-local function insert_kern(current,kern)
- local sub = new_node(math_sub) -- todo: pool
- local noad = new_node(math_noad) -- todo: pool
- sub.head = kern
- kern.next = noad
- noad.nucleus = current
- return sub
-end
-
-local setcolor = nodes.tracers.colors.set
-local italic_kern = new_kern
-local c_positive_d = "trace:db"
-local c_negative_d = "trace:dr"
-
-trackers.register("math.italics", function(v)
- if v then
- italic_kern = function(k,font)
- local ex = 1.5 * fontexheights[font]
- if k > 0 then
- return setcolor(new_rule(k,ex,ex),c_positive_d)
- else
- return concat_nodes {
- old_kern(k),
- setcolor(new_rule(-k,ex,ex),c_negative_d),
- old_kern(k),
- }
- end
- end
- else
- italic_kern = new_kern
- end
-end)
-
-italics[math_char] = function(pointer,what,n,parent)
- local method = pointer[a_mathitalics]
- if method and method > 0 then
- local char = pointer.char
- local font = font_of_family(pointer.fam) -- todo: table
- local correction, visual = getcorrection(method,font,char)
- if correction then
- local pid = parent.id
- local sub, sup
- if pid == math_noad then
- sup = parent.sup
- sub = parent.sub
- end
- if sup or sub then
- local subtype = parent.subtype
- if subtype == noad_oplimits then
- if sup then
- parent.sup = insert_kern(sup,italic_kern(correction,font))
- if trace_italics then
- report_italics("method %a, adding %p italic correction for upper limit of %C",method,correction,char)
- end
- end
- if sub then
- local correction = - correction
- parent.sub = insert_kern(sub,italic_kern(correction,font))
- if trace_italics then
- report_italics("method %a, adding %p italic correction for lower limit of %C",method,correction,char)
- end
- end
- else
- if sup then
- parent.sup = insert_kern(sup,italic_kern(correction,font))
- if trace_italics then
- report_italics("method %a, adding %p italic correction before superscript after %C",method,correction,char)
- end
- end
- end
- else
- local next_noad = parent.next
- 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, adding %p italic correction between %C and end math",method,correctio,char)
- end
- insert_node_after(parent,parent,italic_kern(correction,font))
- end
- elseif next_noad.id == math_noad then
- local next_subtype = next_noad.subtype
- if next_subtype == noad_punct or next_subtype == noad_ord then
- local next_nucleus = next_noad.nucleus
- if next_nucleus.id == math_char then
- local next_char = next_nucleus.char
- local next_data = chardata[next_char]
- local visual = next_data.visual
- if visual == "it" or visual == "bi" then
- -- if trace_italics then
- -- report_italics("method %a, skipping %p italic correction between italic %C and italic %C",method,correction,char,next_char)
- -- end
- else
- local category = next_data.category
- if category == "nd" or category == "ll" or category == "lu" then
- if trace_italics then
- report_italics("method %a, adding %p italic correction between italic %C and non italic %C",method,correction,char,next_char)
- end
- insert_node_after(parent,parent,italic_kern(correction,font))
- -- elseif next_data.height > (fontexheights[font]/2) then
- -- if trace_italics then
- -- report_italics("method %a, adding %p italic correction between %C and ascending %C",method,correction,char,next_char)
- -- end
- -- insert_node_after(parent,parent,italic_kern(correction,font))
- -- elseif trace_italics then
- -- -- report_italics("method %a, skipping %p italic correction between %C and %C",method,correction,char,next_char)
- end
- end
- end
- end
- end
- end
- end
- end
-end
-
-function handlers.italics(head,style,penalties)
- processnoads(head,italics,"italics")
- return true
-end
-
-local enable
-
-enable = function()
- tasks.enableaction("math", "noads.handlers.italics")
- if trace_italics then
- report_italics("enabling math italics")
- end
- enable = false
-end
-
--- best do this only on math mode (less overhead)
-
-function mathematics.setitalics(n)
- if enable then
- enable()
- end
- if n == variables.reset then
- texattribute[a_mathitalics] = unsetvalue
- else
- texattribute[a_mathitalics] = tonumber(n) or unsetvalue
- end
-end
-
-function mathematics.resetitalics()
- texattribute[a_mathitalics] = unsetvalue
-end
-
--- variants
-
-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[math_char] = function(pointer,what,n,parent) -- also set export value
- local char = pointer.char
- local selector = validvariants[char]
- if selector then
- local next = parent.next
- if next and next.id == math_noad then
- local nucleus = next.nucleus
- if nucleus and nucleus.id == math_char and nucleus.char == selector then
- local variant
- local tfmdata = fontdata[font_of_family(pointer.fam)] -- we can also have a famdata
- local mathvariants = tfmdata.resources.variants -- and variantdata
- if mathvariants then
- mathvariants = mathvariants[selector]
- if mathvariants then
- variant = mathvariants[char]
- end
- end
- if variant then
- pointer.char = variant
- 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
- next.prev = pointer
- parent.next = next.next
- free_node(next)
- end
- end
- end
-end
-
-function handlers.variants(head,style,penalties)
- processnoads(head,variants,"unicode variant")
- return true
-end
-
--- the normal builder
-
-function builders.kernel.mlist_to_hlist(head,style,penalties)
- return mlist_to_hlist(head,style,penalties), true
-end
-
--- function builders.kernel.mlist_to_hlist(head,style,penalties)
--- print("!!!!!!! BEFORE",penalties)
--- for n in node.traverse(head) do print(n) end
--- print("!!!!!!!")
--- head = mlist_to_hlist(head,style,penalties)
--- print("!!!!!!! AFTER")
--- for n in node.traverse(head) do print(n) end
--- print("!!!!!!!")
--- return head, true
--- end
-
-tasks.new {
- name = "math",
- arguments = 2,
- processor = utilities.sequencers.nodeprocessor,
- sequence = {
- "before",
- "normalizers",
- "builders",
- "after",
- },
-}
-
-tasks.freezegroup("math", "normalizers") -- experimental
-tasks.freezegroup("math", "builders") -- experimental
-
-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)
- local head, done = actions(head,style,penalties)
- stoptiming(noads)
- return head, done
-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)
-
--- interface
-
-commands.setmathalternate = mathematics.setalternate
-commands.setmathitalics = mathematics.setitalics
-commands.resetmathitalics = mathematics.resetitalics
+if not modules then modules = { } end modules ['math-noa'] = {
+ 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"
+}
+
+-- beware: this is experimental code and there will be a more
+-- generic (attribute value driven) interface too but for the
+-- moment this is ok
+--
+-- 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
+
+local utfchar, utfbyte = utf.char, utf.byte
+local formatters = string.formatters
+
+local fonts, nodes, node, mathematics = fonts, nodes, node, mathematics
+
+local otf = fonts.handlers.otf
+local otffeatures = fonts.constructors.newfeatures("otf")
+local registerotffeature = otffeatures.register
+
+local trace_remapping = false trackers.register("math.remapping", function(v) trace_remapping = v end)
+local trace_processing = false trackers.register("math.processing", function(v) trace_processing = v end)
+local trace_analyzing = false trackers.register("math.analyzing", function(v) trace_analyzing = v end)
+local trace_normalizing = false trackers.register("math.normalizing", function(v) trace_normalizing = v end)
+local trace_collapsing = false trackers.register("math.collapsing", function(v) trace_collapsing = v end)
+local trace_goodies = false trackers.register("math.goodies", function(v) trace_goodies = v end)
+local trace_variants = false trackers.register("math.variants", function(v) trace_variants = v end)
+local trace_alternates = false trackers.register("math.alternates", function(v) trace_alternates = v end)
+local trace_italics = false trackers.register("math.italics", function(v) trace_italics = v end)
+local trace_families = false trackers.register("math.families", function(v) trace_families = v end)
+
+local check_coverage = true directives.register("math.checkcoverage", function(v) check_coverage = v end)
+
+local report_processing = logs.reporter("mathematics","processing")
+local report_remapping = logs.reporter("mathematics","remapping")
+local report_normalizing = logs.reporter("mathematics","normalizing")
+local report_collapsing = logs.reporter("mathematics","collapsing")
+local report_goodies = logs.reporter("mathematics","goodies")
+local report_variants = logs.reporter("mathematics","variants")
+local report_alternates = logs.reporter("mathematics","alternates")
+local report_italics = logs.reporter("mathematics","italics")
+local report_families = logs.reporter("mathematics","families")
+
+local a_mathrendering = attributes.private("mathrendering")
+local a_exportstatus = attributes.private("exportstatus")
+
+local mlist_to_hlist = node.mlist_to_hlist
+local font_of_family = node.family_font
+local insert_node_after = node.insert_after
+local insert_node_before = node.insert_before
+local free_node = node.free
+local new_node = node.new -- todo: pool: math_noad math_sub
+
+local new_kern = nodes.pool.kern
+local new_rule = nodes.pool.rule
+local concat_nodes = nodes.concat
+
+local topoints = number.points
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+local fontcharacters = fonthashes.characters
+local fontproperties = fonthashes.properties
+local fontitalics = fonthashes.italics
+local fontemwidths = fonthashes.emwidths
+local fontexheights = fonthashes.exheights
+
+local variables = interfaces.variables
+local texattribute = tex.attribute
+local unsetvalue = attributes.unsetvalue
+
+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 nodecodes = nodes.nodecodes
+local noadcodes = nodes.noadcodes
+
+local noad_ord = noadcodes.ord
+local noad_rel = noadcodes.rel
+local noad_punct = noadcodes.punct
+local noad_opdisplaylimits= noadcodes.opdisplaylimits
+local noad_oplimits = noadcodes.oplimits
+local noad_opnolimits = noadcodes.opnolimits
+
+local math_noad = nodecodes.noad -- attr nucleus sub sup
+local math_accent = nodecodes.accent -- attr nucleus sub sup accent
+local math_radical = nodecodes.radical -- attr nucleus sub sup left degree
+local math_fraction = nodecodes.fraction -- attr nucleus sub sup left right
+local math_box = nodecodes.subbox -- attr list
+local math_sub = nodecodes.submlist -- attr list
+local math_char = nodecodes.mathchar -- attr fam char
+local math_textchar = nodecodes.mathtextchar -- attr fam char
+local math_delim = nodecodes.delim -- attr small_fam small_char large_fam large_char
+local math_style = nodecodes.style -- attr style
+local math_choice = nodecodes.choice -- attr display text script scriptscript
+local math_fence = nodecodes.fence -- attr subtype
+
+local hlist_code = nodecodes.hlist
+local glyph_code = nodecodes.glyph
+
+local left_fence_code = 1
+
+local function process(start,what,n,parent)
+ if n then n = n + 1 else n = 0 end
+ while start do
+ local id = start.id
+ if trace_processing then
+ if id == math_noad then
+ report_processing("%w%S, class %a",n*2,start,noadcodes[start.subtype])
+ elseif id == math_char then
+ local char = start.char
+ local fam = start.fam
+ local font = font_of_family(fam)
+ report_processing("%w%S, family %a, font %a, char %a, shape %c",n*2,start,fam,font,char,char)
+ else
+ report_processing("%w%S",n*2,start)
+ end
+ end
+ local proc = what[id]
+ if proc then
+ -- report_processing("start processing")
+ local done, newstart = proc(start,what,n,parent) -- prev is bugged: or start.prev
+ if newstart then
+ start = newstart
+ -- report_processing("stop processing (new start)")
+ else
+ -- report_processing("stop processing")
+ end
+ elseif id == math_char or id == math_textchar or id == math_delim then
+ break
+ elseif id == math_noad then
+ local noad = start.nucleus if noad then process(noad,what,n,start) end -- list
+ noad = start.sup if noad then process(noad,what,n,start) end -- list
+ noad = start.sub if noad then process(noad,what,n,start) end -- list
+ elseif id == math_box or id == math_sub then
+ -- local noad = start.list if noad then process(noad,what,n,start) end -- list
+ local noad = start.head if noad then process(noad,what,n,start) end -- list
+ elseif id == math_fraction then
+ local noad = start.num if noad then process(noad,what,n,start) end -- list
+ noad = start.denom if noad then process(noad,what,n,start) end -- list
+ noad = start.left if noad then process(noad,what,n,start) end -- delimiter
+ noad = start.right if noad then process(noad,what,n,start) end -- delimiter
+ elseif id == math_choice then
+ local noad = start.display if noad then process(noad,what,n,start) end -- list
+ noad = start.text if noad then process(noad,what,n,start) end -- list
+ noad = start.script if noad then process(noad,what,n,start) end -- list
+ noad = start.scriptscript if noad then process(noad,what,n,start) end -- list
+ elseif id == math_fence then
+ local noad = start.delim if noad then process(noad,what,n,start) end -- delimiter
+ elseif id == math_radical then
+ local noad = start.nucleus if noad then process(noad,what,n,start) end -- list
+ noad = start.sup if noad then process(noad,what,n,start) end -- list
+ noad = start.sub if noad then process(noad,what,n,start) end -- list
+ noad = start.left if noad then process(noad,what,n,start) end -- delimiter
+ noad = start.degree if noad then process(noad,what,n,start) end -- list
+ elseif id == math_accent then
+ local noad = start.nucleus if noad then process(noad,what,n,start) end -- list
+ noad = start.sup if noad then process(noad,what,n,start) end -- list
+ noad = start.sub if noad then process(noad,what,n,start) end -- list
+ noad = start.accent if noad then process(noad,what,n,start) end -- list
+ noad = start.bot_accent if noad then process(noad,what,n,start) end -- list
+ elseif id == math_style then
+ -- has a next
+ else
+ -- glue, penalty, etc
+ end
+ start = start.next
+ end
+end
+
+local function processnoads(head,actions,banner)
+ if trace_processing then
+ report_processing("start %a",banner)
+ process(head,actions)
+ report_processing("stop %a",banner)
+ else
+ process(head,actions)
+ end
+end
+
+noads.process = processnoads
+
+-- experiment (when not present fall back to fam 0) -- needs documentation
+
+-- 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
+
+local families = { }
+local a_mathfamily = attributes.private("mathfamily")
+local boldmap = mathematics.boldmap
+
+local familymap = { [0] =
+ "regular",
+ "regular",
+ "regular",
+ "bold",
+ "bold",
+ "bold",
+ "pseudobold",
+ "pseudobold",
+ "pseudobold",
+}
+
+families[math_char] = function(pointer)
+ if pointer.fam == 0 then
+ local a = pointer[a_mathfamily]
+ if a and a > 0 then
+ pointer[a_mathfamily] = 0
+ if a > 5 then
+ local char = pointer.char
+ local bold = boldmap[char]
+ local newa = a - 3
+ if bold then
+ pointer[a_exportstatus] = char
+ pointer.char = 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
+ else
+ 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
+ end
+ pointer.fam = newa
+ else
+ if trace_families then
+ local char = pointer.char
+ report_families("family of %C becomes %s with remap %s",char,a,familymap[a])
+ end
+ pointer.fam = a
+ end
+ else
+ -- pointer.fam = 0
+ end
+ end
+end
+
+families[math_delim] = function(pointer)
+ if pointer.small_fam == 0 then
+ local a = pointer[a_mathfamily]
+ if a and a > 0 then
+ pointer[a_mathfamily] = 0
+ if a > 5 then
+ -- no bold delimiters in unicode
+ a = a - 3
+ end
+ pointer.small_fam = a
+ pointer.large_fam = a
+ else
+ pointer.small_fam = 0
+ pointer.large_fam = 0
+ end
+ end
+end
+
+families[math_textchar] = families[math_char]
+
+function handlers.families(head,style,penalties)
+ processnoads(head,families,"families")
+ return true
+end
+
+-- character remapping
+
+local a_mathalphabet = attributes.private("mathalphabet")
+local a_mathgreek = attributes.private("mathgreek")
+
+processors.relocate = { }
+
+local function report_remap(tag,id,old,new,extra)
+ report_remapping("remapping %s in font %s from %C to %C%s",tag,id,old,new,extra)
+end
+
+local remapalphabets = mathematics.remapalphabets
+local fallbackstyleattr = mathematics.fallbackstyleattr
+local setnodecolor = nodes.tracers.colors.set
+
+local function checked(pointer)
+ local char = pointer.char
+ local fam = pointer.fam
+ local id = font_of_family(fam)
+ local tc = fontcharacters[id]
+ if not tc[char] then
+ local specials = characters.data[char].specials
+ if specials and (specials[1] == "char" or specials[1] == "font") then
+ newchar = specials[#specials]
+ if trace_remapping then
+ report_remap("fallback",id,char,newchar)
+ end
+ if trace_analyzing then
+ setnodecolor(pointer,"font:isol")
+ end
+ pointer[a_exportstatus] = char -- testcase: exponentiale
+ pointer.char = newchar
+ return true
+ end
+ end
+end
+
+processors.relocate[math_char] = function(pointer)
+ local g = pointer[a_mathgreek] or 0
+ local a = pointer[a_mathalphabet] or 0
+ if a > 0 or g > 0 then
+ if a > 0 then
+ pointer[a_mathgreek] = 0
+ end
+ if g > 0 then
+ pointer[a_mathalphabet] = 0
+ end
+ local char = pointer.char
+ local newchar = remapalphabets(char,a,g)
+ if newchar then
+ local fam = pointer.fam
+ local id = font_of_family(fam)
+ local characters = fontcharacters[id]
+ if characters[newchar] then
+ if trace_remapping then
+ report_remap("char",id,char,newchar)
+ end
+ if trace_analyzing then
+ setnodecolor(pointer,"font:isol")
+ end
+ pointer.char = 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",id,char,newchar," (fallback remapping used)")
+ end
+ if trace_analyzing then
+ setnodecolor(pointer,"font:isol")
+ end
+ pointer.char = newchar
+ return true
+ elseif trace_remapping then
+ report_remap("char",id,char,newchar," fails (no fallback character)")
+ end
+ elseif trace_remapping then
+ report_remap("char",id,char,newchar," fails (no fallback remap character)")
+ end
+ elseif trace_remapping then
+ report_remap("char",id,char,newchar," fails (no fallback style)")
+ end
+ end
+ end
+ end
+ if trace_analyzing then
+ setnodecolor(pointer,"font:medi")
+ end
+ if check_coverage then
+ return checked(pointer)
+ end
+end
+
+processors.relocate[math_textchar] = function(pointer)
+ if trace_analyzing then
+ setnodecolor(pointer,"font:init")
+ end
+end
+
+processors.relocate[math_delim] = function(pointer)
+ if trace_analyzing then
+ setnodecolor(pointer,"font:fina")
+ end
+end
+
+function handlers.relocate(head,style,penalties)
+ processnoads(head,processors.relocate,"relocate")
+ return true
+end
+
+-- rendering (beware, not exported)
+
+processors.render = { }
+
+local rendersets = mathematics.renderings.numbers or { } -- store
+
+processors.render[math_char] = function(pointer)
+ local attr = pointer[a_mathrendering]
+ if attr and attr > 0 then
+ local char = pointer.char
+ local renderset = rendersets[attr]
+ if renderset then
+ local newchar = renderset[char]
+ if newchar then
+ local fam = pointer.fam
+ local id = font_of_family(fam)
+ local characters = fontcharacters[id]
+ if characters and characters[newchar] then
+ pointer.char = newchar
+ pointer[a_exportstatus] = char
+ end
+ end
+ end
+ end
+end
+
+function handlers.render(head,style,penalties)
+ processnoads(head,processors.render,"render")
+ return true
+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
+
+local mathsize = attributes.private("mathsize")
+
+local resize = { } processors.resize = resize
+
+resize[math_fence] = function(pointer)
+ if pointer.subtype == left_fence_code then
+ local a = pointer[mathsize]
+ if a and a > 0 then
+ pointer[mathsize] = 0
+ local d = pointer.delim
+ local df = d.small_fam
+ local id = font_of_family(df)
+ if id > 0 then
+ local ch = d.small_char
+ d.small_char = mathematics.big(fontdata[id],ch,a)
+ end
+ end
+ end
+end
+
+function handlers.resize(head,style,penalties)
+ processnoads(head,resize,"resize")
+ return true
+end
+
+-- respacing
+
+-- local mathpunctuation = attributes.private("mathpunctuation")
+--
+-- local respace = { } processors.respace = respace
+
+-- only [nd,ll,ul][po][nd,ll,ul]
+
+-- respace[math_char] = function(pointer,what,n,parent) -- not math_noad .. math_char ... and then parent
+-- pointer = parent
+-- if pointer and pointer.subtype == noad_ord then
+-- local a = pointer[mathpunctuation]
+-- if a and a > 0 then
+-- pointer[mathpunctuation] = 0
+-- local current_nucleus = pointer.nucleus
+-- if current_nucleus.id == math_char then
+-- local current_char = current_nucleus.char
+-- local fc = chardata[current_char]
+-- fc = fc and fc.category
+-- if fc == "nd" or fc == "ll" or fc == "lu" then
+-- local next_noad = pointer.next
+-- if next_noad and next_noad.id == math_noad and next_noad.subtype == noad_punct then
+-- local next_nucleus = next_noad.nucleus
+-- if next_nucleus.id == math_char then
+-- local next_char = next_nucleus.char
+-- local nc = chardata[next_char]
+-- nc = nc and nc.category
+-- if nc == "po" then
+-- local last_noad = next_noad.next
+-- if last_noad and last_noad.id == math_noad and last_noad.subtype == noad_ord then
+-- local last_nucleus = last_noad.nucleus
+-- if last_nucleus.id == math_char then
+-- local last_char = last_nucleus.char
+-- local lc = chardata[last_char]
+-- lc = lc and lc.category
+-- if lc == "nd" or lc == "ll" or lc == "lu" then
+-- local ord = new_node(math_noad) -- todo: pool
+-- ord.subtype, ord.nucleus, ord.sub, ord.sup, ord.attr = noad_ord, next_noad.nucleus, next_noad.sub, next_noad.sup, next_noad.attr
+-- -- next_noad.nucleus, next_noad.sub, next_noad.sup, next_noad.attr = nil, nil, nil, nil
+-- next_noad.nucleus, next_noad.sub, next_noad.sup = nil, nil, nil -- else crash with attributes ref count
+-- --~ next_noad.attr = nil
+-- ord.next = last_noad
+-- pointer.next = ord
+-- free_node(next_noad)
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+
+-- local comma = 0x002C
+-- local period = 0x002E
+--
+-- respace[math_char] = function(pointer,what,n,parent)
+-- pointer = parent
+-- if pointer and pointer.subtype == noad_punct then
+-- local current_nucleus = pointer.nucleus
+-- if current_nucleus.id == math_char then
+-- local current_nucleus = pointer.nucleus
+-- if current_nucleus.id == math_char then
+-- local current_char = current_nucleus.char
+-- local a = pointer[mathpunctuation]
+-- if not a or a == 0 then
+-- if current_char == comma then
+-- -- default tex: 2,5 or 2, 5 --> 2, 5
+-- elseif current_char == period then
+-- -- default tex: 2.5 or 2. 5 --> 2.5
+-- pointer.subtype = noad_ord
+-- end
+-- elseif a == 1 then
+-- local next_noad = pointer.next
+-- if next_noad and next_noad.id == math_noad then
+-- local next_nucleus = next_noad.nucleus
+-- if next_nucleus.id == math_char and next_nucleus.char == 0 then
+-- nodes.remove(pointer,next_noad,true)
+-- end
+-- if current_char == comma then
+-- -- default tex: 2,5 or 2, 5 --> 2, 5
+-- elseif current_char == period then
+-- -- default tex: 2.5 or 2. 5 --> 2.5
+-- pointer.subtype = noad_ord
+-- end
+-- end
+-- elseif a == 2 then
+-- if current_char == comma or current_char == period then
+-- local next_noad = pointer.next
+-- if next_noad and next_noad.id == math_noad then
+-- local next_nucleus = next_noad.nucleus
+-- if next_nucleus.id == math_char and next_nucleus.char == 0 then
+-- if current_char == comma then
+-- -- adaptive: 2, 5 --> 2, 5
+-- elseif current_char == period then
+-- -- adaptive: 2. 5 --> 2. 5
+-- end
+-- nodes.remove(pointer,next_noad,true)
+-- else
+-- if current_char == comma then
+-- -- adaptive: 2,5 --> 2,5
+-- pointer.subtype = noad_ord
+-- elseif current_char == period then
+-- -- adaptive: 2.5 --> 2.5
+-- pointer.subtype = noad_ord
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+-- end
+--
+-- function handlers.respace(head,style,penalties)
+-- processnoads(head,respace,"respace")
+-- return true
+-- end
+
+-- The following code is dedicated to Luigi Scarso who pointed me
+-- to the fact that \not= is not producing valid pdf-a code.
+-- The code does not solve this for virtual characters but it does
+-- a decent job on collapsing so that fonts that have the right
+-- glyph will have a decent unicode point. In the meantime this code
+-- has been moved elsewhere.
+
+local collapse = { } processors.collapse = collapse
+
+local mathpairs = characters.mathpairs
+
+mathpairs[0x2032] = { [0x2032] = 0x2033, [0x2033] = 0x2034 } -- (prime,prime) (prime,doubleprime)
+mathpairs[0x2033] = { [0x2032] = 0x2034 } -- (doubleprime,prime)
+
+mathpairs[0x222B] = { [0x222B] = 0x222C, [0x222C] = 0x222D }
+mathpairs[0x222C] = { [0x222B] = 0x222D }
+
+mathpairs[0x007C] = { [0x007C] = 0x2016 } -- double bars
+
+local validpair = {
+ [noad_rel] = true,
+ [noad_ord] = true,
+ [noad_opdisplaylimits] = true,
+ [noad_oplimits] = true,
+ [noad_opnolimits] = true,
+}
+
+local function collapsepair(pointer,what,n,parent) -- todo: switch to turn in on and off
+ if parent then
+ if validpair[parent.subtype] then
+ local current_nucleus = parent.nucleus
+ if not parent.sub and not parent.sup and current_nucleus.id == math_char then
+ local current_char = current_nucleus.char
+ local mathpair = mathpairs[current_char]
+ if mathpair then
+ local next_noad = parent.next
+ if next_noad and next_noad.id == math_noad then
+ if validpair[next_noad.subtype] then
+ local next_nucleus = next_noad.nucleus
+ if next_nucleus.id == math_char then
+ local next_char = next_nucleus.char
+ local newchar = mathpair[next_char]
+ if newchar then
+ local fam = current_nucleus.fam
+ local id = font_of_family(fam)
+ local characters = fontcharacters[id]
+ if characters and characters[newchar] then
+ if trace_collapsing then
+ report_collapsing("%U + %U => %U",current_char,next_char,newchar)
+ end
+ current_nucleus.char = newchar
+ local next_next_noad = next_noad.next
+ if next_next_noad then
+ parent.next = next_next_noad
+ next_next_noad.prev = parent
+ else
+ parent.next = nil
+ end
+ parent.sup = next_noad.sup
+ parent.sub = next_noad.sub
+ next_noad.sup = nil
+ next_noad.sub = nil
+ free_node(next_noad)
+ collapsepair(pointer,what,n,parent)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+collapse[math_char] = collapsepair
+
+function noads.handlers.collapse(head,style,penalties)
+ processnoads(head,collapse,"collapse")
+ return true
+end
+
+-- normalize scripts
+
+local unscript = { } noads.processors.unscript = unscript
+
+local superscripts = characters.superscripts
+local subscripts = characters.subscripts
+
+local replaced = { }
+
+local function replace(pointer,what,n,parent)
+ pointer = parent -- we're following the parent list (chars trigger this)
+ local next = pointer.next
+ local start_super, stop_super, start_sub, stop_sub
+ local mode = "unset"
+ while next and next.id == math_noad do
+ local nextnucleus = next.nucleus
+ if nextnucleus and nextnucleus.id == math_char and not next.sub and not next.sup then
+ local char = nextnucleus.char
+ 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 = next.next
+ nextnucleus.char = 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 = next.next
+ nextnucleus.char = 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
+ pointer.sup = start_super.nucleus
+ else
+ local list = new_node(math_sub) -- todo attr
+ list.head = start_super
+ pointer.sup = list
+ end
+ if mode == "super" then
+ pointer.next = stop_super.next
+ end
+ stop_super.next = nil
+ end
+ if start_sub then
+ if start_sub == stop_sub then
+ pointer.sub = start_sub.nucleus
+ else
+ local list = new_node(math_sub) -- todo attr
+ list.head = start_sub
+ pointer.sub = list
+ end
+ if mode == "sub" then
+ pointer.next = stop_sub.next
+ end
+ stop_sub.next = nil
+ end
+ -- we could return stop
+end
+
+unscript[math_char] = replace -- not noads as we need to recurse
+
+function handlers.unscript(head,style,penalties)
+ processnoads(head,unscript,"unscript")
+ return true
+end
+
+statistics.register("math script replacements", function()
+ if next(replaced) then
+ local n, t = 0, { }
+ for k, v in table.sortedpairs(replaced) do
+ n = n + v
+ t[#t+1] = formatters["%C"](k)
+ end
+ return formatters["% t (n=%s)"](t,n)
+ end
+end)
+
+-- math alternates: (in xits lgf: $ABC$ $\cal ABC$ $\mathalternate{cal}\cal ABC$)
+-- math alternates: (in lucidanova lgf: $ABC \mathalternate{italic} ABC$)
+
+-- todo: set alternate for specific symbols
+
+local function initializemathalternates(tfmdata)
+ local goodies = tfmdata.goodies
+ if goodies then
+ local shared = tfmdata.shared
+ 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
+ local lastattribute, attributes = 0, { }
+ for k, v in next, alternates do
+ lastattribute = lastattribute + 1
+ v.attribute = lastattribute
+ attributes[lastattribute] = v
+ end
+ shared.mathalternates = alternates -- to be checked if shared is ok here
+ shared.mathalternatesattributes = attributes -- to be checked if shared is ok here
+ return
+ end
+ end
+ end
+end
+
+registerotffeature {
+ name = "mathalternates",
+ description = "additional math alternative shapes",
+ initializers = {
+ base = initializemathalternates,
+ node = initializemathalternates,
+ }
+}
+
+local getalternate = otf.getalternate
+
+local a_mathalternate = attributes.private("mathalternate")
+
+local alternate = { } -- processors.alternate = alternate
+
+function mathematics.setalternate(fam,tag)
+ local id = font_of_family(fam)
+ local tfmdata = fontdata[id]
+ local mathalternates = tfmdata.shared and tfmdata.shared.mathalternates
+ if mathalternates then
+ local m = mathalternates[tag]
+ tex.attribute[a_mathalternate] = m and m.attribute or unsetvalue
+ end
+end
+
+alternate[math_char] = function(pointer)
+ local a = pointer[a_mathalternate]
+ if a and a > 0 then
+ pointer[a_mathalternate] = 0
+ local tfmdata = fontdata[font_of_family(pointer.fam)] -- we can also have a famdata
+ local mathalternatesattributes = tfmdata.shared.mathalternatesattributes
+ if mathalternatesattributes then
+ local what = mathalternatesattributes[a]
+ local alt = getalternate(tfmdata,pointer.char,what.feature,what.value)
+ if alt then
+ if trace_alternates then
+ report_alternates("alternate %a, value %a, replacing glyph %U by glyph %U",
+ tostring(what.feature),tostring(what.value),pointer.char,alt)
+ end
+ pointer.char = alt
+ end
+ end
+ end
+end
+
+function handlers.check(head,style,penalties)
+ processnoads(head,alternate,"check")
+ return true
+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)
+
+local a_mathitalics = attributes.private("mathitalics")
+
+local italics = { }
+local default_factor = 1/20
+
+local function getcorrection(method,font,char) -- -- or character.italic -- (this one is for tex)
+
+ local correction, fromvisual
+
+ if method == 1 then
+ -- only font data triggered by fontitalics
+ local italics = fontitalics[font]
+ if italics then
+ local character = fontcharacters[font][char]
+ if character then
+ correction = character.italic_correction
+ if correction and correction ~= 0 then
+ return correction, false
+ end
+ end
+ end
+ elseif method == 2 then
+ -- only font data triggered by fontdata
+ local character = fontcharacters[font][char]
+ if character then
+ correction = character.italic_correction
+ if correction and correction ~= 0 then
+ return correction, false
+ end
+ end
+ elseif method == 3 then
+ -- only quad based by selective
+ local visual = chardata[char].visual
+ if not visual then
+ -- skip
+ elseif visual == "it" or visual == "bi" then
+ correction = fontproperties[font].mathitalic_defaultvalue or default_factor*fontemwidths[font]
+ if correction and correction ~= 0 then
+ return correction, true
+ end
+ end
+ elseif method == 4 then
+ -- combination of 1 and 3
+ local italics = fontitalics[font]
+ if italics then
+ local character = fontcharacters[font][char]
+ if character then
+ correction = character.italic_correction
+ if correction and correction ~= 0 then
+ return correction, false
+ end
+ end
+ end
+ if not correction then
+ local visual = chardata[char].visual
+ if not visual then
+ -- skip
+ elseif visual == "it" or visual == "bi" then
+ correction = fontproperties[font].mathitalic_defaultvalue or default_factor*fontemwidths[font]
+ if correction and correction ~= 0 then
+ return correction, true
+ end
+ end
+ end
+ end
+
+end
+
+local function insert_kern(current,kern)
+ local sub = new_node(math_sub) -- todo: pool
+ local noad = new_node(math_noad) -- todo: pool
+ sub.head = kern
+ kern.next = noad
+ noad.nucleus = current
+ return sub
+end
+
+local setcolor = nodes.tracers.colors.set
+local italic_kern = new_kern
+local c_positive_d = "trace:db"
+local c_negative_d = "trace:dr"
+
+trackers.register("math.italics", function(v)
+ if v then
+ italic_kern = function(k,font)
+ local ex = 1.5 * fontexheights[font]
+ if k > 0 then
+ return setcolor(new_rule(k,ex,ex),c_positive_d)
+ else
+ return concat_nodes {
+ old_kern(k),
+ setcolor(new_rule(-k,ex,ex),c_negative_d),
+ old_kern(k),
+ }
+ end
+ end
+ else
+ italic_kern = new_kern
+ end
+end)
+
+italics[math_char] = function(pointer,what,n,parent)
+ local method = pointer[a_mathitalics]
+ if method and method > 0 then
+ local char = pointer.char
+ local font = font_of_family(pointer.fam) -- todo: table
+ local correction, visual = getcorrection(method,font,char)
+ if correction then
+ local pid = parent.id
+ local sub, sup
+ if pid == math_noad then
+ sup = parent.sup
+ sub = parent.sub
+ end
+ if sup or sub then
+ local subtype = parent.subtype
+ if subtype == noad_oplimits then
+ if sup then
+ parent.sup = insert_kern(sup,italic_kern(correction,font))
+ if trace_italics then
+ report_italics("method %a, adding %p italic correction for upper limit of %C",method,correction,char)
+ end
+ end
+ if sub then
+ local correction = - correction
+ parent.sub = insert_kern(sub,italic_kern(correction,font))
+ if trace_italics then
+ report_italics("method %a, adding %p italic correction for lower limit of %C",method,correction,char)
+ end
+ end
+ else
+ if sup then
+ parent.sup = insert_kern(sup,italic_kern(correction,font))
+ if trace_italics then
+ report_italics("method %a, adding %p italic correction before superscript after %C",method,correction,char)
+ end
+ end
+ end
+ else
+ local next_noad = parent.next
+ 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, adding %p italic correction between %C and end math",method,correctio,char)
+ end
+ insert_node_after(parent,parent,italic_kern(correction,font))
+ end
+ elseif next_noad.id == math_noad then
+ local next_subtype = next_noad.subtype
+ if next_subtype == noad_punct or next_subtype == noad_ord then
+ local next_nucleus = next_noad.nucleus
+ if next_nucleus.id == math_char then
+ local next_char = next_nucleus.char
+ local next_data = chardata[next_char]
+ local visual = next_data.visual
+ if visual == "it" or visual == "bi" then
+ -- if trace_italics then
+ -- report_italics("method %a, skipping %p italic correction between italic %C and italic %C",method,correction,char,next_char)
+ -- end
+ else
+ local category = next_data.category
+ if category == "nd" or category == "ll" or category == "lu" then
+ if trace_italics then
+ report_italics("method %a, adding %p italic correction between italic %C and non italic %C",method,correction,char,next_char)
+ end
+ insert_node_after(parent,parent,italic_kern(correction,font))
+ -- elseif next_data.height > (fontexheights[font]/2) then
+ -- if trace_italics then
+ -- report_italics("method %a, adding %p italic correction between %C and ascending %C",method,correction,char,next_char)
+ -- end
+ -- insert_node_after(parent,parent,italic_kern(correction,font))
+ -- elseif trace_italics then
+ -- -- report_italics("method %a, skipping %p italic correction between %C and %C",method,correction,char,next_char)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+function handlers.italics(head,style,penalties)
+ processnoads(head,italics,"italics")
+ return true
+end
+
+local enable
+
+enable = function()
+ tasks.enableaction("math", "noads.handlers.italics")
+ if trace_italics then
+ report_italics("enabling math italics")
+ end
+ enable = false
+end
+
+-- best do this only on math mode (less overhead)
+
+function mathematics.setitalics(n)
+ if enable then
+ enable()
+ end
+ if n == variables.reset then
+ texattribute[a_mathitalics] = unsetvalue
+ else
+ texattribute[a_mathitalics] = tonumber(n) or unsetvalue
+ end
+end
+
+function mathematics.resetitalics()
+ texattribute[a_mathitalics] = unsetvalue
+end
+
+-- variants
+
+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[math_char] = function(pointer,what,n,parent) -- also set export value
+ local char = pointer.char
+ local selector = validvariants[char]
+ if selector then
+ local next = parent.next
+ if next and next.id == math_noad then
+ local nucleus = next.nucleus
+ if nucleus and nucleus.id == math_char and nucleus.char == selector then
+ local variant
+ local tfmdata = fontdata[font_of_family(pointer.fam)] -- we can also have a famdata
+ local mathvariants = tfmdata.resources.variants -- and variantdata
+ if mathvariants then
+ mathvariants = mathvariants[selector]
+ if mathvariants then
+ variant = mathvariants[char]
+ end
+ end
+ if variant then
+ pointer.char = variant
+ 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
+ next.prev = pointer
+ parent.next = next.next
+ free_node(next)
+ end
+ end
+ end
+end
+
+function handlers.variants(head,style,penalties)
+ processnoads(head,variants,"unicode variant")
+ return true
+end
+
+-- the normal builder
+
+function builders.kernel.mlist_to_hlist(head,style,penalties)
+ return mlist_to_hlist(head,style,penalties), true
+end
+
+-- function builders.kernel.mlist_to_hlist(head,style,penalties)
+-- print("!!!!!!! BEFORE",penalties)
+-- for n in node.traverse(head) do print(n) end
+-- print("!!!!!!!")
+-- head = mlist_to_hlist(head,style,penalties)
+-- print("!!!!!!! AFTER")
+-- for n in node.traverse(head) do print(n) end
+-- print("!!!!!!!")
+-- return head, true
+-- end
+
+tasks.new {
+ name = "math",
+ arguments = 2,
+ processor = utilities.sequencers.nodeprocessor,
+ sequence = {
+ "before",
+ "normalizers",
+ "builders",
+ "after",
+ },
+}
+
+tasks.freezegroup("math", "normalizers") -- experimental
+tasks.freezegroup("math", "builders") -- experimental
+
+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)
+ local head, done = actions(head,style,penalties)
+ stoptiming(noads)
+ return head, done
+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)
+
+-- interface
+
+commands.setmathalternate = mathematics.setalternate
+commands.setmathitalics = mathematics.setitalics
+commands.resetmathitalics = mathematics.resetitalics
diff --git a/tex/context/base/math-ren.lua b/tex/context/base/math-ren.lua
index 348d8a2d9..2e7dba13d 100644
--- a/tex/context/base/math-ren.lua
+++ b/tex/context/base/math-ren.lua
@@ -1,69 +1,69 @@
-if not modules then modules = { } end modules ['math-ren'] = {
- version = 1.001,
- comment = "companion to math-ren.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local next = next
-local gsub = string.gsub
-
-local settings_to_array = utilities.parsers.settings_to_array
-local allocate = storage.allocate
-
-local renderings = { }
-mathematics.renderings = renderings
-
-local mappings = allocate()
-renderings.mappings = mappings
-
-local numbers = allocate()
-renderings.numbers = numbers
-
-local sets = allocate()
-renderings.sets = sets
-
-mappings["blackboard-to-bold"] = {
- [0x1D538] = 0x1D400, [0x1D539] = 0x1D401, [0x02102] = 0x1D402, [0x1D53B] = 0x1D403, [0x1D53C] = 0x1D404,
- [0x1D53D] = 0x1D405, [0x1D53E] = 0x1D406, [0x0210D] = 0x1D407, [0x1D540] = 0x1D408, [0x1D541] = 0x1D409,
- [0x1D542] = 0x1D40A, [0x1D543] = 0x1D40B, [0x1D544] = 0x1D40C, [0x02115] = 0x1D40D, [0x1D546] = 0x1D40E,
- [0x02119] = 0x1D40F, [0x0211A] = 0x1D410, [0x0211D] = 0x1D411, [0x1D54A] = 0x1D412, [0x1D54B] = 0x1D413,
- [0x1D54C] = 0x1D414, [0x1D54D] = 0x1D415, [0x1D54E] = 0x1D416, [0x1D54F] = 0x1D417, [0x1D550] = 0x1D418,
- [0x02124] = 0x1D419,
-}
-
-local function renderset(list) -- order matters
- local tag = gsub(list," ","")
- local n = sets[tag]
- if not n then
- local list = settings_to_array(tag)
- local mapping = { }
- for i=1,#list do
- local m = mappings[list[i]]
- if m then
- for k, v in next, m do
- mapping[k] = v
- end
- end
- end
- if next(mapping) then
- n = #numbers + 1
- numbers[n] = mapping
- else
- n = attributes.unsetvalue
- end
- sets[tag] = n
- end
- return n
-end
-
-mathematics.renderset = renderset
-
-function commands.mathrenderset(list)
- context(renderset(list))
-end
-
--- function commands.setmatrendering(list)
--- tex.setattribute(renderset(list))
--- end
+if not modules then modules = { } end modules ['math-ren'] = {
+ version = 1.001,
+ comment = "companion to math-ren.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local next = next
+local gsub = string.gsub
+
+local settings_to_array = utilities.parsers.settings_to_array
+local allocate = storage.allocate
+
+local renderings = { }
+mathematics.renderings = renderings
+
+local mappings = allocate()
+renderings.mappings = mappings
+
+local numbers = allocate()
+renderings.numbers = numbers
+
+local sets = allocate()
+renderings.sets = sets
+
+mappings["blackboard-to-bold"] = {
+ [0x1D538] = 0x1D400, [0x1D539] = 0x1D401, [0x02102] = 0x1D402, [0x1D53B] = 0x1D403, [0x1D53C] = 0x1D404,
+ [0x1D53D] = 0x1D405, [0x1D53E] = 0x1D406, [0x0210D] = 0x1D407, [0x1D540] = 0x1D408, [0x1D541] = 0x1D409,
+ [0x1D542] = 0x1D40A, [0x1D543] = 0x1D40B, [0x1D544] = 0x1D40C, [0x02115] = 0x1D40D, [0x1D546] = 0x1D40E,
+ [0x02119] = 0x1D40F, [0x0211A] = 0x1D410, [0x0211D] = 0x1D411, [0x1D54A] = 0x1D412, [0x1D54B] = 0x1D413,
+ [0x1D54C] = 0x1D414, [0x1D54D] = 0x1D415, [0x1D54E] = 0x1D416, [0x1D54F] = 0x1D417, [0x1D550] = 0x1D418,
+ [0x02124] = 0x1D419,
+}
+
+local function renderset(list) -- order matters
+ local tag = gsub(list," ","")
+ local n = sets[tag]
+ if not n then
+ local list = settings_to_array(tag)
+ local mapping = { }
+ for i=1,#list do
+ local m = mappings[list[i]]
+ if m then
+ for k, v in next, m do
+ mapping[k] = v
+ end
+ end
+ end
+ if next(mapping) then
+ n = #numbers + 1
+ numbers[n] = mapping
+ else
+ n = attributes.unsetvalue
+ end
+ sets[tag] = n
+ end
+ return n
+end
+
+mathematics.renderset = renderset
+
+function commands.mathrenderset(list)
+ context(renderset(list))
+end
+
+-- function commands.setmatrendering(list)
+-- tex.setattribute(renderset(list))
+-- end
diff --git a/tex/context/base/math-tag.lua b/tex/context/base/math-tag.lua
index 3dafaaa2f..ab5902dd4 100644
--- a/tex/context/base/math-tag.lua
+++ b/tex/context/base/math-tag.lua
@@ -1,345 +1,345 @@
-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"
-}
-
--- use lpeg matchers
-
-local find, match = string.find, string.match
-local insert, remove = table.insert, table.remove
-
-local attributes, nodes = attributes, nodes
-
-local set_attributes = nodes.setattributes
-local traverse_nodes = node.traverse
-
-local nodecodes = nodes.nodecodes
-
-local math_noad_code = nodecodes.noad -- attr nucleus sub sup
-local math_accent_code = nodecodes.accent -- attr nucleus sub sup accent
-local math_radical_code = nodecodes.radical -- attr nucleus sub sup left degree
-local math_fraction_code = nodecodes.fraction -- attr nucleus sub sup left right
-local math_box_code = nodecodes.subbox -- attr list
-local math_sub_code = nodecodes.submlist -- attr list
-local math_char_code = nodecodes.mathchar -- attr fam char
-local math_textchar_code = nodecodes.mathtextchar -- attr fam char
-local math_delim_code = nodecodes.delim -- attr small_fam small_char large_fam large_char
-local math_style_code = nodecodes.style -- attr style
-local math_choice_code = nodecodes.choice -- attr display text script scriptscript
-local math_fence_code = nodecodes.fence -- attr subtype
-
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-local glyph_code = nodecodes.glyph
-local glue_code = nodecodes.glue
-
-local a_tagged = attributes.private('tagged')
-local a_exportstatus = attributes.private('exportstatus')
-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 getmathcode = tex.getmathcode
-local mathcodes = mathematics.codes
-local ordinary_code = mathcodes.ordinary
-local variable_code = mathcodes.variable
-
-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.
- local nucleus, sup, sub = start.nucleus, start.sup, start.sub
- if sub then
- if sup then
- start[a_tagged] = start_tagged("msubsup")
- process(nucleus)
- process(sub)
- process(sup)
- stop_tagged()
- else
- start[a_tagged] = start_tagged("msub")
- process(nucleus)
- process(sub)
- stop_tagged()
- end
- elseif sup then
- start[a_tagged] = start_tagged("msup")
- process(nucleus)
- process(sup)
- stop_tagged()
- else
- process(nucleus)
- end
-end
-
--- todo: check function here and keep attribute the same
-
--- todo: variants -> original
-
-local actionstack = { }
-
-process = function(start) -- we cannot use the processor as we have no finalizers (yet)
- while start do
- local id = start.id
- if id == math_char_code then
- local char = start.char
- -- check for code
- local a = start[a_mathcategory]
- if a then
- a = { detail = a }
- end
- local code = getmathcode(char)
- if code then
- code = code[1]
- end
- local tag
- if code == ordinary_code or code == variable_code 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
- start[a_tagged] = start_tagged(tag,a)
- stop_tagged()
- break -- okay?
- elseif id == math_textchar_code then
- -- check for code
- local a = start[a_mathcategory]
- if a then
- start[a_tagged] = start_tagged("ms",{ detail = a })
- else
- start[a_tagged] = start_tagged("ms")
- end
- stop_tagged()
- break
- elseif id == math_delim_code then
- -- check for code
- start[a_tagged] = start_tagged("mo")
- stop_tagged()
- break
- elseif id == math_style_code then
- -- has a next
- elseif id == math_noad_code then
- processsubsup(start)
- elseif id == math_box_code or id == hlist_code or id == vlist_code then
- -- keep an eye on math_box_code and see what ends up in there
- local attr = start[a_tagged]
- local last = attr and taglist[attr]
- if last and find(last[#last],"formulacaption[:%-]") then
- -- leave alone, will nicely move to the outer level
- else
- local text = start_tagged("mtext")
- start[a_tagged] = text
- local list = start.list
- if not list then
- -- empty list
- elseif not attr then
- -- box comes from strange place
- set_attributes(list,a_tagged,text)
- 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.
- local tagdata = taglist[attr]
- local common = #tagdata + 1
- local function runner(list) -- quite inefficient
- local cache = { } -- we can have nested unboxed mess so best local to runner
- for n in traverse_nodes(list) do
- local id = n.id
- local aa = n[a_tagged]
- if aa then
- local ac = cache[aa]
- if not ac then
- local tagdata = taglist[aa]
- 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
- n[a_tagged] = ac
- else
- n[a_tagged] = text
- end
- if id == hlist_code or id == vlist_code then
- runner(n.list)
- end
- end
- end
- runner(list)
- end
- stop_tagged()
- end
- elseif id == math_sub_code then
- local list = start.list
- if list then
- local attr = start[a_tagged]
- local last = attr and taglist[attr]
- local action = last and match(last[#last],"maction:(.-)%-")
- if action and action ~= "" then
- if actionstack[#actionstack] == action then
- start[a_tagged] = start_tagged("mrow")
- process(list)
- stop_tagged()
- else
- insert(actionstack,action)
- start[a_tagged] = start_tagged("mrow",{ detail = action })
- process(list)
- stop_tagged()
- remove(actionstack)
- end
- else
- start[a_tagged] = start_tagged("mrow")
- process(list)
- stop_tagged()
- end
- end
- elseif id == math_fraction_code then
- local num, denom, left, right = start.num, start.denom, start.left, start.right
- if left then
- left[a_tagged] = start_tagged("mo")
- process(left)
- stop_tagged()
- end
- start[a_tagged] = start_tagged("mfrac")
- process(num)
- process(denom)
- stop_tagged()
- if right then
- right[a_tagged] = start_tagged("mo")
- process(right)
- stop_tagged()
- end
- elseif id == math_choice_code then
- local display, text, script, scriptscript = start.display, start.text, start.script, 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 == math_fence_code then
- local delim = start.delim
- local subtype = start.subtype
- if subtype == 1 then
- -- left
- start[a_tagged] = start_tagged("mfenced")
- if delim then
- start[a_tagged] = start_tagged("mleft")
- process(delim)
- stop_tagged()
- end
- elseif subtype == 2 then
- -- middle
- if delim then
- start[a_tagged] = start_tagged("mmiddle")
- process(delim)
- stop_tagged()
- end
- elseif subtype == 3 then
- if delim then
- start[a_tagged] = start_tagged("mright")
- process(delim)
- stop_tagged()
- end
- stop_tagged()
- else
- -- can't happen
- end
- elseif id == math_radical_code then
- local left, degree = start.left, start.degree
- if left then
- start_tagged("")
- process(left) -- root symbol, ignored
- stop_tagged()
- end
- if degree then -- not good enough, can be empty mlist
- start[a_tagged] = start_tagged("mroot")
- processsubsup(start)
- process(degree)
- stop_tagged()
- else
- start[a_tagged] = start_tagged("msqrt")
- processsubsup(start)
- stop_tagged()
- end
- elseif id == math_accent_code then
- local accent, bot_accent = start.accent, start.bot_accent
- if bot_accent then
- if accent then
- start[a_tagged] = start_tagged("munderover",{ detail = "accent" })
- processsubsup(start)
- process(bot_accent)
- process(accent)
- stop_tagged()
- else
- start[a_tagged] = start_tagged("munder",{ detail = "accent" })
- processsubsup(start)
- process(bot_accent)
- stop_tagged()
- end
- elseif accent then
- start[a_tagged] = start_tagged("mover",{ detail = "accent" })
- processsubsup(start)
- process(accent)
- stop_tagged()
- else
- processsubsup(start)
- end
- elseif id == glue_code then
- start[a_tagged] = start_tagged("mspace")
- stop_tagged()
- else
- start[a_tagged] = start_tagged("merror", { detail = nodecodes[i] })
- stop_tagged()
- end
- start = start.next
- end
-end
-
-function noads.handlers.tags(head,style,penalties)
- local v_math = start_tagged("math")
- local v_mrow = start_tagged("mrow")
- local v_mode = head[a_mathmode]
- head[a_tagged] = v_math
- head[a_tagged] = v_mrow
- tags.setattributehash(v_math,"mode",v_mode == 1 and "display" or "inline")
- process(head)
- stop_tagged()
- stop_tagged()
- return true
-end
+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"
+}
+
+-- use lpeg matchers
+
+local find, match = string.find, string.match
+local insert, remove = table.insert, table.remove
+
+local attributes, nodes = attributes, nodes
+
+local set_attributes = nodes.setattributes
+local traverse_nodes = node.traverse
+
+local nodecodes = nodes.nodecodes
+
+local math_noad_code = nodecodes.noad -- attr nucleus sub sup
+local math_accent_code = nodecodes.accent -- attr nucleus sub sup accent
+local math_radical_code = nodecodes.radical -- attr nucleus sub sup left degree
+local math_fraction_code = nodecodes.fraction -- attr nucleus sub sup left right
+local math_box_code = nodecodes.subbox -- attr list
+local math_sub_code = nodecodes.submlist -- attr list
+local math_char_code = nodecodes.mathchar -- attr fam char
+local math_textchar_code = nodecodes.mathtextchar -- attr fam char
+local math_delim_code = nodecodes.delim -- attr small_fam small_char large_fam large_char
+local math_style_code = nodecodes.style -- attr style
+local math_choice_code = nodecodes.choice -- attr display text script scriptscript
+local math_fence_code = nodecodes.fence -- attr subtype
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local glyph_code = nodecodes.glyph
+local glue_code = nodecodes.glue
+
+local a_tagged = attributes.private('tagged')
+local a_exportstatus = attributes.private('exportstatus')
+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 getmathcode = tex.getmathcode
+local mathcodes = mathematics.codes
+local ordinary_code = mathcodes.ordinary
+local variable_code = mathcodes.variable
+
+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.
+ local nucleus, sup, sub = start.nucleus, start.sup, start.sub
+ if sub then
+ if sup then
+ start[a_tagged] = start_tagged("msubsup")
+ process(nucleus)
+ process(sub)
+ process(sup)
+ stop_tagged()
+ else
+ start[a_tagged] = start_tagged("msub")
+ process(nucleus)
+ process(sub)
+ stop_tagged()
+ end
+ elseif sup then
+ start[a_tagged] = start_tagged("msup")
+ process(nucleus)
+ process(sup)
+ stop_tagged()
+ else
+ process(nucleus)
+ end
+end
+
+-- todo: check function here and keep attribute the same
+
+-- todo: variants -> original
+
+local actionstack = { }
+
+process = function(start) -- we cannot use the processor as we have no finalizers (yet)
+ while start do
+ local id = start.id
+ if id == math_char_code then
+ local char = start.char
+ -- check for code
+ local a = start[a_mathcategory]
+ if a then
+ a = { detail = a }
+ end
+ local code = getmathcode(char)
+ if code then
+ code = code[1]
+ end
+ local tag
+ if code == ordinary_code or code == variable_code 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
+ start[a_tagged] = start_tagged(tag,a)
+ stop_tagged()
+ break -- okay?
+ elseif id == math_textchar_code then
+ -- check for code
+ local a = start[a_mathcategory]
+ if a then
+ start[a_tagged] = start_tagged("ms",{ detail = a })
+ else
+ start[a_tagged] = start_tagged("ms")
+ end
+ stop_tagged()
+ break
+ elseif id == math_delim_code then
+ -- check for code
+ start[a_tagged] = start_tagged("mo")
+ stop_tagged()
+ break
+ elseif id == math_style_code then
+ -- has a next
+ elseif id == math_noad_code then
+ processsubsup(start)
+ elseif id == math_box_code or id == hlist_code or id == vlist_code then
+ -- keep an eye on math_box_code and see what ends up in there
+ local attr = start[a_tagged]
+ local last = attr and taglist[attr]
+ if last and find(last[#last],"formulacaption[:%-]") then
+ -- leave alone, will nicely move to the outer level
+ else
+ local text = start_tagged("mtext")
+ start[a_tagged] = text
+ local list = start.list
+ if not list then
+ -- empty list
+ elseif not attr then
+ -- box comes from strange place
+ set_attributes(list,a_tagged,text)
+ 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.
+ local tagdata = taglist[attr]
+ local common = #tagdata + 1
+ local function runner(list) -- quite inefficient
+ local cache = { } -- we can have nested unboxed mess so best local to runner
+ for n in traverse_nodes(list) do
+ local id = n.id
+ local aa = n[a_tagged]
+ if aa then
+ local ac = cache[aa]
+ if not ac then
+ local tagdata = taglist[aa]
+ 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
+ n[a_tagged] = ac
+ else
+ n[a_tagged] = text
+ end
+ if id == hlist_code or id == vlist_code then
+ runner(n.list)
+ end
+ end
+ end
+ runner(list)
+ end
+ stop_tagged()
+ end
+ elseif id == math_sub_code then
+ local list = start.list
+ if list then
+ local attr = start[a_tagged]
+ local last = attr and taglist[attr]
+ local action = last and match(last[#last],"maction:(.-)%-")
+ if action and action ~= "" then
+ if actionstack[#actionstack] == action then
+ start[a_tagged] = start_tagged("mrow")
+ process(list)
+ stop_tagged()
+ else
+ insert(actionstack,action)
+ start[a_tagged] = start_tagged("mrow",{ detail = action })
+ process(list)
+ stop_tagged()
+ remove(actionstack)
+ end
+ else
+ start[a_tagged] = start_tagged("mrow")
+ process(list)
+ stop_tagged()
+ end
+ end
+ elseif id == math_fraction_code then
+ local num, denom, left, right = start.num, start.denom, start.left, start.right
+ if left then
+ left[a_tagged] = start_tagged("mo")
+ process(left)
+ stop_tagged()
+ end
+ start[a_tagged] = start_tagged("mfrac")
+ process(num)
+ process(denom)
+ stop_tagged()
+ if right then
+ right[a_tagged] = start_tagged("mo")
+ process(right)
+ stop_tagged()
+ end
+ elseif id == math_choice_code then
+ local display, text, script, scriptscript = start.display, start.text, start.script, 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 == math_fence_code then
+ local delim = start.delim
+ local subtype = start.subtype
+ if subtype == 1 then
+ -- left
+ start[a_tagged] = start_tagged("mfenced")
+ if delim then
+ start[a_tagged] = start_tagged("mleft")
+ process(delim)
+ stop_tagged()
+ end
+ elseif subtype == 2 then
+ -- middle
+ if delim then
+ start[a_tagged] = start_tagged("mmiddle")
+ process(delim)
+ stop_tagged()
+ end
+ elseif subtype == 3 then
+ if delim then
+ start[a_tagged] = start_tagged("mright")
+ process(delim)
+ stop_tagged()
+ end
+ stop_tagged()
+ else
+ -- can't happen
+ end
+ elseif id == math_radical_code then
+ local left, degree = start.left, start.degree
+ if left then
+ start_tagged("")
+ process(left) -- root symbol, ignored
+ stop_tagged()
+ end
+ if degree then -- not good enough, can be empty mlist
+ start[a_tagged] = start_tagged("mroot")
+ processsubsup(start)
+ process(degree)
+ stop_tagged()
+ else
+ start[a_tagged] = start_tagged("msqrt")
+ processsubsup(start)
+ stop_tagged()
+ end
+ elseif id == math_accent_code then
+ local accent, bot_accent = start.accent, start.bot_accent
+ if bot_accent then
+ if accent then
+ start[a_tagged] = start_tagged("munderover",{ detail = "accent" })
+ processsubsup(start)
+ process(bot_accent)
+ process(accent)
+ stop_tagged()
+ else
+ start[a_tagged] = start_tagged("munder",{ detail = "accent" })
+ processsubsup(start)
+ process(bot_accent)
+ stop_tagged()
+ end
+ elseif accent then
+ start[a_tagged] = start_tagged("mover",{ detail = "accent" })
+ processsubsup(start)
+ process(accent)
+ stop_tagged()
+ else
+ processsubsup(start)
+ end
+ elseif id == glue_code then
+ start[a_tagged] = start_tagged("mspace")
+ stop_tagged()
+ else
+ start[a_tagged] = start_tagged("merror", { detail = nodecodes[i] })
+ stop_tagged()
+ end
+ start = start.next
+ end
+end
+
+function noads.handlers.tags(head,style,penalties)
+ local v_math = start_tagged("math")
+ local v_mrow = start_tagged("mrow")
+ local v_mode = head[a_mathmode]
+ head[a_tagged] = v_math
+ head[a_tagged] = v_mrow
+ tags.setattributehash(v_math,"mode",v_mode == 1 and "display" or "inline")
+ process(head)
+ stop_tagged()
+ stop_tagged()
+ return true
+end
diff --git a/tex/context/base/math-ttv.lua b/tex/context/base/math-ttv.lua
index e5548c730..1f644e788 100644
--- a/tex/context/base/math-ttv.lua
+++ b/tex/context/base/math-ttv.lua
@@ -1,801 +1,801 @@
-if not modules then modules = { } end modules ['math-ttv'] = {
- version = 1.001,
- comment = "traditional tex vectors, companion to math-vfu.lua",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files",
- dataonly = true,
-}
-
-local vfmath = fonts.handlers.vf.math
-local setletters = vfmath.setletters
-local setdigits = vfmath.setdigits
-
-local mathencodings = fonts.encodings.math
-
--- varphi is part of the alphabet, contrary to the other var*s'
-
-mathencodings["large-to-small"] = {
- [0x00028] = 0x00, -- (
- [0x00029] = 0x01, -- )
- [0x0005B] = 0x02, -- [
- [0x0005D] = 0x03, -- ]
- [0x0230A] = 0x04, -- lfloor
- [0x0230B] = 0x05, -- rfloor
- [0x02308] = 0x06, -- lceil
- [0x02309] = 0x07, -- rceil
- [0x0007B] = 0x08, -- {
- [0x0007D] = 0x09, -- }
- [0x027E8] = 0x0A, -- <
- [0x027E9] = 0x0B, -- >
- [0x0007C] = 0x0C, -- |
- -- [0x0] = 0x0D, -- lVert rVert Vert
- -- [0x0002F] = 0x0E, -- /
- [0x0005C] = 0x0F, -- \
- -- [0x0] = 0x3A, -- lgroup
- -- [0x0] = 0x3B, -- rgroup
- -- [0x0] = 0x3C, -- arrowvert
- -- [0x0] = 0x3D, -- Arrowvert
- [0x02195] = 0x3F, -- updownarrow
- -- [0x0] = 0x40, -- lmoustache
- -- [0x0] = 0x41, -- rmoustache
- [0x0221A] = 0x70, -- sqrt
- [0x021D5] = 0x77, -- Updownarrow
- [0x02191] = 0x78, -- uparrow
- [0x02193] = 0x79, -- downarrow
- [0x021D1] = 0x7E, -- Uparrow
- [0x021D3] = 0x7F, -- Downarrow
- [0x0220F] = 0x59, -- prod
- [0x02210] = 0x61, -- coprod
- [0x02211] = 0x58, -- sum
- [0x0222B] = 0x5A, -- intop
- [0x0222E] = 0x49, -- ointop
- -- [0xFE302] = 0x62, -- widehat
- -- [0xFE303] = 0x65, -- widetilde
- [0x00302] = 0x62, -- widehat
- [0x00303] = 0x65, -- widetilde
- [0x022C0] = 0x5E, -- bigwedge
- [0x022C1] = 0x5F, -- bigvee
- [0x022C2] = 0x5C, -- bigcap
- [0x022C3] = 0x5B, -- bigcup
- [0x02044] = 0x0E, -- /
-}
-
--- Beware: these are (in cm/lm) below the baseline due to limitations
--- in the tfm format bu the engien (combined with the mathclass) takes
--- care of it. If we need them in textmode, we should make them virtual
--- and move them up but we're in no hurry with that.
-
-mathencodings["tex-ex"] = {
- [0x0220F] = 0x51, -- prod
- [0x02210] = 0x60, -- coprod
- [0x02211] = 0x50, -- sum
- [0x0222B] = 0x52, -- intop
- [0x0222E] = 0x48, -- ointop
- [0x022C0] = 0x56, -- bigwedge
- [0x022C1] = 0x57, -- bigvee
- [0x022C2] = 0x54, -- bigcap
- [0x022C3] = 0x53, -- bigcup
- [0x02A00] = 0x4A, -- bigodot -- fixed BJ
- [0x02A01] = 0x4C, -- bigoplus
- [0x02A02] = 0x4E, -- bigotimes
- -- [0x02A03] = , -- bigudot --
- [0x02A04] = 0x55, -- biguplus
- [0x02A06] = 0x46, -- bigsqcup
-}
-
--- only math stuff is needed, since we always use an lm or gyre
--- font as main font
-
-mathencodings["tex-mr"] = {
- [0x00393] = 0x00, -- Gamma
- [0x00394] = 0x01, -- Delta
- [0x00398] = 0x02, -- Theta
- [0x0039B] = 0x03, -- Lambda
- [0x0039E] = 0x04, -- Xi
- [0x003A0] = 0x05, -- Pi
- [0x003A3] = 0x06, -- Sigma
- [0x003A5] = 0x07, -- Upsilon
- [0x003A6] = 0x08, -- Phi
- [0x003A8] = 0x09, -- Psi
- [0x003A9] = 0x0A, -- Omega
--- [0x00060] = 0x12, -- [math]grave
--- [0x000B4] = 0x13, -- [math]acute
--- [0x002C7] = 0x14, -- [math]check
--- [0x002D8] = 0x15, -- [math]breve
--- [0x000AF] = 0x16, -- [math]bar
--- [0x00021] = 0x21, -- !
--- [0x00028] = 0x28, -- (
--- [0x00029] = 0x29, -- )
--- [0x0002B] = 0x2B, -- +
--- [0x0002F] = 0x2F, -- /
--- [0x0003A] = 0x3A, -- :
--- [0x02236] = 0x3A, -- colon
--- [0x0003B] = 0x3B, -- ;
--- [0x0003C] = 0x3C, -- <
--- [0x0003D] = 0x3D, -- =
--- [0x0003E] = 0x3E, -- >
--- [0x0003F] = 0x3F, -- ?
- [0x00391] = 0x41, -- Alpha
- [0x00392] = 0x42, -- Beta
- [0x02145] = 0x44,
- [0x00395] = 0x45, -- Epsilon
- [0x00397] = 0x48, -- Eta
- [0x00399] = 0x49, -- Iota
- [0x0039A] = 0x4B, -- Kappa
- [0x0039C] = 0x4D, -- Mu
- [0x0039D] = 0x4E, -- Nu
- [0x0039F] = 0x4F, -- Omicron
- [0x003A1] = 0x52, -- Rho
- [0x003A4] = 0x54, -- Tau
- [0x003A7] = 0x58, -- Chi
- [0x00396] = 0x5A, -- Zeta
--- [0x0005B] = 0x5B, -- [
--- [0x0005D] = 0x5D, -- ]
--- [0x0005E] = 0x5E, -- [math]hat -- the text one
- [0x00302] = 0x5E, -- [math]hat -- the real math one
--- [0x002D9] = 0x5F, -- [math]dot
- [0x02146] = 0x64,
- [0x02147] = 0x65,
--- [0x002DC] = 0x7E, -- [math]tilde -- the text one
- [0x00303] = 0x7E, -- [math]tilde -- the real one
--- [0x000A8] = 0x7F, -- [math]ddot
-}
-
-mathencodings["tex-mr-missing"] = {
- [0x02236] = 0x3A, -- colon
-}
-
-mathencodings["tex-mi"] = {
- [0x1D6E4] = 0x00, -- Gamma
- [0x1D6E5] = 0x01, -- Delta
- [0x1D6E9] = 0x02, -- Theta
- [0x1D6F3] = 0x02, -- varTheta (not present in TeX)
- [0x1D6EC] = 0x03, -- Lambda
- [0x1D6EF] = 0x04, -- Xi
- [0x1D6F1] = 0x05, -- Pi
- [0x1D6F4] = 0x06, -- Sigma
- [0x1D6F6] = 0x07, -- Upsilon
- [0x1D6F7] = 0x08, -- Phi
- [0x1D6F9] = 0x09, -- Psi
- [0x1D6FA] = 0x0A, -- Omega
- [0x1D6FC] = 0x0B, -- alpha
- [0x1D6FD] = 0x0C, -- beta
- [0x1D6FE] = 0x0D, -- gamma
- [0x1D6FF] = 0x0E, -- delta
- [0x1D716] = 0x0F, -- epsilon TODO: 1D716
- [0x1D701] = 0x10, -- zeta
- [0x1D702] = 0x11, -- eta
- [0x1D703] = 0x12, -- theta TODO: 1D703
- [0x1D704] = 0x13, -- iota
- [0x1D705] = 0x14, -- kappa
- [0x1D718] = 0x14, -- varkappa, not in tex fonts
- [0x1D706] = 0x15, -- lambda
- [0x1D707] = 0x16, -- mu
- [0x1D708] = 0x17, -- nu
- [0x1D709] = 0x18, -- xi
- [0x1D70B] = 0x19, -- pi
- [0x1D70C] = 0x1A, -- rho
- [0x1D70E] = 0x1B, -- sigma
- [0x1D70F] = 0x1C, -- tau
- [0x1D710] = 0x1D, -- upsilon
- [0x1D719] = 0x1E, -- phi
- [0x1D712] = 0x1F, -- chi
- [0x1D713] = 0x20, -- psi
- [0x1D714] = 0x21, -- omega
- [0x1D700] = 0x22, -- varepsilon (the other way around)
- [0x1D717] = 0x23, -- vartheta
- [0x1D71B] = 0x24, -- varpi
- [0x1D71A] = 0x25, -- varrho
- [0x1D70D] = 0x26, -- varsigma
- [0x1D711] = 0x27, -- varphi (the other way around)
- [0x021BC] = 0x28, -- leftharpoonup
- [0x021BD] = 0x29, -- leftharpoondown
- [0x021C0] = 0x2A, -- rightharpoonup
- [0x021C1] = 0x2B, -- rightharpoondown
- [0xFE322] = 0x2C, -- lhook (hook for combining arrows)
- [0xFE323] = 0x2D, -- rhook (hook for combining arrows)
- [0x025B7] = 0x2E, -- triangleright : cf lmmath / BJ
- [0x025C1] = 0x2F, -- triangleleft : cf lmmath / BJ
- [0x022B3] = 0x2E, -- triangleright : cf lmmath this a cramped triangles / BJ / see *
- [0x022B2] = 0x2F, -- triangleleft : cf lmmath this a cramped triangles / BJ / see *
--- [0x00041] = 0x30, -- 0
--- [0x00041] = 0x31, -- 1
--- [0x00041] = 0x32, -- 2
--- [0x00041] = 0x33, -- 3
--- [0x00041] = 0x34, -- 4
--- [0x00041] = 0x35, -- 5
--- [0x00041] = 0x36, -- 6
--- [0x00041] = 0x37, -- 7
--- [0x00041] = 0x38, -- 8
--- [0x00041] = 0x39, -- 9
---~ [0x0002E] = 0x3A, -- .
- [0x0002C] = 0x3B, -- ,
- [0x0003C] = 0x3C, -- <
--- [0x0002F] = 0x3D, -- /, slash, solidus
- [0x02044] = 0x3D, -- / AM: Not sure
- [0x0003E] = 0x3E, -- >
- [0x022C6] = 0x3F, -- star
- [0x02202] = 0x40, -- partial
---
- [0x0266D] = 0x5B, -- flat
- [0x0266E] = 0x5C, -- natural
- [0x0266F] = 0x5D, -- sharp
- [0x02323] = 0x5E, -- smile
- [0x02322] = 0x5F, -- frown
- [0x02113] = 0x60, -- ell
---
- [0x1D6A4] = 0x7B, -- imath (TODO: also 0131)
- [0x1D6A5] = 0x7C, -- jmath (TODO: also 0237)
- [0x02118] = 0x7D, -- wp
- [0x020D7] = 0x7E, -- vec (TODO: not sure)
--- 0x7F, -- (no idea what that could be)
-}
-
-mathencodings["tex-it"] = {
--- [0x1D434] = 0x41, -- A
- [0x1D6E2] = 0x41, -- Alpha
--- [0x1D435] = 0x42, -- B
- [0x1D6E3] = 0x42, -- Beta
--- [0x1D436] = 0x43, -- C
--- [0x1D437] = 0x44, -- D
--- [0x1D438] = 0x45, -- E
- [0x1D6E6] = 0x45, -- Epsilon
--- [0x1D439] = 0x46, -- F
--- [0x1D43A] = 0x47, -- G
--- [0x1D43B] = 0x48, -- H
- [0x1D6E8] = 0x48, -- Eta
--- [0x1D43C] = 0x49, -- I
- [0x1D6EA] = 0x49, -- Iota
--- [0x1D43D] = 0x4A, -- J
--- [0x1D43E] = 0x4B, -- K
- [0x1D6EB] = 0x4B, -- Kappa
--- [0x1D43F] = 0x4C, -- L
--- [0x1D440] = 0x4D, -- M
- [0x1D6ED] = 0x4D, -- Mu
--- [0x1D441] = 0x4E, -- N
- [0x1D6EE] = 0x4E, -- Nu
--- [0x1D442] = 0x4F, -- O
- [0x1D6F0] = 0x4F, -- Omicron
--- [0x1D443] = 0x50, -- P
- [0x1D6F2] = 0x50, -- Rho
--- [0x1D444] = 0x51, -- Q
--- [0x1D445] = 0x52, -- R
--- [0x1D446] = 0x53, -- S
--- [0x1D447] = 0x54, -- T
- [0x1D6F5] = 0x54, -- Tau
--- [0x1D448] = 0x55, -- U
--- [0x1D449] = 0x56, -- V
--- [0x1D44A] = 0x57, -- W
--- [0x1D44B] = 0x58, -- X
- [0x1D6F8] = 0x58, -- Chi
--- [0x1D44C] = 0x59, -- Y
--- [0x1D44D] = 0x5A, -- Z
---
--- [0x1D44E] = 0x61, -- a
--- [0x1D44F] = 0x62, -- b
--- [0x1D450] = 0x63, -- c
--- [0x1D451] = 0x64, -- d
--- [0x1D452] = 0x65, -- e
--- [0x1D453] = 0x66, -- f
--- [0x1D454] = 0x67, -- g
--- [0x1D455] = 0x68, -- h
- [0x0210E] = 0x68, -- Planck constant (h)
--- [0x1D456] = 0x69, -- i
--- [0x1D457] = 0x6A, -- j
--- [0x1D458] = 0x6B, -- k
--- [0x1D459] = 0x6C, -- l
--- [0x1D45A] = 0x6D, -- m
--- [0x1D45B] = 0x6E, -- n
--- [0x1D45C] = 0x6F, -- o
- [0x1D70A] = 0x6F, -- omicron
--- [0x1D45D] = 0x70, -- p
--- [0x1D45E] = 0x71, -- q
--- [0x1D45F] = 0x72, -- r
--- [0x1D460] = 0x73, -- s
--- [0x1D461] = 0x74, -- t
--- [0x1D462] = 0x75, -- u
--- [0x1D463] = 0x76, -- v
--- [0x1D464] = 0x77, -- w
--- [0x1D465] = 0x78, -- x
--- [0x1D466] = 0x79, -- y
--- [0x1D467] = 0x7A, -- z
-}
-
-mathencodings["tex-ss"] = { }
-mathencodings["tex-tt"] = { }
-mathencodings["tex-bf"] = { }
-mathencodings["tex-bi"] = { }
-mathencodings["tex-fraktur"] = { }
-mathencodings["tex-fraktur-bold"] = { }
-
-mathencodings["tex-sy"] = {
- [0x0002D] = 0x00, -- -
- [0x02212] = 0x00, -- -
--- [0x02201] = 0x00, -- complement
--- [0x02206] = 0x00, -- increment
--- [0x02204] = 0x00, -- not exists
--- [0x000B7] = 0x01, -- cdot
- [0x022C5] = 0x01, -- cdot
- [0x000D7] = 0x02, -- times
- [0x0002A] = 0x03, -- *
- [0x02217] = 0x03, -- *
- [0x000F7] = 0x04, -- div
- [0x022C4] = 0x05, -- diamond
- [0x000B1] = 0x06, -- pm
- [0x02213] = 0x07, -- mp
- [0x02295] = 0x08, -- oplus
- [0x02296] = 0x09, -- ominus
- [0x02297] = 0x0A, -- otimes
- [0x02298] = 0x0B, -- oslash
- [0x02299] = 0x0C, -- odot
- [0x025EF] = 0x0D, -- bigcirc, Orb (either 25EF or 25CB) -- todo
- [0x02218] = 0x0E, -- circ
- [0x02219] = 0x0F, -- bullet
- [0x02022] = 0x0F, -- bullet
- [0x0224D] = 0x10, -- asymp
- [0x02261] = 0x11, -- equiv
- [0x02286] = 0x12, -- subseteq
- [0x02287] = 0x13, -- supseteq
- [0x02264] = 0x14, -- leq
- [0x02265] = 0x15, -- geq
- [0x02AAF] = 0x16, -- preceq
--- [0x0227C] = 0x16, -- preceq, AM:No see 2AAF
- [0x02AB0] = 0x17, -- succeq
--- [0x0227D] = 0x17, -- succeq, AM:No see 2AB0
- [0x0223C] = 0x18, -- sim
- [0x02248] = 0x19, -- approx
- [0x02282] = 0x1A, -- subset
- [0x02283] = 0x1B, -- supset
- [0x0226A] = 0x1C, -- ll
- [0x0226B] = 0x1D, -- gg
- [0x0227A] = 0x1E, -- prec
- [0x0227B] = 0x1F, -- succ
- [0x02190] = 0x20, -- leftarrow
- [0x02192] = 0x21, -- rightarrow
---~ [0xFE190] = 0x20, -- leftarrow
---~ [0xFE192] = 0x21, -- rightarrow
- [0x02191] = 0x22, -- uparrow
- [0x02193] = 0x23, -- downarrow
- [0x02194] = 0x24, -- leftrightarrow
- [0x02197] = 0x25, -- nearrow
- [0x02198] = 0x26, -- searrow
- [0x02243] = 0x27, -- simeq
- [0x021D0] = 0x28, -- Leftarrow
- [0x021D2] = 0x29, -- Rightarrow
- [0x021D1] = 0x2A, -- Uparrow
- [0x021D3] = 0x2B, -- Downarrow
- [0x021D4] = 0x2C, -- Leftrightarrow
- [0x02196] = 0x2D, -- nwarrow
- [0x02199] = 0x2E, -- swarrow
- [0x0221D] = 0x2F, -- propto
- [0x02032] = 0x30, -- prime
- [0x0221E] = 0x31, -- infty
- [0x02208] = 0x32, -- in
- [0x0220B] = 0x33, -- ni
- [0x025B3] = 0x34, -- triangle, bigtriangleup
- [0x025BD] = 0x35, -- bigtriangledown
- [0x00338] = 0x36, -- not
--- 0x37, -- (beginning of arrow)
- [0x02200] = 0x38, -- forall
- [0x02203] = 0x39, -- exists
- [0x000AC] = 0x3A, -- neg, lnot
- [0x02205] = 0x3B, -- empty set
- [0x0211C] = 0x3C, -- Re
- [0x02111] = 0x3D, -- Im
- [0x022A4] = 0x3E, -- top
- [0x022A5] = 0x3F, -- bot, perp
- [0x02135] = 0x40, -- aleph
- [0x1D49C] = 0x41, -- script A
- [0x0212C] = 0x42, -- script B
- [0x1D49E] = 0x43, -- script C
- [0x1D49F] = 0x44, -- script D
- [0x02130] = 0x45, -- script E
- [0x02131] = 0x46, -- script F
- [0x1D4A2] = 0x47, -- script G
- [0x0210B] = 0x48, -- script H
- [0x02110] = 0x49, -- script I
- [0x1D4A5] = 0x4A, -- script J
- [0x1D4A6] = 0x4B, -- script K
- [0x02112] = 0x4C, -- script L
- [0x02133] = 0x4D, -- script M
- [0x1D4A9] = 0x4E, -- script N
- [0x1D4AA] = 0x4F, -- script O
- [0x1D4AB] = 0x50, -- script P
- [0x1D4AC] = 0x51, -- script Q
- [0x0211B] = 0x52, -- script R
- [0x1D4AE] = 0x53, -- script S
- [0x1D4AF] = 0x54, -- script T
- [0x1D4B0] = 0x55, -- script U
- [0x1D4B1] = 0x56, -- script V
- [0x1D4B2] = 0x57, -- script W
- [0x1D4B3] = 0x58, -- script X
- [0x1D4B4] = 0x59, -- script Y
- [0x1D4B5] = 0x5A, -- script Z
- [0x0222A] = 0x5B, -- cup
- [0x02229] = 0x5C, -- cap
- [0x0228E] = 0x5D, -- uplus
- [0x02227] = 0x5E, -- wedge, land
- [0x02228] = 0x5F, -- vee, lor
- [0x022A2] = 0x60, -- vdash
- [0x022A3] = 0x61, -- dashv
- [0x0230A] = 0x62, -- lfloor
- [0x0230B] = 0x63, -- rfloor
- [0x02308] = 0x64, -- lceil
- [0x02309] = 0x65, -- rceil
- [0x0007B] = 0x66, -- {, lbrace
- [0x0007D] = 0x67, -- }, rbrace
- [0x027E8] = 0x68, -- <, langle
- [0x027E9] = 0x69, -- >, rangle
- [0x0007C] = 0x6A, -- |, mid, lvert, rvert
- [0x02225] = 0x6B, -- parallel
- -- [0x0 ] = 0x00, -- Vert, lVert, rVert, arrowvert, Arrowvert
- [0x02195] = 0x6C, -- updownarrow
- [0x021D5] = 0x6D, -- Updownarrow
- [0x0005C] = 0x6E, -- \, backslash, setminus
- [0x02216] = 0x6E, -- setminus
- [0x02240] = 0x6F, -- wr
- [0x0221A] = 0x70, -- sqrt. AM: Check surd??
- [0x02A3F] = 0x71, -- amalg
- [0x1D6FB] = 0x72, -- nabla
--- [0x0222B] = 0x73, -- smallint (TODO: what about intop?)
- [0x02294] = 0x74, -- sqcup
- [0x02293] = 0x75, -- sqcap
- [0x02291] = 0x76, -- sqsubseteq
- [0x02292] = 0x77, -- sqsupseteq
- [0x000A7] = 0x78, -- S
- [0x02020] = 0x79, -- dagger, dag
- [0x02021] = 0x7A, -- ddagger, ddag
- [0x000B6] = 0x7B, -- P
- [0x02663] = 0x7C, -- clubsuit
- [0x02662] = 0x7D, -- diamondsuit
- [0x02661] = 0x7E, -- heartsuit
- [0x02660] = 0x7F, -- spadesuit
- [0xFE321] = 0x37, -- mapstochar
-
- [0xFE325] = 0x30, -- prime 0x02032
-}
-
--- The names in masm10.enc can be trusted best and are shown in the first
--- column, while in the second column we show the tex/ams names. As usual
--- it costs hours to figure out such a table.
-
-mathencodings["tex-ma"] = {
- [0x022A1] = 0x00, -- squaredot \boxdot
- [0x0229E] = 0x01, -- squareplus \boxplus
- [0x022A0] = 0x02, -- squaremultiply \boxtimes
- [0x025A1] = 0x03, -- square \square \Box
- [0x025A0] = 0x04, -- squaresolid \blacksquare
- [0x025AA] = 0x05, -- squaresmallsolid \centerdot
- [0x022C4] = 0x06, -- diamond \Diamond \lozenge
- [0x02666] = 0x07, -- diamondsolid \blacklozenge
- [0x021BB] = 0x08, -- clockwise \circlearrowright
- [0x021BA] = 0x09, -- anticlockwise \circlearrowleft
- [0x021CC] = 0x0A, -- harpoonleftright \rightleftharpoons
- [0x021CB] = 0x0B, -- harpoonrightleft \leftrightharpoons
- [0x0229F] = 0x0C, -- squareminus \boxminus
- [0x022A9] = 0x0D, -- forces \Vdash
- [0x022AA] = 0x0E, -- forcesbar \Vvdash
- [0x022A8] = 0x0F, -- satisfies \vDash
- [0x021A0] = 0x10, -- dblarrowheadright \twoheadrightarrow
- [0x0219E] = 0x11, -- dblarrowheadleft \twoheadleftarrow
- [0x021C7] = 0x12, -- dblarrowleft \leftleftarrows
- [0x021C9] = 0x13, -- dblarrowright \rightrightarrows
- [0x021C8] = 0x14, -- dblarrowup \upuparrows
- [0x021CA] = 0x15, -- dblarrowdwn \downdownarrows
- [0x021BE] = 0x16, -- harpoonupright \upharpoonright \restriction
- [0x021C2] = 0x17, -- harpoondownright \downharpoonright
- [0x021BF] = 0x18, -- harpoonupleft \upharpoonleft
- [0x021C3] = 0x19, -- harpoondownleft \downharpoonleft
- [0x021A3] = 0x1A, -- arrowtailright \rightarrowtail
- [0x021A2] = 0x1B, -- arrowtailleft \leftarrowtail
- [0x021C6] = 0x1C, -- arrowparrleftright \leftrightarrows
--- [0x021C5] = 0x00, -- \updownarrows (missing in lm)
- [0x021C4] = 0x1D, -- arrowparrrightleft \rightleftarrows
- [0x021B0] = 0x1E, -- shiftleft \Lsh
- [0x021B1] = 0x1F, -- shiftright \Rsh
- [0x021DD] = 0x20, -- squiggleright \leadsto \rightsquigarrow
- [0x021AD] = 0x21, -- squiggleleftright \leftrightsquigarrow
- [0x021AB] = 0x22, -- curlyleft \looparrowleft
- [0x021AC] = 0x23, -- curlyright \looparrowright
- [0x02257] = 0x24, -- circleequal \circeq
- [0x0227F] = 0x25, -- followsorequal \succsim
- [0x02273] = 0x26, -- greaterorsimilar \gtrsim
- [0x02A86] = 0x27, -- greaterorapproxeql \gtrapprox
- [0x022B8] = 0x28, -- multimap \multimap
- [0x02234] = 0x29, -- therefore \therefore
- [0x02235] = 0x2A, -- because \because
- [0x02251] = 0x2B, -- equalsdots \Doteq \doteqdot
- [0x0225C] = 0x2C, -- defines \triangleq
- [0x0227E] = 0x2D, -- precedesorequal \precsim
- [0x02272] = 0x2E, -- lessorsimilar \lesssim
- [0x02A85] = 0x2F, -- lessorapproxeql \lessapprox
- [0x02A95] = 0x30, -- equalorless \eqslantless
- [0x02A96] = 0x31, -- equalorgreater \eqslantgtr
- [0x022DE] = 0x32, -- equalorprecedes \curlyeqprec
- [0x022DF] = 0x33, -- equalorfollows \curlyeqsucc
- [0x0227C] = 0x34, -- precedesorcurly \preccurlyeq
- [0x02266] = 0x35, -- lessdblequal \leqq
- [0x02A7D] = 0x36, -- lessorequalslant \leqslant
- [0x02276] = 0x37, -- lessorgreater \lessgtr
- [0x02035] = 0x38, -- primereverse \backprime
- -- [0x0] = 0x39, -- axisshort \dabar
- [0x02253] = 0x3A, -- equaldotrightleft \risingdotseq
- [0x02252] = 0x3B, -- equaldotleftright \fallingdotseq
- [0x0227D] = 0x3C, -- followsorcurly \succcurlyeq
- [0x02267] = 0x3D, -- greaterdblequal \geqq
- [0x02A7E] = 0x3E, -- greaterorequalslant \geqslant
- [0x02277] = 0x3F, -- greaterorless \gtrless
- [0x0228F] = 0x40, -- squareimage \sqsubset
- [0x02290] = 0x41, -- squareoriginal \sqsupset
- -- wrong: see **
- -- [0x022B3] = 0x42, -- triangleright \rhd \vartriangleright
- -- [0x022B2] = 0x43, -- triangleleft \lhd \vartriangleleft
- -- cf lm
- [0x022B5] = 0x44, -- trianglerightequal \unrhd \trianglerighteq
- [0x022B4] = 0x45, -- triangleleftequal \unlhd \trianglelefteq
- --
- [0x02605] = 0x46, -- star \bigstar
- [0x0226C] = 0x47, -- between \between
- [0x025BC] = 0x48, -- triangledownsld \blacktriangledown
- [0x025B6] = 0x49, -- trianglerightsld \blacktriangleright
- [0x025C0] = 0x4A, -- triangleleftsld \blacktriangleleft
- -- [0x0] = 0x4B, -- arrowaxisright
- -- [0x0] = 0x4C, -- arrowaxisleft
- [0x025B2] = 0x4D, -- triangle \triangleup \vartriangle
- [0x025B2] = 0x4E, -- trianglesolid \blacktriangle
- [0x025BD] = 0x4F, -- triangleinv \triangledown
- [0x02256] = 0x50, -- ringinequal \eqcirc
- [0x022DA] = 0x51, -- lessequalgreater \lesseqgtr
- [0x022DB] = 0x52, -- greaterlessequal \gtreqless
- [0x02A8B] = 0x53, -- lessdbleqlgreater \lesseqqgtr
- [0x02A8C] = 0x54, -- greaterdbleqlless \gtreqqless
- [0x000A5] = 0x55, -- Yen \yen
- [0x021DB] = 0x56, -- arrowtripleright \Rrightarrow
- [0x021DA] = 0x57, -- arrowtripleleft \Lleftarrow
- [0x02713] = 0x58, -- check \checkmark
- [0x022BB] = 0x59, -- orunderscore \veebar
- [0x022BC] = 0x5A, -- nand \barwedge
- [0x02306] = 0x5B, -- perpcorrespond \doublebarwedge
- [0x02220] = 0x5C, -- angle \angle
- [0x02221] = 0x5D, -- measuredangle \measuredangle
- [0x02222] = 0x5E, -- sphericalangle \sphericalangle
- -- [0x0] = 0x5F, -- proportional \varpropto
- -- [0x0] = 0x60, -- smile \smallsmile
- -- [0x0] = 0x61, -- frown \smallfrown
- [0x022D0] = 0x62, -- subsetdbl \Subset
- [0x022D1] = 0x63, -- supersetdbl \Supset
- [0x022D3] = 0x64, -- uniondbl \doublecup \Cup
- [0x022D2] = 0x65, -- intersectiondbl \doublecap \Cap
- [0x022CF] = 0x66, -- uprise \curlywedge
- [0x022CE] = 0x67, -- downfall \curlyvee
- [0x022CB] = 0x68, -- multiopenleft \leftthreetimes
- [0x022CC] = 0x69, -- multiopenright \rightthreetimes
- [0x02AC5] = 0x6A, -- subsetdblequal \subseteqq
- [0x02AC6] = 0x6B, -- supersetdblequal \supseteqq
- [0x0224F] = 0x6C, -- difference \bumpeq
- [0x0224E] = 0x6D, -- geomequivalent \Bumpeq
- [0x022D8] = 0x6E, -- muchless \lll \llless
- [0x022D9] = 0x6F, -- muchgreater \ggg \gggtr
- [0x0231C] = 0x70, -- rightanglenw \ulcorner
- [0x0231D] = 0x71, -- rightanglene \urcorner
- [0x024C7] = 0x72, -- circleR \circledR
- [0x024C8] = 0x73, -- circleS \circledS
- [0x022D4] = 0x74, -- fork \pitchfork
- [0x02214] = 0x75, -- dotplus \dotplus
- [0x0223D] = 0x76, -- revsimilar \backsim
- [0x022CD] = 0x77, -- revasymptequal \backsimeq -- AM: Check this! I mapped it to simeq.
- [0x0231E] = 0x78, -- rightanglesw \llcorner
- [0x0231F] = 0x79, -- rightanglese \lrcorner
- [0x02720] = 0x7A, -- maltesecross \maltese
- [0x02201] = 0x7B, -- complement \complement
- [0x022BA] = 0x7C, -- intercal \intercal
- [0x0229A] = 0x7D, -- circlering \circledcirc
- [0x0229B] = 0x7E, -- circleasterisk \circledast
- [0x0229D] = 0x7F, -- circleminus \circleddash
-}
-
-mathencodings["tex-mb"] = {
- -- [0x0] = 0x00, -- lessornotequal \lvertneqq
- -- [0x0] = 0x01, -- greaterornotequal \gvertneqq
- [0x02270] = 0x02, -- notlessequal \nleq
- [0x02271] = 0x03, -- notgreaterequal \ngeq
- [0x0226E] = 0x04, -- notless \nless
- [0x0226F] = 0x05, -- notgreater \ngtr
- [0x02280] = 0x06, -- notprecedes \nprec
- [0x02281] = 0x07, -- notfollows \nsucc
- [0x02268] = 0x08, -- lessornotdbleql \lneqq
- [0x02269] = 0x09, -- greaterornotdbleql \gneqq
- -- [0x0] = 0x0A, -- notlessorslnteql \nleqslant
- -- [0x0] = 0x0B, -- notgreaterorslnteql \ngeqslant
- [0x02A87] = 0x0C, -- lessnotequal \lneq
- [0x02A88] = 0x0D, -- greaternotequal \gneq
- -- [0x0] = 0x0E, -- notprecedesoreql \npreceq
- -- [0x0] = 0x0F, -- notfollowsoreql \nsucceq
- [0x022E8] = 0x10, -- precedeornoteqvlnt \precnsim
- [0x022E9] = 0x11, -- followornoteqvlnt \succnsim
- [0x022E6] = 0x12, -- lessornotsimilar \lnsim
- [0x022E7] = 0x13, -- greaterornotsimilar \gnsim
- -- [0x0] = 0x14, -- notlessdblequal \nleqq
- -- [0x0] = 0x15, -- notgreaterdblequal \ngeqq
- [0x02AB5] = 0x16, -- precedenotslnteql \precneqq
- [0x02AB6] = 0x17, -- follownotslnteql \succneqq
- [0x02AB9] = 0x18, -- precedenotdbleqv \precnapprox
- [0x02ABA] = 0x19, -- follownotdbleqv \succnapprox
- [0x02A89] = 0x1A, -- lessnotdblequal \lnapprox
- [0x02A8A] = 0x1B, -- greaternotdblequal \gnapprox
- [0x02241] = 0x1C, -- notsimilar \nsim
- [0x02247] = 0x1D, -- notapproxequal \ncong
- -- [0x0] = 0x1E, -- upslope \diagup
- -- [0x0] = 0x1F, -- downslope \diagdown
- -- [0x0] = 0x20, -- notsubsetoreql \varsubsetneq
- -- [0x0] = 0x21, -- notsupersetoreql \varsupsetneq
- -- [0x0] = 0x22, -- notsubsetordbleql \nsubseteqq
- -- [0x0] = 0x23, -- notsupersetordbleql \nsupseteqq
- [0x02ACB] = 0x24, -- subsetornotdbleql \subsetneqq
- [0x02ACC] = 0x25, -- supersetornotdbleql \supsetneqq
- -- [0x0] = 0x26, -- subsetornoteql \varsubsetneqq
- -- [0x0] = 0x27, -- supersetornoteql \varsupsetneqq
- [0x0228A] = 0x28, -- subsetnoteql \subsetneq
- [0x0228B] = 0x29, -- supersetnoteql \supsetneq
- [0x02288] = 0x2A, -- notsubseteql \nsubseteq
- [0x02289] = 0x2B, -- notsuperseteql \nsupseteq
- [0x02226] = 0x2C, -- notparallel \nparallel
- [0x02224] = 0x2D, -- notbar \nmid \ndivides
- -- [0x0] = 0x2E, -- notshortbar \nshortmid
- -- [0x0] = 0x2F, -- notshortparallel \nshortparallel
- [0x022AC] = 0x30, -- notturnstile \nvdash
- [0x022AE] = 0x31, -- notforces \nVdash
- [0x022AD] = 0x32, -- notsatisfies \nvDash
- [0x022AF] = 0x33, -- notforcesextra \nVDash
- [0x022ED] = 0x34, -- nottriangeqlright \ntrianglerighteq
- [0x022EC] = 0x35, -- nottriangeqlleft \ntrianglelefteq
- [0x022EA] = 0x36, -- nottriangleleft \ntriangleleft
- [0x022EB] = 0x37, -- nottriangleright \ntriangleright
- [0x0219A] = 0x38, -- notarrowleft \nleftarrow
- [0x0219B] = 0x39, -- notarrowright \nrightarrow
- [0x021CD] = 0x3A, -- notdblarrowleft \nLeftarrow
- [0x021CF] = 0x3B, -- notdblarrowright \nRightarrow
- [0x021CE] = 0x3C, -- notdblarrowboth \nLeftrightarrow
- [0x021AE] = 0x3D, -- notarrowboth \nleftrightarrow
- [0x022C7] = 0x3E, -- dividemultiply \divideontimes
- [0x02300] = 0x3F, -- diametersign \varnothing
- [0x02204] = 0x40, -- notexistential \nexists
- [0x1D538] = 0x41, -- A (blackboard A)
- [0x1D539] = 0x42, -- B
- [0x02102] = 0x43, -- C
- [0x1D53B] = 0x44, -- D
- [0x1D53C] = 0x45, -- E
- [0x1D53D] = 0x46, -- F
- [0x1D53E] = 0x47, -- G
- [0x0210D] = 0x48, -- H
- [0x1D540] = 0x49, -- I
- [0x1D541] = 0x4A, -- J
- [0x1D542] = 0x4B, -- K
- [0x1D543] = 0x4C, -- L
- [0x1D544] = 0x4D, -- M
- [0x02115] = 0x4E, -- N
- [0x1D546] = 0x4F, -- O
- [0x02119] = 0x50, -- P
- [0x0211A] = 0x51, -- Q
- [0x0211D] = 0x52, -- R
- [0x1D54A] = 0x53, -- S
- [0x1D54B] = 0x54, -- T
- [0x1D54C] = 0x55, -- U
- [0x1D54D] = 0x56, -- V
- [0x1D54E] = 0x57, -- W
- [0x1D54F] = 0x58, -- X
- [0x1D550] = 0x59, -- Y
- [0x02124] = 0x5A, -- Z (blackboard Z)
- [0x02132] = 0x60, -- finv \Finv
- [0x02141] = 0x61, -- fmir \Game
- -- [0x0] = 0x62, tildewide
- -- [0x0] = 0x63, tildewider
- -- [0x0] = 0x64, Finv
- -- [0x0] = 0x65, Gmir
- [0x02127] = 0x66, -- Omegainv \mho
- [0x000F0] = 0x67, -- eth \eth
- [0x02242] = 0x68, -- equalorsimilar \eqsim
- [0x02136] = 0x69, -- beth \beth
- [0x02137] = 0x6A, -- gimel \gimel
- [0x02138] = 0x6B, -- daleth \daleth
- [0x022D6] = 0x6C, -- lessdot \lessdot
- [0x022D7] = 0x6D, -- greaterdot \gtrdot
- [0x022C9] = 0x6E, -- multicloseleft \ltimes
- [0x022CA] = 0x6F, -- multicloseright \rtimes
- -- [0x0] = 0x70, -- barshort \shortmid
- -- [0x0] = 0x71, -- parallelshort \shortparallel
- -- [0x02216] = 0x72, -- integerdivide \smallsetminus (2216 already part of tex-sy
- -- [0x0] = 0x73, -- similar \thicksim
- -- [0x0] = 0x74, -- approxequal \thickapprox
- [0x0224A] = 0x75, -- approxorequal \approxeq
- [0x02AB8] = 0x76, -- followsorequal \succapprox
- [0x02AB7] = 0x77, -- precedesorequal \precapprox
- [0x021B6] = 0x78, -- archleftdown \curvearrowleft
- [0x021B7] = 0x79, -- archrightdown \curvearrowright
- [0x003DC] = 0x7A, -- Digamma \digamma
- [0x003F0] = 0x7B, -- kappa \varkappa
- [0x1D55C] = 0x7C, -- k \Bbbk (blackboard k)
- [0x0210F] = 0x7D, -- planckover2pi \hslash % 0x7D
- [0x00127] = 0x7E, -- planckover2pi1 \hbar % 0x7E
- [0x003F6] = 0x7F, -- epsiloninv \backepsilon
-}
-
-mathencodings["tex-mc"] = {
- -- this file has no tfm so it gets mapped in the private space
- [0xFE324] = "mapsfromchar",
-}
-
-mathencodings["tex-fraktur"] = {
--- [0x1D504] = 0x41, -- A (fraktur A)
--- [0x1D505] = 0x42, -- B
- [0x0212D] = 0x43, -- C
--- [0x1D507] = 0x44, -- D
--- [0x1D508] = 0x45, -- E
--- [0x1D509] = 0x46, -- F
--- [0x1D50A] = 0x47, -- G
- [0x0210C] = 0x48, -- H
- [0x02111] = 0x49, -- I
--- [0x1D50D] = 0x4A, -- J
--- [0x1D50E] = 0x4B, -- K
--- [0x1D50F] = 0x4C, -- L
--- [0x1D510] = 0x4D, -- M
--- [0x1D511] = 0x4E, -- N
--- [0x1D512] = 0x4F, -- O
--- [0x1D513] = 0x50, -- P
--- [0x1D514] = 0x51, -- Q
- [0x0211C] = 0x52, -- R
--- [0x1D516] = 0x53, -- S
--- [0x1D517] = 0x54, -- T
--- [0x1D518] = 0x55, -- U
--- [0x1D519] = 0x56, -- V
--- [0x1D51A] = 0x57, -- W
--- [0x1D51B] = 0x58, -- X
--- [0x1D51C] = 0x59, -- Y
- [0x02128] = 0x5A, -- Z (fraktur Z)
--- [0x1D51E] = 0x61, -- a (fraktur a)
--- [0x1D51F] = 0x62, -- b
--- [0x1D520] = 0x63, -- c
--- [0x1D521] = 0x64, -- d
--- [0x1D522] = 0x65, -- e
--- [0x1D523] = 0x66, -- f
--- [0x1D524] = 0x67, -- g
--- [0x1D525] = 0x68, -- h
--- [0x1D526] = 0x69, -- i
--- [0x1D527] = 0x6A, -- j
--- [0x1D528] = 0x6B, -- k
--- [0x1D529] = 0x6C, -- l
--- [0x1D52A] = 0x6D, -- m
--- [0x1D52B] = 0x6E, -- n
--- [0x1D52C] = 0x6F, -- o
--- [0x1D52D] = 0x70, -- p
--- [0x1D52E] = 0x71, -- q
--- [0x1D52F] = 0x72, -- r
--- [0x1D530] = 0x73, -- s
--- [0x1D531] = 0x74, -- t
--- [0x1D532] = 0x75, -- u
--- [0x1D533] = 0x76, -- v
--- [0x1D534] = 0x77, -- w
--- [0x1D535] = 0x78, -- x
--- [0x1D536] = 0x79, -- y
--- [0x1D537] = 0x7A, -- z
-}
-
--- now that all other vectors are defined ...
-
-setletters(mathencodings, "tex-it", 0x1D434, 0x1D44E)
-setletters(mathencodings, "tex-ss", 0x1D5A0, 0x1D5BA)
-setletters(mathencodings, "tex-tt", 0x1D670, 0x1D68A)
-setletters(mathencodings, "tex-bf", 0x1D400, 0x1D41A)
-setletters(mathencodings, "tex-bi", 0x1D468, 0x1D482)
-setletters(mathencodings, "tex-fraktur", 0x1D504, 0x1D51E)
-setletters(mathencodings, "tex-fraktur-bold", 0x1D56C, 0x1D586)
-
-setdigits (mathencodings, "tex-ss", 0x1D7E2)
-setdigits (mathencodings, "tex-tt", 0x1D7F6)
-setdigits (mathencodings, "tex-bf", 0x1D7CE)
-
--- setdigits (mathencodings, "tex-bi", 0x1D7CE)
-
--- todo: add ss, tt, bf etc vectors
--- todo: we can make ss tt etc an option
+if not modules then modules = { } end modules ['math-ttv'] = {
+ version = 1.001,
+ comment = "traditional tex vectors, companion to math-vfu.lua",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+ dataonly = true,
+}
+
+local vfmath = fonts.handlers.vf.math
+local setletters = vfmath.setletters
+local setdigits = vfmath.setdigits
+
+local mathencodings = fonts.encodings.math
+
+-- varphi is part of the alphabet, contrary to the other var*s'
+
+mathencodings["large-to-small"] = {
+ [0x00028] = 0x00, -- (
+ [0x00029] = 0x01, -- )
+ [0x0005B] = 0x02, -- [
+ [0x0005D] = 0x03, -- ]
+ [0x0230A] = 0x04, -- lfloor
+ [0x0230B] = 0x05, -- rfloor
+ [0x02308] = 0x06, -- lceil
+ [0x02309] = 0x07, -- rceil
+ [0x0007B] = 0x08, -- {
+ [0x0007D] = 0x09, -- }
+ [0x027E8] = 0x0A, -- <
+ [0x027E9] = 0x0B, -- >
+ [0x0007C] = 0x0C, -- |
+ -- [0x0] = 0x0D, -- lVert rVert Vert
+ -- [0x0002F] = 0x0E, -- /
+ [0x0005C] = 0x0F, -- \
+ -- [0x0] = 0x3A, -- lgroup
+ -- [0x0] = 0x3B, -- rgroup
+ -- [0x0] = 0x3C, -- arrowvert
+ -- [0x0] = 0x3D, -- Arrowvert
+ [0x02195] = 0x3F, -- updownarrow
+ -- [0x0] = 0x40, -- lmoustache
+ -- [0x0] = 0x41, -- rmoustache
+ [0x0221A] = 0x70, -- sqrt
+ [0x021D5] = 0x77, -- Updownarrow
+ [0x02191] = 0x78, -- uparrow
+ [0x02193] = 0x79, -- downarrow
+ [0x021D1] = 0x7E, -- Uparrow
+ [0x021D3] = 0x7F, -- Downarrow
+ [0x0220F] = 0x59, -- prod
+ [0x02210] = 0x61, -- coprod
+ [0x02211] = 0x58, -- sum
+ [0x0222B] = 0x5A, -- intop
+ [0x0222E] = 0x49, -- ointop
+ -- [0xFE302] = 0x62, -- widehat
+ -- [0xFE303] = 0x65, -- widetilde
+ [0x00302] = 0x62, -- widehat
+ [0x00303] = 0x65, -- widetilde
+ [0x022C0] = 0x5E, -- bigwedge
+ [0x022C1] = 0x5F, -- bigvee
+ [0x022C2] = 0x5C, -- bigcap
+ [0x022C3] = 0x5B, -- bigcup
+ [0x02044] = 0x0E, -- /
+}
+
+-- Beware: these are (in cm/lm) below the baseline due to limitations
+-- in the tfm format bu the engien (combined with the mathclass) takes
+-- care of it. If we need them in textmode, we should make them virtual
+-- and move them up but we're in no hurry with that.
+
+mathencodings["tex-ex"] = {
+ [0x0220F] = 0x51, -- prod
+ [0x02210] = 0x60, -- coprod
+ [0x02211] = 0x50, -- sum
+ [0x0222B] = 0x52, -- intop
+ [0x0222E] = 0x48, -- ointop
+ [0x022C0] = 0x56, -- bigwedge
+ [0x022C1] = 0x57, -- bigvee
+ [0x022C2] = 0x54, -- bigcap
+ [0x022C3] = 0x53, -- bigcup
+ [0x02A00] = 0x4A, -- bigodot -- fixed BJ
+ [0x02A01] = 0x4C, -- bigoplus
+ [0x02A02] = 0x4E, -- bigotimes
+ -- [0x02A03] = , -- bigudot --
+ [0x02A04] = 0x55, -- biguplus
+ [0x02A06] = 0x46, -- bigsqcup
+}
+
+-- only math stuff is needed, since we always use an lm or gyre
+-- font as main font
+
+mathencodings["tex-mr"] = {
+ [0x00393] = 0x00, -- Gamma
+ [0x00394] = 0x01, -- Delta
+ [0x00398] = 0x02, -- Theta
+ [0x0039B] = 0x03, -- Lambda
+ [0x0039E] = 0x04, -- Xi
+ [0x003A0] = 0x05, -- Pi
+ [0x003A3] = 0x06, -- Sigma
+ [0x003A5] = 0x07, -- Upsilon
+ [0x003A6] = 0x08, -- Phi
+ [0x003A8] = 0x09, -- Psi
+ [0x003A9] = 0x0A, -- Omega
+-- [0x00060] = 0x12, -- [math]grave
+-- [0x000B4] = 0x13, -- [math]acute
+-- [0x002C7] = 0x14, -- [math]check
+-- [0x002D8] = 0x15, -- [math]breve
+-- [0x000AF] = 0x16, -- [math]bar
+-- [0x00021] = 0x21, -- !
+-- [0x00028] = 0x28, -- (
+-- [0x00029] = 0x29, -- )
+-- [0x0002B] = 0x2B, -- +
+-- [0x0002F] = 0x2F, -- /
+-- [0x0003A] = 0x3A, -- :
+-- [0x02236] = 0x3A, -- colon
+-- [0x0003B] = 0x3B, -- ;
+-- [0x0003C] = 0x3C, -- <
+-- [0x0003D] = 0x3D, -- =
+-- [0x0003E] = 0x3E, -- >
+-- [0x0003F] = 0x3F, -- ?
+ [0x00391] = 0x41, -- Alpha
+ [0x00392] = 0x42, -- Beta
+ [0x02145] = 0x44,
+ [0x00395] = 0x45, -- Epsilon
+ [0x00397] = 0x48, -- Eta
+ [0x00399] = 0x49, -- Iota
+ [0x0039A] = 0x4B, -- Kappa
+ [0x0039C] = 0x4D, -- Mu
+ [0x0039D] = 0x4E, -- Nu
+ [0x0039F] = 0x4F, -- Omicron
+ [0x003A1] = 0x52, -- Rho
+ [0x003A4] = 0x54, -- Tau
+ [0x003A7] = 0x58, -- Chi
+ [0x00396] = 0x5A, -- Zeta
+-- [0x0005B] = 0x5B, -- [
+-- [0x0005D] = 0x5D, -- ]
+-- [0x0005E] = 0x5E, -- [math]hat -- the text one
+ [0x00302] = 0x5E, -- [math]hat -- the real math one
+-- [0x002D9] = 0x5F, -- [math]dot
+ [0x02146] = 0x64,
+ [0x02147] = 0x65,
+-- [0x002DC] = 0x7E, -- [math]tilde -- the text one
+ [0x00303] = 0x7E, -- [math]tilde -- the real one
+-- [0x000A8] = 0x7F, -- [math]ddot
+}
+
+mathencodings["tex-mr-missing"] = {
+ [0x02236] = 0x3A, -- colon
+}
+
+mathencodings["tex-mi"] = {
+ [0x1D6E4] = 0x00, -- Gamma
+ [0x1D6E5] = 0x01, -- Delta
+ [0x1D6E9] = 0x02, -- Theta
+ [0x1D6F3] = 0x02, -- varTheta (not present in TeX)
+ [0x1D6EC] = 0x03, -- Lambda
+ [0x1D6EF] = 0x04, -- Xi
+ [0x1D6F1] = 0x05, -- Pi
+ [0x1D6F4] = 0x06, -- Sigma
+ [0x1D6F6] = 0x07, -- Upsilon
+ [0x1D6F7] = 0x08, -- Phi
+ [0x1D6F9] = 0x09, -- Psi
+ [0x1D6FA] = 0x0A, -- Omega
+ [0x1D6FC] = 0x0B, -- alpha
+ [0x1D6FD] = 0x0C, -- beta
+ [0x1D6FE] = 0x0D, -- gamma
+ [0x1D6FF] = 0x0E, -- delta
+ [0x1D716] = 0x0F, -- epsilon TODO: 1D716
+ [0x1D701] = 0x10, -- zeta
+ [0x1D702] = 0x11, -- eta
+ [0x1D703] = 0x12, -- theta TODO: 1D703
+ [0x1D704] = 0x13, -- iota
+ [0x1D705] = 0x14, -- kappa
+ [0x1D718] = 0x14, -- varkappa, not in tex fonts
+ [0x1D706] = 0x15, -- lambda
+ [0x1D707] = 0x16, -- mu
+ [0x1D708] = 0x17, -- nu
+ [0x1D709] = 0x18, -- xi
+ [0x1D70B] = 0x19, -- pi
+ [0x1D70C] = 0x1A, -- rho
+ [0x1D70E] = 0x1B, -- sigma
+ [0x1D70F] = 0x1C, -- tau
+ [0x1D710] = 0x1D, -- upsilon
+ [0x1D719] = 0x1E, -- phi
+ [0x1D712] = 0x1F, -- chi
+ [0x1D713] = 0x20, -- psi
+ [0x1D714] = 0x21, -- omega
+ [0x1D700] = 0x22, -- varepsilon (the other way around)
+ [0x1D717] = 0x23, -- vartheta
+ [0x1D71B] = 0x24, -- varpi
+ [0x1D71A] = 0x25, -- varrho
+ [0x1D70D] = 0x26, -- varsigma
+ [0x1D711] = 0x27, -- varphi (the other way around)
+ [0x021BC] = 0x28, -- leftharpoonup
+ [0x021BD] = 0x29, -- leftharpoondown
+ [0x021C0] = 0x2A, -- rightharpoonup
+ [0x021C1] = 0x2B, -- rightharpoondown
+ [0xFE322] = 0x2C, -- lhook (hook for combining arrows)
+ [0xFE323] = 0x2D, -- rhook (hook for combining arrows)
+ [0x025B7] = 0x2E, -- triangleright : cf lmmath / BJ
+ [0x025C1] = 0x2F, -- triangleleft : cf lmmath / BJ
+ [0x022B3] = 0x2E, -- triangleright : cf lmmath this a cramped triangles / BJ / see *
+ [0x022B2] = 0x2F, -- triangleleft : cf lmmath this a cramped triangles / BJ / see *
+-- [0x00041] = 0x30, -- 0
+-- [0x00041] = 0x31, -- 1
+-- [0x00041] = 0x32, -- 2
+-- [0x00041] = 0x33, -- 3
+-- [0x00041] = 0x34, -- 4
+-- [0x00041] = 0x35, -- 5
+-- [0x00041] = 0x36, -- 6
+-- [0x00041] = 0x37, -- 7
+-- [0x00041] = 0x38, -- 8
+-- [0x00041] = 0x39, -- 9
+--~ [0x0002E] = 0x3A, -- .
+ [0x0002C] = 0x3B, -- ,
+ [0x0003C] = 0x3C, -- <
+-- [0x0002F] = 0x3D, -- /, slash, solidus
+ [0x02044] = 0x3D, -- / AM: Not sure
+ [0x0003E] = 0x3E, -- >
+ [0x022C6] = 0x3F, -- star
+ [0x02202] = 0x40, -- partial
+--
+ [0x0266D] = 0x5B, -- flat
+ [0x0266E] = 0x5C, -- natural
+ [0x0266F] = 0x5D, -- sharp
+ [0x02323] = 0x5E, -- smile
+ [0x02322] = 0x5F, -- frown
+ [0x02113] = 0x60, -- ell
+--
+ [0x1D6A4] = 0x7B, -- imath (TODO: also 0131)
+ [0x1D6A5] = 0x7C, -- jmath (TODO: also 0237)
+ [0x02118] = 0x7D, -- wp
+ [0x020D7] = 0x7E, -- vec (TODO: not sure)
+-- 0x7F, -- (no idea what that could be)
+}
+
+mathencodings["tex-it"] = {
+-- [0x1D434] = 0x41, -- A
+ [0x1D6E2] = 0x41, -- Alpha
+-- [0x1D435] = 0x42, -- B
+ [0x1D6E3] = 0x42, -- Beta
+-- [0x1D436] = 0x43, -- C
+-- [0x1D437] = 0x44, -- D
+-- [0x1D438] = 0x45, -- E
+ [0x1D6E6] = 0x45, -- Epsilon
+-- [0x1D439] = 0x46, -- F
+-- [0x1D43A] = 0x47, -- G
+-- [0x1D43B] = 0x48, -- H
+ [0x1D6E8] = 0x48, -- Eta
+-- [0x1D43C] = 0x49, -- I
+ [0x1D6EA] = 0x49, -- Iota
+-- [0x1D43D] = 0x4A, -- J
+-- [0x1D43E] = 0x4B, -- K
+ [0x1D6EB] = 0x4B, -- Kappa
+-- [0x1D43F] = 0x4C, -- L
+-- [0x1D440] = 0x4D, -- M
+ [0x1D6ED] = 0x4D, -- Mu
+-- [0x1D441] = 0x4E, -- N
+ [0x1D6EE] = 0x4E, -- Nu
+-- [0x1D442] = 0x4F, -- O
+ [0x1D6F0] = 0x4F, -- Omicron
+-- [0x1D443] = 0x50, -- P
+ [0x1D6F2] = 0x50, -- Rho
+-- [0x1D444] = 0x51, -- Q
+-- [0x1D445] = 0x52, -- R
+-- [0x1D446] = 0x53, -- S
+-- [0x1D447] = 0x54, -- T
+ [0x1D6F5] = 0x54, -- Tau
+-- [0x1D448] = 0x55, -- U
+-- [0x1D449] = 0x56, -- V
+-- [0x1D44A] = 0x57, -- W
+-- [0x1D44B] = 0x58, -- X
+ [0x1D6F8] = 0x58, -- Chi
+-- [0x1D44C] = 0x59, -- Y
+-- [0x1D44D] = 0x5A, -- Z
+--
+-- [0x1D44E] = 0x61, -- a
+-- [0x1D44F] = 0x62, -- b
+-- [0x1D450] = 0x63, -- c
+-- [0x1D451] = 0x64, -- d
+-- [0x1D452] = 0x65, -- e
+-- [0x1D453] = 0x66, -- f
+-- [0x1D454] = 0x67, -- g
+-- [0x1D455] = 0x68, -- h
+ [0x0210E] = 0x68, -- Planck constant (h)
+-- [0x1D456] = 0x69, -- i
+-- [0x1D457] = 0x6A, -- j
+-- [0x1D458] = 0x6B, -- k
+-- [0x1D459] = 0x6C, -- l
+-- [0x1D45A] = 0x6D, -- m
+-- [0x1D45B] = 0x6E, -- n
+-- [0x1D45C] = 0x6F, -- o
+ [0x1D70A] = 0x6F, -- omicron
+-- [0x1D45D] = 0x70, -- p
+-- [0x1D45E] = 0x71, -- q
+-- [0x1D45F] = 0x72, -- r
+-- [0x1D460] = 0x73, -- s
+-- [0x1D461] = 0x74, -- t
+-- [0x1D462] = 0x75, -- u
+-- [0x1D463] = 0x76, -- v
+-- [0x1D464] = 0x77, -- w
+-- [0x1D465] = 0x78, -- x
+-- [0x1D466] = 0x79, -- y
+-- [0x1D467] = 0x7A, -- z
+}
+
+mathencodings["tex-ss"] = { }
+mathencodings["tex-tt"] = { }
+mathencodings["tex-bf"] = { }
+mathencodings["tex-bi"] = { }
+mathencodings["tex-fraktur"] = { }
+mathencodings["tex-fraktur-bold"] = { }
+
+mathencodings["tex-sy"] = {
+ [0x0002D] = 0x00, -- -
+ [0x02212] = 0x00, -- -
+-- [0x02201] = 0x00, -- complement
+-- [0x02206] = 0x00, -- increment
+-- [0x02204] = 0x00, -- not exists
+-- [0x000B7] = 0x01, -- cdot
+ [0x022C5] = 0x01, -- cdot
+ [0x000D7] = 0x02, -- times
+ [0x0002A] = 0x03, -- *
+ [0x02217] = 0x03, -- *
+ [0x000F7] = 0x04, -- div
+ [0x022C4] = 0x05, -- diamond
+ [0x000B1] = 0x06, -- pm
+ [0x02213] = 0x07, -- mp
+ [0x02295] = 0x08, -- oplus
+ [0x02296] = 0x09, -- ominus
+ [0x02297] = 0x0A, -- otimes
+ [0x02298] = 0x0B, -- oslash
+ [0x02299] = 0x0C, -- odot
+ [0x025EF] = 0x0D, -- bigcirc, Orb (either 25EF or 25CB) -- todo
+ [0x02218] = 0x0E, -- circ
+ [0x02219] = 0x0F, -- bullet
+ [0x02022] = 0x0F, -- bullet
+ [0x0224D] = 0x10, -- asymp
+ [0x02261] = 0x11, -- equiv
+ [0x02286] = 0x12, -- subseteq
+ [0x02287] = 0x13, -- supseteq
+ [0x02264] = 0x14, -- leq
+ [0x02265] = 0x15, -- geq
+ [0x02AAF] = 0x16, -- preceq
+-- [0x0227C] = 0x16, -- preceq, AM:No see 2AAF
+ [0x02AB0] = 0x17, -- succeq
+-- [0x0227D] = 0x17, -- succeq, AM:No see 2AB0
+ [0x0223C] = 0x18, -- sim
+ [0x02248] = 0x19, -- approx
+ [0x02282] = 0x1A, -- subset
+ [0x02283] = 0x1B, -- supset
+ [0x0226A] = 0x1C, -- ll
+ [0x0226B] = 0x1D, -- gg
+ [0x0227A] = 0x1E, -- prec
+ [0x0227B] = 0x1F, -- succ
+ [0x02190] = 0x20, -- leftarrow
+ [0x02192] = 0x21, -- rightarrow
+--~ [0xFE190] = 0x20, -- leftarrow
+--~ [0xFE192] = 0x21, -- rightarrow
+ [0x02191] = 0x22, -- uparrow
+ [0x02193] = 0x23, -- downarrow
+ [0x02194] = 0x24, -- leftrightarrow
+ [0x02197] = 0x25, -- nearrow
+ [0x02198] = 0x26, -- searrow
+ [0x02243] = 0x27, -- simeq
+ [0x021D0] = 0x28, -- Leftarrow
+ [0x021D2] = 0x29, -- Rightarrow
+ [0x021D1] = 0x2A, -- Uparrow
+ [0x021D3] = 0x2B, -- Downarrow
+ [0x021D4] = 0x2C, -- Leftrightarrow
+ [0x02196] = 0x2D, -- nwarrow
+ [0x02199] = 0x2E, -- swarrow
+ [0x0221D] = 0x2F, -- propto
+ [0x02032] = 0x30, -- prime
+ [0x0221E] = 0x31, -- infty
+ [0x02208] = 0x32, -- in
+ [0x0220B] = 0x33, -- ni
+ [0x025B3] = 0x34, -- triangle, bigtriangleup
+ [0x025BD] = 0x35, -- bigtriangledown
+ [0x00338] = 0x36, -- not
+-- 0x37, -- (beginning of arrow)
+ [0x02200] = 0x38, -- forall
+ [0x02203] = 0x39, -- exists
+ [0x000AC] = 0x3A, -- neg, lnot
+ [0x02205] = 0x3B, -- empty set
+ [0x0211C] = 0x3C, -- Re
+ [0x02111] = 0x3D, -- Im
+ [0x022A4] = 0x3E, -- top
+ [0x022A5] = 0x3F, -- bot, perp
+ [0x02135] = 0x40, -- aleph
+ [0x1D49C] = 0x41, -- script A
+ [0x0212C] = 0x42, -- script B
+ [0x1D49E] = 0x43, -- script C
+ [0x1D49F] = 0x44, -- script D
+ [0x02130] = 0x45, -- script E
+ [0x02131] = 0x46, -- script F
+ [0x1D4A2] = 0x47, -- script G
+ [0x0210B] = 0x48, -- script H
+ [0x02110] = 0x49, -- script I
+ [0x1D4A5] = 0x4A, -- script J
+ [0x1D4A6] = 0x4B, -- script K
+ [0x02112] = 0x4C, -- script L
+ [0x02133] = 0x4D, -- script M
+ [0x1D4A9] = 0x4E, -- script N
+ [0x1D4AA] = 0x4F, -- script O
+ [0x1D4AB] = 0x50, -- script P
+ [0x1D4AC] = 0x51, -- script Q
+ [0x0211B] = 0x52, -- script R
+ [0x1D4AE] = 0x53, -- script S
+ [0x1D4AF] = 0x54, -- script T
+ [0x1D4B0] = 0x55, -- script U
+ [0x1D4B1] = 0x56, -- script V
+ [0x1D4B2] = 0x57, -- script W
+ [0x1D4B3] = 0x58, -- script X
+ [0x1D4B4] = 0x59, -- script Y
+ [0x1D4B5] = 0x5A, -- script Z
+ [0x0222A] = 0x5B, -- cup
+ [0x02229] = 0x5C, -- cap
+ [0x0228E] = 0x5D, -- uplus
+ [0x02227] = 0x5E, -- wedge, land
+ [0x02228] = 0x5F, -- vee, lor
+ [0x022A2] = 0x60, -- vdash
+ [0x022A3] = 0x61, -- dashv
+ [0x0230A] = 0x62, -- lfloor
+ [0x0230B] = 0x63, -- rfloor
+ [0x02308] = 0x64, -- lceil
+ [0x02309] = 0x65, -- rceil
+ [0x0007B] = 0x66, -- {, lbrace
+ [0x0007D] = 0x67, -- }, rbrace
+ [0x027E8] = 0x68, -- <, langle
+ [0x027E9] = 0x69, -- >, rangle
+ [0x0007C] = 0x6A, -- |, mid, lvert, rvert
+ [0x02225] = 0x6B, -- parallel
+ -- [0x0 ] = 0x00, -- Vert, lVert, rVert, arrowvert, Arrowvert
+ [0x02195] = 0x6C, -- updownarrow
+ [0x021D5] = 0x6D, -- Updownarrow
+ [0x0005C] = 0x6E, -- \, backslash, setminus
+ [0x02216] = 0x6E, -- setminus
+ [0x02240] = 0x6F, -- wr
+ [0x0221A] = 0x70, -- sqrt. AM: Check surd??
+ [0x02A3F] = 0x71, -- amalg
+ [0x1D6FB] = 0x72, -- nabla
+-- [0x0222B] = 0x73, -- smallint (TODO: what about intop?)
+ [0x02294] = 0x74, -- sqcup
+ [0x02293] = 0x75, -- sqcap
+ [0x02291] = 0x76, -- sqsubseteq
+ [0x02292] = 0x77, -- sqsupseteq
+ [0x000A7] = 0x78, -- S
+ [0x02020] = 0x79, -- dagger, dag
+ [0x02021] = 0x7A, -- ddagger, ddag
+ [0x000B6] = 0x7B, -- P
+ [0x02663] = 0x7C, -- clubsuit
+ [0x02662] = 0x7D, -- diamondsuit
+ [0x02661] = 0x7E, -- heartsuit
+ [0x02660] = 0x7F, -- spadesuit
+ [0xFE321] = 0x37, -- mapstochar
+
+ [0xFE325] = 0x30, -- prime 0x02032
+}
+
+-- The names in masm10.enc can be trusted best and are shown in the first
+-- column, while in the second column we show the tex/ams names. As usual
+-- it costs hours to figure out such a table.
+
+mathencodings["tex-ma"] = {
+ [0x022A1] = 0x00, -- squaredot \boxdot
+ [0x0229E] = 0x01, -- squareplus \boxplus
+ [0x022A0] = 0x02, -- squaremultiply \boxtimes
+ [0x025A1] = 0x03, -- square \square \Box
+ [0x025A0] = 0x04, -- squaresolid \blacksquare
+ [0x025AA] = 0x05, -- squaresmallsolid \centerdot
+ [0x022C4] = 0x06, -- diamond \Diamond \lozenge
+ [0x02666] = 0x07, -- diamondsolid \blacklozenge
+ [0x021BB] = 0x08, -- clockwise \circlearrowright
+ [0x021BA] = 0x09, -- anticlockwise \circlearrowleft
+ [0x021CC] = 0x0A, -- harpoonleftright \rightleftharpoons
+ [0x021CB] = 0x0B, -- harpoonrightleft \leftrightharpoons
+ [0x0229F] = 0x0C, -- squareminus \boxminus
+ [0x022A9] = 0x0D, -- forces \Vdash
+ [0x022AA] = 0x0E, -- forcesbar \Vvdash
+ [0x022A8] = 0x0F, -- satisfies \vDash
+ [0x021A0] = 0x10, -- dblarrowheadright \twoheadrightarrow
+ [0x0219E] = 0x11, -- dblarrowheadleft \twoheadleftarrow
+ [0x021C7] = 0x12, -- dblarrowleft \leftleftarrows
+ [0x021C9] = 0x13, -- dblarrowright \rightrightarrows
+ [0x021C8] = 0x14, -- dblarrowup \upuparrows
+ [0x021CA] = 0x15, -- dblarrowdwn \downdownarrows
+ [0x021BE] = 0x16, -- harpoonupright \upharpoonright \restriction
+ [0x021C2] = 0x17, -- harpoondownright \downharpoonright
+ [0x021BF] = 0x18, -- harpoonupleft \upharpoonleft
+ [0x021C3] = 0x19, -- harpoondownleft \downharpoonleft
+ [0x021A3] = 0x1A, -- arrowtailright \rightarrowtail
+ [0x021A2] = 0x1B, -- arrowtailleft \leftarrowtail
+ [0x021C6] = 0x1C, -- arrowparrleftright \leftrightarrows
+-- [0x021C5] = 0x00, -- \updownarrows (missing in lm)
+ [0x021C4] = 0x1D, -- arrowparrrightleft \rightleftarrows
+ [0x021B0] = 0x1E, -- shiftleft \Lsh
+ [0x021B1] = 0x1F, -- shiftright \Rsh
+ [0x021DD] = 0x20, -- squiggleright \leadsto \rightsquigarrow
+ [0x021AD] = 0x21, -- squiggleleftright \leftrightsquigarrow
+ [0x021AB] = 0x22, -- curlyleft \looparrowleft
+ [0x021AC] = 0x23, -- curlyright \looparrowright
+ [0x02257] = 0x24, -- circleequal \circeq
+ [0x0227F] = 0x25, -- followsorequal \succsim
+ [0x02273] = 0x26, -- greaterorsimilar \gtrsim
+ [0x02A86] = 0x27, -- greaterorapproxeql \gtrapprox
+ [0x022B8] = 0x28, -- multimap \multimap
+ [0x02234] = 0x29, -- therefore \therefore
+ [0x02235] = 0x2A, -- because \because
+ [0x02251] = 0x2B, -- equalsdots \Doteq \doteqdot
+ [0x0225C] = 0x2C, -- defines \triangleq
+ [0x0227E] = 0x2D, -- precedesorequal \precsim
+ [0x02272] = 0x2E, -- lessorsimilar \lesssim
+ [0x02A85] = 0x2F, -- lessorapproxeql \lessapprox
+ [0x02A95] = 0x30, -- equalorless \eqslantless
+ [0x02A96] = 0x31, -- equalorgreater \eqslantgtr
+ [0x022DE] = 0x32, -- equalorprecedes \curlyeqprec
+ [0x022DF] = 0x33, -- equalorfollows \curlyeqsucc
+ [0x0227C] = 0x34, -- precedesorcurly \preccurlyeq
+ [0x02266] = 0x35, -- lessdblequal \leqq
+ [0x02A7D] = 0x36, -- lessorequalslant \leqslant
+ [0x02276] = 0x37, -- lessorgreater \lessgtr
+ [0x02035] = 0x38, -- primereverse \backprime
+ -- [0x0] = 0x39, -- axisshort \dabar
+ [0x02253] = 0x3A, -- equaldotrightleft \risingdotseq
+ [0x02252] = 0x3B, -- equaldotleftright \fallingdotseq
+ [0x0227D] = 0x3C, -- followsorcurly \succcurlyeq
+ [0x02267] = 0x3D, -- greaterdblequal \geqq
+ [0x02A7E] = 0x3E, -- greaterorequalslant \geqslant
+ [0x02277] = 0x3F, -- greaterorless \gtrless
+ [0x0228F] = 0x40, -- squareimage \sqsubset
+ [0x02290] = 0x41, -- squareoriginal \sqsupset
+ -- wrong: see **
+ -- [0x022B3] = 0x42, -- triangleright \rhd \vartriangleright
+ -- [0x022B2] = 0x43, -- triangleleft \lhd \vartriangleleft
+ -- cf lm
+ [0x022B5] = 0x44, -- trianglerightequal \unrhd \trianglerighteq
+ [0x022B4] = 0x45, -- triangleleftequal \unlhd \trianglelefteq
+ --
+ [0x02605] = 0x46, -- star \bigstar
+ [0x0226C] = 0x47, -- between \between
+ [0x025BC] = 0x48, -- triangledownsld \blacktriangledown
+ [0x025B6] = 0x49, -- trianglerightsld \blacktriangleright
+ [0x025C0] = 0x4A, -- triangleleftsld \blacktriangleleft
+ -- [0x0] = 0x4B, -- arrowaxisright
+ -- [0x0] = 0x4C, -- arrowaxisleft
+ [0x025B2] = 0x4D, -- triangle \triangleup \vartriangle
+ [0x025B2] = 0x4E, -- trianglesolid \blacktriangle
+ [0x025BD] = 0x4F, -- triangleinv \triangledown
+ [0x02256] = 0x50, -- ringinequal \eqcirc
+ [0x022DA] = 0x51, -- lessequalgreater \lesseqgtr
+ [0x022DB] = 0x52, -- greaterlessequal \gtreqless
+ [0x02A8B] = 0x53, -- lessdbleqlgreater \lesseqqgtr
+ [0x02A8C] = 0x54, -- greaterdbleqlless \gtreqqless
+ [0x000A5] = 0x55, -- Yen \yen
+ [0x021DB] = 0x56, -- arrowtripleright \Rrightarrow
+ [0x021DA] = 0x57, -- arrowtripleleft \Lleftarrow
+ [0x02713] = 0x58, -- check \checkmark
+ [0x022BB] = 0x59, -- orunderscore \veebar
+ [0x022BC] = 0x5A, -- nand \barwedge
+ [0x02306] = 0x5B, -- perpcorrespond \doublebarwedge
+ [0x02220] = 0x5C, -- angle \angle
+ [0x02221] = 0x5D, -- measuredangle \measuredangle
+ [0x02222] = 0x5E, -- sphericalangle \sphericalangle
+ -- [0x0] = 0x5F, -- proportional \varpropto
+ -- [0x0] = 0x60, -- smile \smallsmile
+ -- [0x0] = 0x61, -- frown \smallfrown
+ [0x022D0] = 0x62, -- subsetdbl \Subset
+ [0x022D1] = 0x63, -- supersetdbl \Supset
+ [0x022D3] = 0x64, -- uniondbl \doublecup \Cup
+ [0x022D2] = 0x65, -- intersectiondbl \doublecap \Cap
+ [0x022CF] = 0x66, -- uprise \curlywedge
+ [0x022CE] = 0x67, -- downfall \curlyvee
+ [0x022CB] = 0x68, -- multiopenleft \leftthreetimes
+ [0x022CC] = 0x69, -- multiopenright \rightthreetimes
+ [0x02AC5] = 0x6A, -- subsetdblequal \subseteqq
+ [0x02AC6] = 0x6B, -- supersetdblequal \supseteqq
+ [0x0224F] = 0x6C, -- difference \bumpeq
+ [0x0224E] = 0x6D, -- geomequivalent \Bumpeq
+ [0x022D8] = 0x6E, -- muchless \lll \llless
+ [0x022D9] = 0x6F, -- muchgreater \ggg \gggtr
+ [0x0231C] = 0x70, -- rightanglenw \ulcorner
+ [0x0231D] = 0x71, -- rightanglene \urcorner
+ [0x024C7] = 0x72, -- circleR \circledR
+ [0x024C8] = 0x73, -- circleS \circledS
+ [0x022D4] = 0x74, -- fork \pitchfork
+ [0x02214] = 0x75, -- dotplus \dotplus
+ [0x0223D] = 0x76, -- revsimilar \backsim
+ [0x022CD] = 0x77, -- revasymptequal \backsimeq -- AM: Check this! I mapped it to simeq.
+ [0x0231E] = 0x78, -- rightanglesw \llcorner
+ [0x0231F] = 0x79, -- rightanglese \lrcorner
+ [0x02720] = 0x7A, -- maltesecross \maltese
+ [0x02201] = 0x7B, -- complement \complement
+ [0x022BA] = 0x7C, -- intercal \intercal
+ [0x0229A] = 0x7D, -- circlering \circledcirc
+ [0x0229B] = 0x7E, -- circleasterisk \circledast
+ [0x0229D] = 0x7F, -- circleminus \circleddash
+}
+
+mathencodings["tex-mb"] = {
+ -- [0x0] = 0x00, -- lessornotequal \lvertneqq
+ -- [0x0] = 0x01, -- greaterornotequal \gvertneqq
+ [0x02270] = 0x02, -- notlessequal \nleq
+ [0x02271] = 0x03, -- notgreaterequal \ngeq
+ [0x0226E] = 0x04, -- notless \nless
+ [0x0226F] = 0x05, -- notgreater \ngtr
+ [0x02280] = 0x06, -- notprecedes \nprec
+ [0x02281] = 0x07, -- notfollows \nsucc
+ [0x02268] = 0x08, -- lessornotdbleql \lneqq
+ [0x02269] = 0x09, -- greaterornotdbleql \gneqq
+ -- [0x0] = 0x0A, -- notlessorslnteql \nleqslant
+ -- [0x0] = 0x0B, -- notgreaterorslnteql \ngeqslant
+ [0x02A87] = 0x0C, -- lessnotequal \lneq
+ [0x02A88] = 0x0D, -- greaternotequal \gneq
+ -- [0x0] = 0x0E, -- notprecedesoreql \npreceq
+ -- [0x0] = 0x0F, -- notfollowsoreql \nsucceq
+ [0x022E8] = 0x10, -- precedeornoteqvlnt \precnsim
+ [0x022E9] = 0x11, -- followornoteqvlnt \succnsim
+ [0x022E6] = 0x12, -- lessornotsimilar \lnsim
+ [0x022E7] = 0x13, -- greaterornotsimilar \gnsim
+ -- [0x0] = 0x14, -- notlessdblequal \nleqq
+ -- [0x0] = 0x15, -- notgreaterdblequal \ngeqq
+ [0x02AB5] = 0x16, -- precedenotslnteql \precneqq
+ [0x02AB6] = 0x17, -- follownotslnteql \succneqq
+ [0x02AB9] = 0x18, -- precedenotdbleqv \precnapprox
+ [0x02ABA] = 0x19, -- follownotdbleqv \succnapprox
+ [0x02A89] = 0x1A, -- lessnotdblequal \lnapprox
+ [0x02A8A] = 0x1B, -- greaternotdblequal \gnapprox
+ [0x02241] = 0x1C, -- notsimilar \nsim
+ [0x02247] = 0x1D, -- notapproxequal \ncong
+ -- [0x0] = 0x1E, -- upslope \diagup
+ -- [0x0] = 0x1F, -- downslope \diagdown
+ -- [0x0] = 0x20, -- notsubsetoreql \varsubsetneq
+ -- [0x0] = 0x21, -- notsupersetoreql \varsupsetneq
+ -- [0x0] = 0x22, -- notsubsetordbleql \nsubseteqq
+ -- [0x0] = 0x23, -- notsupersetordbleql \nsupseteqq
+ [0x02ACB] = 0x24, -- subsetornotdbleql \subsetneqq
+ [0x02ACC] = 0x25, -- supersetornotdbleql \supsetneqq
+ -- [0x0] = 0x26, -- subsetornoteql \varsubsetneqq
+ -- [0x0] = 0x27, -- supersetornoteql \varsupsetneqq
+ [0x0228A] = 0x28, -- subsetnoteql \subsetneq
+ [0x0228B] = 0x29, -- supersetnoteql \supsetneq
+ [0x02288] = 0x2A, -- notsubseteql \nsubseteq
+ [0x02289] = 0x2B, -- notsuperseteql \nsupseteq
+ [0x02226] = 0x2C, -- notparallel \nparallel
+ [0x02224] = 0x2D, -- notbar \nmid \ndivides
+ -- [0x0] = 0x2E, -- notshortbar \nshortmid
+ -- [0x0] = 0x2F, -- notshortparallel \nshortparallel
+ [0x022AC] = 0x30, -- notturnstile \nvdash
+ [0x022AE] = 0x31, -- notforces \nVdash
+ [0x022AD] = 0x32, -- notsatisfies \nvDash
+ [0x022AF] = 0x33, -- notforcesextra \nVDash
+ [0x022ED] = 0x34, -- nottriangeqlright \ntrianglerighteq
+ [0x022EC] = 0x35, -- nottriangeqlleft \ntrianglelefteq
+ [0x022EA] = 0x36, -- nottriangleleft \ntriangleleft
+ [0x022EB] = 0x37, -- nottriangleright \ntriangleright
+ [0x0219A] = 0x38, -- notarrowleft \nleftarrow
+ [0x0219B] = 0x39, -- notarrowright \nrightarrow
+ [0x021CD] = 0x3A, -- notdblarrowleft \nLeftarrow
+ [0x021CF] = 0x3B, -- notdblarrowright \nRightarrow
+ [0x021CE] = 0x3C, -- notdblarrowboth \nLeftrightarrow
+ [0x021AE] = 0x3D, -- notarrowboth \nleftrightarrow
+ [0x022C7] = 0x3E, -- dividemultiply \divideontimes
+ [0x02300] = 0x3F, -- diametersign \varnothing
+ [0x02204] = 0x40, -- notexistential \nexists
+ [0x1D538] = 0x41, -- A (blackboard A)
+ [0x1D539] = 0x42, -- B
+ [0x02102] = 0x43, -- C
+ [0x1D53B] = 0x44, -- D
+ [0x1D53C] = 0x45, -- E
+ [0x1D53D] = 0x46, -- F
+ [0x1D53E] = 0x47, -- G
+ [0x0210D] = 0x48, -- H
+ [0x1D540] = 0x49, -- I
+ [0x1D541] = 0x4A, -- J
+ [0x1D542] = 0x4B, -- K
+ [0x1D543] = 0x4C, -- L
+ [0x1D544] = 0x4D, -- M
+ [0x02115] = 0x4E, -- N
+ [0x1D546] = 0x4F, -- O
+ [0x02119] = 0x50, -- P
+ [0x0211A] = 0x51, -- Q
+ [0x0211D] = 0x52, -- R
+ [0x1D54A] = 0x53, -- S
+ [0x1D54B] = 0x54, -- T
+ [0x1D54C] = 0x55, -- U
+ [0x1D54D] = 0x56, -- V
+ [0x1D54E] = 0x57, -- W
+ [0x1D54F] = 0x58, -- X
+ [0x1D550] = 0x59, -- Y
+ [0x02124] = 0x5A, -- Z (blackboard Z)
+ [0x02132] = 0x60, -- finv \Finv
+ [0x02141] = 0x61, -- fmir \Game
+ -- [0x0] = 0x62, tildewide
+ -- [0x0] = 0x63, tildewider
+ -- [0x0] = 0x64, Finv
+ -- [0x0] = 0x65, Gmir
+ [0x02127] = 0x66, -- Omegainv \mho
+ [0x000F0] = 0x67, -- eth \eth
+ [0x02242] = 0x68, -- equalorsimilar \eqsim
+ [0x02136] = 0x69, -- beth \beth
+ [0x02137] = 0x6A, -- gimel \gimel
+ [0x02138] = 0x6B, -- daleth \daleth
+ [0x022D6] = 0x6C, -- lessdot \lessdot
+ [0x022D7] = 0x6D, -- greaterdot \gtrdot
+ [0x022C9] = 0x6E, -- multicloseleft \ltimes
+ [0x022CA] = 0x6F, -- multicloseright \rtimes
+ -- [0x0] = 0x70, -- barshort \shortmid
+ -- [0x0] = 0x71, -- parallelshort \shortparallel
+ -- [0x02216] = 0x72, -- integerdivide \smallsetminus (2216 already part of tex-sy
+ -- [0x0] = 0x73, -- similar \thicksim
+ -- [0x0] = 0x74, -- approxequal \thickapprox
+ [0x0224A] = 0x75, -- approxorequal \approxeq
+ [0x02AB8] = 0x76, -- followsorequal \succapprox
+ [0x02AB7] = 0x77, -- precedesorequal \precapprox
+ [0x021B6] = 0x78, -- archleftdown \curvearrowleft
+ [0x021B7] = 0x79, -- archrightdown \curvearrowright
+ [0x003DC] = 0x7A, -- Digamma \digamma
+ [0x003F0] = 0x7B, -- kappa \varkappa
+ [0x1D55C] = 0x7C, -- k \Bbbk (blackboard k)
+ [0x0210F] = 0x7D, -- planckover2pi \hslash % 0x7D
+ [0x00127] = 0x7E, -- planckover2pi1 \hbar % 0x7E
+ [0x003F6] = 0x7F, -- epsiloninv \backepsilon
+}
+
+mathencodings["tex-mc"] = {
+ -- this file has no tfm so it gets mapped in the private space
+ [0xFE324] = "mapsfromchar",
+}
+
+mathencodings["tex-fraktur"] = {
+-- [0x1D504] = 0x41, -- A (fraktur A)
+-- [0x1D505] = 0x42, -- B
+ [0x0212D] = 0x43, -- C
+-- [0x1D507] = 0x44, -- D
+-- [0x1D508] = 0x45, -- E
+-- [0x1D509] = 0x46, -- F
+-- [0x1D50A] = 0x47, -- G
+ [0x0210C] = 0x48, -- H
+ [0x02111] = 0x49, -- I
+-- [0x1D50D] = 0x4A, -- J
+-- [0x1D50E] = 0x4B, -- K
+-- [0x1D50F] = 0x4C, -- L
+-- [0x1D510] = 0x4D, -- M
+-- [0x1D511] = 0x4E, -- N
+-- [0x1D512] = 0x4F, -- O
+-- [0x1D513] = 0x50, -- P
+-- [0x1D514] = 0x51, -- Q
+ [0x0211C] = 0x52, -- R
+-- [0x1D516] = 0x53, -- S
+-- [0x1D517] = 0x54, -- T
+-- [0x1D518] = 0x55, -- U
+-- [0x1D519] = 0x56, -- V
+-- [0x1D51A] = 0x57, -- W
+-- [0x1D51B] = 0x58, -- X
+-- [0x1D51C] = 0x59, -- Y
+ [0x02128] = 0x5A, -- Z (fraktur Z)
+-- [0x1D51E] = 0x61, -- a (fraktur a)
+-- [0x1D51F] = 0x62, -- b
+-- [0x1D520] = 0x63, -- c
+-- [0x1D521] = 0x64, -- d
+-- [0x1D522] = 0x65, -- e
+-- [0x1D523] = 0x66, -- f
+-- [0x1D524] = 0x67, -- g
+-- [0x1D525] = 0x68, -- h
+-- [0x1D526] = 0x69, -- i
+-- [0x1D527] = 0x6A, -- j
+-- [0x1D528] = 0x6B, -- k
+-- [0x1D529] = 0x6C, -- l
+-- [0x1D52A] = 0x6D, -- m
+-- [0x1D52B] = 0x6E, -- n
+-- [0x1D52C] = 0x6F, -- o
+-- [0x1D52D] = 0x70, -- p
+-- [0x1D52E] = 0x71, -- q
+-- [0x1D52F] = 0x72, -- r
+-- [0x1D530] = 0x73, -- s
+-- [0x1D531] = 0x74, -- t
+-- [0x1D532] = 0x75, -- u
+-- [0x1D533] = 0x76, -- v
+-- [0x1D534] = 0x77, -- w
+-- [0x1D535] = 0x78, -- x
+-- [0x1D536] = 0x79, -- y
+-- [0x1D537] = 0x7A, -- z
+}
+
+-- now that all other vectors are defined ...
+
+setletters(mathencodings, "tex-it", 0x1D434, 0x1D44E)
+setletters(mathencodings, "tex-ss", 0x1D5A0, 0x1D5BA)
+setletters(mathencodings, "tex-tt", 0x1D670, 0x1D68A)
+setletters(mathencodings, "tex-bf", 0x1D400, 0x1D41A)
+setletters(mathencodings, "tex-bi", 0x1D468, 0x1D482)
+setletters(mathencodings, "tex-fraktur", 0x1D504, 0x1D51E)
+setletters(mathencodings, "tex-fraktur-bold", 0x1D56C, 0x1D586)
+
+setdigits (mathencodings, "tex-ss", 0x1D7E2)
+setdigits (mathencodings, "tex-tt", 0x1D7F6)
+setdigits (mathencodings, "tex-bf", 0x1D7CE)
+
+-- setdigits (mathencodings, "tex-bi", 0x1D7CE)
+
+-- todo: add ss, tt, bf etc vectors
+-- todo: we can make ss tt etc an option
diff --git a/tex/context/base/meta-fun.lua b/tex/context/base/meta-fun.lua
index e12298e8b..78ee25baf 100644
--- a/tex/context/base/meta-fun.lua
+++ b/tex/context/base/meta-fun.lua
@@ -1,57 +1,57 @@
-if not modules then modules = { } end modules ['meta-fun'] = {
- version = 1.001,
- comment = "companion to meta-fun.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files",
-}
-
--- very experimental, actually a joke ... see metafun manual for usage
-
-local format, load, type = string.format, load, type
-
-local metapost = metapost
-
-metapost.metafun = metapost.metafun or { }
-local metafun = metapost.metafun
-
-function metafun.topath(t,connector)
- context("(")
- if #t > 0 then
- for i=1,#t do
- if i > 1 then
- context(connector or "..")
- end
- local ti = t[i]
- if type(ti) == "string" then
- context(ti)
- else
- context("(%s,%s)",ti.x or ti[1] or 0,ti.y or ti[2] or 0)
- end
- end
- else
- context("origin")
- end
- context(")")
-end
-
-function metafun.interpolate(f,b,e,s,c)
- local done = false
- context("(")
- for i=b,e,(e-b)/s do
- local d = load(format("return function(x) return %s end",f))
- if d then
- d = d()
- if done then
- context(c or "...")
- else
- done = true
- end
- context("(%s,%s)",i,d(i))
- end
- end
- if not done then
- context("origin")
- end
- context(")")
-end
+if not modules then modules = { } end modules ['meta-fun'] = {
+ version = 1.001,
+ comment = "companion to meta-fun.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+}
+
+-- very experimental, actually a joke ... see metafun manual for usage
+
+local format, load, type = string.format, load, type
+
+local metapost = metapost
+
+metapost.metafun = metapost.metafun or { }
+local metafun = metapost.metafun
+
+function metafun.topath(t,connector)
+ context("(")
+ if #t > 0 then
+ for i=1,#t do
+ if i > 1 then
+ context(connector or "..")
+ end
+ local ti = t[i]
+ if type(ti) == "string" then
+ context(ti)
+ else
+ context("(%s,%s)",ti.x or ti[1] or 0,ti.y or ti[2] or 0)
+ end
+ end
+ else
+ context("origin")
+ end
+ context(")")
+end
+
+function metafun.interpolate(f,b,e,s,c)
+ local done = false
+ context("(")
+ for i=b,e,(e-b)/s do
+ local d = load(format("return function(x) return %s end",f))
+ if d then
+ d = d()
+ if done then
+ context(c or "...")
+ else
+ done = true
+ end
+ context("(%s,%s)",i,d(i))
+ end
+ end
+ if not done then
+ context("origin")
+ end
+ context(")")
+end
diff --git a/tex/context/base/meta-ini.lua b/tex/context/base/meta-ini.lua
index 460738930..713ba3d5d 100644
--- a/tex/context/base/meta-ini.lua
+++ b/tex/context/base/meta-ini.lua
@@ -1,165 +1,165 @@
-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 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
-
-local context = context
-
-metapost = metapost or { }
-
--- for the moment downward compatible
-
-local report_metapost = logs.reporter ("metapost")
-local status_metapost = logs.messenger("metapost")
-
-local patterns = { "meta-imp-%s.mkiv", "meta-imp-%s.tex", "meta-%s.mkiv", "meta-%s.tex" } -- we are compatible
-
-local function action(name,foundname)
- status_metapost("library %a is loaded",name)
- context.startreadingfile()
- context.input(foundname)
- context.stopreadingfile()
-end
-
-local function failure(name)
- report_metapost("library %a is unknown or invalid",name)
-end
-
-function commands.useMPlibrary(name)
- commands.uselibrary {
- name = name,
- patterns = patterns,
- action = action,
- failure = failure,
- onlyonce = true,
- }
-end
-
--- experimental
-
-local colorhash = attributes.list[attributes.private('color')]
-
-local textype = tex.type
-local MPcolor = context.MPcolor
-
--- local validdimen = lpegpatterns.validdimen * P(-1)
---
--- function commands.prepareMPvariable(v) -- slow but ok
--- if v == "" then
--- MPcolor("black")
--- else
--- local typ, var = match(v,"(.):(.*)")
--- if not typ then
--- -- parse
--- if colorhash[v] then
--- MPcolor(v)
--- elseif tonumber(v) then
--- context(v)
--- elseif lpegmatch(validdimen,v) then
--- return context("\\the\\dimexpr %s",v)
--- else
--- for s in gmatch(v,"\\([a-zA-Z]+)") do -- can have trailing space
--- local t = textype(s)
--- if t == "dimen" then
--- return context("\\the\\dimexpr %s",v)
--- elseif t == "count" then
--- return context("\\the\\numexpr %s",v)
--- end
--- end
--- context("\\number %s",v) -- 0.4 ...
--- end
--- elseif typ == "d" then -- to be documented
--- -- dimension
--- context("\\the\\dimexpr %s",var)
--- elseif typ == "n" then -- to be documented
--- -- number
--- context("\\the\\numexpr %s",var)
--- elseif typ == "s" then -- to be documented
--- -- string
--- context(var)
--- elseif typ == "c" then -- to be documented
--- -- color
--- MPcolor(var)
--- else
--- context(var)
--- end
--- end
--- end
-
--- we can actually get the dimen/count values here
-
-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",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)
-
-function commands.prepareMPvariable(v) -- slow but ok
- if v == "" then
- MPcolor("black")
- else
- local typ, var = lpegmatch(splitter,v)
- if not var then
- -- parse
- if colorhash[v] then
- MPcolor(v)
- 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",var)
- elseif typ == "n" then -- to be documented
- -- number
- context("\\the\\numexpr %s",var)
- elseif typ == "s" then -- to be documented
- -- string
- context(var)
- elseif typ == "c" then -- to be documented
- -- color
- MPcolor(var)
- else
- context(var)
- end
- end
-end
-
--- 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) 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
- context.mathematics(lpegmatch(two,format(lpegmatch(one,fmt),n)))
-end
+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 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
+
+local context = context
+
+metapost = metapost or { }
+
+-- for the moment downward compatible
+
+local report_metapost = logs.reporter ("metapost")
+local status_metapost = logs.messenger("metapost")
+
+local patterns = { "meta-imp-%s.mkiv", "meta-imp-%s.tex", "meta-%s.mkiv", "meta-%s.tex" } -- we are compatible
+
+local function action(name,foundname)
+ status_metapost("library %a is loaded",name)
+ context.startreadingfile()
+ context.input(foundname)
+ context.stopreadingfile()
+end
+
+local function failure(name)
+ report_metapost("library %a is unknown or invalid",name)
+end
+
+function commands.useMPlibrary(name)
+ commands.uselibrary {
+ name = name,
+ patterns = patterns,
+ action = action,
+ failure = failure,
+ onlyonce = true,
+ }
+end
+
+-- experimental
+
+local colorhash = attributes.list[attributes.private('color')]
+
+local textype = tex.type
+local MPcolor = context.MPcolor
+
+-- local validdimen = lpegpatterns.validdimen * P(-1)
+--
+-- function commands.prepareMPvariable(v) -- slow but ok
+-- if v == "" then
+-- MPcolor("black")
+-- else
+-- local typ, var = match(v,"(.):(.*)")
+-- if not typ then
+-- -- parse
+-- if colorhash[v] then
+-- MPcolor(v)
+-- elseif tonumber(v) then
+-- context(v)
+-- elseif lpegmatch(validdimen,v) then
+-- return context("\\the\\dimexpr %s",v)
+-- else
+-- for s in gmatch(v,"\\([a-zA-Z]+)") do -- can have trailing space
+-- local t = textype(s)
+-- if t == "dimen" then
+-- return context("\\the\\dimexpr %s",v)
+-- elseif t == "count" then
+-- return context("\\the\\numexpr %s",v)
+-- end
+-- end
+-- context("\\number %s",v) -- 0.4 ...
+-- end
+-- elseif typ == "d" then -- to be documented
+-- -- dimension
+-- context("\\the\\dimexpr %s",var)
+-- elseif typ == "n" then -- to be documented
+-- -- number
+-- context("\\the\\numexpr %s",var)
+-- elseif typ == "s" then -- to be documented
+-- -- string
+-- context(var)
+-- elseif typ == "c" then -- to be documented
+-- -- color
+-- MPcolor(var)
+-- else
+-- context(var)
+-- end
+-- end
+-- end
+
+-- we can actually get the dimen/count values here
+
+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",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)
+
+function commands.prepareMPvariable(v) -- slow but ok
+ if v == "" then
+ MPcolor("black")
+ else
+ local typ, var = lpegmatch(splitter,v)
+ if not var then
+ -- parse
+ if colorhash[v] then
+ MPcolor(v)
+ 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",var)
+ elseif typ == "n" then -- to be documented
+ -- number
+ context("\\the\\numexpr %s",var)
+ elseif typ == "s" then -- to be documented
+ -- string
+ context(var)
+ elseif typ == "c" then -- to be documented
+ -- color
+ MPcolor(var)
+ else
+ context(var)
+ end
+ end
+end
+
+-- 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) 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
+ context.mathematics(lpegmatch(two,format(lpegmatch(one,fmt),n)))
+end
diff --git a/tex/context/base/meta-pdf.lua b/tex/context/base/meta-pdf.lua
index 15211b560..32e48902a 100644
--- a/tex/context/base/meta-pdf.lua
+++ b/tex/context/base/meta-pdf.lua
@@ -1,567 +1,567 @@
-if not modules then modules = { } end modules ['meta-pdf'] = {
- version = 1.001,
- comment = "companion to meta-pdf.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- Finally we used an optimized version. The test code can be found in
--- meta-pdh.lua but since we no longer want to overload functione we use
--- more locals now. This module keeps changing as it is also a testbed.
---
--- We can make it even more efficient if needed, but as we don't use this
--- code often in \MKIV\ it makes no sense.
-
-local concat, unpack = table.concat, table.unpack
-local gsub, find, byte, gmatch, match = string.gsub, string.find, string.byte, string.gmatch, string.match
-local lpegmatch = lpeg.match
-local round = math.round
-local formatters, format = string.formatters, string.format
-
-local report_mptopdf = logs.reporter("graphics","mptopdf")
-
-local mplib, metapost, lpdf, context = mplib, metapost, lpdf, context
-
-local pdfrgbcode = lpdf.rgbcode
-local pdfcmykcode = lpdf.cmykcode
-local pdfgraycode = lpdf.graycode
-local pdfspotcode = lpdf.spotcode
-local pdftransparencycode = lpdf.transparencycode
-local pdffinishtransparencycode = lpdf.finishtransparencycode
-local pdfliteral = nodes.pool.pdfliteral
-
-metapost.mptopdf = metapost.mptopdf or { }
-local mptopdf = metapost.mptopdf
-
-mptopdf.nofconverted = 0
-
-local f_translate = formatters["1 0 0 0 1 %f %f cm"] -- no %s due to 1e-035 issues
-local f_concat = formatters["%f %f %f %f %f %f cm"] -- no %s due to 1e-035 issues
-
-local m_path, m_stack, m_texts, m_version, m_date, m_shortcuts = { }, { }, { }, 0, 0, false
-
-local m_stack_close, m_stack_path, m_stack_concat = false, { }, nil
-local extra_path_code, ignore_path = nil, false
-local specials = { }
-
-local function resetpath()
- m_stack_close, m_stack_path, m_stack_concat = false, { }, nil
-end
-
-local function resetall()
- m_path, m_stack, m_texts, m_version, m_shortcuts = { }, { }, { }, 0, false
- extra_path_code, ignore_path = nil, false
- specials = { }
- resetpath()
-end
-
-resetall()
-
--- -- this does not work as expected (displacement of text) beware, needs another
--- -- comment hack
---
--- local function pdfcode(str)
--- context(pdfliteral(str))
--- end
-
-local pdfcode = context.pdfliteral
-
-local function mpscode(str)
- if ignore_path then
- pdfcode("h W n")
- if extra_path_code then
- pdfcode(extra_path_code)
- extra_path_code = nil
- end
- ignore_path = false
- else
- pdfcode(str)
- end
-end
-
--- auxiliary functions
-
-local function flushconcat()
- if m_stack_concat then
- mpscode(f_concatm(unpack(m_stack_concat)))
- m_stack_concat = nil
- end
-end
-
-local function flushpath(cmd)
- if #m_stack_path > 0 then
- local path = { }
- if m_stack_concat then
- local sx, sy = m_stack_concat[1], m_stack_concat[4]
- local rx, ry = m_stack_concat[2], m_stack_concat[3]
- local tx, ty = m_stack_concat[5], m_stack_concat[6]
- local d = (sx*sy) - (rx*ry)
- for k=1,#m_stack_path do
- local v = m_stack_path[k]
- local px, py = v[1], v[2] ; v[1], v[2] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[1],v[2])
- if #v == 7 then
- local px, py = v[3], v[4] ; v[3], v[4] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[3],v[4])
- local px, py = v[5], v[6] ; v[5], v[6] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[5],v[6])
- end
- path[k] = concat(v," ")
- end
- else
- for k=1,#m_stack_path do
- path[k] = concat(m_stack_path[k]," ")
- end
- end
- flushconcat()
- pdfcode(concat(path," "))
- if m_stack_close then
- mpscode("h " .. cmd)
- else
- mpscode(cmd)
- end
- end
- resetpath()
-end
-
--- mp interface
-
-local mps = { }
-
-function mps.creator(a, b, c)
- m_version = tonumber(b)
-end
-
-function mps.creationdate(a)
- m_date = a
-end
-
-function mps.newpath()
- m_stack_path = { }
-end
-
-function mps.boundingbox(llx, lly, urx, ury)
- context.setMPboundingbox(llx,lly,urx,ury)
-end
-
-function mps.moveto(x,y)
- m_stack_path[#m_stack_path+1] = { x, y, "m" }
-end
-
-function mps.curveto(ax, ay, bx, by, cx, cy)
- m_stack_path[#m_stack_path+1] = { ax, ay, bx, by, cx, cy, "c" }
-end
-
-function mps.lineto(x,y)
- m_stack_path[#m_stack_path+1] = { x, y, "l" }
-end
-
-function mps.rlineto(x,y)
- local dx, dy = 0, 0
- local topofstack = #m_stack_path
- if topofstack > 0 then
- local msp = m_stack_path[topofstack]
- dx = msp[1]
- dy = msp[2]
- end
- m_stack_path[topofstack+1] = {dx,dy,"l"}
-end
-
-function mps.translate(tx,ty)
- mpscode(f_translate(tx,ty)
-end
-
-function mps.scale(sx,sy)
- m_stack_concat = {sx,0,0,sy,0,0}
-end
-
-function mps.concat(sx, rx, ry, sy, tx, ty)
- m_stack_concat = {sx,rx,ry,sy,tx,ty}
-end
-
-function mps.setlinejoin(d)
- mpscode(d .. " j")
-end
-
-function mps.setlinecap(d)
- mpscode(d .. " J")
-end
-
-function mps.setmiterlimit(d)
- mpscode(d .. " M")
-end
-
-function mps.gsave()
- mpscode("q")
-end
-
-function mps.grestore()
- mpscode("Q")
-end
-
-function mps.setdash(...) -- can be made faster, operate on t = { ... }
- local n = select("#",...)
- mpscode("[" .. concat({...}," ",1,n-1) .. "] " .. select(n,...) .. " d")
- -- mpscode("[" .. concat({select(1,n-1)}," ") .. "] " .. select(n,...) .. " d")
-end
-
-function mps.resetdash()
- mpscode("[ ] 0 d")
-end
-
-function mps.setlinewidth(d)
- mpscode(d .. " w")
-end
-
-function mps.closepath()
- m_stack_close = true
-end
-
-function mps.fill()
- flushpath('f')
-end
-
-function mps.stroke()
- flushpath('S')
-end
-
-function mps.both()
- flushpath('B')
-end
-
-function mps.clip()
- flushpath('W n')
-end
-
-function mps.textext(font, scale, str) -- old parser
- local dx, dy = 0, 0
- if #m_stack_path > 0 then
- dx, dy = m_stack_path[1][1], m_stack_path[1][2]
- end
- flushconcat()
- context.MPtextext(font,scale,str,dx,dy)
- resetpath()
-end
-
-local handlers = { }
-
-handlers[1] = function(s)
- pdfcode(pdffinishtransparencycode())
- pdfcode(pdfcmykcode(mps.colormodel,s[3],s[4],s[5],s[6]))
-end
-handlers[2] = function(s)
- pdfcode(pdffinishtransparencycode())
- pdfcode(pdfspotcode(mps.colormodel,s[3],s[4],s[5],s[6]))
-end
-handlers[3] = function(s)
- pdfcode(pdfrgbcode(mps.colormodel,s[4],s[5],s[6]))
- pdfcode(pdftransparencycode(s[2],s[3]))
-end
-handlers[4] = function(s)
- pdfcode(pdfcmykcode(mps.colormodel,s[4],s[5],s[6],s[7]))
- pdfcode(pdftransparencycode(s[2],s[3]))
-end
-handlers[5] = function(s)
- pdfcode(pdfspotcode(mps.colormodel,s[4],s[5],s[6],s[7]))
- pdfcode(pdftransparencycode(s[2],s[3]))
-end
-
--- todo: color conversion
-
-local nofshades, tn = 0, tonumber
-
-local function linearshade(colorspace,domain,ca,cb,coordinates)
- pdfcode(pdffinishtransparencycode())
- nofshades = nofshades + 1
- local name = formatters["MpsSh%s"](nofshades)
- lpdf.linearshade(name,domain,ca,cb,1,colorspace,coordinates)
- extra_path_code, ignore_path = formatters["/%s sh Q"](name), true
- pdfcode("q /Pattern cs")
-end
-
-local function circularshade(colorspace,domain,ca,cb,coordinates)
- pdfcode(pdffinishtransparencycode())
- nofshades = nofshades + 1
- local name = formatters["MpsSh%s"](nofshades)
- lpdf.circularshade(name,domain,ca,cb,1,colorspace,coordinates)
- extra_path_code, ignore_path = formatters["/%s sh Q"](name), true
- pdfcode("q /Pattern cs")
-end
-
-handlers[30] = function(s)
- linearshade("DeviceRGB", { tn(s[ 2]), tn(s[ 3]) },
- { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]) }, { tn(s[10]), tn(s[11]), tn(s[12]) },
- { tn(s[ 8]), tn(s[ 9]), tn(s[13]), tn(s[14]) } )
-end
-
-handlers[31] = function(s)
- circularshade("DeviceRGB", { tn(s[ 2]), tn(s[ 3]) },
- { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]) }, { tn(s[11]), tn(s[12]), tn(s[13]) },
- { tn(s[ 8]), tn(s[ 9]), tn(s[10]), tn(s[14]), tn(s[15]), tn(s[16]) } )
-end
-
-handlers[32] = function(s)
- linearshade("DeviceCMYK", { tn(s[ 2]), tn(s[ 3]) },
- { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]), tn(s[ 8]) }, { tn(s[11]), tn(s[12]), tn(s[13]), tn(s[14]) },
- { tn(s[ 9]), tn(s[10]), tn(s[15]), tn(s[16]) } )
-end
-
-handlers[33] = function(s)
- circularshade("DeviceCMYK", { tn(s[ 2]), tn(s[ 3]) },
- { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]), tn(s[ 8]) }, { tn(s[12]), tn(s[13]), tn(s[14]), tn(s[15]) },
- { tn(s[ 9]), tn(s[10]), tn(s[11]), tn(s[16]), tn(s[17]), tn(s[18]) } )
-end
-
-handlers[34] = function(s) -- todo (after further cleanup)
- linearshade("DeviceGray", { tn(s[ 2]), tn(s[ 3]) }, { 0 }, { 1 }, { tn(s[9]), tn(s[10]), tn(s[15]), tn(s[16]) } )
-end
-
-handlers[35] = function(s) -- todo (after further cleanup)
- circularshade("DeviceGray", { tn(s[ 2]), tn(s[ 3]) }, { 0 }, { 1 }, { tn(s[9]), tn(s[10]), tn(s[15]), tn(s[16]) } )
-end
-
--- not supported in mkiv , use mplib instead
-
-handlers[10] = function() report_mptopdf("skipping special %s",10) end
-handlers[20] = function() report_mptopdf("skipping special %s",20) end
-handlers[50] = function() report_mptopdf("skipping special %s",50) end
-
---end of not supported
-
-function mps.setrgbcolor(r,g,b) -- extra check
- r, g = tonumber(r), tonumber(g) -- needed when we use lpeg
- if r == 0.0123 and g < 0.1 then
- g, b = round(g*10000), round(b*10000)
- local s = specials[b]
- local h = round(s[#s])
- local handler = handlers[h]
- if handler then
- handler(s)
- else
- report_mptopdf("unknown special handler %s (1)",h)
- end
- elseif r == 0.123 and g < 0.1 then
- g, b = round(g*1000), round(b*1000)
- local s = specials[b]
- local h = round(s[#s])
- local handler = handlers[h]
- if handler then
- handler(s)
- else
- report_mptopdf("unknown special handler %s (2)",h)
- end
- else
- pdfcode(pdffinishtransparencycode())
- pdfcode(pdfrgbcode(mps.colormodel,r,g,b))
- end
-end
-
-function mps.setcmykcolor(c,m,y,k)
- pdfcode(pdffinishtransparencycode())
- pdfcode(pdfcmykcode(mps.colormodel,c,m,y,k))
-end
-
-function mps.setgray(s)
- pdfcode(pdffinishtransparencycode())
- pdfcode(pdfgraycode(mps.colormodel,s))
-end
-
-function mps.specials(version,signal,factor) -- 2.0 123 1000
-end
-
-function mps.special(...) -- 7 1 0.5 1 0 0 1 3
- local t = { ... }
- local n = tonumber(t[#t-1])
- specials[n] = t
-end
-
-function mps.begindata()
-end
-
-function mps.enddata()
-end
-
-function mps.showpage()
-end
-
--- lpeg parser
-
--- The lpeg based parser is rather optimized for the kind of output
--- that MetaPost produces. It's my first real lpeg code, which may
--- show. Because the parser binds to functions, we define it last.
-
-local lpegP, lpegR, lpegS, lpegC, lpegCc, lpegCs = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cs
-
-local digit = lpegR("09")
-local eol = lpegS('\r\n')^1
-local sp = lpegP(' ')^1
-local space = lpegS(' \r\n')^1
-local number = lpegS('0123456789.-+')^1
-local nonspace = lpegP(1-lpegS(' \r\n'))^1
-
-local spec = digit^2 * lpegP("::::") * digit^2
-local text = lpegCc("{") * (
- lpegP("\\") * ( (digit * digit * digit) / function(n) return "c" .. tonumber(n,8) end) +
- lpegP(" ") / function(n) return "\\c32" end + -- never in new mp
- lpegP(1) / function(n) return "\\c" .. byte(n) end
- ) * lpegCc("}")
-local package = lpegCs(spec + text^0)
-
-function mps.fshow(str,font,scale) -- lpeg parser
- mps.textext(font,scale,lpegmatch(package,str))
-end
-
-local cnumber = lpegC(number)
-local cstring = lpegC(nonspace)
-
-local specials = (lpegP("%%MetaPostSpecials:") * sp * (cstring * sp^0)^0 * eol) / mps.specials
-local special = (lpegP("%%MetaPostSpecial:") * sp * (cstring * sp^0)^0 * eol) / mps.special
-local boundingbox = (lpegP("%%BoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox
-local highresboundingbox = (lpegP("%%HiResBoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox
-
-local setup = lpegP("%%BeginSetup") * (1 - lpegP("%%EndSetup") )^1
-local prolog = lpegP("%%BeginProlog") * (1 - lpegP("%%EndProlog"))^1
-local comment = lpegP('%')^1 * (1 - eol)^1
-
-local curveto = ((cnumber * sp)^6 * lpegP("curveto") ) / mps.curveto
-local lineto = ((cnumber * sp)^2 * lpegP("lineto") ) / mps.lineto
-local rlineto = ((cnumber * sp)^2 * lpegP("rlineto") ) / mps.rlineto
-local moveto = ((cnumber * sp)^2 * lpegP("moveto") ) / mps.moveto
-local setrgbcolor = ((cnumber * sp)^3 * lpegP("setrgbcolor") ) / mps.setrgbcolor
-local setcmykcolor = ((cnumber * sp)^4 * lpegP("setcmykcolor") ) / mps.setcmykcolor
-local setgray = ((cnumber * sp)^1 * lpegP("setgray") ) / mps.setgray
-local newpath = ( lpegP("newpath") ) / mps.newpath
-local closepath = ( lpegP("closepath") ) / mps.closepath
-local fill = ( lpegP("fill") ) / mps.fill
-local stroke = ( lpegP("stroke") ) / mps.stroke
-local clip = ( lpegP("clip") ) / mps.clip
-local both = ( lpegP("gsave fill grestore")) / mps.both
-local showpage = ( lpegP("showpage") )
-local setlinejoin = ((cnumber * sp)^1 * lpegP("setlinejoin") ) / mps.setlinejoin
-local setlinecap = ((cnumber * sp)^1 * lpegP("setlinecap") ) / mps.setlinecap
-local setmiterlimit = ((cnumber * sp)^1 * lpegP("setmiterlimit") ) / mps.setmiterlimit
-local gsave = ( lpegP("gsave") ) / mps.gsave
-local grestore = ( lpegP("grestore") ) / mps.grestore
-
-local setdash = (lpegP("[") * (cnumber * sp^0)^0 * lpegP("]") * sp * cnumber * sp * lpegP("setdash")) / mps.setdash
-local concat = (lpegP("[") * (cnumber * sp^0)^6 * lpegP("]") * sp * lpegP("concat") ) / mps.concat
-local scale = ( (cnumber * sp^0)^6 * sp * lpegP("concat") ) / mps.concat
-
-local fshow = (lpegP("(") * lpegC((1-lpegP(")"))^1) * lpegP(")") * space * cstring * space * cnumber * space * lpegP("fshow")) / mps.fshow
-local fshow = (lpegP("(") * lpegCs( ( lpegP("\\(")/"\\050" + lpegP("\\)")/"\\051" + (1-lpegP(")")) )^1 )
- * lpegP(")") * space * cstring * space * cnumber * space * lpegP("fshow")) / mps.fshow
-
-local setlinewidth_x = (lpegP("0") * sp * cnumber * sp * lpegP("dtransform truncate idtransform setlinewidth pop")) / mps.setlinewidth
-local setlinewidth_y = (cnumber * sp * lpegP("0 dtransform exch truncate exch idtransform pop setlinewidth") ) / mps.setlinewidth
-
-local c = ((cnumber * sp)^6 * lpegP("c") ) / mps.curveto -- ^6 very inefficient, ^1 ok too
-local l = ((cnumber * sp)^2 * lpegP("l") ) / mps.lineto
-local r = ((cnumber * sp)^2 * lpegP("r") ) / mps.rlineto
-local m = ((cnumber * sp)^2 * lpegP("m") ) / mps.moveto
-local vlw = ((cnumber * sp)^1 * lpegP("vlw")) / mps.setlinewidth
-local hlw = ((cnumber * sp)^1 * lpegP("hlw")) / mps.setlinewidth
-
-local R = ((cnumber * sp)^3 * lpegP("R") ) / mps.setrgbcolor
-local C = ((cnumber * sp)^4 * lpegP("C") ) / mps.setcmykcolor
-local G = ((cnumber * sp)^1 * lpegP("G") ) / mps.setgray
-
-local lj = ((cnumber * sp)^1 * lpegP("lj") ) / mps.setlinejoin
-local ml = ((cnumber * sp)^1 * lpegP("ml") ) / mps.setmiterlimit
-local lc = ((cnumber * sp)^1 * lpegP("lc") ) / mps.setlinecap
-
-local n = lpegP("n") / mps.newpath
-local p = lpegP("p") / mps.closepath
-local S = lpegP("S") / mps.stroke
-local F = lpegP("F") / mps.fill
-local B = lpegP("B") / mps.both
-local W = lpegP("W") / mps.clip
-local P = lpegP("P") / mps.showpage
-
-local q = lpegP("q") / mps.gsave
-local Q = lpegP("Q") / mps.grestore
-
-local sd = (lpegP("[") * (cnumber * sp^0)^0 * lpegP("]") * sp * cnumber * sp * lpegP("sd")) / mps.setdash
-local rd = ( lpegP("rd")) / mps.resetdash
-
-local s = ( (cnumber * sp^0)^2 * lpegP("s") ) / mps.scale
-local t = (lpegP("[") * (cnumber * sp^0)^6 * lpegP("]") * sp * lpegP("t") ) / mps.concat
-
--- experimental
-
-local preamble = (
- prolog + setup +
- boundingbox + highresboundingbox + specials + special +
- comment
-)
-
-local procset = (
- lj + ml + lc +
- c + l + m + n + p + r +
- R + C + G +
- S + F + B + W +
- vlw + hlw +
- Q + q +
- sd + rd +
- t + s +
- fshow +
- P
-)
-
-local verbose = (
- curveto + lineto + moveto + newpath + closepath + rlineto +
- setrgbcolor + setcmykcolor + setgray +
- setlinejoin + setmiterlimit + setlinecap +
- stroke + fill + clip + both +
- setlinewidth_x + setlinewidth_y +
- gsave + grestore +
- concat + scale +
- fshow +
- setdash + -- no resetdash
- showpage
-)
-
--- order matters in terms of speed / we could check for procset first
-
-local captures_old = ( space + verbose + preamble )^0
-local captures_new = ( space + verbose + procset + preamble )^0
-
-local function parse(m_data)
- if find(m_data,"%%%%BeginResource: procset mpost") then
- lpegmatch(captures_new,m_data)
- else
- lpegmatch(captures_old,m_data)
- end
-end
-
--- main converter
-
-local a_colorspace = attributes.private('colormodel')
-
-function mptopdf.convertmpstopdf(name)
- resetall()
- local ok, m_data, n = resolvers.loadbinfile(name, 'tex') -- we need a binary load !
- if ok then
- mps.colormodel = tex.attribute[a_colorspace]
- statistics.starttiming(mptopdf)
- mptopdf.nofconverted = mptopdf.nofconverted + 1
- pdfcode(formatters["\\letterpercent\\space mptopdf begin: n=%s, file=%s"](mptopdf.nofconverted,file.basename(name)))
- pdfcode("q 1 0 0 1 0 0 cm")
- parse(m_data)
- pdfcode(pdffinishtransparencycode())
- pdfcode("Q")
- pdfcode("\\letterpercent\\space mptopdf end")
- resetall()
- statistics.stoptiming(mptopdf)
- else
- report_mptopdf("file %a not found",name)
- end
-end
-
--- status info
-
-statistics.register("mps conversion time",function()
- local n = mptopdf.nofconverted
- if n > 0 then
- return format("%s seconds, %s conversions", statistics.elapsedtime(mptopdf),n)
- else
- return nil
- end
-end)
+if not modules then modules = { } end modules ['meta-pdf'] = {
+ version = 1.001,
+ comment = "companion to meta-pdf.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Finally we used an optimized version. The test code can be found in
+-- meta-pdh.lua but since we no longer want to overload functione we use
+-- more locals now. This module keeps changing as it is also a testbed.
+--
+-- We can make it even more efficient if needed, but as we don't use this
+-- code often in \MKIV\ it makes no sense.
+
+local concat, unpack = table.concat, table.unpack
+local gsub, find, byte, gmatch, match = string.gsub, string.find, string.byte, string.gmatch, string.match
+local lpegmatch = lpeg.match
+local round = math.round
+local formatters, format = string.formatters, string.format
+
+local report_mptopdf = logs.reporter("graphics","mptopdf")
+
+local mplib, metapost, lpdf, context = mplib, metapost, lpdf, context
+
+local pdfrgbcode = lpdf.rgbcode
+local pdfcmykcode = lpdf.cmykcode
+local pdfgraycode = lpdf.graycode
+local pdfspotcode = lpdf.spotcode
+local pdftransparencycode = lpdf.transparencycode
+local pdffinishtransparencycode = lpdf.finishtransparencycode
+local pdfliteral = nodes.pool.pdfliteral
+
+metapost.mptopdf = metapost.mptopdf or { }
+local mptopdf = metapost.mptopdf
+
+mptopdf.nofconverted = 0
+
+local f_translate = formatters["1 0 0 0 1 %f %f cm"] -- no %s due to 1e-035 issues
+local f_concat = formatters["%f %f %f %f %f %f cm"] -- no %s due to 1e-035 issues
+
+local m_path, m_stack, m_texts, m_version, m_date, m_shortcuts = { }, { }, { }, 0, 0, false
+
+local m_stack_close, m_stack_path, m_stack_concat = false, { }, nil
+local extra_path_code, ignore_path = nil, false
+local specials = { }
+
+local function resetpath()
+ m_stack_close, m_stack_path, m_stack_concat = false, { }, nil
+end
+
+local function resetall()
+ m_path, m_stack, m_texts, m_version, m_shortcuts = { }, { }, { }, 0, false
+ extra_path_code, ignore_path = nil, false
+ specials = { }
+ resetpath()
+end
+
+resetall()
+
+-- -- this does not work as expected (displacement of text) beware, needs another
+-- -- comment hack
+--
+-- local function pdfcode(str)
+-- context(pdfliteral(str))
+-- end
+
+local pdfcode = context.pdfliteral
+
+local function mpscode(str)
+ if ignore_path then
+ pdfcode("h W n")
+ if extra_path_code then
+ pdfcode(extra_path_code)
+ extra_path_code = nil
+ end
+ ignore_path = false
+ else
+ pdfcode(str)
+ end
+end
+
+-- auxiliary functions
+
+local function flushconcat()
+ if m_stack_concat then
+ mpscode(f_concatm(unpack(m_stack_concat)))
+ m_stack_concat = nil
+ end
+end
+
+local function flushpath(cmd)
+ if #m_stack_path > 0 then
+ local path = { }
+ if m_stack_concat then
+ local sx, sy = m_stack_concat[1], m_stack_concat[4]
+ local rx, ry = m_stack_concat[2], m_stack_concat[3]
+ local tx, ty = m_stack_concat[5], m_stack_concat[6]
+ local d = (sx*sy) - (rx*ry)
+ for k=1,#m_stack_path do
+ local v = m_stack_path[k]
+ local px, py = v[1], v[2] ; v[1], v[2] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[1],v[2])
+ if #v == 7 then
+ local px, py = v[3], v[4] ; v[3], v[4] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[3],v[4])
+ local px, py = v[5], v[6] ; v[5], v[6] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[5],v[6])
+ end
+ path[k] = concat(v," ")
+ end
+ else
+ for k=1,#m_stack_path do
+ path[k] = concat(m_stack_path[k]," ")
+ end
+ end
+ flushconcat()
+ pdfcode(concat(path," "))
+ if m_stack_close then
+ mpscode("h " .. cmd)
+ else
+ mpscode(cmd)
+ end
+ end
+ resetpath()
+end
+
+-- mp interface
+
+local mps = { }
+
+function mps.creator(a, b, c)
+ m_version = tonumber(b)
+end
+
+function mps.creationdate(a)
+ m_date = a
+end
+
+function mps.newpath()
+ m_stack_path = { }
+end
+
+function mps.boundingbox(llx, lly, urx, ury)
+ context.setMPboundingbox(llx,lly,urx,ury)
+end
+
+function mps.moveto(x,y)
+ m_stack_path[#m_stack_path+1] = { x, y, "m" }
+end
+
+function mps.curveto(ax, ay, bx, by, cx, cy)
+ m_stack_path[#m_stack_path+1] = { ax, ay, bx, by, cx, cy, "c" }
+end
+
+function mps.lineto(x,y)
+ m_stack_path[#m_stack_path+1] = { x, y, "l" }
+end
+
+function mps.rlineto(x,y)
+ local dx, dy = 0, 0
+ local topofstack = #m_stack_path
+ if topofstack > 0 then
+ local msp = m_stack_path[topofstack]
+ dx = msp[1]
+ dy = msp[2]
+ end
+ m_stack_path[topofstack+1] = {dx,dy,"l"}
+end
+
+function mps.translate(tx,ty)
+ mpscode(f_translate(tx,ty)
+end
+
+function mps.scale(sx,sy)
+ m_stack_concat = {sx,0,0,sy,0,0}
+end
+
+function mps.concat(sx, rx, ry, sy, tx, ty)
+ m_stack_concat = {sx,rx,ry,sy,tx,ty}
+end
+
+function mps.setlinejoin(d)
+ mpscode(d .. " j")
+end
+
+function mps.setlinecap(d)
+ mpscode(d .. " J")
+end
+
+function mps.setmiterlimit(d)
+ mpscode(d .. " M")
+end
+
+function mps.gsave()
+ mpscode("q")
+end
+
+function mps.grestore()
+ mpscode("Q")
+end
+
+function mps.setdash(...) -- can be made faster, operate on t = { ... }
+ local n = select("#",...)
+ mpscode("[" .. concat({...}," ",1,n-1) .. "] " .. select(n,...) .. " d")
+ -- mpscode("[" .. concat({select(1,n-1)}," ") .. "] " .. select(n,...) .. " d")
+end
+
+function mps.resetdash()
+ mpscode("[ ] 0 d")
+end
+
+function mps.setlinewidth(d)
+ mpscode(d .. " w")
+end
+
+function mps.closepath()
+ m_stack_close = true
+end
+
+function mps.fill()
+ flushpath('f')
+end
+
+function mps.stroke()
+ flushpath('S')
+end
+
+function mps.both()
+ flushpath('B')
+end
+
+function mps.clip()
+ flushpath('W n')
+end
+
+function mps.textext(font, scale, str) -- old parser
+ local dx, dy = 0, 0
+ if #m_stack_path > 0 then
+ dx, dy = m_stack_path[1][1], m_stack_path[1][2]
+ end
+ flushconcat()
+ context.MPtextext(font,scale,str,dx,dy)
+ resetpath()
+end
+
+local handlers = { }
+
+handlers[1] = function(s)
+ pdfcode(pdffinishtransparencycode())
+ pdfcode(pdfcmykcode(mps.colormodel,s[3],s[4],s[5],s[6]))
+end
+handlers[2] = function(s)
+ pdfcode(pdffinishtransparencycode())
+ pdfcode(pdfspotcode(mps.colormodel,s[3],s[4],s[5],s[6]))
+end
+handlers[3] = function(s)
+ pdfcode(pdfrgbcode(mps.colormodel,s[4],s[5],s[6]))
+ pdfcode(pdftransparencycode(s[2],s[3]))
+end
+handlers[4] = function(s)
+ pdfcode(pdfcmykcode(mps.colormodel,s[4],s[5],s[6],s[7]))
+ pdfcode(pdftransparencycode(s[2],s[3]))
+end
+handlers[5] = function(s)
+ pdfcode(pdfspotcode(mps.colormodel,s[4],s[5],s[6],s[7]))
+ pdfcode(pdftransparencycode(s[2],s[3]))
+end
+
+-- todo: color conversion
+
+local nofshades, tn = 0, tonumber
+
+local function linearshade(colorspace,domain,ca,cb,coordinates)
+ pdfcode(pdffinishtransparencycode())
+ nofshades = nofshades + 1
+ local name = formatters["MpsSh%s"](nofshades)
+ lpdf.linearshade(name,domain,ca,cb,1,colorspace,coordinates)
+ extra_path_code, ignore_path = formatters["/%s sh Q"](name), true
+ pdfcode("q /Pattern cs")
+end
+
+local function circularshade(colorspace,domain,ca,cb,coordinates)
+ pdfcode(pdffinishtransparencycode())
+ nofshades = nofshades + 1
+ local name = formatters["MpsSh%s"](nofshades)
+ lpdf.circularshade(name,domain,ca,cb,1,colorspace,coordinates)
+ extra_path_code, ignore_path = formatters["/%s sh Q"](name), true
+ pdfcode("q /Pattern cs")
+end
+
+handlers[30] = function(s)
+ linearshade("DeviceRGB", { tn(s[ 2]), tn(s[ 3]) },
+ { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]) }, { tn(s[10]), tn(s[11]), tn(s[12]) },
+ { tn(s[ 8]), tn(s[ 9]), tn(s[13]), tn(s[14]) } )
+end
+
+handlers[31] = function(s)
+ circularshade("DeviceRGB", { tn(s[ 2]), tn(s[ 3]) },
+ { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]) }, { tn(s[11]), tn(s[12]), tn(s[13]) },
+ { tn(s[ 8]), tn(s[ 9]), tn(s[10]), tn(s[14]), tn(s[15]), tn(s[16]) } )
+end
+
+handlers[32] = function(s)
+ linearshade("DeviceCMYK", { tn(s[ 2]), tn(s[ 3]) },
+ { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]), tn(s[ 8]) }, { tn(s[11]), tn(s[12]), tn(s[13]), tn(s[14]) },
+ { tn(s[ 9]), tn(s[10]), tn(s[15]), tn(s[16]) } )
+end
+
+handlers[33] = function(s)
+ circularshade("DeviceCMYK", { tn(s[ 2]), tn(s[ 3]) },
+ { tn(s[ 5]), tn(s[ 6]), tn(s[ 7]), tn(s[ 8]) }, { tn(s[12]), tn(s[13]), tn(s[14]), tn(s[15]) },
+ { tn(s[ 9]), tn(s[10]), tn(s[11]), tn(s[16]), tn(s[17]), tn(s[18]) } )
+end
+
+handlers[34] = function(s) -- todo (after further cleanup)
+ linearshade("DeviceGray", { tn(s[ 2]), tn(s[ 3]) }, { 0 }, { 1 }, { tn(s[9]), tn(s[10]), tn(s[15]), tn(s[16]) } )
+end
+
+handlers[35] = function(s) -- todo (after further cleanup)
+ circularshade("DeviceGray", { tn(s[ 2]), tn(s[ 3]) }, { 0 }, { 1 }, { tn(s[9]), tn(s[10]), tn(s[15]), tn(s[16]) } )
+end
+
+-- not supported in mkiv , use mplib instead
+
+handlers[10] = function() report_mptopdf("skipping special %s",10) end
+handlers[20] = function() report_mptopdf("skipping special %s",20) end
+handlers[50] = function() report_mptopdf("skipping special %s",50) end
+
+--end of not supported
+
+function mps.setrgbcolor(r,g,b) -- extra check
+ r, g = tonumber(r), tonumber(g) -- needed when we use lpeg
+ if r == 0.0123 and g < 0.1 then
+ g, b = round(g*10000), round(b*10000)
+ local s = specials[b]
+ local h = round(s[#s])
+ local handler = handlers[h]
+ if handler then
+ handler(s)
+ else
+ report_mptopdf("unknown special handler %s (1)",h)
+ end
+ elseif r == 0.123 and g < 0.1 then
+ g, b = round(g*1000), round(b*1000)
+ local s = specials[b]
+ local h = round(s[#s])
+ local handler = handlers[h]
+ if handler then
+ handler(s)
+ else
+ report_mptopdf("unknown special handler %s (2)",h)
+ end
+ else
+ pdfcode(pdffinishtransparencycode())
+ pdfcode(pdfrgbcode(mps.colormodel,r,g,b))
+ end
+end
+
+function mps.setcmykcolor(c,m,y,k)
+ pdfcode(pdffinishtransparencycode())
+ pdfcode(pdfcmykcode(mps.colormodel,c,m,y,k))
+end
+
+function mps.setgray(s)
+ pdfcode(pdffinishtransparencycode())
+ pdfcode(pdfgraycode(mps.colormodel,s))
+end
+
+function mps.specials(version,signal,factor) -- 2.0 123 1000
+end
+
+function mps.special(...) -- 7 1 0.5 1 0 0 1 3
+ local t = { ... }
+ local n = tonumber(t[#t-1])
+ specials[n] = t
+end
+
+function mps.begindata()
+end
+
+function mps.enddata()
+end
+
+function mps.showpage()
+end
+
+-- lpeg parser
+
+-- The lpeg based parser is rather optimized for the kind of output
+-- that MetaPost produces. It's my first real lpeg code, which may
+-- show. Because the parser binds to functions, we define it last.
+
+local lpegP, lpegR, lpegS, lpegC, lpegCc, lpegCs = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cs
+
+local digit = lpegR("09")
+local eol = lpegS('\r\n')^1
+local sp = lpegP(' ')^1
+local space = lpegS(' \r\n')^1
+local number = lpegS('0123456789.-+')^1
+local nonspace = lpegP(1-lpegS(' \r\n'))^1
+
+local spec = digit^2 * lpegP("::::") * digit^2
+local text = lpegCc("{") * (
+ lpegP("\\") * ( (digit * digit * digit) / function(n) return "c" .. tonumber(n,8) end) +
+ lpegP(" ") / function(n) return "\\c32" end + -- never in new mp
+ lpegP(1) / function(n) return "\\c" .. byte(n) end
+ ) * lpegCc("}")
+local package = lpegCs(spec + text^0)
+
+function mps.fshow(str,font,scale) -- lpeg parser
+ mps.textext(font,scale,lpegmatch(package,str))
+end
+
+local cnumber = lpegC(number)
+local cstring = lpegC(nonspace)
+
+local specials = (lpegP("%%MetaPostSpecials:") * sp * (cstring * sp^0)^0 * eol) / mps.specials
+local special = (lpegP("%%MetaPostSpecial:") * sp * (cstring * sp^0)^0 * eol) / mps.special
+local boundingbox = (lpegP("%%BoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox
+local highresboundingbox = (lpegP("%%HiResBoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox
+
+local setup = lpegP("%%BeginSetup") * (1 - lpegP("%%EndSetup") )^1
+local prolog = lpegP("%%BeginProlog") * (1 - lpegP("%%EndProlog"))^1
+local comment = lpegP('%')^1 * (1 - eol)^1
+
+local curveto = ((cnumber * sp)^6 * lpegP("curveto") ) / mps.curveto
+local lineto = ((cnumber * sp)^2 * lpegP("lineto") ) / mps.lineto
+local rlineto = ((cnumber * sp)^2 * lpegP("rlineto") ) / mps.rlineto
+local moveto = ((cnumber * sp)^2 * lpegP("moveto") ) / mps.moveto
+local setrgbcolor = ((cnumber * sp)^3 * lpegP("setrgbcolor") ) / mps.setrgbcolor
+local setcmykcolor = ((cnumber * sp)^4 * lpegP("setcmykcolor") ) / mps.setcmykcolor
+local setgray = ((cnumber * sp)^1 * lpegP("setgray") ) / mps.setgray
+local newpath = ( lpegP("newpath") ) / mps.newpath
+local closepath = ( lpegP("closepath") ) / mps.closepath
+local fill = ( lpegP("fill") ) / mps.fill
+local stroke = ( lpegP("stroke") ) / mps.stroke
+local clip = ( lpegP("clip") ) / mps.clip
+local both = ( lpegP("gsave fill grestore")) / mps.both
+local showpage = ( lpegP("showpage") )
+local setlinejoin = ((cnumber * sp)^1 * lpegP("setlinejoin") ) / mps.setlinejoin
+local setlinecap = ((cnumber * sp)^1 * lpegP("setlinecap") ) / mps.setlinecap
+local setmiterlimit = ((cnumber * sp)^1 * lpegP("setmiterlimit") ) / mps.setmiterlimit
+local gsave = ( lpegP("gsave") ) / mps.gsave
+local grestore = ( lpegP("grestore") ) / mps.grestore
+
+local setdash = (lpegP("[") * (cnumber * sp^0)^0 * lpegP("]") * sp * cnumber * sp * lpegP("setdash")) / mps.setdash
+local concat = (lpegP("[") * (cnumber * sp^0)^6 * lpegP("]") * sp * lpegP("concat") ) / mps.concat
+local scale = ( (cnumber * sp^0)^6 * sp * lpegP("concat") ) / mps.concat
+
+local fshow = (lpegP("(") * lpegC((1-lpegP(")"))^1) * lpegP(")") * space * cstring * space * cnumber * space * lpegP("fshow")) / mps.fshow
+local fshow = (lpegP("(") * lpegCs( ( lpegP("\\(")/"\\050" + lpegP("\\)")/"\\051" + (1-lpegP(")")) )^1 )
+ * lpegP(")") * space * cstring * space * cnumber * space * lpegP("fshow")) / mps.fshow
+
+local setlinewidth_x = (lpegP("0") * sp * cnumber * sp * lpegP("dtransform truncate idtransform setlinewidth pop")) / mps.setlinewidth
+local setlinewidth_y = (cnumber * sp * lpegP("0 dtransform exch truncate exch idtransform pop setlinewidth") ) / mps.setlinewidth
+
+local c = ((cnumber * sp)^6 * lpegP("c") ) / mps.curveto -- ^6 very inefficient, ^1 ok too
+local l = ((cnumber * sp)^2 * lpegP("l") ) / mps.lineto
+local r = ((cnumber * sp)^2 * lpegP("r") ) / mps.rlineto
+local m = ((cnumber * sp)^2 * lpegP("m") ) / mps.moveto
+local vlw = ((cnumber * sp)^1 * lpegP("vlw")) / mps.setlinewidth
+local hlw = ((cnumber * sp)^1 * lpegP("hlw")) / mps.setlinewidth
+
+local R = ((cnumber * sp)^3 * lpegP("R") ) / mps.setrgbcolor
+local C = ((cnumber * sp)^4 * lpegP("C") ) / mps.setcmykcolor
+local G = ((cnumber * sp)^1 * lpegP("G") ) / mps.setgray
+
+local lj = ((cnumber * sp)^1 * lpegP("lj") ) / mps.setlinejoin
+local ml = ((cnumber * sp)^1 * lpegP("ml") ) / mps.setmiterlimit
+local lc = ((cnumber * sp)^1 * lpegP("lc") ) / mps.setlinecap
+
+local n = lpegP("n") / mps.newpath
+local p = lpegP("p") / mps.closepath
+local S = lpegP("S") / mps.stroke
+local F = lpegP("F") / mps.fill
+local B = lpegP("B") / mps.both
+local W = lpegP("W") / mps.clip
+local P = lpegP("P") / mps.showpage
+
+local q = lpegP("q") / mps.gsave
+local Q = lpegP("Q") / mps.grestore
+
+local sd = (lpegP("[") * (cnumber * sp^0)^0 * lpegP("]") * sp * cnumber * sp * lpegP("sd")) / mps.setdash
+local rd = ( lpegP("rd")) / mps.resetdash
+
+local s = ( (cnumber * sp^0)^2 * lpegP("s") ) / mps.scale
+local t = (lpegP("[") * (cnumber * sp^0)^6 * lpegP("]") * sp * lpegP("t") ) / mps.concat
+
+-- experimental
+
+local preamble = (
+ prolog + setup +
+ boundingbox + highresboundingbox + specials + special +
+ comment
+)
+
+local procset = (
+ lj + ml + lc +
+ c + l + m + n + p + r +
+ R + C + G +
+ S + F + B + W +
+ vlw + hlw +
+ Q + q +
+ sd + rd +
+ t + s +
+ fshow +
+ P
+)
+
+local verbose = (
+ curveto + lineto + moveto + newpath + closepath + rlineto +
+ setrgbcolor + setcmykcolor + setgray +
+ setlinejoin + setmiterlimit + setlinecap +
+ stroke + fill + clip + both +
+ setlinewidth_x + setlinewidth_y +
+ gsave + grestore +
+ concat + scale +
+ fshow +
+ setdash + -- no resetdash
+ showpage
+)
+
+-- order matters in terms of speed / we could check for procset first
+
+local captures_old = ( space + verbose + preamble )^0
+local captures_new = ( space + verbose + procset + preamble )^0
+
+local function parse(m_data)
+ if find(m_data,"%%%%BeginResource: procset mpost") then
+ lpegmatch(captures_new,m_data)
+ else
+ lpegmatch(captures_old,m_data)
+ end
+end
+
+-- main converter
+
+local a_colorspace = attributes.private('colormodel')
+
+function mptopdf.convertmpstopdf(name)
+ resetall()
+ local ok, m_data, n = resolvers.loadbinfile(name, 'tex') -- we need a binary load !
+ if ok then
+ mps.colormodel = tex.attribute[a_colorspace]
+ statistics.starttiming(mptopdf)
+ mptopdf.nofconverted = mptopdf.nofconverted + 1
+ pdfcode(formatters["\\letterpercent\\space mptopdf begin: n=%s, file=%s"](mptopdf.nofconverted,file.basename(name)))
+ pdfcode("q 1 0 0 1 0 0 cm")
+ parse(m_data)
+ pdfcode(pdffinishtransparencycode())
+ pdfcode("Q")
+ pdfcode("\\letterpercent\\space mptopdf end")
+ resetall()
+ statistics.stoptiming(mptopdf)
+ else
+ report_mptopdf("file %a not found",name)
+ end
+end
+
+-- status info
+
+statistics.register("mps conversion time",function()
+ local n = mptopdf.nofconverted
+ if n > 0 then
+ return format("%s seconds, %s conversions", statistics.elapsedtime(mptopdf),n)
+ else
+ return nil
+ end
+end)
diff --git a/tex/context/base/meta-pdh.lua b/tex/context/base/meta-pdh.lua
index 10fbad141..5040715c4 100644
--- a/tex/context/base/meta-pdh.lua
+++ b/tex/context/base/meta-pdh.lua
@@ -1,610 +1,610 @@
-if not modules then modules = { } end modules ['meta-pdf'] = {
- version = 1.001,
- comment = "companion to meta-pdf.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-if true then
- return -- or os.exit()
-end
-
--- This file contains the history of the converter. We keep it around as it
--- relates to the development of luatex.
-
--- This is the third version. Version 1 converted to Lua code,
--- version 2 gsubbed the file into TeX code, and version 3 uses
--- the new lpeg functionality and streams the result into TeX.
-
--- We will move old stuff to edu.
-
---~ old lpeg 0.4 lpeg 0.5
---~ 100 times test graphic 2.45 (T:1.07) 0.72 (T:0.24) 0.580 (0.560 no table) -- 0.54 optimized for one space (T:0.19)
---~ 100 times big graphic 10.44 4.30/3.35 nogb 2.914 (2.050 no table) -- 1.99 optimized for one space (T:0.85)
---~ 500 times test graphic T:1.29 T:1.16 (T:1.10 no table) -- T:1.10
-
--- only needed for mp output on disk
-
-local concat, format, find, gsub, gmatch = table.concat, string.format, string.find, string.gsub, string.gmatch
-local tostring, tonumber, select = tostring, tonumber, select
-local lpegmatch = lpeg.match
-
-local metapost = metapost
-
-metapost.mptopdf = metapost.mptopdf or { }
-local mptopdf = metapost.mptopdf
-
-mptopdf.parsers = { }
-mptopdf.parser = 'none'
-mptopdf.nofconverted = 0
-
-function mptopdf.reset()
- mptopdf.data = ""
- mptopdf.path = { }
- mptopdf.stack = { }
- mptopdf.texts = { }
- mptopdf.version = 0
- mptopdf.shortcuts = false
- mptopdf.resetpath()
-end
-
-function mptopdf.resetpath()
- mptopdf.stack.close = false
- mptopdf.stack.path = { }
- mptopdf.stack.concat = nil
- mptopdf.stack.special = false
-end
-
-mptopdf.reset()
-
-function mptopdf.parsers.none()
- -- no parser set
-end
-
-function mptopdf.parse()
- mptopdf.parsers[mptopdf.parser]()
-end
-
--- old code
-
-mptopdf.steps = { }
-
-mptopdf.descapes = {
- ['('] = "\\\\char40 ",
- [')'] = "\\\\char41 ",
- ['"'] = "\\\\char92 "
-}
-
-function mptopdf.descape(str)
- str = gsub(str,"\\(%d%d%d)",function(n)
- return "\\char" .. tonumber(n,8) .. " "
- end)
- return gsub(str,"\\([%(%)\\])",mptopdf.descapes)
-end
-
-function mptopdf.steps.descape(str)
- str = gsub(str,"\\(%d%d%d)",function(n)
- return "\\\\char" .. tonumber(n,8) .. " "
- end)
- return gsub(str,"\\([%(%)\\])",mptopdf.descapes)
-end
-
-function mptopdf.steps.strip() -- .3 per expr
- mptopdf.data = gsub(mptopdf.data,"^(.-)%%+Page:.-%c+(.*)%s+%a+%s+%%+EOF.*$", function(preamble, graphic)
- local bbox = "0 0 0 0"
- for b in gmatch(preamble,"%%%%%a+oundingBox: +(.-)%c+") do
- bbox = b
- end
- local name, version = gmatch(preamble,"%%%%Creator: +(.-) +(.-) ")
- mptopdf.version = tostring(version or "0")
- if find(preamble,"/hlw{0 dtransform") then
- mptopdf.shortcuts = true
- end
- -- the boundingbox specification needs to come before data, well, not really
- return bbox .. " boundingbox\n" .. "\nbegindata\n" .. graphic .. "\nenddata\n"
- end, 1)
- mptopdf.data = gsub(mptopdf.data,"%%%%MetaPostSpecials: +(.-)%c+", "%1 specials\n", 1)
- mptopdf.data = gsub(mptopdf.data,"%%%%MetaPostSpecial: +(.-)%c+", "%1 special\n")
- mptopdf.data = gsub(mptopdf.data,"%%.-%c+", "")
-end
-
-function mptopdf.steps.cleanup()
- if not mptopdf.shortcuts then
- mptopdf.data = gsub(mptopdf.data,"gsave%s+fill%s+grestore%s+stroke", "both")
- mptopdf.data = gsub(mptopdf.data,"([%d%.]+)%s+([%d%.]+)%s+dtransform%s+exch%s+truncate%s+exch%s+idtransform%s+pop%s+setlinewidth", function(wx,wy)
- if tonumber(wx) > 0 then return wx .. " setlinewidth" else return wy .. " setlinewidth" end
- end)
- mptopdf.data = gsub(mptopdf.data,"([%d%.]+)%s+([%d%.]+)%s+dtransform%s+truncate%s+idtransform%s+setlinewidth%s+pop", function(wx,wy)
- if tonumber(wx) > 0 then return wx .. " setlinewidth" else return wy .. " setlinewidth" end
- end)
- end
-end
-
-function mptopdf.steps.convert()
- mptopdf.data = gsub(mptopdf.data,"%c%((.-)%) (.-) (.-) fshow", function(str,font,scale)
- mptopdf.texts[mptopdf.texts+1] = {mptopdf.steps.descape(str), font, scale}
- return "\n" .. #mptopdf.texts .. " textext"
- end)
- mptopdf.data = gsub(mptopdf.data,"%[%s*(.-)%s*%]", function(str)
- return gsub(str,"%s+"," ")
- end)
- local t
- mptopdf.data = gsub(mptopdf.data,"%s*([^%a]-)%s*(%a+)", function(args,cmd)
- if cmd == "textext" then
- t = mptopdf.texts[tonumber(args)]
- return "metapost.mps.textext(" .. "\"" .. t[2] .. "\"," .. t[3] .. ",\"" .. t[1] .. "\")\n"
- else
- return "metapost.mps." .. cmd .. "(" .. gsub(args," +",",") .. ")\n"
- end
- end)
-end
-
-function mptopdf.steps.process()
- assert(loadstring(mptopdf.data))() -- () runs the loaded chunk
-end
-
-function mptopdf.parsers.gsub()
- mptopdf.steps.strip()
- mptopdf.steps.cleanup()
- mptopdf.steps.convert()
- mptopdf.steps.process()
-end
-
--- end of old code
-
--- from lua to tex
-
-function mptopdf.pdfcode(str)
- context.pdfliteral(str) -- \\MPScode
-end
-
-function mptopdf.texcode(str)
- context(str)
-end
-
--- auxiliary functions
-
-function mptopdf.flushconcat()
- if mptopdf.stack.concat then
- mptopdf.pdfcode(concat(mptopdf.stack.concat," ") .. " cm")
- mptopdf.stack.concat = nil
- end
-end
-
-function mptopdf.flushpath(cmd)
- -- faster: no local function and loop
- if #mptopdf.stack.path > 0 then
- local path = { }
- if mptopdf.stack.concat then
- local sx, sy = mptopdf.stack.concat[1], mptopdf.stack.concat[4]
- local rx, ry = mptopdf.stack.concat[2], mptopdf.stack.concat[3]
- local tx, ty = mptopdf.stack.concat[5], mptopdf.stack.concat[6]
- local d = (sx*sy) - (rx*ry)
- local function mpconcat(px, py)
- return (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d
- end
- local stackpath = mptopdf.stack.path
- for k=1,#stackpath do
- local v = stackpath[k]
- v[1],v[2] = mpconcat(v[1],v[2])
- if #v == 7 then
- v[3],v[4] = mpconcat(v[3],v[4])
- v[5],v[6] = mpconcat(v[5],v[6])
- end
- path[#path+1] = concat(v," ")
- end
- else
- local stackpath = mptopdf.stack.path
- for k=1,#stackpath do
- path[#path+1] = concat(stackpath[k]," ")
- end
- end
- mptopdf.flushconcat()
- mptopdf.texcode("\\MPSpath{" .. concat(path," ") .. "}")
- if mptopdf.stack.close then
- mptopdf.texcode("\\MPScode{h " .. cmd .. "}")
- else
- mptopdf.texcode("\\MPScode{" .. cmd .."}")
- end
- end
- mptopdf.resetpath()
-end
-
-function mptopdf.loaded(name)
- local ok, n
- mptopdf.reset()
- ok, mptopdf.data, n = resolvers.loadbinfile(name, 'tex') -- we need a binary load !
- return ok
-end
-
-if not mptopdf.parse then
- function mptopdf.parse() end -- forward declaration
-end
-
-function mptopdf.convertmpstopdf(name)
- if mptopdf.loaded(name) then
- mptopdf.nofconverted = mptopdf.nofconverted + 1
- statistics.starttiming(mptopdf)
- mptopdf.parse()
- mptopdf.reset()
- statistics.stoptiming(mptopdf)
- else
- context("file " .. name .. " not found")
- end
-end
-
--- mp interface
-
-metapost.mps = metapost.mps or { }
-local mps = metapost.mps or { }
-
-function mps.creator(a, b, c)
- mptopdf.version = tonumber(b)
-end
-
-function mps.creationdate(a)
- mptopdf.date= a
-end
-
-function mps.newpath()
- mptopdf.stack.path = { }
-end
-
-function mps.boundingbox(llx, lly, urx, ury)
- mptopdf.texcode("\\MPSboundingbox{" .. llx .. "}{" .. lly .. "}{" .. urx .. "}{" .. ury .. "}")
-end
-
-function mps.moveto(x,y)
- mptopdf.stack.path[#mptopdf.stack.path+1] = {x,y,"m"}
-end
-
-function mps.curveto(ax, ay, bx, by, cx, cy)
- mptopdf.stack.path[#mptopdf.stack.path+1] = {ax,ay,bx,by,cx,cy,"c"}
-end
-
-function mps.lineto(x,y)
- mptopdf.stack.path[#mptopdf.stack.path+1] = {x,y,"l"}
-end
-
-function mps.rlineto(x,y)
- local dx, dy = 0, 0
- if #mptopdf.stack.path > 0 then
- dx, dy = mptopdf.stack.path[#mptopdf.stack.path][1], mptopdf.stack.path[#mptopdf.stack.path][2]
- end
- mptopdf.stack.path[#mptopdf.stack.path+1] = {dx,dy,"l"}
-end
-
-function mps.translate(tx,ty)
- mptopdf.pdfcode("1 0 0 0 1 " .. tx .. " " .. ty .. " cm")
-end
-
-function mps.scale(sx,sy)
- mptopdf.stack.concat = {sx,0,0,sy,0,0}
-end
-
-function mps.concat(sx, rx, ry, sy, tx, ty)
- mptopdf.stack.concat = {sx,rx,ry,sy,tx,ty}
-end
-
-function mps.setlinejoin(d)
- mptopdf.pdfcode(d .. " j")
-end
-
-function mps.setlinecap(d)
- mptopdf.pdfcode(d .. " J")
-end
-
-function mps.setmiterlimit(d)
- mptopdf.pdfcode(d .. " M")
-end
-
-function mps.gsave()
- mptopdf.pdfcode("q")
-end
-
-function mps.grestore()
- mptopdf.pdfcode("Q")
-end
-
-function mps.setdash(...)
- local n = select("#",...)
- mptopdf.pdfcode("[" .. concat({...}," ",1,n-1) .. "] " .. select(n,...) .. " d")
-end
-
-function mps.resetdash()
- mptopdf.pdfcode("[ ] 0 d")
-end
-
-function mps.setlinewidth(d)
- mptopdf.pdfcode(d .. " w")
-end
-
-function mps.closepath()
- mptopdf.stack.close = true
-end
-
-function mps.fill()
- mptopdf.flushpath('f')
-end
-
-function mps.stroke()
- mptopdf.flushpath('S')
-end
-
-function mps.both()
- mptopdf.flushpath('B')
-end
-
-function mps.clip()
- mptopdf.flushpath('W n')
-end
-
-function mps.textext(font, scale, str) -- old parser
- local dx, dy = 0, 0
- if #mptopdf.stack.path > 0 then
- dx, dy = mptopdf.stack.path[1][1], mptopdf.stack.path[1][2]
- end
- mptopdf.flushconcat()
- mptopdf.texcode("\\MPStextext{"..font.."}{"..scale.."}{"..str.."}{"..dx.."}{"..dy.."}")
- mptopdf.resetpath()
-end
-
---~ function mps.handletext(font,scale.str,dx,dy)
---~ local one, two = string.match(str, "^(%d+)::::(%d+)")
---~ if one and two then
---~ mptopdf.texcode("\\MPTOPDFtextext{"..font.."}{"..scale.."}{"..one.."}{"..two.."}{"..dx.."}{"..dy.."}")
---~ else
---~ mptopdf.texcode("\\MPTOPDFtexcode{"..font.."}{"..scale.."}{"..str.."}{"..dx.."}{"..dy.."}")
---~ end
---~ end
-
-function mps.setrgbcolor(r,g,b) -- extra check
- r, g = tonumber(r), tonumber(g) -- needed when we use lpeg
- if r == 0.0123 and g < 0.1 then
- mptopdf.texcode("\\MPSspecial{" .. g*10000 .. "}{" .. b*10000 .. "}")
- elseif r == 0.123 and g < 0.1 then
- mptopdf.texcode("\\MPSspecial{" .. g* 1000 .. "}{" .. b* 1000 .. "}")
- else
- mptopdf.texcode("\\MPSrgb{" .. r .. "}{" .. g .. "}{" .. b .. "}")
- end
-end
-
-function mps.setcmykcolor(c,m,y,k)
- mptopdf.texcode("\\MPScmyk{" .. c .. "}{" .. m .. "}{" .. y .. "}{" .. k .. "}")
-end
-
-function mps.setgray(s)
- mptopdf.texcode("\\MPSgray{" .. s .. "}")
-end
-
-function mps.specials(version,signal,factor) -- 2.0 123 1000
-end
-
-function mps.special(...) -- 7 1 0.5 1 0 0 1 3
- local n = select("#",...)
- mptopdf.texcode("\\MPSbegin\\MPSset{" .. concat({...},"}\\MPSset{",2,n) .. "}\\MPSend")
-end
-
-function mps.begindata()
-end
-
-function mps.enddata()
-end
-
-function mps.showpage()
-end
-
-mps.n = mps.newpath -- n
-mps.p = mps.closepath -- h
-mps.l = mps.lineto -- l
-mps.r = mps.rlineto -- r
-mps.m = mps.moveto -- m
-mps.c = mps.curveto -- c
-mps.hlw = mps.setlinewidth
-mps.vlw = mps.setlinewidth
-
-mps.C = mps.setcmykcolor -- k
-mps.G = mps.setgray -- g
-mps.R = mps.setrgbcolor -- rg
-
-mps.lj = mps.setlinejoin -- j
-mps.ml = mps.setmiterlimit -- M
-mps.lc = mps.setlinecap -- J
-mps.sd = mps.setdash -- d
-mps.rd = mps.resetdash
-
-mps.S = mps.stroke -- S
-mps.F = mps.fill -- f
-mps.B = mps.both -- B
-mps.W = mps.clip -- W
-
-mps.q = mps.gsave -- q
-mps.Q = mps.grestore -- Q
-
-mps.s = mps.scale -- (not in pdf)
-mps.t = mps.concat -- (not the same as pdf anyway)
-
-mps.P = mps.showpage
-
--- experimental
-
-function mps.attribute(id,value)
- mptopdf.texcode("\\attribute " .. id .. "=" .. value .. " ")
--- mptopdf.texcode("\\dompattribute{" .. id .. "}{" .. value .. "}")
-end
-
--- lpeg parser
-
--- The lpeg based parser is rather optimized for the kind of output
--- that MetaPost produces. It's my first real lpeg code, which may
--- show. Because the parser binds to functions, we define it last.
-
-do -- assumes \let\c\char
-
- local byte = string.byte
- local digit = lpeg.R("09")
- local spec = digit^2 * lpeg.P("::::") * digit^2
- local text = lpeg.Cc("{") * (
- lpeg.P("\\") * ( (digit * digit * digit) / function(n) return "c" .. tonumber(n,8) end) +
- lpeg.P(" ") / function(n) return "\\c32" end + -- never in new mp
- lpeg.P(1) / function(n) return "\\c" .. byte(n) end
- ) * lpeg.Cc("}")
- local package = lpeg.Cs(spec + text^0)
-
- function mps.fshow(str,font,scale) -- lpeg parser
- mps.textext(font,scale,lpegmatch(package,str))
- end
-
-end
-
-do
-
- local eol = lpeg.S('\r\n')^1
- local sp = lpeg.P(' ')^1
- local space = lpeg.S(' \r\n')^1
- local number = lpeg.S('0123456789.-+')^1
- local nonspace = lpeg.P(1-lpeg.S(' \r\n'))^1
-
- local cnumber = lpeg.C(number)
- local cstring = lpeg.C(nonspace)
-
- local specials = (lpeg.P("%%MetaPostSpecials:") * sp * (cstring * sp^0)^0 * eol) / mps.specials
- local special = (lpeg.P("%%MetaPostSpecial:") * sp * (cstring * sp^0)^0 * eol) / mps.special
- local boundingbox = (lpeg.P("%%BoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox
- local highresboundingbox = (lpeg.P("%%HiResBoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox
-
- local setup = lpeg.P("%%BeginSetup") * (1 - lpeg.P("%%EndSetup") )^1
- local prolog = lpeg.P("%%BeginProlog") * (1 - lpeg.P("%%EndProlog"))^1
- local comment = lpeg.P('%')^1 * (1 - eol)^1
-
- local curveto = ((cnumber * sp)^6 * lpeg.P("curveto") ) / mps.curveto
- local lineto = ((cnumber * sp)^2 * lpeg.P("lineto") ) / mps.lineto
- local rlineto = ((cnumber * sp)^2 * lpeg.P("rlineto") ) / mps.rlineto
- local moveto = ((cnumber * sp)^2 * lpeg.P("moveto") ) / mps.moveto
- local setrgbcolor = ((cnumber * sp)^3 * lpeg.P("setrgbcolor") ) / mps.setrgbcolor
- local setcmykcolor = ((cnumber * sp)^4 * lpeg.P("setcmykcolor") ) / mps.setcmykcolor
- local setgray = ((cnumber * sp)^1 * lpeg.P("setgray") ) / mps.setgray
- local newpath = ( lpeg.P("newpath") ) / mps.newpath
- local closepath = ( lpeg.P("closepath") ) / mps.closepath
- local fill = ( lpeg.P("fill") ) / mps.fill
- local stroke = ( lpeg.P("stroke") ) / mps.stroke
- local clip = ( lpeg.P("clip") ) / mps.clip
- local both = ( lpeg.P("gsave fill grestore")) / mps.both
- local showpage = ( lpeg.P("showpage") )
- local setlinejoin = ((cnumber * sp)^1 * lpeg.P("setlinejoin") ) / mps.setlinejoin
- local setlinecap = ((cnumber * sp)^1 * lpeg.P("setlinecap") ) / mps.setlinecap
- local setmiterlimit = ((cnumber * sp)^1 * lpeg.P("setmiterlimit") ) / mps.setmiterlimit
- local gsave = ( lpeg.P("gsave") ) / mps.gsave
- local grestore = ( lpeg.P("grestore") ) / mps.grestore
-
- local setdash = (lpeg.P("[") * (cnumber * sp^0)^0 * lpeg.P("]") * sp * cnumber * sp * lpeg.P("setdash")) / mps.setdash
- local concat = (lpeg.P("[") * (cnumber * sp^0)^6 * lpeg.P("]") * sp * lpeg.P("concat") ) / mps.concat
- local scale = ( (cnumber * sp^0)^6 * sp * lpeg.P("concat") ) / mps.concat
-
- local fshow = (lpeg.P("(") * lpeg.C((1-lpeg.P(")"))^1) * lpeg.P(")") * space * cstring * space * cnumber * space * lpeg.P("fshow")) / mps.fshow
- local fshow = (lpeg.P("(") *
- lpeg.Cs( ( lpeg.P("\\(")/"\\050" + lpeg.P("\\)")/"\\051" + (1-lpeg.P(")")) )^1 )
- * lpeg.P(")") * space * cstring * space * cnumber * space * lpeg.P("fshow")) / mps.fshow
-
- local setlinewidth_x = (lpeg.P("0") * sp * cnumber * sp * lpeg.P("dtransform truncate idtransform setlinewidth pop")) / mps.setlinewidth
- local setlinewidth_y = (cnumber * sp * lpeg.P("0 dtransform exch truncate exch idtransform pop setlinewidth") ) / mps.setlinewidth
-
- local c = ((cnumber * sp)^6 * lpeg.P("c") ) / mps.curveto -- ^6 very inefficient, ^1 ok too
- local l = ((cnumber * sp)^2 * lpeg.P("l") ) / mps.lineto
- local r = ((cnumber * sp)^2 * lpeg.P("r") ) / mps.rlineto
- local m = ((cnumber * sp)^2 * lpeg.P("m") ) / mps.moveto
- local vlw = ((cnumber * sp)^1 * lpeg.P("vlw")) / mps.setlinewidth
- local hlw = ((cnumber * sp)^1 * lpeg.P("hlw")) / mps.setlinewidth
-
- local R = ((cnumber * sp)^3 * lpeg.P("R") ) / mps.setrgbcolor
- local C = ((cnumber * sp)^4 * lpeg.P("C") ) / mps.setcmykcolor
- local G = ((cnumber * sp)^1 * lpeg.P("G") ) / mps.setgray
-
- local lj = ((cnumber * sp)^1 * lpeg.P("lj") ) / mps.setlinejoin
- local ml = ((cnumber * sp)^1 * lpeg.P("ml") ) / mps.setmiterlimit
- local lc = ((cnumber * sp)^1 * lpeg.P("lc") ) / mps.setlinecap
-
- local n = lpeg.P("n") / mps.newpath
- local p = lpeg.P("p") / mps.closepath
- local S = lpeg.P("S") / mps.stroke
- local F = lpeg.P("F") / mps.fill
- local B = lpeg.P("B") / mps.both
- local W = lpeg.P("W") / mps.clip
- local P = lpeg.P("P") / mps.showpage
-
- local q = lpeg.P("q") / mps.gsave
- local Q = lpeg.P("Q") / mps.grestore
-
- local sd = (lpeg.P("[") * (cnumber * sp^0)^0 * lpeg.P("]") * sp * cnumber * sp * lpeg.P("sd")) / mps.setdash
- local rd = ( lpeg.P("rd")) / mps.resetdash
-
- local s = ( (cnumber * sp^0)^2 * lpeg.P("s") ) / mps.scale
- local t = (lpeg.P("[") * (cnumber * sp^0)^6 * lpeg.P("]") * sp * lpeg.P("t") ) / mps.concat
-
- -- experimental
-
- local attribute = ((cnumber * sp)^2 * lpeg.P("attribute")) / mps.attribute
- local A = ((cnumber * sp)^2 * lpeg.P("A")) / mps.attribute
-
- local preamble = (
- prolog + setup +
- boundingbox + highresboundingbox + specials + special +
- comment
- )
-
- local procset = (
- lj + ml + lc +
- c + l + m + n + p + r +
- A +
- R + C + G +
- S + F + B + W +
- vlw + hlw +
- Q + q +
- sd + rd +
- t + s +
- fshow +
- P
- )
-
- local verbose = (
- curveto + lineto + moveto + newpath + closepath + rlineto +
- setrgbcolor + setcmykcolor + setgray +
- attribute +
- setlinejoin + setmiterlimit + setlinecap +
- stroke + fill + clip + both +
- setlinewidth_x + setlinewidth_y +
- gsave + grestore +
- concat + scale +
- fshow +
- setdash + -- no resetdash
- showpage
- )
-
- -- order matters in terms of speed / we could check for procset first
-
- local captures_old = ( space + verbose + preamble )^0
- local captures_new = ( space + procset + preamble + verbose )^0
-
- function mptopdf.parsers.lpeg()
- if find(mptopdf.data,"%%%%BeginResource: procset mpost") then
- lpegmatch(captures_new,mptopdf.data)
- else
- lpegmatch(captures_old,mptopdf.data)
- end
- end
-
-end
-
-mptopdf.parser = 'lpeg'
-
--- status info
-
-statistics.register("mps conversion time",function()
- local n = mptopdf.nofconverted
- if n > 0 then
- return format("%s seconds, %s conversions", statistics.elapsedtime(mptopdf),n)
- else
- return nil
- end
-end)
+if not modules then modules = { } end modules ['meta-pdf'] = {
+ version = 1.001,
+ comment = "companion to meta-pdf.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+if true then
+ return -- or os.exit()
+end
+
+-- This file contains the history of the converter. We keep it around as it
+-- relates to the development of luatex.
+
+-- This is the third version. Version 1 converted to Lua code,
+-- version 2 gsubbed the file into TeX code, and version 3 uses
+-- the new lpeg functionality and streams the result into TeX.
+
+-- We will move old stuff to edu.
+
+--~ old lpeg 0.4 lpeg 0.5
+--~ 100 times test graphic 2.45 (T:1.07) 0.72 (T:0.24) 0.580 (0.560 no table) -- 0.54 optimized for one space (T:0.19)
+--~ 100 times big graphic 10.44 4.30/3.35 nogb 2.914 (2.050 no table) -- 1.99 optimized for one space (T:0.85)
+--~ 500 times test graphic T:1.29 T:1.16 (T:1.10 no table) -- T:1.10
+
+-- only needed for mp output on disk
+
+local concat, format, find, gsub, gmatch = table.concat, string.format, string.find, string.gsub, string.gmatch
+local tostring, tonumber, select = tostring, tonumber, select
+local lpegmatch = lpeg.match
+
+local metapost = metapost
+
+metapost.mptopdf = metapost.mptopdf or { }
+local mptopdf = metapost.mptopdf
+
+mptopdf.parsers = { }
+mptopdf.parser = 'none'
+mptopdf.nofconverted = 0
+
+function mptopdf.reset()
+ mptopdf.data = ""
+ mptopdf.path = { }
+ mptopdf.stack = { }
+ mptopdf.texts = { }
+ mptopdf.version = 0
+ mptopdf.shortcuts = false
+ mptopdf.resetpath()
+end
+
+function mptopdf.resetpath()
+ mptopdf.stack.close = false
+ mptopdf.stack.path = { }
+ mptopdf.stack.concat = nil
+ mptopdf.stack.special = false
+end
+
+mptopdf.reset()
+
+function mptopdf.parsers.none()
+ -- no parser set
+end
+
+function mptopdf.parse()
+ mptopdf.parsers[mptopdf.parser]()
+end
+
+-- old code
+
+mptopdf.steps = { }
+
+mptopdf.descapes = {
+ ['('] = "\\\\char40 ",
+ [')'] = "\\\\char41 ",
+ ['"'] = "\\\\char92 "
+}
+
+function mptopdf.descape(str)
+ str = gsub(str,"\\(%d%d%d)",function(n)
+ return "\\char" .. tonumber(n,8) .. " "
+ end)
+ return gsub(str,"\\([%(%)\\])",mptopdf.descapes)
+end
+
+function mptopdf.steps.descape(str)
+ str = gsub(str,"\\(%d%d%d)",function(n)
+ return "\\\\char" .. tonumber(n,8) .. " "
+ end)
+ return gsub(str,"\\([%(%)\\])",mptopdf.descapes)
+end
+
+function mptopdf.steps.strip() -- .3 per expr
+ mptopdf.data = gsub(mptopdf.data,"^(.-)%%+Page:.-%c+(.*)%s+%a+%s+%%+EOF.*$", function(preamble, graphic)
+ local bbox = "0 0 0 0"
+ for b in gmatch(preamble,"%%%%%a+oundingBox: +(.-)%c+") do
+ bbox = b
+ end
+ local name, version = gmatch(preamble,"%%%%Creator: +(.-) +(.-) ")
+ mptopdf.version = tostring(version or "0")
+ if find(preamble,"/hlw{0 dtransform") then
+ mptopdf.shortcuts = true
+ end
+ -- the boundingbox specification needs to come before data, well, not really
+ return bbox .. " boundingbox\n" .. "\nbegindata\n" .. graphic .. "\nenddata\n"
+ end, 1)
+ mptopdf.data = gsub(mptopdf.data,"%%%%MetaPostSpecials: +(.-)%c+", "%1 specials\n", 1)
+ mptopdf.data = gsub(mptopdf.data,"%%%%MetaPostSpecial: +(.-)%c+", "%1 special\n")
+ mptopdf.data = gsub(mptopdf.data,"%%.-%c+", "")
+end
+
+function mptopdf.steps.cleanup()
+ if not mptopdf.shortcuts then
+ mptopdf.data = gsub(mptopdf.data,"gsave%s+fill%s+grestore%s+stroke", "both")
+ mptopdf.data = gsub(mptopdf.data,"([%d%.]+)%s+([%d%.]+)%s+dtransform%s+exch%s+truncate%s+exch%s+idtransform%s+pop%s+setlinewidth", function(wx,wy)
+ if tonumber(wx) > 0 then return wx .. " setlinewidth" else return wy .. " setlinewidth" end
+ end)
+ mptopdf.data = gsub(mptopdf.data,"([%d%.]+)%s+([%d%.]+)%s+dtransform%s+truncate%s+idtransform%s+setlinewidth%s+pop", function(wx,wy)
+ if tonumber(wx) > 0 then return wx .. " setlinewidth" else return wy .. " setlinewidth" end
+ end)
+ end
+end
+
+function mptopdf.steps.convert()
+ mptopdf.data = gsub(mptopdf.data,"%c%((.-)%) (.-) (.-) fshow", function(str,font,scale)
+ mptopdf.texts[mptopdf.texts+1] = {mptopdf.steps.descape(str), font, scale}
+ return "\n" .. #mptopdf.texts .. " textext"
+ end)
+ mptopdf.data = gsub(mptopdf.data,"%[%s*(.-)%s*%]", function(str)
+ return gsub(str,"%s+"," ")
+ end)
+ local t
+ mptopdf.data = gsub(mptopdf.data,"%s*([^%a]-)%s*(%a+)", function(args,cmd)
+ if cmd == "textext" then
+ t = mptopdf.texts[tonumber(args)]
+ return "metapost.mps.textext(" .. "\"" .. t[2] .. "\"," .. t[3] .. ",\"" .. t[1] .. "\")\n"
+ else
+ return "metapost.mps." .. cmd .. "(" .. gsub(args," +",",") .. ")\n"
+ end
+ end)
+end
+
+function mptopdf.steps.process()
+ assert(loadstring(mptopdf.data))() -- () runs the loaded chunk
+end
+
+function mptopdf.parsers.gsub()
+ mptopdf.steps.strip()
+ mptopdf.steps.cleanup()
+ mptopdf.steps.convert()
+ mptopdf.steps.process()
+end
+
+-- end of old code
+
+-- from lua to tex
+
+function mptopdf.pdfcode(str)
+ context.pdfliteral(str) -- \\MPScode
+end
+
+function mptopdf.texcode(str)
+ context(str)
+end
+
+-- auxiliary functions
+
+function mptopdf.flushconcat()
+ if mptopdf.stack.concat then
+ mptopdf.pdfcode(concat(mptopdf.stack.concat," ") .. " cm")
+ mptopdf.stack.concat = nil
+ end
+end
+
+function mptopdf.flushpath(cmd)
+ -- faster: no local function and loop
+ if #mptopdf.stack.path > 0 then
+ local path = { }
+ if mptopdf.stack.concat then
+ local sx, sy = mptopdf.stack.concat[1], mptopdf.stack.concat[4]
+ local rx, ry = mptopdf.stack.concat[2], mptopdf.stack.concat[3]
+ local tx, ty = mptopdf.stack.concat[5], mptopdf.stack.concat[6]
+ local d = (sx*sy) - (rx*ry)
+ local function mpconcat(px, py)
+ return (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d
+ end
+ local stackpath = mptopdf.stack.path
+ for k=1,#stackpath do
+ local v = stackpath[k]
+ v[1],v[2] = mpconcat(v[1],v[2])
+ if #v == 7 then
+ v[3],v[4] = mpconcat(v[3],v[4])
+ v[5],v[6] = mpconcat(v[5],v[6])
+ end
+ path[#path+1] = concat(v," ")
+ end
+ else
+ local stackpath = mptopdf.stack.path
+ for k=1,#stackpath do
+ path[#path+1] = concat(stackpath[k]," ")
+ end
+ end
+ mptopdf.flushconcat()
+ mptopdf.texcode("\\MPSpath{" .. concat(path," ") .. "}")
+ if mptopdf.stack.close then
+ mptopdf.texcode("\\MPScode{h " .. cmd .. "}")
+ else
+ mptopdf.texcode("\\MPScode{" .. cmd .."}")
+ end
+ end
+ mptopdf.resetpath()
+end
+
+function mptopdf.loaded(name)
+ local ok, n
+ mptopdf.reset()
+ ok, mptopdf.data, n = resolvers.loadbinfile(name, 'tex') -- we need a binary load !
+ return ok
+end
+
+if not mptopdf.parse then
+ function mptopdf.parse() end -- forward declaration
+end
+
+function mptopdf.convertmpstopdf(name)
+ if mptopdf.loaded(name) then
+ mptopdf.nofconverted = mptopdf.nofconverted + 1
+ statistics.starttiming(mptopdf)
+ mptopdf.parse()
+ mptopdf.reset()
+ statistics.stoptiming(mptopdf)
+ else
+ context("file " .. name .. " not found")
+ end
+end
+
+-- mp interface
+
+metapost.mps = metapost.mps or { }
+local mps = metapost.mps or { }
+
+function mps.creator(a, b, c)
+ mptopdf.version = tonumber(b)
+end
+
+function mps.creationdate(a)
+ mptopdf.date= a
+end
+
+function mps.newpath()
+ mptopdf.stack.path = { }
+end
+
+function mps.boundingbox(llx, lly, urx, ury)
+ mptopdf.texcode("\\MPSboundingbox{" .. llx .. "}{" .. lly .. "}{" .. urx .. "}{" .. ury .. "}")
+end
+
+function mps.moveto(x,y)
+ mptopdf.stack.path[#mptopdf.stack.path+1] = {x,y,"m"}
+end
+
+function mps.curveto(ax, ay, bx, by, cx, cy)
+ mptopdf.stack.path[#mptopdf.stack.path+1] = {ax,ay,bx,by,cx,cy,"c"}
+end
+
+function mps.lineto(x,y)
+ mptopdf.stack.path[#mptopdf.stack.path+1] = {x,y,"l"}
+end
+
+function mps.rlineto(x,y)
+ local dx, dy = 0, 0
+ if #mptopdf.stack.path > 0 then
+ dx, dy = mptopdf.stack.path[#mptopdf.stack.path][1], mptopdf.stack.path[#mptopdf.stack.path][2]
+ end
+ mptopdf.stack.path[#mptopdf.stack.path+1] = {dx,dy,"l"}
+end
+
+function mps.translate(tx,ty)
+ mptopdf.pdfcode("1 0 0 0 1 " .. tx .. " " .. ty .. " cm")
+end
+
+function mps.scale(sx,sy)
+ mptopdf.stack.concat = {sx,0,0,sy,0,0}
+end
+
+function mps.concat(sx, rx, ry, sy, tx, ty)
+ mptopdf.stack.concat = {sx,rx,ry,sy,tx,ty}
+end
+
+function mps.setlinejoin(d)
+ mptopdf.pdfcode(d .. " j")
+end
+
+function mps.setlinecap(d)
+ mptopdf.pdfcode(d .. " J")
+end
+
+function mps.setmiterlimit(d)
+ mptopdf.pdfcode(d .. " M")
+end
+
+function mps.gsave()
+ mptopdf.pdfcode("q")
+end
+
+function mps.grestore()
+ mptopdf.pdfcode("Q")
+end
+
+function mps.setdash(...)
+ local n = select("#",...)
+ mptopdf.pdfcode("[" .. concat({...}," ",1,n-1) .. "] " .. select(n,...) .. " d")
+end
+
+function mps.resetdash()
+ mptopdf.pdfcode("[ ] 0 d")
+end
+
+function mps.setlinewidth(d)
+ mptopdf.pdfcode(d .. " w")
+end
+
+function mps.closepath()
+ mptopdf.stack.close = true
+end
+
+function mps.fill()
+ mptopdf.flushpath('f')
+end
+
+function mps.stroke()
+ mptopdf.flushpath('S')
+end
+
+function mps.both()
+ mptopdf.flushpath('B')
+end
+
+function mps.clip()
+ mptopdf.flushpath('W n')
+end
+
+function mps.textext(font, scale, str) -- old parser
+ local dx, dy = 0, 0
+ if #mptopdf.stack.path > 0 then
+ dx, dy = mptopdf.stack.path[1][1], mptopdf.stack.path[1][2]
+ end
+ mptopdf.flushconcat()
+ mptopdf.texcode("\\MPStextext{"..font.."}{"..scale.."}{"..str.."}{"..dx.."}{"..dy.."}")
+ mptopdf.resetpath()
+end
+
+--~ function mps.handletext(font,scale.str,dx,dy)
+--~ local one, two = string.match(str, "^(%d+)::::(%d+)")
+--~ if one and two then
+--~ mptopdf.texcode("\\MPTOPDFtextext{"..font.."}{"..scale.."}{"..one.."}{"..two.."}{"..dx.."}{"..dy.."}")
+--~ else
+--~ mptopdf.texcode("\\MPTOPDFtexcode{"..font.."}{"..scale.."}{"..str.."}{"..dx.."}{"..dy.."}")
+--~ end
+--~ end
+
+function mps.setrgbcolor(r,g,b) -- extra check
+ r, g = tonumber(r), tonumber(g) -- needed when we use lpeg
+ if r == 0.0123 and g < 0.1 then
+ mptopdf.texcode("\\MPSspecial{" .. g*10000 .. "}{" .. b*10000 .. "}")
+ elseif r == 0.123 and g < 0.1 then
+ mptopdf.texcode("\\MPSspecial{" .. g* 1000 .. "}{" .. b* 1000 .. "}")
+ else
+ mptopdf.texcode("\\MPSrgb{" .. r .. "}{" .. g .. "}{" .. b .. "}")
+ end
+end
+
+function mps.setcmykcolor(c,m,y,k)
+ mptopdf.texcode("\\MPScmyk{" .. c .. "}{" .. m .. "}{" .. y .. "}{" .. k .. "}")
+end
+
+function mps.setgray(s)
+ mptopdf.texcode("\\MPSgray{" .. s .. "}")
+end
+
+function mps.specials(version,signal,factor) -- 2.0 123 1000
+end
+
+function mps.special(...) -- 7 1 0.5 1 0 0 1 3
+ local n = select("#",...)
+ mptopdf.texcode("\\MPSbegin\\MPSset{" .. concat({...},"}\\MPSset{",2,n) .. "}\\MPSend")
+end
+
+function mps.begindata()
+end
+
+function mps.enddata()
+end
+
+function mps.showpage()
+end
+
+mps.n = mps.newpath -- n
+mps.p = mps.closepath -- h
+mps.l = mps.lineto -- l
+mps.r = mps.rlineto -- r
+mps.m = mps.moveto -- m
+mps.c = mps.curveto -- c
+mps.hlw = mps.setlinewidth
+mps.vlw = mps.setlinewidth
+
+mps.C = mps.setcmykcolor -- k
+mps.G = mps.setgray -- g
+mps.R = mps.setrgbcolor -- rg
+
+mps.lj = mps.setlinejoin -- j
+mps.ml = mps.setmiterlimit -- M
+mps.lc = mps.setlinecap -- J
+mps.sd = mps.setdash -- d
+mps.rd = mps.resetdash
+
+mps.S = mps.stroke -- S
+mps.F = mps.fill -- f
+mps.B = mps.both -- B
+mps.W = mps.clip -- W
+
+mps.q = mps.gsave -- q
+mps.Q = mps.grestore -- Q
+
+mps.s = mps.scale -- (not in pdf)
+mps.t = mps.concat -- (not the same as pdf anyway)
+
+mps.P = mps.showpage
+
+-- experimental
+
+function mps.attribute(id,value)
+ mptopdf.texcode("\\attribute " .. id .. "=" .. value .. " ")
+-- mptopdf.texcode("\\dompattribute{" .. id .. "}{" .. value .. "}")
+end
+
+-- lpeg parser
+
+-- The lpeg based parser is rather optimized for the kind of output
+-- that MetaPost produces. It's my first real lpeg code, which may
+-- show. Because the parser binds to functions, we define it last.
+
+do -- assumes \let\c\char
+
+ local byte = string.byte
+ local digit = lpeg.R("09")
+ local spec = digit^2 * lpeg.P("::::") * digit^2
+ local text = lpeg.Cc("{") * (
+ lpeg.P("\\") * ( (digit * digit * digit) / function(n) return "c" .. tonumber(n,8) end) +
+ lpeg.P(" ") / function(n) return "\\c32" end + -- never in new mp
+ lpeg.P(1) / function(n) return "\\c" .. byte(n) end
+ ) * lpeg.Cc("}")
+ local package = lpeg.Cs(spec + text^0)
+
+ function mps.fshow(str,font,scale) -- lpeg parser
+ mps.textext(font,scale,lpegmatch(package,str))
+ end
+
+end
+
+do
+
+ local eol = lpeg.S('\r\n')^1
+ local sp = lpeg.P(' ')^1
+ local space = lpeg.S(' \r\n')^1
+ local number = lpeg.S('0123456789.-+')^1
+ local nonspace = lpeg.P(1-lpeg.S(' \r\n'))^1
+
+ local cnumber = lpeg.C(number)
+ local cstring = lpeg.C(nonspace)
+
+ local specials = (lpeg.P("%%MetaPostSpecials:") * sp * (cstring * sp^0)^0 * eol) / mps.specials
+ local special = (lpeg.P("%%MetaPostSpecial:") * sp * (cstring * sp^0)^0 * eol) / mps.special
+ local boundingbox = (lpeg.P("%%BoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox
+ local highresboundingbox = (lpeg.P("%%HiResBoundingBox:") * sp * (cnumber * sp^0)^4 * eol) / mps.boundingbox
+
+ local setup = lpeg.P("%%BeginSetup") * (1 - lpeg.P("%%EndSetup") )^1
+ local prolog = lpeg.P("%%BeginProlog") * (1 - lpeg.P("%%EndProlog"))^1
+ local comment = lpeg.P('%')^1 * (1 - eol)^1
+
+ local curveto = ((cnumber * sp)^6 * lpeg.P("curveto") ) / mps.curveto
+ local lineto = ((cnumber * sp)^2 * lpeg.P("lineto") ) / mps.lineto
+ local rlineto = ((cnumber * sp)^2 * lpeg.P("rlineto") ) / mps.rlineto
+ local moveto = ((cnumber * sp)^2 * lpeg.P("moveto") ) / mps.moveto
+ local setrgbcolor = ((cnumber * sp)^3 * lpeg.P("setrgbcolor") ) / mps.setrgbcolor
+ local setcmykcolor = ((cnumber * sp)^4 * lpeg.P("setcmykcolor") ) / mps.setcmykcolor
+ local setgray = ((cnumber * sp)^1 * lpeg.P("setgray") ) / mps.setgray
+ local newpath = ( lpeg.P("newpath") ) / mps.newpath
+ local closepath = ( lpeg.P("closepath") ) / mps.closepath
+ local fill = ( lpeg.P("fill") ) / mps.fill
+ local stroke = ( lpeg.P("stroke") ) / mps.stroke
+ local clip = ( lpeg.P("clip") ) / mps.clip
+ local both = ( lpeg.P("gsave fill grestore")) / mps.both
+ local showpage = ( lpeg.P("showpage") )
+ local setlinejoin = ((cnumber * sp)^1 * lpeg.P("setlinejoin") ) / mps.setlinejoin
+ local setlinecap = ((cnumber * sp)^1 * lpeg.P("setlinecap") ) / mps.setlinecap
+ local setmiterlimit = ((cnumber * sp)^1 * lpeg.P("setmiterlimit") ) / mps.setmiterlimit
+ local gsave = ( lpeg.P("gsave") ) / mps.gsave
+ local grestore = ( lpeg.P("grestore") ) / mps.grestore
+
+ local setdash = (lpeg.P("[") * (cnumber * sp^0)^0 * lpeg.P("]") * sp * cnumber * sp * lpeg.P("setdash")) / mps.setdash
+ local concat = (lpeg.P("[") * (cnumber * sp^0)^6 * lpeg.P("]") * sp * lpeg.P("concat") ) / mps.concat
+ local scale = ( (cnumber * sp^0)^6 * sp * lpeg.P("concat") ) / mps.concat
+
+ local fshow = (lpeg.P("(") * lpeg.C((1-lpeg.P(")"))^1) * lpeg.P(")") * space * cstring * space * cnumber * space * lpeg.P("fshow")) / mps.fshow
+ local fshow = (lpeg.P("(") *
+ lpeg.Cs( ( lpeg.P("\\(")/"\\050" + lpeg.P("\\)")/"\\051" + (1-lpeg.P(")")) )^1 )
+ * lpeg.P(")") * space * cstring * space * cnumber * space * lpeg.P("fshow")) / mps.fshow
+
+ local setlinewidth_x = (lpeg.P("0") * sp * cnumber * sp * lpeg.P("dtransform truncate idtransform setlinewidth pop")) / mps.setlinewidth
+ local setlinewidth_y = (cnumber * sp * lpeg.P("0 dtransform exch truncate exch idtransform pop setlinewidth") ) / mps.setlinewidth
+
+ local c = ((cnumber * sp)^6 * lpeg.P("c") ) / mps.curveto -- ^6 very inefficient, ^1 ok too
+ local l = ((cnumber * sp)^2 * lpeg.P("l") ) / mps.lineto
+ local r = ((cnumber * sp)^2 * lpeg.P("r") ) / mps.rlineto
+ local m = ((cnumber * sp)^2 * lpeg.P("m") ) / mps.moveto
+ local vlw = ((cnumber * sp)^1 * lpeg.P("vlw")) / mps.setlinewidth
+ local hlw = ((cnumber * sp)^1 * lpeg.P("hlw")) / mps.setlinewidth
+
+ local R = ((cnumber * sp)^3 * lpeg.P("R") ) / mps.setrgbcolor
+ local C = ((cnumber * sp)^4 * lpeg.P("C") ) / mps.setcmykcolor
+ local G = ((cnumber * sp)^1 * lpeg.P("G") ) / mps.setgray
+
+ local lj = ((cnumber * sp)^1 * lpeg.P("lj") ) / mps.setlinejoin
+ local ml = ((cnumber * sp)^1 * lpeg.P("ml") ) / mps.setmiterlimit
+ local lc = ((cnumber * sp)^1 * lpeg.P("lc") ) / mps.setlinecap
+
+ local n = lpeg.P("n") / mps.newpath
+ local p = lpeg.P("p") / mps.closepath
+ local S = lpeg.P("S") / mps.stroke
+ local F = lpeg.P("F") / mps.fill
+ local B = lpeg.P("B") / mps.both
+ local W = lpeg.P("W") / mps.clip
+ local P = lpeg.P("P") / mps.showpage
+
+ local q = lpeg.P("q") / mps.gsave
+ local Q = lpeg.P("Q") / mps.grestore
+
+ local sd = (lpeg.P("[") * (cnumber * sp^0)^0 * lpeg.P("]") * sp * cnumber * sp * lpeg.P("sd")) / mps.setdash
+ local rd = ( lpeg.P("rd")) / mps.resetdash
+
+ local s = ( (cnumber * sp^0)^2 * lpeg.P("s") ) / mps.scale
+ local t = (lpeg.P("[") * (cnumber * sp^0)^6 * lpeg.P("]") * sp * lpeg.P("t") ) / mps.concat
+
+ -- experimental
+
+ local attribute = ((cnumber * sp)^2 * lpeg.P("attribute")) / mps.attribute
+ local A = ((cnumber * sp)^2 * lpeg.P("A")) / mps.attribute
+
+ local preamble = (
+ prolog + setup +
+ boundingbox + highresboundingbox + specials + special +
+ comment
+ )
+
+ local procset = (
+ lj + ml + lc +
+ c + l + m + n + p + r +
+ A +
+ R + C + G +
+ S + F + B + W +
+ vlw + hlw +
+ Q + q +
+ sd + rd +
+ t + s +
+ fshow +
+ P
+ )
+
+ local verbose = (
+ curveto + lineto + moveto + newpath + closepath + rlineto +
+ setrgbcolor + setcmykcolor + setgray +
+ attribute +
+ setlinejoin + setmiterlimit + setlinecap +
+ stroke + fill + clip + both +
+ setlinewidth_x + setlinewidth_y +
+ gsave + grestore +
+ concat + scale +
+ fshow +
+ setdash + -- no resetdash
+ showpage
+ )
+
+ -- order matters in terms of speed / we could check for procset first
+
+ local captures_old = ( space + verbose + preamble )^0
+ local captures_new = ( space + procset + preamble + verbose )^0
+
+ function mptopdf.parsers.lpeg()
+ if find(mptopdf.data,"%%%%BeginResource: procset mpost") then
+ lpegmatch(captures_new,mptopdf.data)
+ else
+ lpegmatch(captures_old,mptopdf.data)
+ end
+ end
+
+end
+
+mptopdf.parser = 'lpeg'
+
+-- status info
+
+statistics.register("mps conversion time",function()
+ local n = mptopdf.nofconverted
+ if n > 0 then
+ return format("%s seconds, %s conversions", statistics.elapsedtime(mptopdf),n)
+ else
+ return nil
+ end
+end)
diff --git a/tex/context/base/meta-tex.lua b/tex/context/base/meta-tex.lua
index 117d604b3..c29498ad1 100644
--- a/tex/context/base/meta-tex.lua
+++ b/tex/context/base/meta-tex.lua
@@ -1,38 +1,38 @@
-if not modules then modules = { } end modules ['meta-tex'] = {
- version = 1.001,
- comment = "companion to meta-tex.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
---~ local P, C, lpegmatch = lpeg.P, lpeg.C, lpeg.match
-
--- local left = P("[")
--- local right = P("]")
--- local space = P(" ")
--- local argument = left * C((1-right)^1) * right
--- local pattern = (argument + space)^0
-
--- function metapost.sometxt(optional,str)
--- if optional == "" then
--- context.sometxta(str)
--- else
--- local one, two = lpegmatch(pattern,optional)
--- if two then
--- context.sometxtc(one,two,str)
--- elseif one then
--- context.sometxtb(one,str)
--- else
--- context.sometxta(str)
--- end
--- end
--- end
-
-local P, Cs, lpegmatch = lpeg.P, lpeg.Cs, lpeg.match
-
-local pattern = Cs((P([[\"]]) + P([["]])/"\\quotedbl{}" + P(1))^0) -- or \char
-
-function metapost.escaped(str)
- context(lpegmatch(pattern,str))
-end
+if not modules then modules = { } end modules ['meta-tex'] = {
+ version = 1.001,
+ comment = "companion to meta-tex.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--~ local P, C, lpegmatch = lpeg.P, lpeg.C, lpeg.match
+
+-- local left = P("[")
+-- local right = P("]")
+-- local space = P(" ")
+-- local argument = left * C((1-right)^1) * right
+-- local pattern = (argument + space)^0
+
+-- function metapost.sometxt(optional,str)
+-- if optional == "" then
+-- context.sometxta(str)
+-- else
+-- local one, two = lpegmatch(pattern,optional)
+-- if two then
+-- context.sometxtc(one,two,str)
+-- elseif one then
+-- context.sometxtb(one,str)
+-- else
+-- context.sometxta(str)
+-- end
+-- end
+-- end
+
+local P, Cs, lpegmatch = lpeg.P, lpeg.Cs, lpeg.match
+
+local pattern = Cs((P([[\"]]) + P([["]])/"\\quotedbl{}" + P(1))^0) -- or \char
+
+function metapost.escaped(str)
+ context(lpegmatch(pattern,str))
+end
diff --git a/tex/context/base/mlib-ctx.lua b/tex/context/base/mlib-ctx.lua
index 8d6d7aa3e..04e0efcb4 100644
--- a/tex/context/base/mlib-ctx.lua
+++ b/tex/context/base/mlib-ctx.lua
@@ -1,178 +1,178 @@
-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",
-}
-
--- todo
-
-local format, concat = string.format, table.concat
-local settings_to_hash = utilities.parsers.settings_to_hash
-
-local report_metapost = logs.reporter("metapost")
-
-local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming
-
-local mplib = mplib
-
-metapost = metapost or {}
-local metapost = metapost
-
-local v_no = interfaces.variables.no
-
-metapost.defaultformat = "metafun"
-metapost.defaultinstance = "metafun"
-metapost.defaultmethod = "default"
-
-local function setmpsformat(specification)
- local instance = specification.instance
- local format = specification.format
- local method = specification.method
- 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
- specification.mpx = metapost.format(instance,format,method)
-end
-
-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
-
-function commands.getmpextensions(instance,state)
- context(metapost.getextensions(instance,state))
-end
-
-function metapost.graphic(specification)
- setmpsformat(specification)
- metapost.graphic_base_pass(specification)
-end
-
-function metapost.getclippath(specification) -- why not a special instance for this
- setmpsformat(specification)
- local mpx = specification.mpx
- 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)
- return result
- 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
- result = concat(metapost.flushnormalpath(result),"\n")
- context(result)
- end
-end
-
-statistics.register("metapost processing time", function()
- local n = metapost.n
- if n and n > 0 then
- local nofconverted = metapost.makempy.nofconverted
- local elapsedtime = statistics.elapsedtime
- local elapsed = statistics.elapsed
- local str = format("%s seconds, loading: %s, execution: %s, n: %s, average: %s",
- elapsedtime(metapost), elapsedtime(mplib), elapsedtime(metapost.exectime), n,
- elapsedtime((elapsed(metapost) + elapsed(mplib) + elapsed(metapost.exectime)) / n))
- if nofconverted > 0 then
- return format("%s, external: %s (%s calls)",
- str, elapsedtime(metapost.makempy), nofconverted)
- else
- return str
- end
- else
- return nil
- end
-end)
-
--- only used in graphictexts
-
-metapost.tex = metapost.tex or { }
-
-local environments = { }
-
-function metapost.tex.set(str)
- environments[#environments+1] = str
-end
-
-function metapost.tex.reset()
- environments = { }
-end
-
-function metapost.tex.get()
- return concat(environments,"\n")
-end
+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",
+}
+
+-- todo
+
+local format, concat = string.format, table.concat
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+local report_metapost = logs.reporter("metapost")
+
+local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming
+
+local mplib = mplib
+
+metapost = metapost or {}
+local metapost = metapost
+
+local v_no = interfaces.variables.no
+
+metapost.defaultformat = "metafun"
+metapost.defaultinstance = "metafun"
+metapost.defaultmethod = "default"
+
+local function setmpsformat(specification)
+ local instance = specification.instance
+ local format = specification.format
+ local method = specification.method
+ 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
+ specification.mpx = metapost.format(instance,format,method)
+end
+
+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
+
+function commands.getmpextensions(instance,state)
+ context(metapost.getextensions(instance,state))
+end
+
+function metapost.graphic(specification)
+ setmpsformat(specification)
+ metapost.graphic_base_pass(specification)
+end
+
+function metapost.getclippath(specification) -- why not a special instance for this
+ setmpsformat(specification)
+ local mpx = specification.mpx
+ 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)
+ return result
+ 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
+ result = concat(metapost.flushnormalpath(result),"\n")
+ context(result)
+ end
+end
+
+statistics.register("metapost processing time", function()
+ local n = metapost.n
+ if n and n > 0 then
+ local nofconverted = metapost.makempy.nofconverted
+ local elapsedtime = statistics.elapsedtime
+ local elapsed = statistics.elapsed
+ local str = format("%s seconds, loading: %s, execution: %s, n: %s, average: %s",
+ elapsedtime(metapost), elapsedtime(mplib), elapsedtime(metapost.exectime), n,
+ elapsedtime((elapsed(metapost) + elapsed(mplib) + elapsed(metapost.exectime)) / n))
+ if nofconverted > 0 then
+ return format("%s, external: %s (%s calls)",
+ str, elapsedtime(metapost.makempy), nofconverted)
+ else
+ return str
+ end
+ else
+ return nil
+ end
+end)
+
+-- only used in graphictexts
+
+metapost.tex = metapost.tex or { }
+
+local environments = { }
+
+function metapost.tex.set(str)
+ environments[#environments+1] = str
+end
+
+function metapost.tex.reset()
+ environments = { }
+end
+
+function metapost.tex.get()
+ return concat(environments,"\n")
+end
diff --git a/tex/context/base/mlib-pdf.lua b/tex/context/base/mlib-pdf.lua
index 6ca50a12f..963309951 100644
--- a/tex/context/base/mlib-pdf.lua
+++ b/tex/context/base/mlib-pdf.lua
@@ -1,530 +1,530 @@
-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",
-}
-
--- maybe %s is better than %f
-
-local format, concat, gsub = string.format, table.concat, string.gsub
-local abs, sqrt, round = math.abs, math.sqrt, math.round
-local setmetatable = setmetatable
-local Cf, C, Cg, Ct, P, S, lpegmatch = lpeg.Cf, lpeg.C, lpeg.Cg, lpeg.Ct, lpeg.P, lpeg.S, lpeg.match
-local formatters = string.formatters
-
-local report_metapost = logs.reporter("metapost")
-
-local mplib, context = mplib, context
-
-local allocate = utilities.storage.allocate
-
-local copy_node = node.copy
-local write_node = node.write
-
-metapost = metapost or { }
-local metapost = metapost
-
-metapost.flushers = metapost.flushers or { }
-local pdfflusher = { }
-metapost.flushers.pdf = pdfflusher
-
-metapost.multipass = false
-metapost.n = 0
-metapost.optimize = true -- false
-
-local experiment = true -- uses context(node) that already does delayed nodes
-
-local savedliterals = nil -- needs checking
-local mpsliteral = nodes.pool.register(node.new("whatsit",nodes.whatsitcodes.pdfliteral)) -- pdfliteral.mode = 1
-
-local pdfliteral = function(s)
- local literal = copy_node(mpsliteral)
- literal.data = s
- return literal
-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,f)
- if metapost.optimize then
- local objects = result.objects
- if not objects then
- result.objects = { }
- end
- objects = result.objects[f]
- if not objects then
- objects = figure:objects()
- result.objects[f] = objects
- end
- return objects
- else
- return figure:objects()
- end
-end
-
-function metapost.convert(result, trialrun, flusher, multipass, askedfig)
- if trialrun then
- metapost.multipass = false
- metapost.parse(result, askedfig)
- if multipass and not metapost.multipass and metapost.optimize then
- metapost.flush(result, flusher, askedfig) -- saves a run
- else
- return false
- end
- else
- metapost.flush(result, flusher, askedfig)
- end
- return true -- done
-end
-
-function metapost.flushliteral(d)
- if savedliterals then
- local literal = copy_node(mpsliteral)
- literal.data = savedliterals[d]
- write_node(literal)
- else
- report_metapost("problem flushing literal %a",d)
- end
-end
-
-function metapost.flushreset() -- will become obsolete and internal
- savedliterals = nil
-end
-
-function pdfflusher.comment(message)
- if message then
- message = formatters["%% mps graphic %s: %s"](metapost.n,message)
- if experiment then
- context(pdfliteral(message))
- else
- if savedliterals then
- local last = #savedliterals + 1
- savedliterals[last] = message
- context.MPLIBtoPDF(last)
- else
- savedliterals = { message }
- context.MPLIBtoPDF(1)
- end
- end
- end
-end
-
-function pdfflusher.startfigure(n,llx,lly,urx,ury,message)
- savedliterals = nil
- metapost.n = metapost.n + 1
- context.startMPLIBtoPDF(llx,lly,urx,ury)
- if message then pdfflusher.comment(message) end
-end
-
-function pdfflusher.stopfigure(message)
- if message then pdfflusher.comment(message) end
- context.stopMPLIBtoPDF()
- context.MPLIBflushreset() -- maybe just at the beginning
-end
-
-function pdfflusher.flushfigure(pdfliterals) -- table
- if #pdfliterals > 0 then
- pdfliterals = concat(pdfliterals,"\n")
- if experiment then
- context(pdfliteral(pdfliterals))
- else
- if savedliterals then
- local last = #savedliterals + 1
- savedliterals[last] = pdfliterals
- context.MPLIBtoPDF(last)
- else
- savedliterals = { pdfliterals }
- context.MPLIBtoPDF(1)
- end
- end
- 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 pen_info = mplib.pen_info
-
-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
- if t then
- nt = #t
- else
- t = { }
- nt = 0
- end
- for i=1,#path do
- nt = nt + 1
- pth = path[i]
- if not ith then
- t[nt] = formatters["%f %f m"](pth.x_coord,pth.y_coord)
- elseif curved(ith,pth) then
- t[nt] = formatters["%f %f %f %f %f %f c"](ith.right_x,ith.right_y,pth.left_x,pth.left_y,pth.x_coord,pth.y_coord)
- else
- t[nt] = formatters["%f %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
- t[nt] = formatters["%f %f %f %f %f %f c"](pth.right_x,pth.right_y,one.left_x,one.left_y,one.x_coord,one.y_coord )
- else
- t[nt] = formatters["%f %f l"](one.x_coord,one.y_coord)
- end
- elseif #path == 1 then
- -- special case .. draw point
- local one = path[1]
- nt = nt + 1
- t[nt] = formatters["%f %f l"](one.x_coord,one.y_coord)
- end
- return t
-end
-
-local function flushconcatpath(path, t, open)
- local pth, ith, nt
- if t then
- nt = #t
- else
- t = { }
- nt = 0
- end
- nt = nt + 1
- t[nt] = formatters["%f %f %f %f %f %f cm"](sx,rx,ry,sy,tx,ty)
- for i=1,#path do
- nt = nt + 1
- pth = path[i]
- if not ith then
- t[nt] = formatters["%f %f m"](mpconcat(pth.x_coord,pth.y_coord))
- elseif curved(ith,pth) then
- local a, b = mpconcat(ith.right_x,ith.right_y)
- local c, d = mpconcat(pth.left_x,pth.left_y)
- t[nt] = formatters["%f %f %f %f %f %f c"](a,b,c,d,mpconcat(pth.x_coord,pth.y_coord))
- else
- t[nt] = formatters["%f %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
- local a, b = mpconcat(pth.right_x,pth.right_y)
- local c, d = mpconcat(one.left_x,one.left_y)
- t[nt] = formatters["%f %f %f %f %f %f c"](a,b,c,d,mpconcat(one.x_coord, one.y_coord))
- else
- t[nt] = formatters["%f %f l"](mpconcat(one.x_coord,one.y_coord))
- end
- elseif #path == 1 then
- -- special case .. draw point
- nt = nt + 1
- local one = path[1]
- t[nt] = formatters["%f %f l"](mpconcat(one.x_coord,one.y_coord))
- end
- return t
-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.
-
-local function ignore() end
-
-function metapost.flush(result,flusher,askedfig)
- if result then
- 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
- for f=1, #figures do
- local figure = figures[f]
- local objects = getobjects(result,figure,f)
- local fignum = figure:charcode() or 0
- if askedfig == "direct" or askedfig == "all" or askedfig == fignum then
- local t = { }
- local miterlimit, linecap, linejoin, dashed = -1, -1, -1, false
- local bbox = figure:boundingbox()
- local llx, lly, urx, ury = bbox[1], bbox[2], bbox[3], bbox[4]
- metapost.llx = llx
- metapost.lly = lly
- metapost.urx = urx
- metapost.ury = ury
- if urx < llx then
- -- invalid
- startfigure(fignum,0,0,0,0,"invalid",figure)
- stopfigure()
- else
- startfigure(fignum,llx,lly,urx,ury,"begin",figure)
- t[#t+1] = "q"
- if objects then
- resetplugins(t) -- we should move the colorinitializer here
- for o=1,#objects do
- local object = objects[o]
- local objecttype = object.type
- if objecttype == "start_bounds" or objecttype == "stop_bounds" or objecttype == "special" then
- -- skip
- elseif objecttype == "start_clip" then
- t[#t+1] = "q"
- flushnormalpath(object.path,t,false)
- t[#t+1] = "W n"
- elseif objecttype == "stop_clip" then
- t[#t+1] = "Q"
- miterlimit, linecap, linejoin, dashed = -1, -1, -1, false
- elseif objecttype == "text" then
- t[#t+1] = "q"
- local ot = object.transform -- 3,4,5,6,1,2
- t[#t+1] = formatters["%f %f %f %f %f %f cm"](ot[3],ot[4],ot[5],ot[6],ot[1],ot[2]) -- TH: formatters["%f %f m %f %f %f %f 0 0 cm"](unpack(ot))
- flushfigure(t) -- flush accumulated literals
- t = { }
- textfigure(object.font,object.dsize,object.text,object.width,object.height,object.depth)
- t[#t+1] = "Q"
- else
- -- 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
- })
- -- first we analyze
- local before, after = processplugins(object)
- local objecttype = object.type -- can have changed
- if before then
- t = pluginactions(before,t,flushfigure)
- end
- local ml = object.miterlimit
- if ml and ml ~= miterlimit then
- miterlimit = ml
- t[#t+1] = formatters["%f M"](ml)
- end
- local lj = object.linejoin
- if lj and lj ~= linejoin then
- linejoin = lj
- t[#t+1] = formatters["%i j"](lj)
- end
- local lc = object.linecap
- if lc and lc ~= linecap then
- linecap = lc
- t[#t+1] = formatters["%i J"](lc)
- end
- local dl = object.dash
- if dl then
- local d = formatters["[%s] %f d"](concat(dl.dashes or {}," "),dl.offset)
- if d ~= dashed then
- dashed = d
- t[#t+1] = dashed
- end
- elseif dashed then
- t[#t+1] = "[] 0 d"
- dashed = false
- end
- local path = object.path -- newpath
- local transformed, penwidth = false, 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
- t[#t+1] = formatters["%f w"](penwidth) -- todo: only if changed
- if objecttype == 'fill' then
- objecttype = 'both'
- end
- else -- calculated by mplib itself
- objecttype = 'fill'
- end
- end
- if transformed then
- t[#t+1] = "q"
- end
- if path then
- if transformed then
- flushconcatpath(path,t,open)
- else
- flushnormalpath(path,t,open)
- end
- if objecttype == "fill" then
- t[#t+1] = "h f"
- elseif objecttype == "outline" then
- t[#t+1] = (open and "S") or "h S"
- elseif objecttype == "both" then
- t[#t+1] = "h B"
- end
- end
- if transformed then
- t[#t+1] = "Q"
- end
- local path = object.htap
- if path then
- if transformed then
- t[#t+1] = "q"
- end
- if transformed then
- flushconcatpath(path,t,open)
- else
- flushnormalpath(path,t,open)
- end
- if objecttype == "fill" then
- t[#t+1] = "h f"
- elseif objecttype == "outline" then
- t[#t+1] = (open and "S") or "h S"
- elseif objecttype == "both" then
- t[#t+1] = "h B"
- end
- if transformed then
- t[#t+1] = "Q"
- end
- end
- if after then
- t = pluginactions(after,t,flushfigure)
- end
- if object.grouped then
- -- can be qQ'd so changes can end up in groups
- miterlimit, linecap, linejoin, dashed = -1, -1, -1, false
- end
- end
- end
- end
- t[#t+1] = "Q"
- flushfigure(t)
- stopfigure("end")
- end
- if askedfig ~= "all" then
- break
- end
- end
- end
- end
- end
-end
-
-function metapost.parse(result,askedfig)
- if result then
- local figures = result.fig
- if figures then
- local analyzeplugins = metapost.analyzeplugins -- each object
- for f=1,#figures do
- local figure = figures[f]
- local fignum = figure:charcode() or 0
- if askedfig == "direct" or askedfig == "all" or askedfig == fignum then
- local bbox = figure:boundingbox()
- metapost.llx = bbox[1]
- metapost.lly = bbox[2]
- metapost.urx = bbox[3]
- metapost.ury = bbox[4]
- local objects = getobjects(result,figure,f)
- if objects then
- for o=1,#objects do
- analyzeplugins(objects[o])
- end
- end
- if askedfig ~= "all" then
- break
- end
- end
- end
- end
- end
-end
-
--- tracing:
-
-local t = { }
-
-local flusher = {
- startfigure = function()
- t = { }
- context.startnointerference()
- end,
- flushfigure = function(literals)
- local n = #t
- for i=1, #literals do
- n = n + 1
- t[n] = literals[i]
- end
- end,
- stopfigure = function()
- context.stopnointerference()
- end
-}
-
-function metapost.pdfliterals(result)
- metapost.flush(result,flusher)
- return t
-end
-
--- so far
-
-function metapost.totable(result)
- local figure = result and result.fig and result.fig[1]
- if figure then
- local t = { }
- local objects = figure:objects()
- for o=1,#objects do
- local object = objects[o]
- local tt = { }
- local fields = mplib.fields(object)
- for f=1,#fields do
- local field = fields[f]
- tt[field] = object[field]
- end
- t[o] = tt
- end
- local b = figure:boundingbox()
- return {
- boundingbox = { llx = b[1], lly = b[2], urx = b[3], ury = b[4] },
- objects = t
- }
- else
- return nil
- end
-end
+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",
+}
+
+-- maybe %s is better than %f
+
+local format, concat, gsub = string.format, table.concat, string.gsub
+local abs, sqrt, round = math.abs, math.sqrt, math.round
+local setmetatable = setmetatable
+local Cf, C, Cg, Ct, P, S, lpegmatch = lpeg.Cf, lpeg.C, lpeg.Cg, lpeg.Ct, lpeg.P, lpeg.S, lpeg.match
+local formatters = string.formatters
+
+local report_metapost = logs.reporter("metapost")
+
+local mplib, context = mplib, context
+
+local allocate = utilities.storage.allocate
+
+local copy_node = node.copy
+local write_node = node.write
+
+metapost = metapost or { }
+local metapost = metapost
+
+metapost.flushers = metapost.flushers or { }
+local pdfflusher = { }
+metapost.flushers.pdf = pdfflusher
+
+metapost.multipass = false
+metapost.n = 0
+metapost.optimize = true -- false
+
+local experiment = true -- uses context(node) that already does delayed nodes
+
+local savedliterals = nil -- needs checking
+local mpsliteral = nodes.pool.register(node.new("whatsit",nodes.whatsitcodes.pdfliteral)) -- pdfliteral.mode = 1
+
+local pdfliteral = function(s)
+ local literal = copy_node(mpsliteral)
+ literal.data = s
+ return literal
+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,f)
+ if metapost.optimize then
+ local objects = result.objects
+ if not objects then
+ result.objects = { }
+ end
+ objects = result.objects[f]
+ if not objects then
+ objects = figure:objects()
+ result.objects[f] = objects
+ end
+ return objects
+ else
+ return figure:objects()
+ end
+end
+
+function metapost.convert(result, trialrun, flusher, multipass, askedfig)
+ if trialrun then
+ metapost.multipass = false
+ metapost.parse(result, askedfig)
+ if multipass and not metapost.multipass and metapost.optimize then
+ metapost.flush(result, flusher, askedfig) -- saves a run
+ else
+ return false
+ end
+ else
+ metapost.flush(result, flusher, askedfig)
+ end
+ return true -- done
+end
+
+function metapost.flushliteral(d)
+ if savedliterals then
+ local literal = copy_node(mpsliteral)
+ literal.data = savedliterals[d]
+ write_node(literal)
+ else
+ report_metapost("problem flushing literal %a",d)
+ end
+end
+
+function metapost.flushreset() -- will become obsolete and internal
+ savedliterals = nil
+end
+
+function pdfflusher.comment(message)
+ if message then
+ message = formatters["%% mps graphic %s: %s"](metapost.n,message)
+ if experiment then
+ context(pdfliteral(message))
+ else
+ if savedliterals then
+ local last = #savedliterals + 1
+ savedliterals[last] = message
+ context.MPLIBtoPDF(last)
+ else
+ savedliterals = { message }
+ context.MPLIBtoPDF(1)
+ end
+ end
+ end
+end
+
+function pdfflusher.startfigure(n,llx,lly,urx,ury,message)
+ savedliterals = nil
+ metapost.n = metapost.n + 1
+ context.startMPLIBtoPDF(llx,lly,urx,ury)
+ if message then pdfflusher.comment(message) end
+end
+
+function pdfflusher.stopfigure(message)
+ if message then pdfflusher.comment(message) end
+ context.stopMPLIBtoPDF()
+ context.MPLIBflushreset() -- maybe just at the beginning
+end
+
+function pdfflusher.flushfigure(pdfliterals) -- table
+ if #pdfliterals > 0 then
+ pdfliterals = concat(pdfliterals,"\n")
+ if experiment then
+ context(pdfliteral(pdfliterals))
+ else
+ if savedliterals then
+ local last = #savedliterals + 1
+ savedliterals[last] = pdfliterals
+ context.MPLIBtoPDF(last)
+ else
+ savedliterals = { pdfliterals }
+ context.MPLIBtoPDF(1)
+ end
+ end
+ 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 pen_info = mplib.pen_info
+
+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
+ if t then
+ nt = #t
+ else
+ t = { }
+ nt = 0
+ end
+ for i=1,#path do
+ nt = nt + 1
+ pth = path[i]
+ if not ith then
+ t[nt] = formatters["%f %f m"](pth.x_coord,pth.y_coord)
+ elseif curved(ith,pth) then
+ t[nt] = formatters["%f %f %f %f %f %f c"](ith.right_x,ith.right_y,pth.left_x,pth.left_y,pth.x_coord,pth.y_coord)
+ else
+ t[nt] = formatters["%f %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
+ t[nt] = formatters["%f %f %f %f %f %f c"](pth.right_x,pth.right_y,one.left_x,one.left_y,one.x_coord,one.y_coord )
+ else
+ t[nt] = formatters["%f %f l"](one.x_coord,one.y_coord)
+ end
+ elseif #path == 1 then
+ -- special case .. draw point
+ local one = path[1]
+ nt = nt + 1
+ t[nt] = formatters["%f %f l"](one.x_coord,one.y_coord)
+ end
+ return t
+end
+
+local function flushconcatpath(path, t, open)
+ local pth, ith, nt
+ if t then
+ nt = #t
+ else
+ t = { }
+ nt = 0
+ end
+ nt = nt + 1
+ t[nt] = formatters["%f %f %f %f %f %f cm"](sx,rx,ry,sy,tx,ty)
+ for i=1,#path do
+ nt = nt + 1
+ pth = path[i]
+ if not ith then
+ t[nt] = formatters["%f %f m"](mpconcat(pth.x_coord,pth.y_coord))
+ elseif curved(ith,pth) then
+ local a, b = mpconcat(ith.right_x,ith.right_y)
+ local c, d = mpconcat(pth.left_x,pth.left_y)
+ t[nt] = formatters["%f %f %f %f %f %f c"](a,b,c,d,mpconcat(pth.x_coord,pth.y_coord))
+ else
+ t[nt] = formatters["%f %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
+ local a, b = mpconcat(pth.right_x,pth.right_y)
+ local c, d = mpconcat(one.left_x,one.left_y)
+ t[nt] = formatters["%f %f %f %f %f %f c"](a,b,c,d,mpconcat(one.x_coord, one.y_coord))
+ else
+ t[nt] = formatters["%f %f l"](mpconcat(one.x_coord,one.y_coord))
+ end
+ elseif #path == 1 then
+ -- special case .. draw point
+ nt = nt + 1
+ local one = path[1]
+ t[nt] = formatters["%f %f l"](mpconcat(one.x_coord,one.y_coord))
+ end
+ return t
+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.
+
+local function ignore() end
+
+function metapost.flush(result,flusher,askedfig)
+ if result then
+ 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
+ for f=1, #figures do
+ local figure = figures[f]
+ local objects = getobjects(result,figure,f)
+ local fignum = figure:charcode() or 0
+ if askedfig == "direct" or askedfig == "all" or askedfig == fignum then
+ local t = { }
+ local miterlimit, linecap, linejoin, dashed = -1, -1, -1, false
+ local bbox = figure:boundingbox()
+ local llx, lly, urx, ury = bbox[1], bbox[2], bbox[3], bbox[4]
+ metapost.llx = llx
+ metapost.lly = lly
+ metapost.urx = urx
+ metapost.ury = ury
+ if urx < llx then
+ -- invalid
+ startfigure(fignum,0,0,0,0,"invalid",figure)
+ stopfigure()
+ else
+ startfigure(fignum,llx,lly,urx,ury,"begin",figure)
+ t[#t+1] = "q"
+ if objects then
+ resetplugins(t) -- we should move the colorinitializer here
+ for o=1,#objects do
+ local object = objects[o]
+ local objecttype = object.type
+ if objecttype == "start_bounds" or objecttype == "stop_bounds" or objecttype == "special" then
+ -- skip
+ elseif objecttype == "start_clip" then
+ t[#t+1] = "q"
+ flushnormalpath(object.path,t,false)
+ t[#t+1] = "W n"
+ elseif objecttype == "stop_clip" then
+ t[#t+1] = "Q"
+ miterlimit, linecap, linejoin, dashed = -1, -1, -1, false
+ elseif objecttype == "text" then
+ t[#t+1] = "q"
+ local ot = object.transform -- 3,4,5,6,1,2
+ t[#t+1] = formatters["%f %f %f %f %f %f cm"](ot[3],ot[4],ot[5],ot[6],ot[1],ot[2]) -- TH: formatters["%f %f m %f %f %f %f 0 0 cm"](unpack(ot))
+ flushfigure(t) -- flush accumulated literals
+ t = { }
+ textfigure(object.font,object.dsize,object.text,object.width,object.height,object.depth)
+ t[#t+1] = "Q"
+ else
+ -- 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
+ })
+ -- first we analyze
+ local before, after = processplugins(object)
+ local objecttype = object.type -- can have changed
+ if before then
+ t = pluginactions(before,t,flushfigure)
+ end
+ local ml = object.miterlimit
+ if ml and ml ~= miterlimit then
+ miterlimit = ml
+ t[#t+1] = formatters["%f M"](ml)
+ end
+ local lj = object.linejoin
+ if lj and lj ~= linejoin then
+ linejoin = lj
+ t[#t+1] = formatters["%i j"](lj)
+ end
+ local lc = object.linecap
+ if lc and lc ~= linecap then
+ linecap = lc
+ t[#t+1] = formatters["%i J"](lc)
+ end
+ local dl = object.dash
+ if dl then
+ local d = formatters["[%s] %f d"](concat(dl.dashes or {}," "),dl.offset)
+ if d ~= dashed then
+ dashed = d
+ t[#t+1] = dashed
+ end
+ elseif dashed then
+ t[#t+1] = "[] 0 d"
+ dashed = false
+ end
+ local path = object.path -- newpath
+ local transformed, penwidth = false, 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
+ t[#t+1] = formatters["%f w"](penwidth) -- todo: only if changed
+ if objecttype == 'fill' then
+ objecttype = 'both'
+ end
+ else -- calculated by mplib itself
+ objecttype = 'fill'
+ end
+ end
+ if transformed then
+ t[#t+1] = "q"
+ end
+ if path then
+ if transformed then
+ flushconcatpath(path,t,open)
+ else
+ flushnormalpath(path,t,open)
+ end
+ if objecttype == "fill" then
+ t[#t+1] = "h f"
+ elseif objecttype == "outline" then
+ t[#t+1] = (open and "S") or "h S"
+ elseif objecttype == "both" then
+ t[#t+1] = "h B"
+ end
+ end
+ if transformed then
+ t[#t+1] = "Q"
+ end
+ local path = object.htap
+ if path then
+ if transformed then
+ t[#t+1] = "q"
+ end
+ if transformed then
+ flushconcatpath(path,t,open)
+ else
+ flushnormalpath(path,t,open)
+ end
+ if objecttype == "fill" then
+ t[#t+1] = "h f"
+ elseif objecttype == "outline" then
+ t[#t+1] = (open and "S") or "h S"
+ elseif objecttype == "both" then
+ t[#t+1] = "h B"
+ end
+ if transformed then
+ t[#t+1] = "Q"
+ end
+ end
+ if after then
+ t = pluginactions(after,t,flushfigure)
+ end
+ if object.grouped then
+ -- can be qQ'd so changes can end up in groups
+ miterlimit, linecap, linejoin, dashed = -1, -1, -1, false
+ end
+ end
+ end
+ end
+ t[#t+1] = "Q"
+ flushfigure(t)
+ stopfigure("end")
+ end
+ if askedfig ~= "all" then
+ break
+ end
+ end
+ end
+ end
+ end
+end
+
+function metapost.parse(result,askedfig)
+ if result then
+ local figures = result.fig
+ if figures then
+ local analyzeplugins = metapost.analyzeplugins -- each object
+ for f=1,#figures do
+ local figure = figures[f]
+ local fignum = figure:charcode() or 0
+ if askedfig == "direct" or askedfig == "all" or askedfig == fignum then
+ local bbox = figure:boundingbox()
+ metapost.llx = bbox[1]
+ metapost.lly = bbox[2]
+ metapost.urx = bbox[3]
+ metapost.ury = bbox[4]
+ local objects = getobjects(result,figure,f)
+ if objects then
+ for o=1,#objects do
+ analyzeplugins(objects[o])
+ end
+ end
+ if askedfig ~= "all" then
+ break
+ end
+ end
+ end
+ end
+ end
+end
+
+-- tracing:
+
+local t = { }
+
+local flusher = {
+ startfigure = function()
+ t = { }
+ context.startnointerference()
+ end,
+ flushfigure = function(literals)
+ local n = #t
+ for i=1, #literals do
+ n = n + 1
+ t[n] = literals[i]
+ end
+ end,
+ stopfigure = function()
+ context.stopnointerference()
+ end
+}
+
+function metapost.pdfliterals(result)
+ metapost.flush(result,flusher)
+ return t
+end
+
+-- so far
+
+function metapost.totable(result)
+ local figure = result and result.fig and result.fig[1]
+ if figure then
+ local t = { }
+ local objects = figure:objects()
+ for o=1,#objects do
+ local object = objects[o]
+ local tt = { }
+ local fields = mplib.fields(object)
+ for f=1,#fields do
+ local field = fields[f]
+ tt[field] = object[field]
+ end
+ t[o] = tt
+ end
+ local b = figure:boundingbox()
+ return {
+ boundingbox = { llx = b[1], lly = b[2], urx = b[3], ury = b[4] },
+ objects = t
+ }
+ else
+ return nil
+ end
+end
diff --git a/tex/context/base/mlib-pps.lua b/tex/context/base/mlib-pps.lua
index 217625bcb..93bddc2dd 100644
--- a/tex/context/base/mlib-pps.lua
+++ b/tex/context/base/mlib-pps.lua
@@ -1,1216 +1,1216 @@
-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",
-}
-
--- todo: make a hashed textext variant where we only process the text once (normally
--- we cannot assume that no macros are involved which influence a next textext
-
-local format, gmatch, match, split = string.format, string.gmatch, string.match, string.split
-local tonumber, type = tonumber, type
-local round = math.round
-local insert, concat = table.insert, 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 = lpeg.match
-local formatters = string.formatters
-
-local mplib, metapost, lpdf, context = mplib, metapost, lpdf, context
-
-local texbox = tex.box
-local copy_list = node.copy_list
-local free_list = node.flush_list
-local setmetatableindex = table.setmetatableindex
-local sortedhash = table.sortedhash
-
-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 report_metapost = logs.reporter("metapost")
-local report_textexts = logs.reporter("metapost","textexts")
-local report_scripts = logs.reporter("metapost","scripts")
-
-local colors = attributes.colors
-
-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
-
-metapost.makempy = metapost.makempy or { nofconverted = 0 }
-local makempy = metapost.makempy
-
-local nooutercolor = "0 g 0 G"
-local nooutertransparency = "/Tr0 gs" -- only when set
-local outercolormode = 0
-local outercolor = nooutercolor
-local outertransparency = nooutertransparency
-local innercolor = nooutercolor
-local innertransparency = nooutertransparency
-
-local pdfcolor = lpdf.color
-local pdftransparency = lpdf.transparency
-local registercolor = colors.register
-local registerspotcolor = colors.registerspotcolor
-
-local transparencies = attributes.transparencies
-local registertransparency = transparencies.register
-
-function metapost.setoutercolor(mode,colormodel,colorattribute,transparencyattribute)
- -- has always to be called before conversion
- -- todo: transparency (not in the mood now)
- outercolormode = mode
- 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
-
-local f_gray = formatters["%.3f g %.3f G"]
-local f_rgb = formatters["%.3f %.3f %.3f rg %.3f %.3f %.3f RG"]
-local f_cmyk = formatters["%.3f %.3f %.3f %.3f k %.3f %.3f %.3f %.3f K"]
-local f_cm = formatters["q %f %f %f %f %f %f cm"]
-local f_shade = formatters["MpSh%s"]
-
-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 = lpeg.tsplitat(" ")
-local colorsplitter = lpeg.tsplitter(":",tonumber) -- no need for :
-local domainsplitter = lpeg.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
-
--- todo: check for the same colorspace (actually a backend issue), now we can
--- have several similar resources
---
--- normalize(ca,cb) fails for spotcolors
-
-local function spotcolorconverter(parent, n, d, p)
- registerspotcolor(parent)
- return pdfcolor(colors.model,registercolor(nil,'spot',parent,n,d,p)), outercolor
-end
-
-local commasplitter = lpeg.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)
- 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
- local model = colors.model
- 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, b = 1-ca[1], 1-cb[1]
- ca = { a, a, a }
- cb = { b, b, b }
- end
- return ca, cb, "DeviceRGB", name
- 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
- 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
- end
- end
-end
-
-local current_format, current_graphic, current_initializations
-
-metapost.multipass = false
-
-local textexts = { } -- all boxes, optionally with a different color
-local texslots = { } -- references to textexts in order or usage
-local texorder = { } -- references to textexts by mp index
-local textrial = 0
-local texfinal = 0
-local scratchbox = 0
-
-local function freeboxes()
- for n, box in next, textexts do
- local tn = textexts[n]
- if tn then
- free_list(tn)
- -- texbox[scratchbox] = tn
- -- texbox[scratchbox] = nil -- this frees too
- if trace_textexts then
- report_textexts("freeing box %s",n)
- end
- end
- end
- textexts = { }
- texslots = { }
- texorder = { }
- textrial = 0
- texfinal = 0
-end
-
-metapost.resettextexts = freeboxes
-
-function metapost.settext(box,slot)
- textexts[slot] = copy_list(texbox[box])
- texbox[box] = nil
- -- this will become
- -- textexts[slot] = texbox[box]
- -- unsetbox(box)
-end
-
-function metapost.gettext(box,slot)
- texbox[box] = copy_list(textexts[slot])
- if trace_textexts then
- report_textexts("putting text %s in box %s",slot,box)
- end
- -- textexts[slot] = nil -- no, pictures can be placed several times
-end
-
--- 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, g, b = cr[1], cr[2], 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, m, y, k = cr[1], cr[2], cr[3], 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, g, b = cr[1], cr[2], cr[3]
- return checked_color_pair(f_rgb,r,g,b,r,g,b)
- else
- local c, m, y, k = cr[1], cr[2], cr[3], 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, g, b = cr[1], cr[2], 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, m, y, k = cr[1], cr[2], cr[3], 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, g, b
- if n == 3 then
- r, g, b = cmyktorgb(cr[1],cr[2],cr[3],cr[4])
- else
- r, g, b = cr[1], cr[2], cr[3]
- 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, g, b = cr[1], cr[2], 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, m, y, k = cr[1], cr[2], cr[3], cr[4]
- if c == m and m == y and y == 0 then
- k = k - 1
- 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, m, y, k
- if n == 3 then
- c, m, y, k = rgbtocmyk(cr[1],cr[2],cr[3])
- else
- c, m, y, k = cr[1], cr[2], cr[3], cr[4]
- end
- return checked_color_pair(f_cmyk,c,m,y,k,c,m,y,k)
- end
-end
-
-function models.gray(cr)
- local n, s = #cr, 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
-
-setmetatableindex(models, function(t,k)
- local v = models.gray
- t[k] = v
- return v
-end)
-
-local function colorconverter(cs)
- return models[colors.model](cs)
-end
-
-local btex = P("btex")
-local etex = P(" etex")
-local vtex = P("verbatimtex")
-local ttex = P("textext")
-local gtex = P("graphictext")
-local multipass = P("forcemultipass")
-local spacing = S(" \n\r\t\v")^0
-local dquote = P('"')
-
-local found, forced = false, false
-
-local function convert(str)
- found = true
- return "rawtextext(\"" .. str .. "\")" -- centered
-end
-local function ditto(str)
- return "\" & ditto & \""
-end
-local function register()
- found = true
-end
-local function force()
- forced = true
-end
-
-local texmess = (dquote/ditto + (1 - etex))^0
-
-local function ignore(s)
- report_metapost("ignoring verbatim tex: %s",s)
- return ""
-end
-
--- local parser = P {
--- [1] = Cs((V(2)/register + V(4)/ignore + V(3)/convert + V(5)/force + 1)^0),
--- [2] = ttex + gtex,
--- [3] = btex * spacing * Cs(texmess) * etex,
--- [4] = vtex * spacing * Cs(texmess) * etex,
--- [5] = multipass, -- experimental, only for testing
--- }
-
--- currently a a one-liner produces less code
-
--- textext.*(".*") can have "'s but tricky parsing as we can have concatenated strings
--- so this is something for a boring plain or train trip and we might assume proper mp
--- input anyway
-
-local parser = Cs((
- (ttex + gtex)/register
- + (btex * spacing * Cs(texmess) * etex)/convert
- + (vtex * spacing * Cs(texmess) * etex)/ignore
- + 1
-)^0)
-
-local function checktexts(str)
- found, forced = false, false
- return lpegmatch(parser,str), found, forced
-end
-
-metapost.checktexts = checktexts
-
-local factor = 65536*(7227/7200)
-
-function metapost.edefsxsy(wd,ht,dp) -- helper for figure
- local hd = ht + dp
- context.setvalue("sx",wd ~= 0 and factor/wd or 0)
- context.setvalue("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
-
-local no_first_run = "mfun_first_run := false ;"
-local do_first_run = "mfun_first_run := true ;"
-local no_trial_run = "mfun_trial_run := false ;"
-local do_trial_run = "mfun_trial_run := true ;"
-local do_begin_fig = "; beginfig(1) ; "
-local do_end_fig = "; endfig ;"
-local do_safeguard = ";"
-
-local f_text_data = formatters["mfun_tt_w[%i] := %f ; mfun_tt_h[%i] := %f ; mfun_tt_d[%i] := %f ;"]
-
-function metapost.textextsdata()
- local t, nt, n = { }, 0, 0
- for n=1,#texorder do
- local box = textexts[texorder[n]]
- if box then
- local wd, ht, dp = box.width/factor, box.height/factor, box.depth/factor
- if trace_textexts then
- report_textexts("passed data item %s: (%p,%p,%p)",n,wd,ht,dp)
- end
- nt = nt + 1
- t[nt] = f_text_data(n,wd,n,ht,n,dp)
- else
- break
- end
- end
--- inspect(t)
- return t
-end
-
-metapost.intermediate = metapost.intermediate or {}
-metapost.intermediate.actions = metapost.intermediate.actions or {}
-metapost.intermediate.needed = false
-
-metapost.method = 1 -- 1:dumb 2:clever
-
--- maybe we can latelua the texts some day
-
-local nofruns = 0 -- askedfig: "all", "first", number
-
-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
-
-function metapost.graphic_base_pass(specification)
- local mpx = specification.mpx -- mandate
- local data = specification.data or ""
- local definitions = specification.definitions or ""
--- local extensions = metapost.getextensions(specification.instance,specification.useextensions)
- local extensions = specification.extensions or ""
- local inclusions = specification.inclusions or ""
- local initializations = specification.initializations or ""
- local askedfig = specification.figure -- no default else no wrapper
- --
- nofruns = nofruns + 1
- local askedfig, wrappit = checkaskedfig(askedfig)
- local done_1, done_2, done_3, forced_1, forced_2, forced_3
- data, done_1, forced_1 = checktexts(data)
- -- we had preamble = extensions + inclusions
- if extensions == "" then
- extensions, done_2, forced_2 = "", false, false
- else
- extensions, done_2, forced_2 = checktexts(extensions)
- end
- if inclusions == "" then
- inclusions, done_3, forced_3 = "", false, false
- else
- inclusions, done_3, forced_3 = checktexts(inclusions)
- end
- metapost.intermediate.needed = false
- metapost.multipass = false -- no needed here
- current_format = mpx
- current_graphic = data
- current_initializations = initializations
- local method = metapost.method
- if trace_runs then
- if method == 1 then
- report_metapost("forcing two runs due to library configuration")
- elseif method ~= 2 then
- report_metapost("ignoring run due to library configuration")
- elseif not (done_1 or done_2 or done_3) then
- report_metapost("forcing one run only due to analysis")
- elseif done_1 then
- report_metapost("forcing at max two runs due to main code")
- elseif done_2 then
- report_metapost("forcing at max two runs due to extensions")
- else
- report_metapost("forcing at max two runs due to inclusions")
- end
- end
- if method == 1 or (method == 2 and (done_1 or done_2 or done_3)) then
- if trace_runs then
- report_metapost("first run of job %s, asked figure %a",nofruns,askedfig)
- end
- -- first true means: trialrun, second true means: avoid extra run if no multipass
- local flushed = metapost.process(mpx, {
- definitions,
- extensions,
- inclusions,
- wrappit and do_begin_fig or "",
- do_first_run,
- do_trial_run,
- current_initializations,
- do_safeguard,
- current_graphic,
- wrappit and do_end_fig or "",
- }, true, nil, not (forced_1 or forced_2 or forced_3), false, askedfig)
- if metapost.intermediate.needed then
- for _, action in next, metapost.intermediate.actions do
- action()
- end
- end
- if not flushed or not metapost.optimize then
- -- tricky, we can only ask once for objects and therefore
- -- we really need a second run when not optimized
- context.MPLIBextrapass(askedfig)
- end
- else
- if trace_runs then
- report_metapost("running job %s, asked figure %a",nofruns,askedfig)
- end
- metapost.process(mpx, {
- preamble,
- wrappit and do_begin_fig or "",
- do_first_run,
- no_trial_run,
- current_initializations,
- do_safeguard,
- current_graphic,
- wrappit and do_end_fig or "",
- }, false, nil, false, false, askedfig)
- end
-end
-
-function metapost.graphic_extra_pass(askedfig)
- if trace_runs then
- report_metapost("second run of job %s, asked figure %a",nofruns,askedfig)
- end
- local askedfig, wrappit = checkaskedfig(askedfig)
- metapost.process(current_format, {
- wrappit and do_begin_fig or "",
- no_trial_run,
- concat(metapost.textextsdata()," ;\n"),
- current_initializations,
- do_safeguard,
- current_graphic,
- wrappit and do_end_fig or "",
- }, false, nil, false, true, askedfig)
- context.MPLIBresettexts() -- must happen afterwards
-end
-
-local start = [[\starttext]]
-local preamble = [[\long\def\MPLIBgraphictext#1{\startTEXpage[scale=10000]#1\stopTEXpage}]]
-local stop = [[\stoptext]]
-
-function makempy.processgraphics(graphics)
- if #graphics > 0 then
- makempy.nofconverted = makempy.nofconverted + 1
- starttiming(makempy)
- local mpofile = tex.jobname .. "-mpgraph"
- local mpyfile = file.replacesuffix(mpofile,"mpy")
- local pdffile = file.replacesuffix(mpofile,"pdf")
- local texfile = file.replacesuffix(mpofile,"tex")
- io.savedata(texfile, { start, preamble, metapost.tex.get(), concat(graphics,"\n"), stop }, "\n")
- local command = format("context --once %s %s", (tex.interactionmode == 0 and "--batchmode") or "", texfile)
- os.execute(command)
- if io.exists(pdffile) then
- command = format("pstoedit -ssp -dt -f mpost %s %s", pdffile, mpyfile)
- os.execute(command)
- local result, r = { }, 0
- if io.exists(mpyfile) then
- local data = io.loaddata(mpyfile)
- for figure in gmatch(data,"beginfig(.-)endfig") do
- r = r + 1
- result[r] = formatters["begingraphictextfig%sendgraphictextfig ;\n"](figure)
- end
- io.savedata(mpyfile,concat(result,""))
- end
- end
- stoptiming(makempy)
- end
-end
-
--- -- the new plugin handler -- --
-
-local sequencers = utilities.sequencers
-local appendgroup = sequencers.appendgroup
-local appendaction = sequencers.appendaction
-
-local resetter = nil
-local analyzer = nil
-local processor = nil
-
-local resetteractions = sequencers.new { arguments = "t" }
-local analyzeractions = sequencers.new { arguments = "object,prescript" }
-local processoractions = sequencers.new { arguments = "object,prescript,before,after" }
-
-appendgroup(resetteractions, "system")
-appendgroup(analyzeractions, "system")
-appendgroup(processoractions, "system")
-
--- later entries come first
-
---~ local scriptsplitter = Cf(Ct("") * (
---~ Cg(C((1-S("= "))^1) * S("= ")^1 * C((1-S("\n\r"))^0) * S("\n\r")^0)
---~ )^0, rawset)
-
-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]
- hash[h[1]] = h[2]
- end
- if trace_scripts then
- report_scripts(table.serialize(hash,"prescript"))
- end
- return hash
-end
-
--- -- 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
- 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
-
-function metapost.resetplugins(t) -- intialize plugins, before figure
- -- plugins can have been added
- resetter = resetteractions .runner
- analyzer = analyzeractions .runner
- processor = processoractions .runner
- -- let's apply one runner
- resetter(t)
-end
-
-function metapost.analyzeplugins(object) -- each object (first pass)
- local prescript = object.prescript -- specifications
- if prescript and #prescript > 0 then
- return analyzer(object,splitprescript(prescript))
- end
-end
-
-function metapost.processplugins(object) -- each object (second pass)
- local prescript = object.prescript -- specifications
- if prescript and #prescript > 0 then
- local before = { }
- local after = { }
- processor(object,splitprescript(prescript),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
-
--- helpers
-
-local basepoints = number.dimenfactors["bp"]
-
-local function cm(object)
- local op = object.path
- if op then
- local first, second, fourth = op[1], op[2], op[4]
- local tx, ty = first.x_coord , first.y_coord
- local sx, sy = second.x_coord - tx, fourth.y_coord - ty
- local rx, ry = second.y_coord - ty, 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
- else
- return 1, 0, 0, 1, 0, 0 -- weird case
- end
-end
-
--- color
-
-local function cl_reset(t)
- t[#t+1] = metapost.colorinitializer() -- only color
-end
-
-local tx_hash = { }
-local tx_last = 0
-
-local function tx_reset()
- tx_hash = { }
- tx_last = 0
-end
-
-local fmt = formatters["%s %s %s % t"]
-
-local function tx_analyze(object,prescript) -- todo: hash content and reuse them
- local tx_stage = prescript.tx_stage
- if tx_stage == "trial" then
- textrial = textrial + 1
- local tx_number = tonumber(prescript.tx_number)
- local s = object.postscript or ""
- local c = object.color -- only simple ones, no transparency
- local a = prescript.tr_alternative
- local t = prescript.tr_transparency
- local h = fmt(tx_number,a or "?",t or "?",c)
- local n = tx_hash[h] -- todo: hashed variant with s (nicer for similar labels)
- if not n then
- tx_last = tx_last + 1
- if not c then
- -- no color
- elseif #c == 1 then
- if a and t then
- s = formatters["\\directcolored[s=%f,a=%f,t=%f]%s"](c[1],a,t,s)
- else
- s = formatters["\\directcolored[s=%f]%s"](c[1],s)
- end
- elseif #c == 3 then
- if a and t then
- s = formatters["\\directcolored[r=%f,g=%f,b=%f,a=%f,t=%f]%s"](c[1],c[2],c[3],a,t,s)
- else
- s = formatters["\\directcolored[r=%f,g=%f,b=%f]%s"](c[1],c[2],c[3],s)
- end
- elseif #c == 4 then
- if a and t then
- s = formatters["\\directcolored[c=%f,m=%f,y=%f,k=%f,a=%f,t=%f]%s"](c[1],c[2],c[3],c[4],a,t,s)
- else
- s = formatters["\\directcolored[c=%f,m=%f,y=%f,k=%f]%s"](c[1],c[2],c[3],c[4],s)
- end
- end
- context.MPLIBsettext(tx_last,s)
- metapost.multipass = true
- tx_hash[h] = tx_last
- texslots[textrial] = tx_last
- texorder[tx_number] = tx_last
- if trace_textexts then
- report_textexts("stage %a, usage %a, number %a, new %a, hash %a",tx_stage,textrial,tx_number,tx_last,h)
- end
- else
- texslots[textrial] = n
- if trace_textexts then
- report_textexts("stage %a, usage %a, number %a, new %a, hash %a",tx_stage,textrial,tx_number,n,h)
- end
- end
- elseif tx_stage == "extra" then
- textrial = textrial + 1
- local tx_number = tonumber(prescript.tx_number)
- if not texorder[tx_number] then
- local s = object.postscript or ""
- tx_last = tx_last + 1
- context.MPLIBsettext(tx_last,s)
- metapost.multipass = true
- texslots[textrial] = tx_last
- texorder[tx_number] = tx_last
- if trace_textexts then
- report_textexts("stage %a, usage %a, number %a, extra %a",tx_stage,textrial,tx_number,tx_last)
- end
- end
- end
-end
-
-local function tx_process(object,prescript,before,after)
- local tx_number = prescript.tx_number
- if tx_number then
- tx_number = tonumber(tx_number)
- local tx_stage = prescript.tx_stage
- if tx_stage == "final" then
- texfinal = texfinal + 1
- local n = texslots[texfinal]
- if trace_textexts then
- report_textexts("stage %a, usage %a, number %a, use %a",tx_stage,texfinal,tx_number,n)
- end
- local sx, rx, ry, sy, tx, ty = cm(object) -- needs to be frozen outside the function
- local box = textexts[n]
- if box then
- before[#before+1] = function()
- -- flush always happens, we can have a special flush function injected before
- context.MPLIBgettextscaledcm(n,
- format("%f",sx), -- bah ... %s no longer checks
- format("%f",rx), -- bah ... %s no longer checks
- format("%f",ry), -- bah ... %s no longer checks
- format("%f",sy), -- bah ... %s no longer checks
- format("%f",tx), -- bah ... %s no longer checks
- format("%f",ty), -- bah ... %s no longer checks
- sxsy(box.width,box.height,box.depth))
- end
- else
- before[#before+1] = function()
- report_textexts("unknown %s",tx_number)
- end
- end
- if not trace_textexts then
- object.path = false -- else: keep it
- end
- object.color = false
- object.grouped = true
- end
- end
-end
-
--- graphics
-
-local graphics = { }
-
-function metapost.intermediate.actions.makempy()
- if #graphics > 0 then
- makempy.processgraphics(graphics)
- graphics = { } -- ?
- end
-end
-
-local function gt_analyze(object,prescript)
- local gt_stage = prescript.gt_stage
- if gt_stage == "trial" then
- graphics[#graphics+1] = formatters["\\MPLIBgraphictext{%s}"](object.postscript or "")
- metapost.intermediate.needed = true
- metapost.multipass = true
- end
-end
-
--- local function gt_process(object,prescript,before,after)
--- local gt_stage = prescript.gt_stage
--- if gt_stage == "final" then
--- 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)
- local centera = lpegmatch(centersplitter,prescript.sh_center_a)
- local centerb = lpegmatch(centersplitter,prescript.sh_center_b)
- --
- local sh_color_a = prescript.sh_color_a or "1"
- local sh_color_b = prescript.sh_color_b or "1"
- local ca, cb, colorspace, name, separation
- 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 = checkandconvert(colora,colorb)
- end
- if not ca or not cb then
- ca, cb, colorspace, name = checkandconvert()
- end
- if sh_type == "linear" then
- local coordinates = { centera[1], centera[2], centerb[1], centerb[2] }
- lpdf.linearshade(name,domain,ca,cb,1,colorspace,coordinates,separation) -- backend specific (will be renamed)
- elseif sh_type == "circular" then
- local radiusa = tonumber(prescript.sh_radius_a)
- local radiusb = tonumber(prescript.sh_radius_b)
- local coordinates = { centera[1], centera[2], radiusa, centerb[1], centerb[2], radiusb }
- lpdf.circularshade(name,domain,ca,cb,1,colorspace,coordinates,separation) -- backend specific (will be renamed)
- else
- -- fatal error
- end
- before[#before+1], after[#after+1] = "q /Pattern cs", 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(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] = "Q"
- 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, third = op[1], op[3]
- local x, y = first.x_coord, first.y_coord
- local w, h = third.x_coord - x, third.y_coord - y
- x = x - metapost.llx
- y = metapost.ury - y
- before[#before+1] = function()
- context.MPLIBpositionwhd(ps_label,x,y,w,h)
- end
- object.path = false
- end
-end
-
--- figures
-
-local function fg_process(object,prescript,before,after)
- local fg_name = prescript.fg_name
- if fg_name then
- before[#before+1] = f_cm(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] = "Q"
- object.path = false
- object.grouped = true
- end
-end
-
--- color and transparency
-
-local value = Cs ( (
- (Carg(1) * C((1-P(","))^1)) / function(a,b) return format("%0.3f",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 function tr_process(object,prescript,before,after)
- -- before can be shortcut to t
- local tr_alternative = prescript.tr_alternative
- if tr_alternative then
- tr_alternative = tonumber(tr_alternative)
- local tr_transparency = tonumber(prescript.tr_transparency)
- before[#before+1] = formatters["/Tr%s gs"](registertransparency(nil,tr_alternative,tr_transparency,true))
- after[#after+1] = "/Tr0 gs" -- outertransparency
- 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)
- elseif sp_type == "spot" or sp_type == "multitone" then
- local sp_name = prescript.sp_name or "black"
- local sp_fractions = prescript.sp_fractions or 1
- local sp_components = prescript.sp_components or ""
- local sp_value = prescript.sp_value or "1"
- local cf = cs[1]
- if cf ~= 1 then
- -- beware, we do scale the spotcolors but not the alternative representation
- sp_value = lpegmatch(value,sp_value,1,cf) or sp_value
- end
- c_b, c_a = spotcolorconverter(sp_name,sp_fractions,sp_components,sp_value)
- elseif 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)
- local sp_name = prescript.sp_name or "black"
- 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 attributes.transparencies.value(t)
- if v then
- before[#before+1] = formatters["/Tr%s gs"](registertransparency(nil,v[1],v[2],true))
- after[#after+1] = "/Tr0 gs" -- outertransparency
- end
- end
- local c = c_list[sp_name] -- string or attribute
- local v = c and attributes.colors.value(c)
- if v then
- -- all=1 gray=2 rgb=3 cmyk=4
- local colorspace = v[1]
- local f = cs[1]
- if colorspace == 2 then
- local s = f*v[2]
- c_b, c_a = checked_color_pair(f_gray,s,s)
- elseif colorspace == 3 then
- local r, g, b = f*v[3], f*v[4], f*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, m, y, k = f*v[6], f*v[7], f*v[8], f*v[9]
- c_b, c_a = checked_color_pair(f_cmyk,c,m,y,k,c,m,y,k)
- else
- local s = f*v[2]
- c_b, c_a = checked_color_pair(f_gray,s,s)
- 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 types = {
- isolated
-}
-
-local function gr_process(object,prescript,before,after)
- local gr_state = prescript.gr_state
- if gr_state then
- if gr_state == "start" then
- local gr_type = utilities.parsers.settings_to_hash(prescript.gr_type)
- before[#before+1] = function()
- context.MPLIBstartgroup(
- gr_type.isolated and 1 or 0,
- gr_type.knockout and 1 or 0,
- prescript.gr_llx,
- prescript.gr_lly,
- prescript.gr_urx,
- prescript.gr_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
-end
-
--- definitions
-
-appendaction(resetteractions, "system",cl_reset)
-appendaction(resetteractions, "system",tx_reset)
-
-appendaction(processoractions,"system",gr_process)
-
-appendaction(analyzeractions, "system",tx_analyze)
-appendaction(analyzeractions, "system",gt_analyze)
-
-appendaction(processoractions,"system",sh_process)
--- (processoractions,"system",gt_process)
-appendaction(processoractions,"system",bm_process)
-appendaction(processoractions,"system",tx_process)
-appendaction(processoractions,"system",ps_process)
-appendaction(processoractions,"system",fg_process)
-appendaction(processoractions,"system",tr_process) -- last, as color can be reset
-
-appendaction(processoractions,"system",la_process)
-
--- we're nice and set them already
-
-resetter = resetteractions .runner
-analyzer = analyzeractions .runner
-processor = processoractions.runner
+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",
+}
+
+-- todo: make a hashed textext variant where we only process the text once (normally
+-- we cannot assume that no macros are involved which influence a next textext
+
+local format, gmatch, match, split = string.format, string.gmatch, string.match, string.split
+local tonumber, type = tonumber, type
+local round = math.round
+local insert, concat = table.insert, 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 = lpeg.match
+local formatters = string.formatters
+
+local mplib, metapost, lpdf, context = mplib, metapost, lpdf, context
+
+local texbox = tex.box
+local copy_list = node.copy_list
+local free_list = node.flush_list
+local setmetatableindex = table.setmetatableindex
+local sortedhash = table.sortedhash
+
+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 report_metapost = logs.reporter("metapost")
+local report_textexts = logs.reporter("metapost","textexts")
+local report_scripts = logs.reporter("metapost","scripts")
+
+local colors = attributes.colors
+
+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
+
+metapost.makempy = metapost.makempy or { nofconverted = 0 }
+local makempy = metapost.makempy
+
+local nooutercolor = "0 g 0 G"
+local nooutertransparency = "/Tr0 gs" -- only when set
+local outercolormode = 0
+local outercolor = nooutercolor
+local outertransparency = nooutertransparency
+local innercolor = nooutercolor
+local innertransparency = nooutertransparency
+
+local pdfcolor = lpdf.color
+local pdftransparency = lpdf.transparency
+local registercolor = colors.register
+local registerspotcolor = colors.registerspotcolor
+
+local transparencies = attributes.transparencies
+local registertransparency = transparencies.register
+
+function metapost.setoutercolor(mode,colormodel,colorattribute,transparencyattribute)
+ -- has always to be called before conversion
+ -- todo: transparency (not in the mood now)
+ outercolormode = mode
+ 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
+
+local f_gray = formatters["%.3f g %.3f G"]
+local f_rgb = formatters["%.3f %.3f %.3f rg %.3f %.3f %.3f RG"]
+local f_cmyk = formatters["%.3f %.3f %.3f %.3f k %.3f %.3f %.3f %.3f K"]
+local f_cm = formatters["q %f %f %f %f %f %f cm"]
+local f_shade = formatters["MpSh%s"]
+
+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 = lpeg.tsplitat(" ")
+local colorsplitter = lpeg.tsplitter(":",tonumber) -- no need for :
+local domainsplitter = lpeg.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
+
+-- todo: check for the same colorspace (actually a backend issue), now we can
+-- have several similar resources
+--
+-- normalize(ca,cb) fails for spotcolors
+
+local function spotcolorconverter(parent, n, d, p)
+ registerspotcolor(parent)
+ return pdfcolor(colors.model,registercolor(nil,'spot',parent,n,d,p)), outercolor
+end
+
+local commasplitter = lpeg.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)
+ 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
+ local model = colors.model
+ 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, b = 1-ca[1], 1-cb[1]
+ ca = { a, a, a }
+ cb = { b, b, b }
+ end
+ return ca, cb, "DeviceRGB", name
+ 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
+ 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
+ end
+ end
+end
+
+local current_format, current_graphic, current_initializations
+
+metapost.multipass = false
+
+local textexts = { } -- all boxes, optionally with a different color
+local texslots = { } -- references to textexts in order or usage
+local texorder = { } -- references to textexts by mp index
+local textrial = 0
+local texfinal = 0
+local scratchbox = 0
+
+local function freeboxes()
+ for n, box in next, textexts do
+ local tn = textexts[n]
+ if tn then
+ free_list(tn)
+ -- texbox[scratchbox] = tn
+ -- texbox[scratchbox] = nil -- this frees too
+ if trace_textexts then
+ report_textexts("freeing box %s",n)
+ end
+ end
+ end
+ textexts = { }
+ texslots = { }
+ texorder = { }
+ textrial = 0
+ texfinal = 0
+end
+
+metapost.resettextexts = freeboxes
+
+function metapost.settext(box,slot)
+ textexts[slot] = copy_list(texbox[box])
+ texbox[box] = nil
+ -- this will become
+ -- textexts[slot] = texbox[box]
+ -- unsetbox(box)
+end
+
+function metapost.gettext(box,slot)
+ texbox[box] = copy_list(textexts[slot])
+ if trace_textexts then
+ report_textexts("putting text %s in box %s",slot,box)
+ end
+ -- textexts[slot] = nil -- no, pictures can be placed several times
+end
+
+-- 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, g, b = cr[1], cr[2], 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, m, y, k = cr[1], cr[2], cr[3], 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, g, b = cr[1], cr[2], cr[3]
+ return checked_color_pair(f_rgb,r,g,b,r,g,b)
+ else
+ local c, m, y, k = cr[1], cr[2], cr[3], 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, g, b = cr[1], cr[2], 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, m, y, k = cr[1], cr[2], cr[3], 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, g, b
+ if n == 3 then
+ r, g, b = cmyktorgb(cr[1],cr[2],cr[3],cr[4])
+ else
+ r, g, b = cr[1], cr[2], cr[3]
+ 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, g, b = cr[1], cr[2], 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, m, y, k = cr[1], cr[2], cr[3], cr[4]
+ if c == m and m == y and y == 0 then
+ k = k - 1
+ 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, m, y, k
+ if n == 3 then
+ c, m, y, k = rgbtocmyk(cr[1],cr[2],cr[3])
+ else
+ c, m, y, k = cr[1], cr[2], cr[3], cr[4]
+ end
+ return checked_color_pair(f_cmyk,c,m,y,k,c,m,y,k)
+ end
+end
+
+function models.gray(cr)
+ local n, s = #cr, 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
+
+setmetatableindex(models, function(t,k)
+ local v = models.gray
+ t[k] = v
+ return v
+end)
+
+local function colorconverter(cs)
+ return models[colors.model](cs)
+end
+
+local btex = P("btex")
+local etex = P(" etex")
+local vtex = P("verbatimtex")
+local ttex = P("textext")
+local gtex = P("graphictext")
+local multipass = P("forcemultipass")
+local spacing = S(" \n\r\t\v")^0
+local dquote = P('"')
+
+local found, forced = false, false
+
+local function convert(str)
+ found = true
+ return "rawtextext(\"" .. str .. "\")" -- centered
+end
+local function ditto(str)
+ return "\" & ditto & \""
+end
+local function register()
+ found = true
+end
+local function force()
+ forced = true
+end
+
+local texmess = (dquote/ditto + (1 - etex))^0
+
+local function ignore(s)
+ report_metapost("ignoring verbatim tex: %s",s)
+ return ""
+end
+
+-- local parser = P {
+-- [1] = Cs((V(2)/register + V(4)/ignore + V(3)/convert + V(5)/force + 1)^0),
+-- [2] = ttex + gtex,
+-- [3] = btex * spacing * Cs(texmess) * etex,
+-- [4] = vtex * spacing * Cs(texmess) * etex,
+-- [5] = multipass, -- experimental, only for testing
+-- }
+
+-- currently a a one-liner produces less code
+
+-- textext.*(".*") can have "'s but tricky parsing as we can have concatenated strings
+-- so this is something for a boring plain or train trip and we might assume proper mp
+-- input anyway
+
+local parser = Cs((
+ (ttex + gtex)/register
+ + (btex * spacing * Cs(texmess) * etex)/convert
+ + (vtex * spacing * Cs(texmess) * etex)/ignore
+ + 1
+)^0)
+
+local function checktexts(str)
+ found, forced = false, false
+ return lpegmatch(parser,str), found, forced
+end
+
+metapost.checktexts = checktexts
+
+local factor = 65536*(7227/7200)
+
+function metapost.edefsxsy(wd,ht,dp) -- helper for figure
+ local hd = ht + dp
+ context.setvalue("sx",wd ~= 0 and factor/wd or 0)
+ context.setvalue("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
+
+local no_first_run = "mfun_first_run := false ;"
+local do_first_run = "mfun_first_run := true ;"
+local no_trial_run = "mfun_trial_run := false ;"
+local do_trial_run = "mfun_trial_run := true ;"
+local do_begin_fig = "; beginfig(1) ; "
+local do_end_fig = "; endfig ;"
+local do_safeguard = ";"
+
+local f_text_data = formatters["mfun_tt_w[%i] := %f ; mfun_tt_h[%i] := %f ; mfun_tt_d[%i] := %f ;"]
+
+function metapost.textextsdata()
+ local t, nt, n = { }, 0, 0
+ for n=1,#texorder do
+ local box = textexts[texorder[n]]
+ if box then
+ local wd, ht, dp = box.width/factor, box.height/factor, box.depth/factor
+ if trace_textexts then
+ report_textexts("passed data item %s: (%p,%p,%p)",n,wd,ht,dp)
+ end
+ nt = nt + 1
+ t[nt] = f_text_data(n,wd,n,ht,n,dp)
+ else
+ break
+ end
+ end
+-- inspect(t)
+ return t
+end
+
+metapost.intermediate = metapost.intermediate or {}
+metapost.intermediate.actions = metapost.intermediate.actions or {}
+metapost.intermediate.needed = false
+
+metapost.method = 1 -- 1:dumb 2:clever
+
+-- maybe we can latelua the texts some day
+
+local nofruns = 0 -- askedfig: "all", "first", number
+
+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
+
+function metapost.graphic_base_pass(specification)
+ local mpx = specification.mpx -- mandate
+ local data = specification.data or ""
+ local definitions = specification.definitions or ""
+-- local extensions = metapost.getextensions(specification.instance,specification.useextensions)
+ local extensions = specification.extensions or ""
+ local inclusions = specification.inclusions or ""
+ local initializations = specification.initializations or ""
+ local askedfig = specification.figure -- no default else no wrapper
+ --
+ nofruns = nofruns + 1
+ local askedfig, wrappit = checkaskedfig(askedfig)
+ local done_1, done_2, done_3, forced_1, forced_2, forced_3
+ data, done_1, forced_1 = checktexts(data)
+ -- we had preamble = extensions + inclusions
+ if extensions == "" then
+ extensions, done_2, forced_2 = "", false, false
+ else
+ extensions, done_2, forced_2 = checktexts(extensions)
+ end
+ if inclusions == "" then
+ inclusions, done_3, forced_3 = "", false, false
+ else
+ inclusions, done_3, forced_3 = checktexts(inclusions)
+ end
+ metapost.intermediate.needed = false
+ metapost.multipass = false -- no needed here
+ current_format = mpx
+ current_graphic = data
+ current_initializations = initializations
+ local method = metapost.method
+ if trace_runs then
+ if method == 1 then
+ report_metapost("forcing two runs due to library configuration")
+ elseif method ~= 2 then
+ report_metapost("ignoring run due to library configuration")
+ elseif not (done_1 or done_2 or done_3) then
+ report_metapost("forcing one run only due to analysis")
+ elseif done_1 then
+ report_metapost("forcing at max two runs due to main code")
+ elseif done_2 then
+ report_metapost("forcing at max two runs due to extensions")
+ else
+ report_metapost("forcing at max two runs due to inclusions")
+ end
+ end
+ if method == 1 or (method == 2 and (done_1 or done_2 or done_3)) then
+ if trace_runs then
+ report_metapost("first run of job %s, asked figure %a",nofruns,askedfig)
+ end
+ -- first true means: trialrun, second true means: avoid extra run if no multipass
+ local flushed = metapost.process(mpx, {
+ definitions,
+ extensions,
+ inclusions,
+ wrappit and do_begin_fig or "",
+ do_first_run,
+ do_trial_run,
+ current_initializations,
+ do_safeguard,
+ current_graphic,
+ wrappit and do_end_fig or "",
+ }, true, nil, not (forced_1 or forced_2 or forced_3), false, askedfig)
+ if metapost.intermediate.needed then
+ for _, action in next, metapost.intermediate.actions do
+ action()
+ end
+ end
+ if not flushed or not metapost.optimize then
+ -- tricky, we can only ask once for objects and therefore
+ -- we really need a second run when not optimized
+ context.MPLIBextrapass(askedfig)
+ end
+ else
+ if trace_runs then
+ report_metapost("running job %s, asked figure %a",nofruns,askedfig)
+ end
+ metapost.process(mpx, {
+ preamble,
+ wrappit and do_begin_fig or "",
+ do_first_run,
+ no_trial_run,
+ current_initializations,
+ do_safeguard,
+ current_graphic,
+ wrappit and do_end_fig or "",
+ }, false, nil, false, false, askedfig)
+ end
+end
+
+function metapost.graphic_extra_pass(askedfig)
+ if trace_runs then
+ report_metapost("second run of job %s, asked figure %a",nofruns,askedfig)
+ end
+ local askedfig, wrappit = checkaskedfig(askedfig)
+ metapost.process(current_format, {
+ wrappit and do_begin_fig or "",
+ no_trial_run,
+ concat(metapost.textextsdata()," ;\n"),
+ current_initializations,
+ do_safeguard,
+ current_graphic,
+ wrappit and do_end_fig or "",
+ }, false, nil, false, true, askedfig)
+ context.MPLIBresettexts() -- must happen afterwards
+end
+
+local start = [[\starttext]]
+local preamble = [[\long\def\MPLIBgraphictext#1{\startTEXpage[scale=10000]#1\stopTEXpage}]]
+local stop = [[\stoptext]]
+
+function makempy.processgraphics(graphics)
+ if #graphics > 0 then
+ makempy.nofconverted = makempy.nofconverted + 1
+ starttiming(makempy)
+ local mpofile = tex.jobname .. "-mpgraph"
+ local mpyfile = file.replacesuffix(mpofile,"mpy")
+ local pdffile = file.replacesuffix(mpofile,"pdf")
+ local texfile = file.replacesuffix(mpofile,"tex")
+ io.savedata(texfile, { start, preamble, metapost.tex.get(), concat(graphics,"\n"), stop }, "\n")
+ local command = format("context --once %s %s", (tex.interactionmode == 0 and "--batchmode") or "", texfile)
+ os.execute(command)
+ if io.exists(pdffile) then
+ command = format("pstoedit -ssp -dt -f mpost %s %s", pdffile, mpyfile)
+ os.execute(command)
+ local result, r = { }, 0
+ if io.exists(mpyfile) then
+ local data = io.loaddata(mpyfile)
+ for figure in gmatch(data,"beginfig(.-)endfig") do
+ r = r + 1
+ result[r] = formatters["begingraphictextfig%sendgraphictextfig ;\n"](figure)
+ end
+ io.savedata(mpyfile,concat(result,""))
+ end
+ end
+ stoptiming(makempy)
+ end
+end
+
+-- -- the new plugin handler -- --
+
+local sequencers = utilities.sequencers
+local appendgroup = sequencers.appendgroup
+local appendaction = sequencers.appendaction
+
+local resetter = nil
+local analyzer = nil
+local processor = nil
+
+local resetteractions = sequencers.new { arguments = "t" }
+local analyzeractions = sequencers.new { arguments = "object,prescript" }
+local processoractions = sequencers.new { arguments = "object,prescript,before,after" }
+
+appendgroup(resetteractions, "system")
+appendgroup(analyzeractions, "system")
+appendgroup(processoractions, "system")
+
+-- later entries come first
+
+--~ local scriptsplitter = Cf(Ct("") * (
+--~ Cg(C((1-S("= "))^1) * S("= ")^1 * C((1-S("\n\r"))^0) * S("\n\r")^0)
+--~ )^0, rawset)
+
+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]
+ hash[h[1]] = h[2]
+ end
+ if trace_scripts then
+ report_scripts(table.serialize(hash,"prescript"))
+ end
+ return hash
+end
+
+-- -- 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
+ 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
+
+function metapost.resetplugins(t) -- intialize plugins, before figure
+ -- plugins can have been added
+ resetter = resetteractions .runner
+ analyzer = analyzeractions .runner
+ processor = processoractions .runner
+ -- let's apply one runner
+ resetter(t)
+end
+
+function metapost.analyzeplugins(object) -- each object (first pass)
+ local prescript = object.prescript -- specifications
+ if prescript and #prescript > 0 then
+ return analyzer(object,splitprescript(prescript))
+ end
+end
+
+function metapost.processplugins(object) -- each object (second pass)
+ local prescript = object.prescript -- specifications
+ if prescript and #prescript > 0 then
+ local before = { }
+ local after = { }
+ processor(object,splitprescript(prescript),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
+
+-- helpers
+
+local basepoints = number.dimenfactors["bp"]
+
+local function cm(object)
+ local op = object.path
+ if op then
+ local first, second, fourth = op[1], op[2], op[4]
+ local tx, ty = first.x_coord , first.y_coord
+ local sx, sy = second.x_coord - tx, fourth.y_coord - ty
+ local rx, ry = second.y_coord - ty, 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
+ else
+ return 1, 0, 0, 1, 0, 0 -- weird case
+ end
+end
+
+-- color
+
+local function cl_reset(t)
+ t[#t+1] = metapost.colorinitializer() -- only color
+end
+
+local tx_hash = { }
+local tx_last = 0
+
+local function tx_reset()
+ tx_hash = { }
+ tx_last = 0
+end
+
+local fmt = formatters["%s %s %s % t"]
+
+local function tx_analyze(object,prescript) -- todo: hash content and reuse them
+ local tx_stage = prescript.tx_stage
+ if tx_stage == "trial" then
+ textrial = textrial + 1
+ local tx_number = tonumber(prescript.tx_number)
+ local s = object.postscript or ""
+ local c = object.color -- only simple ones, no transparency
+ local a = prescript.tr_alternative
+ local t = prescript.tr_transparency
+ local h = fmt(tx_number,a or "?",t or "?",c)
+ local n = tx_hash[h] -- todo: hashed variant with s (nicer for similar labels)
+ if not n then
+ tx_last = tx_last + 1
+ if not c then
+ -- no color
+ elseif #c == 1 then
+ if a and t then
+ s = formatters["\\directcolored[s=%f,a=%f,t=%f]%s"](c[1],a,t,s)
+ else
+ s = formatters["\\directcolored[s=%f]%s"](c[1],s)
+ end
+ elseif #c == 3 then
+ if a and t then
+ s = formatters["\\directcolored[r=%f,g=%f,b=%f,a=%f,t=%f]%s"](c[1],c[2],c[3],a,t,s)
+ else
+ s = formatters["\\directcolored[r=%f,g=%f,b=%f]%s"](c[1],c[2],c[3],s)
+ end
+ elseif #c == 4 then
+ if a and t then
+ s = formatters["\\directcolored[c=%f,m=%f,y=%f,k=%f,a=%f,t=%f]%s"](c[1],c[2],c[3],c[4],a,t,s)
+ else
+ s = formatters["\\directcolored[c=%f,m=%f,y=%f,k=%f]%s"](c[1],c[2],c[3],c[4],s)
+ end
+ end
+ context.MPLIBsettext(tx_last,s)
+ metapost.multipass = true
+ tx_hash[h] = tx_last
+ texslots[textrial] = tx_last
+ texorder[tx_number] = tx_last
+ if trace_textexts then
+ report_textexts("stage %a, usage %a, number %a, new %a, hash %a",tx_stage,textrial,tx_number,tx_last,h)
+ end
+ else
+ texslots[textrial] = n
+ if trace_textexts then
+ report_textexts("stage %a, usage %a, number %a, new %a, hash %a",tx_stage,textrial,tx_number,n,h)
+ end
+ end
+ elseif tx_stage == "extra" then
+ textrial = textrial + 1
+ local tx_number = tonumber(prescript.tx_number)
+ if not texorder[tx_number] then
+ local s = object.postscript or ""
+ tx_last = tx_last + 1
+ context.MPLIBsettext(tx_last,s)
+ metapost.multipass = true
+ texslots[textrial] = tx_last
+ texorder[tx_number] = tx_last
+ if trace_textexts then
+ report_textexts("stage %a, usage %a, number %a, extra %a",tx_stage,textrial,tx_number,tx_last)
+ end
+ end
+ end
+end
+
+local function tx_process(object,prescript,before,after)
+ local tx_number = prescript.tx_number
+ if tx_number then
+ tx_number = tonumber(tx_number)
+ local tx_stage = prescript.tx_stage
+ if tx_stage == "final" then
+ texfinal = texfinal + 1
+ local n = texslots[texfinal]
+ if trace_textexts then
+ report_textexts("stage %a, usage %a, number %a, use %a",tx_stage,texfinal,tx_number,n)
+ end
+ local sx, rx, ry, sy, tx, ty = cm(object) -- needs to be frozen outside the function
+ local box = textexts[n]
+ if box then
+ before[#before+1] = function()
+ -- flush always happens, we can have a special flush function injected before
+ context.MPLIBgettextscaledcm(n,
+ format("%f",sx), -- bah ... %s no longer checks
+ format("%f",rx), -- bah ... %s no longer checks
+ format("%f",ry), -- bah ... %s no longer checks
+ format("%f",sy), -- bah ... %s no longer checks
+ format("%f",tx), -- bah ... %s no longer checks
+ format("%f",ty), -- bah ... %s no longer checks
+ sxsy(box.width,box.height,box.depth))
+ end
+ else
+ before[#before+1] = function()
+ report_textexts("unknown %s",tx_number)
+ end
+ end
+ if not trace_textexts then
+ object.path = false -- else: keep it
+ end
+ object.color = false
+ object.grouped = true
+ end
+ end
+end
+
+-- graphics
+
+local graphics = { }
+
+function metapost.intermediate.actions.makempy()
+ if #graphics > 0 then
+ makempy.processgraphics(graphics)
+ graphics = { } -- ?
+ end
+end
+
+local function gt_analyze(object,prescript)
+ local gt_stage = prescript.gt_stage
+ if gt_stage == "trial" then
+ graphics[#graphics+1] = formatters["\\MPLIBgraphictext{%s}"](object.postscript or "")
+ metapost.intermediate.needed = true
+ metapost.multipass = true
+ end
+end
+
+-- local function gt_process(object,prescript,before,after)
+-- local gt_stage = prescript.gt_stage
+-- if gt_stage == "final" then
+-- 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)
+ local centera = lpegmatch(centersplitter,prescript.sh_center_a)
+ local centerb = lpegmatch(centersplitter,prescript.sh_center_b)
+ --
+ local sh_color_a = prescript.sh_color_a or "1"
+ local sh_color_b = prescript.sh_color_b or "1"
+ local ca, cb, colorspace, name, separation
+ 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 = checkandconvert(colora,colorb)
+ end
+ if not ca or not cb then
+ ca, cb, colorspace, name = checkandconvert()
+ end
+ if sh_type == "linear" then
+ local coordinates = { centera[1], centera[2], centerb[1], centerb[2] }
+ lpdf.linearshade(name,domain,ca,cb,1,colorspace,coordinates,separation) -- backend specific (will be renamed)
+ elseif sh_type == "circular" then
+ local radiusa = tonumber(prescript.sh_radius_a)
+ local radiusb = tonumber(prescript.sh_radius_b)
+ local coordinates = { centera[1], centera[2], radiusa, centerb[1], centerb[2], radiusb }
+ lpdf.circularshade(name,domain,ca,cb,1,colorspace,coordinates,separation) -- backend specific (will be renamed)
+ else
+ -- fatal error
+ end
+ before[#before+1], after[#after+1] = "q /Pattern cs", 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(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] = "Q"
+ 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, third = op[1], op[3]
+ local x, y = first.x_coord, first.y_coord
+ local w, h = third.x_coord - x, third.y_coord - y
+ x = x - metapost.llx
+ y = metapost.ury - y
+ before[#before+1] = function()
+ context.MPLIBpositionwhd(ps_label,x,y,w,h)
+ end
+ object.path = false
+ end
+end
+
+-- figures
+
+local function fg_process(object,prescript,before,after)
+ local fg_name = prescript.fg_name
+ if fg_name then
+ before[#before+1] = f_cm(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] = "Q"
+ object.path = false
+ object.grouped = true
+ end
+end
+
+-- color and transparency
+
+local value = Cs ( (
+ (Carg(1) * C((1-P(","))^1)) / function(a,b) return format("%0.3f",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 function tr_process(object,prescript,before,after)
+ -- before can be shortcut to t
+ local tr_alternative = prescript.tr_alternative
+ if tr_alternative then
+ tr_alternative = tonumber(tr_alternative)
+ local tr_transparency = tonumber(prescript.tr_transparency)
+ before[#before+1] = formatters["/Tr%s gs"](registertransparency(nil,tr_alternative,tr_transparency,true))
+ after[#after+1] = "/Tr0 gs" -- outertransparency
+ 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)
+ elseif sp_type == "spot" or sp_type == "multitone" then
+ local sp_name = prescript.sp_name or "black"
+ local sp_fractions = prescript.sp_fractions or 1
+ local sp_components = prescript.sp_components or ""
+ local sp_value = prescript.sp_value or "1"
+ local cf = cs[1]
+ if cf ~= 1 then
+ -- beware, we do scale the spotcolors but not the alternative representation
+ sp_value = lpegmatch(value,sp_value,1,cf) or sp_value
+ end
+ c_b, c_a = spotcolorconverter(sp_name,sp_fractions,sp_components,sp_value)
+ elseif 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)
+ local sp_name = prescript.sp_name or "black"
+ 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 attributes.transparencies.value(t)
+ if v then
+ before[#before+1] = formatters["/Tr%s gs"](registertransparency(nil,v[1],v[2],true))
+ after[#after+1] = "/Tr0 gs" -- outertransparency
+ end
+ end
+ local c = c_list[sp_name] -- string or attribute
+ local v = c and attributes.colors.value(c)
+ if v then
+ -- all=1 gray=2 rgb=3 cmyk=4
+ local colorspace = v[1]
+ local f = cs[1]
+ if colorspace == 2 then
+ local s = f*v[2]
+ c_b, c_a = checked_color_pair(f_gray,s,s)
+ elseif colorspace == 3 then
+ local r, g, b = f*v[3], f*v[4], f*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, m, y, k = f*v[6], f*v[7], f*v[8], f*v[9]
+ c_b, c_a = checked_color_pair(f_cmyk,c,m,y,k,c,m,y,k)
+ else
+ local s = f*v[2]
+ c_b, c_a = checked_color_pair(f_gray,s,s)
+ 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 types = {
+ isolated
+}
+
+local function gr_process(object,prescript,before,after)
+ local gr_state = prescript.gr_state
+ if gr_state then
+ if gr_state == "start" then
+ local gr_type = utilities.parsers.settings_to_hash(prescript.gr_type)
+ before[#before+1] = function()
+ context.MPLIBstartgroup(
+ gr_type.isolated and 1 or 0,
+ gr_type.knockout and 1 or 0,
+ prescript.gr_llx,
+ prescript.gr_lly,
+ prescript.gr_urx,
+ prescript.gr_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
+end
+
+-- definitions
+
+appendaction(resetteractions, "system",cl_reset)
+appendaction(resetteractions, "system",tx_reset)
+
+appendaction(processoractions,"system",gr_process)
+
+appendaction(analyzeractions, "system",tx_analyze)
+appendaction(analyzeractions, "system",gt_analyze)
+
+appendaction(processoractions,"system",sh_process)
+-- (processoractions,"system",gt_process)
+appendaction(processoractions,"system",bm_process)
+appendaction(processoractions,"system",tx_process)
+appendaction(processoractions,"system",ps_process)
+appendaction(processoractions,"system",fg_process)
+appendaction(processoractions,"system",tr_process) -- last, as color can be reset
+
+appendaction(processoractions,"system",la_process)
+
+-- we're nice and set them already
+
+resetter = resetteractions .runner
+analyzer = analyzeractions .runner
+processor = processoractions.runner
diff --git a/tex/context/base/mlib-run.lua b/tex/context/base/mlib-run.lua
index 3915196b0..1fc36dd80 100644
--- a/tex/context/base/mlib-run.lua
+++ b/tex/context/base/mlib-run.lua
@@ -1,591 +1,591 @@
-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 = type, tostring, tonumber
-local format, gsub, match, find = string.format, string.gsub, string.match, string.find
-local concat = table.concat
-local emptystring = string.is_empty
-local lpegmatch, P = lpeg.match, lpeg.P
-
-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 mplib = mplib
-metapost = metapost or { }
-local metapost = metapost
-
-local mplibone = tonumber(mplib.version()) <= 1.50
-
-metapost.showlog = false
-metapost.lastlog = ""
-metapost.collapse = true -- currently mplib cannot deal with begingroup/endgroup mismatch in stepwise processing
-metapost.texerrors = false
-metapost.exectime = metapost.exectime or { } -- hack
-
--- metapost.collapse = false
-
-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
-
------ mpbasepath = lpeg.instringchecker(lpeg.append { "/metapost/context/", "/metapost/base/" })
-local mpbasepath = lpeg.instringchecker(P("/metapost/") * (P("context") + P("base")) * P("/"))
-
--- local function i_finder(askedname,mode,ftype) -- fake message for mpost.map and metafun.mpvi
--- local foundname = file.is_qualified_path(askedname) and askedname or resolvers.findfile(askedname,ftype)
--- if not mpbasepath(foundname) then
--- -- we could use the via file but we don't have a complete io interface yet
--- local data, found, forced = metapost.checktexts(io.loaddata(foundname) or "")
--- if found then
--- local tempname = luatex.registertempfile(foundname,true)
--- io.savedata(tempname,data)
--- foundname = tempname
--- end
--- end
--- return foundname
--- end
-
--- mplib has no real io interface so we have a different mechanism than
--- tex (as soon as we have more control, we will use the normal code)
-
-local finders = { }
-mplib.finders = finders
-
--- for some reason mp sometimes calls this function twice which is inefficient
--- but we cannot catch this
-
-local function preprocessed(name)
- if not mpbasepath(name) then
- -- we could use the via file but we don't have a complete io interface yet
- local data, found, forced = metapost.checktexts(io.loaddata(name) or "")
- if found then
- local temp = luatex.registertempfile(name,true)
- io.savedata(temp,data)
- return temp
- end
- end
- return name
-end
-
-mplib.preprocessed = preprocessed -- helper
-
-finders.file = function(specification,name,mode,ftype)
- return preprocessed(resolvers.findfile(name,ftype))
-end
-
-local function i_finder(name,mode,ftype) -- fake message for mpost.map and metafun.mpvi
- local specification = url.hashed(name)
- local finder = finders[specification.scheme] or finders.file
- return finder(specification,name,mode,ftype)
-end
-
-local function o_finder(name,mode,ftype)
- -- report_metapost("output file %a, mode %a, ftype %a",name,mode,ftype)
- return name
-end
-
-local function finder(name, mode, ftype)
- if mode == "w" then
- return o_finder(name,mode,ftype)
- else
- return i_finder(name,mode,ftype)
- end
-end
-
-local i_limited = false
-local o_limited = false
-
-directives.register("system.inputmode", function(v)
- if not i_limited then
- local i_limiter = io.i_limiter(v)
- if i_limiter then
- i_finder = i_limiter.protect(i_finder)
- i_limited = true
- end
- end
-end)
-
-directives.register("system.outputmode", function(v)
- if not o_limited then
- local o_limiter = io.o_limiter(v)
- if o_limiter then
- o_finder = o_limiter.protect(o_finder)
- o_limited = true
- end
- end
-end)
-
--- -- --
-
-metapost.finder = finder
-
-function metapost.reporterror(result)
- if not result then
- report_metapost("error: no result object returned")
- elseif result.status > 0 then
- local t, e, l = result.term, result.error, result.log
- if t and t ~= "" then
- (metapost.texerrors and texerrormessage or report_metapost)("terminal: %s",t)
- end
- if e == "" or e == "no-error" then
- e = nil
- end
- if e then
- (metapost.texerrors and texerrormessage or report_metapost)("error: %s",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
- else
- return false
- end
- return true
-end
-
-if mplibone then
-
- report_metapost("fatal error: mplib is too old")
-
- os.exit()
-
- -- local preamble = [[
- -- boolean mplib ; mplib := true ;
- -- string mp_parent_version ; mp_parent_version := "%s" ;
- -- input "%s" ; dump ;
- -- ]]
- --
- -- metapost.parameters = {
- -- hash_size = 100000,
- -- main_memory = 4000000,
- -- max_in_open = 50,
- -- param_size = 100000,
- -- }
- --
- -- function metapost.make(name, target, version)
- -- starttiming(mplib)
- -- target = file.replacesuffix(target or name, "mem") -- redundant
- -- local mpx = mplib.new ( table.merged (
- -- metapost.parameters,
- -- {
- -- ini_version = true,
- -- find_file = finder,
- -- job_name = file.removesuffix(target),
- -- }
- -- ) )
- -- if mpx then
- -- starttiming(metapost.exectime)
- -- local result = mpx:execute(format(preamble,version or "unknown",name))
- -- stoptiming(metapost.exectime)
- -- mpx:finish()
- -- end
- -- stoptiming(mplib)
- -- end
- --
- -- function metapost.load(name)
- -- starttiming(mplib)
- -- local mpx = mplib.new ( table.merged (
- -- metapost.parameters,
- -- {
- -- ini_version = false,
- -- mem_name = file.replacesuffix(name,"mem"),
- -- find_file = finder,
- -- -- job_name = "mplib",
- -- }
- -- ) )
- -- local result
- -- if not mpx then
- -- result = { status = 99, error = "out of memory"}
- -- end
- -- stoptiming(mplib)
- -- return mpx, result
- -- end
- --
- -- function metapost.checkformat(mpsinput)
- -- local mpsversion = environment.version or "unset version"
- -- local mpsinput = file.addsuffix(mpsinput or "metafun", "mp")
- -- local mpsformat = file.removesuffix(file.basename(texconfig.formatname or (tex and tex.formatname) or mpsinput))
- -- local mpsbase = file.removesuffix(file.basename(mpsinput))
- -- if mpsbase ~= mpsformat then
- -- mpsformat = mpsformat .. "-" .. mpsbase
- -- end
- -- mpsformat = file.addsuffix(mpsformat, "mem")
- -- local mpsformatfullname = caches.getfirstreadablefile(mpsformat,"formats","metapost") or ""
- -- if mpsformatfullname ~= "" then
- -- report_metapost("loading %a from %a", mpsinput, mpsformatfullname)
- -- local mpx, result = metapost.load(mpsformatfullname)
- -- if mpx then
- -- local result = mpx:execute("show mp_parent_version ;")
- -- if not result.log then
- -- metapost.reporterror(result)
- -- else
- -- local version = match(result.log,">> *(.-)[\n\r]") or "unknown"
- -- version = gsub(version,"[\'\"]","")
- -- if version ~= mpsversion then
- -- report_metapost("version mismatch: %s <> %s", version or "unknown", mpsversion)
- -- else
- -- return mpx
- -- end
- -- end
- -- else
- -- report_metapost("error in loading %a from %a", mpsinput, mpsformatfullname)
- -- metapost.reporterror(result)
- -- end
- -- end
- -- local mpsformatfullname = caches.setfirstwritablefile(mpsformat,"formats")
- -- report_metapost("making %a into %a", mpsinput, mpsformatfullname)
- -- metapost.make(mpsinput,mpsformatfullname,mpsversion) -- somehow return ... fails here
- -- if lfs.isfile(mpsformatfullname) then
- -- report_metapost("loading %a from %a", mpsinput, mpsformatfullname)
- -- return metapost.load(mpsformatfullname)
- -- else
- -- report_metapost("problems with %a from %a", mpsinput, mpsformatfullname)
- -- end
- -- end
-
-else
-
- local preamble = [[
- boolean mplib ; mplib := true ;
- let dump = endinput ;
- input "%s" ;
- ]]
-
- local methods = {
- double = "double",
- scaled = "scaled",
- default = "scaled",
- decimal = false, -- for the moment
- }
-
- function metapost.load(name,method)
- starttiming(mplib)
- method = method and methods[method] or "scaled"
- local mpx = mplib.new {
- ini_version = true,
- find_file = finder,
- math_mode = method,
- }
- report_metapost("initializing number mode %a",method)
- local result
- if not mpx then
- result = { status = 99, error = "out of memory"}
- else
- result = mpx:execute(format(preamble, file.addsuffix(name,"mp"))) -- addsuffix is redundant
- end
- stoptiming(mplib)
- metapost.reporterror(result)
- return mpx, result
- end
-
- function metapost.checkformat(mpsinput,method)
- local mpsversion = environment.version or "unset version"
- local mpsinput = mpsinput or "metafun"
- local foundfile = ""
- if file.suffix(mpsinput) ~= "" then
- foundfile = finder(mpsinput) or ""
- end
- if foundfile == "" then
- foundfile = finder(file.replacesuffix(mpsinput,"mpvi")) or ""
- end
- if foundfile == "" then
- foundfile = finder(file.replacesuffix(mpsinput,"mpiv")) or ""
- end
- if foundfile == "" then
- foundfile = finder(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
-
-end
-
-function metapost.unload(mpx)
- starttiming(mplib)
- if mpx then
- mpx:finish()
- end
- stoptiming(mplib)
-end
-
-local mpxformats = { }
-
-function metapost.format(instance,name,method)
- if not instance or instance == "" then
- instance = "metafun" -- brrr
- end
- name = name or instance
- local mpx = mpxformats[instance]
- if not mpx then
- report_metapost("initializing instance %a using format %a",instance,name)
- mpx = metapost.checkformat(name,method)
- mpxformats[instance] = mpx
- end
- return mpx
-end
-
-function metapost.instance(instance)
- return mpxformats[instance]
-end
-
-function metapost.reset(mpx)
- if not mpx then
- -- nothing
- elseif type(mpx) == "string" then
- if mpxformats[mpx] then
- mpxformats[mpx]:finish()
- mpxformats[mpx] = nil
- end
- else
- for name, instance in next, mpxformats do
- if instance == mpx then
- mpx:finish()
- mpxformats[name] = nil
- break
- end
- end
- end
-end
-
-local mp_inp, mp_log, mp_tag = { }, { }, 0
-
--- key/values
-
-function metapost.process(mpx, data, trialrun, flusher, multipass, isextrapass, askedfig)
- local converted, result = false, { }
- if type(mpx) == "string" then
- mpx = metapost.format(mpx) -- goody
- end
- if mpx and data then
- starttiming(metapost)
- if trace_graphics then
- if not mp_inp[mpx] then
- mp_tag = mp_tag + 1
- local jobname = tex.jobname
- mp_inp[mpx] = io.open(format("%s-mplib-run-%03i.mp", jobname,mp_tag),"w")
- mp_log[mpx] = io.open(format("%s-mplib-run-%03i.log",jobname,mp_tag),"w")
- end
- local banner = format("%% begin graphic: n=%s, trialrun=%s, multipass=%s, isextrapass=%s\n\n", metapost.n, tostring(trialrun), tostring(multipass), tostring(isextrapass))
- mp_inp[mpx]:write(banner)
- mp_log[mpx]:write(banner)
- end
- if type(data) == "table" then
- -- this hack is needed because the library currently barks on \n\n
- -- eventually we can text for "" in the next loop
- local n = 0
- local nofsnippets = #data
- for i=1,nofsnippets do
- local d = data[i]
- if d ~= "" then
- n = n + 1
- data[n] = d
- end
- end
- for i=nofsnippets,n+1,-1 do
- data[i] = nil
- end
- -- and this one because mp cannot handle snippets due to grouping issues
- if metapost.collapse then
- if #data > 1 then
- data = concat(data,"\n")
- else
- data = data[1]
- end
- end
- -- end of hacks
- end
- if type(data) == "table" then
- if trace_tracingall then
- mpx:execute("tracingall;")
- end
- -- table.insert(data,2,"")
- for i=1,#data do
- local d = data[i]
- -- d = string.gsub(d,"\r","")
- if d then
- if trace_graphics then
- mp_inp[mpx]:write(format("\n%% begin snippet %s\n",i))
- mp_inp[mpx]:write(d)
- mp_inp[mpx]:write(format("\n%% end snippet %s\n",i))
- end
- starttiming(metapost.exectime)
- result = mpx:execute(d)
- stoptiming(metapost.exectime)
- if trace_graphics and result then
- local str = result.log or result.error
- if str and str ~= "" then
- mp_log[mpx]:write(str)
- end
- end
- if not metapost.reporterror(result) then
- if metapost.showlog then
- 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(result, trialrun, flusher, multipass, askedfig)
- end
- end
- else
- report_metapost("error: invalid graphic component %s",i)
- end
- end
- else
- if trace_tracingall then
- data = "tracingall;" .. data
- end
- if trace_graphics then
- mp_inp[mpx]:write(data)
- end
- starttiming(metapost.exectime)
- result = mpx:execute(data)
- stoptiming(metapost.exectime)
- if trace_graphics and result then
- local str = result.log or result.error
- if str and str ~= "" then
- mp_log[mpx]:write(str)
- end
- end
- -- todo: error message
- 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
- if result.fig then
- converted = metapost.convert(result, trialrun, flusher, multipass, askedfig)
- end
- end
- end
- if trace_graphics then
- local banner = "\n% end graphic\n\n"
- mp_inp[mpx]:write(banner)
- mp_log[mpx]:write(banner)
- end
- stoptiming(metapost)
- end
- return converted, result
-end
-
-function metapost.convert()
- report_metapost('warning: no converter set')
-end
-
--- handy
-
-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 = mpx:execute(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 = format("%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
+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 = type, tostring, tonumber
+local format, gsub, match, find = string.format, string.gsub, string.match, string.find
+local concat = table.concat
+local emptystring = string.is_empty
+local lpegmatch, P = lpeg.match, lpeg.P
+
+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 mplib = mplib
+metapost = metapost or { }
+local metapost = metapost
+
+local mplibone = tonumber(mplib.version()) <= 1.50
+
+metapost.showlog = false
+metapost.lastlog = ""
+metapost.collapse = true -- currently mplib cannot deal with begingroup/endgroup mismatch in stepwise processing
+metapost.texerrors = false
+metapost.exectime = metapost.exectime or { } -- hack
+
+-- metapost.collapse = false
+
+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
+
+----- mpbasepath = lpeg.instringchecker(lpeg.append { "/metapost/context/", "/metapost/base/" })
+local mpbasepath = lpeg.instringchecker(P("/metapost/") * (P("context") + P("base")) * P("/"))
+
+-- local function i_finder(askedname,mode,ftype) -- fake message for mpost.map and metafun.mpvi
+-- local foundname = file.is_qualified_path(askedname) and askedname or resolvers.findfile(askedname,ftype)
+-- if not mpbasepath(foundname) then
+-- -- we could use the via file but we don't have a complete io interface yet
+-- local data, found, forced = metapost.checktexts(io.loaddata(foundname) or "")
+-- if found then
+-- local tempname = luatex.registertempfile(foundname,true)
+-- io.savedata(tempname,data)
+-- foundname = tempname
+-- end
+-- end
+-- return foundname
+-- end
+
+-- mplib has no real io interface so we have a different mechanism than
+-- tex (as soon as we have more control, we will use the normal code)
+
+local finders = { }
+mplib.finders = finders
+
+-- for some reason mp sometimes calls this function twice which is inefficient
+-- but we cannot catch this
+
+local function preprocessed(name)
+ if not mpbasepath(name) then
+ -- we could use the via file but we don't have a complete io interface yet
+ local data, found, forced = metapost.checktexts(io.loaddata(name) or "")
+ if found then
+ local temp = luatex.registertempfile(name,true)
+ io.savedata(temp,data)
+ return temp
+ end
+ end
+ return name
+end
+
+mplib.preprocessed = preprocessed -- helper
+
+finders.file = function(specification,name,mode,ftype)
+ return preprocessed(resolvers.findfile(name,ftype))
+end
+
+local function i_finder(name,mode,ftype) -- fake message for mpost.map and metafun.mpvi
+ local specification = url.hashed(name)
+ local finder = finders[specification.scheme] or finders.file
+ return finder(specification,name,mode,ftype)
+end
+
+local function o_finder(name,mode,ftype)
+ -- report_metapost("output file %a, mode %a, ftype %a",name,mode,ftype)
+ return name
+end
+
+local function finder(name, mode, ftype)
+ if mode == "w" then
+ return o_finder(name,mode,ftype)
+ else
+ return i_finder(name,mode,ftype)
+ end
+end
+
+local i_limited = false
+local o_limited = false
+
+directives.register("system.inputmode", function(v)
+ if not i_limited then
+ local i_limiter = io.i_limiter(v)
+ if i_limiter then
+ i_finder = i_limiter.protect(i_finder)
+ i_limited = true
+ end
+ end
+end)
+
+directives.register("system.outputmode", function(v)
+ if not o_limited then
+ local o_limiter = io.o_limiter(v)
+ if o_limiter then
+ o_finder = o_limiter.protect(o_finder)
+ o_limited = true
+ end
+ end
+end)
+
+-- -- --
+
+metapost.finder = finder
+
+function metapost.reporterror(result)
+ if not result then
+ report_metapost("error: no result object returned")
+ elseif result.status > 0 then
+ local t, e, l = result.term, result.error, result.log
+ if t and t ~= "" then
+ (metapost.texerrors and texerrormessage or report_metapost)("terminal: %s",t)
+ end
+ if e == "" or e == "no-error" then
+ e = nil
+ end
+ if e then
+ (metapost.texerrors and texerrormessage or report_metapost)("error: %s",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
+ else
+ return false
+ end
+ return true
+end
+
+if mplibone then
+
+ report_metapost("fatal error: mplib is too old")
+
+ os.exit()
+
+ -- local preamble = [[
+ -- boolean mplib ; mplib := true ;
+ -- string mp_parent_version ; mp_parent_version := "%s" ;
+ -- input "%s" ; dump ;
+ -- ]]
+ --
+ -- metapost.parameters = {
+ -- hash_size = 100000,
+ -- main_memory = 4000000,
+ -- max_in_open = 50,
+ -- param_size = 100000,
+ -- }
+ --
+ -- function metapost.make(name, target, version)
+ -- starttiming(mplib)
+ -- target = file.replacesuffix(target or name, "mem") -- redundant
+ -- local mpx = mplib.new ( table.merged (
+ -- metapost.parameters,
+ -- {
+ -- ini_version = true,
+ -- find_file = finder,
+ -- job_name = file.removesuffix(target),
+ -- }
+ -- ) )
+ -- if mpx then
+ -- starttiming(metapost.exectime)
+ -- local result = mpx:execute(format(preamble,version or "unknown",name))
+ -- stoptiming(metapost.exectime)
+ -- mpx:finish()
+ -- end
+ -- stoptiming(mplib)
+ -- end
+ --
+ -- function metapost.load(name)
+ -- starttiming(mplib)
+ -- local mpx = mplib.new ( table.merged (
+ -- metapost.parameters,
+ -- {
+ -- ini_version = false,
+ -- mem_name = file.replacesuffix(name,"mem"),
+ -- find_file = finder,
+ -- -- job_name = "mplib",
+ -- }
+ -- ) )
+ -- local result
+ -- if not mpx then
+ -- result = { status = 99, error = "out of memory"}
+ -- end
+ -- stoptiming(mplib)
+ -- return mpx, result
+ -- end
+ --
+ -- function metapost.checkformat(mpsinput)
+ -- local mpsversion = environment.version or "unset version"
+ -- local mpsinput = file.addsuffix(mpsinput or "metafun", "mp")
+ -- local mpsformat = file.removesuffix(file.basename(texconfig.formatname or (tex and tex.formatname) or mpsinput))
+ -- local mpsbase = file.removesuffix(file.basename(mpsinput))
+ -- if mpsbase ~= mpsformat then
+ -- mpsformat = mpsformat .. "-" .. mpsbase
+ -- end
+ -- mpsformat = file.addsuffix(mpsformat, "mem")
+ -- local mpsformatfullname = caches.getfirstreadablefile(mpsformat,"formats","metapost") or ""
+ -- if mpsformatfullname ~= "" then
+ -- report_metapost("loading %a from %a", mpsinput, mpsformatfullname)
+ -- local mpx, result = metapost.load(mpsformatfullname)
+ -- if mpx then
+ -- local result = mpx:execute("show mp_parent_version ;")
+ -- if not result.log then
+ -- metapost.reporterror(result)
+ -- else
+ -- local version = match(result.log,">> *(.-)[\n\r]") or "unknown"
+ -- version = gsub(version,"[\'\"]","")
+ -- if version ~= mpsversion then
+ -- report_metapost("version mismatch: %s <> %s", version or "unknown", mpsversion)
+ -- else
+ -- return mpx
+ -- end
+ -- end
+ -- else
+ -- report_metapost("error in loading %a from %a", mpsinput, mpsformatfullname)
+ -- metapost.reporterror(result)
+ -- end
+ -- end
+ -- local mpsformatfullname = caches.setfirstwritablefile(mpsformat,"formats")
+ -- report_metapost("making %a into %a", mpsinput, mpsformatfullname)
+ -- metapost.make(mpsinput,mpsformatfullname,mpsversion) -- somehow return ... fails here
+ -- if lfs.isfile(mpsformatfullname) then
+ -- report_metapost("loading %a from %a", mpsinput, mpsformatfullname)
+ -- return metapost.load(mpsformatfullname)
+ -- else
+ -- report_metapost("problems with %a from %a", mpsinput, mpsformatfullname)
+ -- end
+ -- end
+
+else
+
+ local preamble = [[
+ boolean mplib ; mplib := true ;
+ let dump = endinput ;
+ input "%s" ;
+ ]]
+
+ local methods = {
+ double = "double",
+ scaled = "scaled",
+ default = "scaled",
+ decimal = false, -- for the moment
+ }
+
+ function metapost.load(name,method)
+ starttiming(mplib)
+ method = method and methods[method] or "scaled"
+ local mpx = mplib.new {
+ ini_version = true,
+ find_file = finder,
+ math_mode = method,
+ }
+ report_metapost("initializing number mode %a",method)
+ local result
+ if not mpx then
+ result = { status = 99, error = "out of memory"}
+ else
+ result = mpx:execute(format(preamble, file.addsuffix(name,"mp"))) -- addsuffix is redundant
+ end
+ stoptiming(mplib)
+ metapost.reporterror(result)
+ return mpx, result
+ end
+
+ function metapost.checkformat(mpsinput,method)
+ local mpsversion = environment.version or "unset version"
+ local mpsinput = mpsinput or "metafun"
+ local foundfile = ""
+ if file.suffix(mpsinput) ~= "" then
+ foundfile = finder(mpsinput) or ""
+ end
+ if foundfile == "" then
+ foundfile = finder(file.replacesuffix(mpsinput,"mpvi")) or ""
+ end
+ if foundfile == "" then
+ foundfile = finder(file.replacesuffix(mpsinput,"mpiv")) or ""
+ end
+ if foundfile == "" then
+ foundfile = finder(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
+
+end
+
+function metapost.unload(mpx)
+ starttiming(mplib)
+ if mpx then
+ mpx:finish()
+ end
+ stoptiming(mplib)
+end
+
+local mpxformats = { }
+
+function metapost.format(instance,name,method)
+ if not instance or instance == "" then
+ instance = "metafun" -- brrr
+ end
+ name = name or instance
+ local mpx = mpxformats[instance]
+ if not mpx then
+ report_metapost("initializing instance %a using format %a",instance,name)
+ mpx = metapost.checkformat(name,method)
+ mpxformats[instance] = mpx
+ end
+ return mpx
+end
+
+function metapost.instance(instance)
+ return mpxformats[instance]
+end
+
+function metapost.reset(mpx)
+ if not mpx then
+ -- nothing
+ elseif type(mpx) == "string" then
+ if mpxformats[mpx] then
+ mpxformats[mpx]:finish()
+ mpxformats[mpx] = nil
+ end
+ else
+ for name, instance in next, mpxformats do
+ if instance == mpx then
+ mpx:finish()
+ mpxformats[name] = nil
+ break
+ end
+ end
+ end
+end
+
+local mp_inp, mp_log, mp_tag = { }, { }, 0
+
+-- key/values
+
+function metapost.process(mpx, data, trialrun, flusher, multipass, isextrapass, askedfig)
+ local converted, result = false, { }
+ if type(mpx) == "string" then
+ mpx = metapost.format(mpx) -- goody
+ end
+ if mpx and data then
+ starttiming(metapost)
+ if trace_graphics then
+ if not mp_inp[mpx] then
+ mp_tag = mp_tag + 1
+ local jobname = tex.jobname
+ mp_inp[mpx] = io.open(format("%s-mplib-run-%03i.mp", jobname,mp_tag),"w")
+ mp_log[mpx] = io.open(format("%s-mplib-run-%03i.log",jobname,mp_tag),"w")
+ end
+ local banner = format("%% begin graphic: n=%s, trialrun=%s, multipass=%s, isextrapass=%s\n\n", metapost.n, tostring(trialrun), tostring(multipass), tostring(isextrapass))
+ mp_inp[mpx]:write(banner)
+ mp_log[mpx]:write(banner)
+ end
+ if type(data) == "table" then
+ -- this hack is needed because the library currently barks on \n\n
+ -- eventually we can text for "" in the next loop
+ local n = 0
+ local nofsnippets = #data
+ for i=1,nofsnippets do
+ local d = data[i]
+ if d ~= "" then
+ n = n + 1
+ data[n] = d
+ end
+ end
+ for i=nofsnippets,n+1,-1 do
+ data[i] = nil
+ end
+ -- and this one because mp cannot handle snippets due to grouping issues
+ if metapost.collapse then
+ if #data > 1 then
+ data = concat(data,"\n")
+ else
+ data = data[1]
+ end
+ end
+ -- end of hacks
+ end
+ if type(data) == "table" then
+ if trace_tracingall then
+ mpx:execute("tracingall;")
+ end
+ -- table.insert(data,2,"")
+ for i=1,#data do
+ local d = data[i]
+ -- d = string.gsub(d,"\r","")
+ if d then
+ if trace_graphics then
+ mp_inp[mpx]:write(format("\n%% begin snippet %s\n",i))
+ mp_inp[mpx]:write(d)
+ mp_inp[mpx]:write(format("\n%% end snippet %s\n",i))
+ end
+ starttiming(metapost.exectime)
+ result = mpx:execute(d)
+ stoptiming(metapost.exectime)
+ if trace_graphics and result then
+ local str = result.log or result.error
+ if str and str ~= "" then
+ mp_log[mpx]:write(str)
+ end
+ end
+ if not metapost.reporterror(result) then
+ if metapost.showlog then
+ 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(result, trialrun, flusher, multipass, askedfig)
+ end
+ end
+ else
+ report_metapost("error: invalid graphic component %s",i)
+ end
+ end
+ else
+ if trace_tracingall then
+ data = "tracingall;" .. data
+ end
+ if trace_graphics then
+ mp_inp[mpx]:write(data)
+ end
+ starttiming(metapost.exectime)
+ result = mpx:execute(data)
+ stoptiming(metapost.exectime)
+ if trace_graphics and result then
+ local str = result.log or result.error
+ if str and str ~= "" then
+ mp_log[mpx]:write(str)
+ end
+ end
+ -- todo: error message
+ 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
+ if result.fig then
+ converted = metapost.convert(result, trialrun, flusher, multipass, askedfig)
+ end
+ end
+ end
+ if trace_graphics then
+ local banner = "\n% end graphic\n\n"
+ mp_inp[mpx]:write(banner)
+ mp_log[mpx]:write(banner)
+ end
+ stoptiming(metapost)
+ end
+ return converted, result
+end
+
+function metapost.convert()
+ report_metapost('warning: no converter set')
+end
+
+-- handy
+
+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 = mpx:execute(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 = format("%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
diff --git a/tex/context/base/mult-aux.lua b/tex/context/base/mult-aux.lua
index 05dd112a8..3c4cbcc0f 100644
--- a/tex/context/base/mult-aux.lua
+++ b/tex/context/base/mult-aux.lua
@@ -1,154 +1,154 @@
-if not modules then modules = { } end modules ['mult-aux'] = {
- version = 1.001,
- comment = "companion to mult-aux.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local find = string.find
-
-interfaces.namespaces = interfaces.namespaces or { }
-local namespaces = interfaces.namespaces
-local variables = interfaces.variables
-
-local trace_namespaces = false trackers.register("interfaces.namespaces", function(v) trace_namespaces = v end)
-
-local report_namespaces = logs.reporter("interface","namespaces")
-
-local v_yes, v_list = variables.yes, variables.list
-
-local prefix = "????"
-local meaning = "@@@@"
-
-local data = { }
-
-function namespaces.define(namespace,settings)
- if trace_namespaces then
- report_namespaces("installing namespace %a with settings %a",namespace,settings)
- end
- if data[namespace] then
- report_namespaces("namespace %a is already taken",namespace)
- end
- if #namespace < 2 then
- report_namespaces("namespace %a should have more than 1 character",namespace)
- end
- local ns = { }
- data[namespace] = ns
- utilities.parsers.settings_to_hash(settings,ns)
- local name = ns.name
- if not name or name == "" then
- report_namespaces("provide a (command) name in namespace %a",namespace)
- end
- local self = "\\" .. prefix .. namespace
- context.unprotect()
- -- context.installnamespace(namespace)
- context("\\def\\%s%s{%s%s}",prefix,namespace,meaning,namespace) -- or context.setvalue
- if trace_namespaces then
- report_namespaces("using namespace %a for %a",namespace,name)
- end
- local parent = ns.parent or ""
- if parent ~= "" then
- if trace_namespaces then
- report_namespaces("namespace %a for %a uses parent %a",namespace,name,parent)
- end
- if not find(parent,"\\") then
- parent = "\\" .. prefix .. parent
- -- todo: check if defined
- end
- end
- context.installparameterhandler(self,name)
- if trace_namespaces then
- report_namespaces("installing parameter handler for %a",name)
- end
- context.installparameterhashhandler(self,name)
- if trace_namespaces then
- report_namespaces("installing parameterhash handler for %a",name)
- end
- local style = ns.style
- if style == v_yes then
- context.installstyleandcolorhandler(self,name)
- if trace_namespaces then
- report_namespaces("installing attribute handler for %a",name)
- end
- end
- local command = ns.command
- if command == v_yes then
- context.installdefinehandler(self,name,parent)
- if trace_namespaces then
- report_namespaces("installing definition command for %a (single)",name)
- end
- elseif command == v_list then
- context.installdefinehandler(self,name,parent)
- if trace_namespaces then
- report_namespaces("installing definition command for %a (multiple)",name)
- end
- end
- local setup = ns.setup
- if setup == v_yes then
- context.installsetuphandler(self,name)
- if trace_namespaces then
- report_namespaces("installing setup command for %a (%s)",name,"single")
- end
- elseif setup == v_list then
- context.installsetuphandler(self,name)
- if trace_namespaces then
- report_namespaces("installing setup command for %a (%s)",name,"multiple")
- end
- end
- local set = ns.set
- if set == v_yes then
- context.installparametersethandler(self,name)
- if trace_namespaces then
- report_namespaces("installing set/let/reset command for %a (%s)",name,"single")
- end
- elseif set == v_list then
- context.installparametersethandler(self,name)
- if trace_namespaces then
- report_namespaces("installing set/let/reset command for %a (%s)",name,"multiple")
- end
- end
- local frame = ns.frame
- if frame == v_yes then
- context.installinheritedframed(name)
- if trace_namespaces then
- report_namespaces("installing framed command for %a",name)
- end
- end
- context.protect()
-end
-
-function utilities.formatters.list(data,key,keys)
- if not keys then
- keys = { }
- for _, v in next, data do
- for k, _ in next, v do
- keys[k] = true
- end
- end
- keys = table.sortedkeys(keys)
- end
- context.starttabulate { "|"..string.rep("l|",#keys+1) }
- context.NC()
- context(key)
- for i=1,#keys do
- context.NC()
- context(keys[i])
- end context.NR()
- context.HL()
- for k, v in table.sortedhash(data) do
- context.NC()
- context(k)
- for i=1,#keys do
- context.NC()
- context(v[keys[i]])
- end context.NR()
- end
- context.stoptabulate()
-end
-
-function namespaces.list()
- -- utilities.formatters.list(data,"namespace")
- local keys = { "type", "name", "comment", "version", "parent", "definition", "setup", "style" }
- utilities.formatters.list(data,"namespace",keys)
-end
+if not modules then modules = { } end modules ['mult-aux'] = {
+ version = 1.001,
+ comment = "companion to mult-aux.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local find = string.find
+
+interfaces.namespaces = interfaces.namespaces or { }
+local namespaces = interfaces.namespaces
+local variables = interfaces.variables
+
+local trace_namespaces = false trackers.register("interfaces.namespaces", function(v) trace_namespaces = v end)
+
+local report_namespaces = logs.reporter("interface","namespaces")
+
+local v_yes, v_list = variables.yes, variables.list
+
+local prefix = "????"
+local meaning = "@@@@"
+
+local data = { }
+
+function namespaces.define(namespace,settings)
+ if trace_namespaces then
+ report_namespaces("installing namespace %a with settings %a",namespace,settings)
+ end
+ if data[namespace] then
+ report_namespaces("namespace %a is already taken",namespace)
+ end
+ if #namespace < 2 then
+ report_namespaces("namespace %a should have more than 1 character",namespace)
+ end
+ local ns = { }
+ data[namespace] = ns
+ utilities.parsers.settings_to_hash(settings,ns)
+ local name = ns.name
+ if not name or name == "" then
+ report_namespaces("provide a (command) name in namespace %a",namespace)
+ end
+ local self = "\\" .. prefix .. namespace
+ context.unprotect()
+ -- context.installnamespace(namespace)
+ context("\\def\\%s%s{%s%s}",prefix,namespace,meaning,namespace) -- or context.setvalue
+ if trace_namespaces then
+ report_namespaces("using namespace %a for %a",namespace,name)
+ end
+ local parent = ns.parent or ""
+ if parent ~= "" then
+ if trace_namespaces then
+ report_namespaces("namespace %a for %a uses parent %a",namespace,name,parent)
+ end
+ if not find(parent,"\\") then
+ parent = "\\" .. prefix .. parent
+ -- todo: check if defined
+ end
+ end
+ context.installparameterhandler(self,name)
+ if trace_namespaces then
+ report_namespaces("installing parameter handler for %a",name)
+ end
+ context.installparameterhashhandler(self,name)
+ if trace_namespaces then
+ report_namespaces("installing parameterhash handler for %a",name)
+ end
+ local style = ns.style
+ if style == v_yes then
+ context.installstyleandcolorhandler(self,name)
+ if trace_namespaces then
+ report_namespaces("installing attribute handler for %a",name)
+ end
+ end
+ local command = ns.command
+ if command == v_yes then
+ context.installdefinehandler(self,name,parent)
+ if trace_namespaces then
+ report_namespaces("installing definition command for %a (single)",name)
+ end
+ elseif command == v_list then
+ context.installdefinehandler(self,name,parent)
+ if trace_namespaces then
+ report_namespaces("installing definition command for %a (multiple)",name)
+ end
+ end
+ local setup = ns.setup
+ if setup == v_yes then
+ context.installsetuphandler(self,name)
+ if trace_namespaces then
+ report_namespaces("installing setup command for %a (%s)",name,"single")
+ end
+ elseif setup == v_list then
+ context.installsetuphandler(self,name)
+ if trace_namespaces then
+ report_namespaces("installing setup command for %a (%s)",name,"multiple")
+ end
+ end
+ local set = ns.set
+ if set == v_yes then
+ context.installparametersethandler(self,name)
+ if trace_namespaces then
+ report_namespaces("installing set/let/reset command for %a (%s)",name,"single")
+ end
+ elseif set == v_list then
+ context.installparametersethandler(self,name)
+ if trace_namespaces then
+ report_namespaces("installing set/let/reset command for %a (%s)",name,"multiple")
+ end
+ end
+ local frame = ns.frame
+ if frame == v_yes then
+ context.installinheritedframed(name)
+ if trace_namespaces then
+ report_namespaces("installing framed command for %a",name)
+ end
+ end
+ context.protect()
+end
+
+function utilities.formatters.list(data,key,keys)
+ if not keys then
+ keys = { }
+ for _, v in next, data do
+ for k, _ in next, v do
+ keys[k] = true
+ end
+ end
+ keys = table.sortedkeys(keys)
+ end
+ context.starttabulate { "|"..string.rep("l|",#keys+1) }
+ context.NC()
+ context(key)
+ for i=1,#keys do
+ context.NC()
+ context(keys[i])
+ end context.NR()
+ context.HL()
+ for k, v in table.sortedhash(data) do
+ context.NC()
+ context(k)
+ for i=1,#keys do
+ context.NC()
+ context(v[keys[i]])
+ end context.NR()
+ end
+ context.stoptabulate()
+end
+
+function namespaces.list()
+ -- utilities.formatters.list(data,"namespace")
+ local keys = { "type", "name", "comment", "version", "parent", "definition", "setup", "style" }
+ utilities.formatters.list(data,"namespace",keys)
+end
diff --git a/tex/context/base/mult-chk.lua b/tex/context/base/mult-chk.lua
index 06e7aa8e6..2a2dfcd4b 100644
--- a/tex/context/base/mult-chk.lua
+++ b/tex/context/base/mult-chk.lua
@@ -1,76 +1,76 @@
-if not modules then modules = { } end modules ['mult-chk'] = {
- version = 1.001,
- comment = "companion to mult-chk.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 lpegmatch = lpeg.match
-local type = type
-
-local make_settings_to_hash_pattern = utilities.parsers.make_settings_to_hash_pattern
-local settings_to_set = utilities.parsers.settings_to_set
-local allocate = utilities.storage.allocate
-
-local report_interface = logs.reporter("interface","checking")
-
-interfaces = interfaces or { }
-
-interfaces.syntax = allocate {
- test = { keys = table.tohash { "a","b","c","d","e","f","g" } }
-}
-
-function interfaces.invalidkey(category,key)
- report_interface("invalid key %a for %a in line %a",key,category,tex.inputlineno)
-end
-
-function interfaces.setvalidkeys(category,list)
- local s = interfaces.syntax[category]
- if not s then
- interfaces.syntax[category] = {
- keys = settings_to_set(list)
- }
- else
- s.keys = settings_to_set(list)
- end
-end
-
-function interfaces.addvalidkeys(category,list)
- local s = interfaces.syntax[category]
- if not s then
- interfaces.syntax[category] = {
- keys = settings_to_set(list)
- }
- else
- settings_to_set(list,s.keys)
- end
-end
-
--- weird code, looks incomplete ... probably an experiment
-
-local prefix, category, keys
-
-local setsomevalue = context.setsomevalue
-local invalidkey = interfaces.invalidkey
-
-local function set(key,value)
- if keys and not keys[key] then
- invalidkey(category,key)
- else
- setsomevalue(prefix,key,value)
- end
-end
-
-local pattern = make_settings_to_hash_pattern(set,"tolerant")
-
-function interfaces.getcheckedparameters(k,p,s)
- if s and s ~= "" then
- prefix, category = p, k
- keys = k and k ~= "" and interfaces.syntax[k].keys
- lpegmatch(pattern,s)
- end
-end
-
--- _igcp_ = interfaces.getcheckedparameters
+if not modules then modules = { } end modules ['mult-chk'] = {
+ version = 1.001,
+ comment = "companion to mult-chk.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 lpegmatch = lpeg.match
+local type = type
+
+local make_settings_to_hash_pattern = utilities.parsers.make_settings_to_hash_pattern
+local settings_to_set = utilities.parsers.settings_to_set
+local allocate = utilities.storage.allocate
+
+local report_interface = logs.reporter("interface","checking")
+
+interfaces = interfaces or { }
+
+interfaces.syntax = allocate {
+ test = { keys = table.tohash { "a","b","c","d","e","f","g" } }
+}
+
+function interfaces.invalidkey(category,key)
+ report_interface("invalid key %a for %a in line %a",key,category,tex.inputlineno)
+end
+
+function interfaces.setvalidkeys(category,list)
+ local s = interfaces.syntax[category]
+ if not s then
+ interfaces.syntax[category] = {
+ keys = settings_to_set(list)
+ }
+ else
+ s.keys = settings_to_set(list)
+ end
+end
+
+function interfaces.addvalidkeys(category,list)
+ local s = interfaces.syntax[category]
+ if not s then
+ interfaces.syntax[category] = {
+ keys = settings_to_set(list)
+ }
+ else
+ settings_to_set(list,s.keys)
+ end
+end
+
+-- weird code, looks incomplete ... probably an experiment
+
+local prefix, category, keys
+
+local setsomevalue = context.setsomevalue
+local invalidkey = interfaces.invalidkey
+
+local function set(key,value)
+ if keys and not keys[key] then
+ invalidkey(category,key)
+ else
+ setsomevalue(prefix,key,value)
+ end
+end
+
+local pattern = make_settings_to_hash_pattern(set,"tolerant")
+
+function interfaces.getcheckedparameters(k,p,s)
+ if s and s ~= "" then
+ prefix, category = p, k
+ keys = k and k ~= "" and interfaces.syntax[k].keys
+ lpegmatch(pattern,s)
+ end
+end
+
+-- _igcp_ = interfaces.getcheckedparameters
diff --git a/tex/context/base/mult-fun.lua b/tex/context/base/mult-fun.lua
index e263c3559..a661c53bb 100644
--- a/tex/context/base/mult-fun.lua
+++ b/tex/context/base/mult-fun.lua
@@ -1,101 +1,101 @@
-return {
- internals = {
- --
- "nocolormodel", "greycolormodel", "graycolormodel", "rgbcolormodel", "cmykcolormodel",
- "shadefactor",
- "textextoffset",
- "normaltransparent", "multiplytransparent", "screentransparent", "overlaytransparent", "softlighttransparent",
- "hardlighttransparent", "colordodgetransparent", "colorburntransparent", "darkentransparent", "lightentransparent",
- "differencetransparent", "exclusiontransparent", "huetransparent", "saturationtransparent", "colortransparent", "luminositytransparent",
--- "originlength", "tickstep ", "ticklength",
--- "autoarrows", "ahfactor",
--- "angleoffset", anglelength", anglemethod",
- "metapostversion",
- "maxdimensions",
- },
- commands = {
- --
- "sqr", "log", "ln", "exp", "inv", "pow", "pi", "radian",
- "tand", "cotd", "sin", "cos", "tan", "cot", "atan", "asin", "acos",
- "invsin", "invcos", "acosh", "asinh", "sinh", "cosh",
- "paired", "tripled",
- "unitcircle", "fulldiamond", "unitdiamond", "fullsquare",
- -- "halfcircle", "quartercircle",
- "llcircle", "lrcircle", "urcircle", "ulcircle",
- "tcircle", "bcircle", "lcircle", "rcircle",
- "lltriangle", "lrtriangle", "urtriangle", "ultriangle",
- "smoothed", "cornered", "superellipsed", "randomized", "squeezed", "enlonged", "shortened",
- "punked", "curved", "unspiked", "simplified", "blownup", "stretched",
- "enlarged", "leftenlarged", "topenlarged", "rightenlarged", "bottomenlarged",
- "crossed", "laddered", "randomshifted", "interpolated", "paralleled", "cutends", "peepholed",
- "llenlarged", "lrenlarged", "urenlarged", "ulenlarged",
- "llmoved", "lrmoved", "urmoved", "ulmoved",
- "rightarrow", "leftarrow", "centerarrow",
- "boundingbox", "innerboundingbox", "outerboundingbox", "pushboundingbox", "popboundingbox",
- "bottomboundary", "leftboundary", "topboundary", "rightboundary",
- "xsized", "ysized", "xysized", "sized", "xyscaled",
- "intersection_point", "intersection_found", "penpoint",
- "bbwidth", "bbheight",
- "withshade", "withlinearshading", "withcircularshading", "withfromshadecolor", "withtoshadecolor", "withshading", "shadedinto",
- "withcircularshade", "withlinearshade",
- "cmyk", "spotcolor", "multitonecolor", "namedcolor",
- "drawfill", "undrawfill",
- "inverted", "uncolored", "softened", "grayed", "greyed",
- "onlayer",
- "along",
- "graphictext", "loadfigure", "externalfigure", "withmask", "figure", "register", "bitmapimage",
- "colordecimals", "ddecimal", "dddecimal", "ddddecimal",
- "textext", "thetextext", "rawtextext", "textextoffset", "verbatim", "thelabel", "label", "autoalign",
- "transparent", "withtransparency",
- "property", "properties", "withproperties",
- "asgroup",
- "infont", -- redefined usign textext
- -- "property", "withproperties", "properties", -- not yet
- "set_linear_vector", "linear_shade", "define_linear_shade", "define_circular_linear_shade", "define_sampled_linear_shade",
- "set_circular_vector", "circular_shade", "define_circular_shade", "define_circular_linear_shade", "define_sampled_circular_shade",
- "space", "CRLF",
- "grayscale", "greyscale", "withgray", "withgrey",
- "colorpart",
- "readfile",
- "clearxy", "unitvector", "center", -- redefined
- "epsed", "anchored",
- "originpath", "infinite",
- "break",
- "xstretched", "ystretched", "snapped",
- --
- "pathconnectors", "function", "constructedpath", "constructedpairs",
- "punkedfunction", "curvedfunction", "tightfunction",
- "punkedpath", "curvedpath", "tightpath",
- "punkedpairs", "curvedpairs", "tightpairs",
- --
- "evenly", "oddly",
- --
- "condition",
- --
- "pushcurrentpicture", "popcurrentpicture",
- --
- "arrowpath",
--- "colorlike", "dowithpath", "rangepath", "straightpath", "addbackground",
--- "cleanstring", "asciistring", "setunstringed", "getunstringed", "unstringed",
--- "showgrid",
--- "phantom",
--- "xshifted", "yshifted",
--- "drawarrowpath", "midarrowhead", "arrowheadonpath",
--- "drawxticks", "drawyticks", "drawticks",
--- "pointarrow",
--- "thefreelabel", "freelabel", "freedotlabel",
--- "anglebetween", "colorcircle",
--- "remapcolors", "normalcolors", "resetcolormap", "remapcolor", "remappedcolor",
--- "recolor", "refill", "redraw", "retext", "untext", "restroke", "reprocess", "repathed",
- "tensecircle", "roundedsquare",
- "colortype", "whitecolor", "blackcolor",
- --
--- "swappointlabels",
- "normalfill", "normaldraw", "visualizepaths", "naturalizepaths",
- "drawboundary", "drawwholepath", "visualizeddraw", "visualizedfill", "draworigin", "drawboundingbox",
- "drawpath", "drawpoint", "drawpoints", "drawcontrolpoints", "drawcontrollines", "drawpointlabels",
- "drawlineoptions", "drawpointoptions", "drawcontroloptions", "drawlabeloptions", "draworiginoptions", "drawboundoptions", "drawpathoptions", "resetdrawoptions",
- --
- "decorated", "redecorated", "undecorated",
- },
-}
+return {
+ internals = {
+ --
+ "nocolormodel", "greycolormodel", "graycolormodel", "rgbcolormodel", "cmykcolormodel",
+ "shadefactor",
+ "textextoffset",
+ "normaltransparent", "multiplytransparent", "screentransparent", "overlaytransparent", "softlighttransparent",
+ "hardlighttransparent", "colordodgetransparent", "colorburntransparent", "darkentransparent", "lightentransparent",
+ "differencetransparent", "exclusiontransparent", "huetransparent", "saturationtransparent", "colortransparent", "luminositytransparent",
+-- "originlength", "tickstep ", "ticklength",
+-- "autoarrows", "ahfactor",
+-- "angleoffset", anglelength", anglemethod",
+ "metapostversion",
+ "maxdimensions",
+ },
+ commands = {
+ --
+ "sqr", "log", "ln", "exp", "inv", "pow", "pi", "radian",
+ "tand", "cotd", "sin", "cos", "tan", "cot", "atan", "asin", "acos",
+ "invsin", "invcos", "acosh", "asinh", "sinh", "cosh",
+ "paired", "tripled",
+ "unitcircle", "fulldiamond", "unitdiamond", "fullsquare",
+ -- "halfcircle", "quartercircle",
+ "llcircle", "lrcircle", "urcircle", "ulcircle",
+ "tcircle", "bcircle", "lcircle", "rcircle",
+ "lltriangle", "lrtriangle", "urtriangle", "ultriangle",
+ "smoothed", "cornered", "superellipsed", "randomized", "squeezed", "enlonged", "shortened",
+ "punked", "curved", "unspiked", "simplified", "blownup", "stretched",
+ "enlarged", "leftenlarged", "topenlarged", "rightenlarged", "bottomenlarged",
+ "crossed", "laddered", "randomshifted", "interpolated", "paralleled", "cutends", "peepholed",
+ "llenlarged", "lrenlarged", "urenlarged", "ulenlarged",
+ "llmoved", "lrmoved", "urmoved", "ulmoved",
+ "rightarrow", "leftarrow", "centerarrow",
+ "boundingbox", "innerboundingbox", "outerboundingbox", "pushboundingbox", "popboundingbox",
+ "bottomboundary", "leftboundary", "topboundary", "rightboundary",
+ "xsized", "ysized", "xysized", "sized", "xyscaled",
+ "intersection_point", "intersection_found", "penpoint",
+ "bbwidth", "bbheight",
+ "withshade", "withlinearshading", "withcircularshading", "withfromshadecolor", "withtoshadecolor", "withshading", "shadedinto",
+ "withcircularshade", "withlinearshade",
+ "cmyk", "spotcolor", "multitonecolor", "namedcolor",
+ "drawfill", "undrawfill",
+ "inverted", "uncolored", "softened", "grayed", "greyed",
+ "onlayer",
+ "along",
+ "graphictext", "loadfigure", "externalfigure", "withmask", "figure", "register", "bitmapimage",
+ "colordecimals", "ddecimal", "dddecimal", "ddddecimal",
+ "textext", "thetextext", "rawtextext", "textextoffset", "verbatim", "thelabel", "label", "autoalign",
+ "transparent", "withtransparency",
+ "property", "properties", "withproperties",
+ "asgroup",
+ "infont", -- redefined usign textext
+ -- "property", "withproperties", "properties", -- not yet
+ "set_linear_vector", "linear_shade", "define_linear_shade", "define_circular_linear_shade", "define_sampled_linear_shade",
+ "set_circular_vector", "circular_shade", "define_circular_shade", "define_circular_linear_shade", "define_sampled_circular_shade",
+ "space", "CRLF",
+ "grayscale", "greyscale", "withgray", "withgrey",
+ "colorpart",
+ "readfile",
+ "clearxy", "unitvector", "center", -- redefined
+ "epsed", "anchored",
+ "originpath", "infinite",
+ "break",
+ "xstretched", "ystretched", "snapped",
+ --
+ "pathconnectors", "function", "constructedpath", "constructedpairs",
+ "punkedfunction", "curvedfunction", "tightfunction",
+ "punkedpath", "curvedpath", "tightpath",
+ "punkedpairs", "curvedpairs", "tightpairs",
+ --
+ "evenly", "oddly",
+ --
+ "condition",
+ --
+ "pushcurrentpicture", "popcurrentpicture",
+ --
+ "arrowpath",
+-- "colorlike", "dowithpath", "rangepath", "straightpath", "addbackground",
+-- "cleanstring", "asciistring", "setunstringed", "getunstringed", "unstringed",
+-- "showgrid",
+-- "phantom",
+-- "xshifted", "yshifted",
+-- "drawarrowpath", "midarrowhead", "arrowheadonpath",
+-- "drawxticks", "drawyticks", "drawticks",
+-- "pointarrow",
+-- "thefreelabel", "freelabel", "freedotlabel",
+-- "anglebetween", "colorcircle",
+-- "remapcolors", "normalcolors", "resetcolormap", "remapcolor", "remappedcolor",
+-- "recolor", "refill", "redraw", "retext", "untext", "restroke", "reprocess", "repathed",
+ "tensecircle", "roundedsquare",
+ "colortype", "whitecolor", "blackcolor",
+ --
+-- "swappointlabels",
+ "normalfill", "normaldraw", "visualizepaths", "naturalizepaths",
+ "drawboundary", "drawwholepath", "visualizeddraw", "visualizedfill", "draworigin", "drawboundingbox",
+ "drawpath", "drawpoint", "drawpoints", "drawcontrolpoints", "drawcontrollines", "drawpointlabels",
+ "drawlineoptions", "drawpointoptions", "drawcontroloptions", "drawlabeloptions", "draworiginoptions", "drawboundoptions", "drawpathoptions", "resetdrawoptions",
+ --
+ "decorated", "redecorated", "undecorated",
+ },
+}
diff --git a/tex/context/base/mult-ini.lua b/tex/context/base/mult-ini.lua
index 491557446..3b18738de 100644
--- a/tex/context/base/mult-ini.lua
+++ b/tex/context/base/mult-ini.lua
@@ -1,333 +1,333 @@
-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 = string.format, string.gmatch, string.match
-local lpegmatch = lpeg.match
-local serialize = table.serialize
-
-local allocate = utilities.storage.allocate
-local mark = utilities.storage.mark
-local prtcatcodes = catcodes.numbers.prtcatcodes
-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.corenamespaces = mark(interfaces.corenamespaces 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 corenamespaces = interfaces.corenamespaces
-local reporters = { } -- just an optimization
-
-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/corenamespaces", corenamespaces, "interfaces.corenamespaces")
-
-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
-
-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)
-
-function interfaces.registernamespace(n,namespace)
- corenamespaces[n] = namespace
-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
-
--- 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 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)
-
--- initialization
-
-function interfaces.setuserinterface(interface,response)
- sharedstorage.currentinterface, currentinterface = interface, interface
- sharedstorage.currentresponse, currentresponse = response, response
- if environment.initex then
- local nofconstants = 0
- for given, constant in next, complete.constants do
- constant = constant[interface] or constant.en or given
- constants[constant] = given -- breedte -> width
- contextsprint(prtcatcodes,"\\ui_c{",given,"}{",constant,"}") -- user interface constant
- nofconstants = nofconstants + 1
- end
- local nofvariables = 0
- for given, variable in next, complete.variables do
- variable = variable[interface] or variable.en or given
- variables[given] = variable -- ja -> yes
- contextsprint(prtcatcodes,"\\ui_v{",given,"}{",variable,"}") -- user interface variable
- nofvariables = nofvariables + 1
- end
- local nofelements = 0
- for given, element in next, complete.elements do
- element = element[interface] or element.en or given
- elements[element] = given
- contextsprint(prtcatcodes,"\\ui_e{",given,"}{",element,"}") -- user interface element
- nofelements = nofelements + 1
- end
- local nofcommands = 0
- for given, command in next, complete.commands do
- command = command[interface] or command.en or given
- if command ~= given then
- contextsprint(prtcatcodes,"\\ui_m{",given,"}{",command,"}") -- user interface macro
- end
- nofcommands = nofcommands + 1
- end
- local nofformats = 0
- for given, format in next, complete.messages.formats do
- formats[given] = format[interface] or format.en or given
- nofformats = nofformats + 1
- end
- local noftranslations = 0
- for given, translation in next, complete.messages.translations do
- translations[given] = translation[interface] or translation.en or given
- noftranslations = noftranslations + 1
- end
- report_interface("definitions: %a constants, %a variables, %a elements, %a commands, %a formats, %a translations",
- nofconstants,nofvariables,nofelements,nofcommands,nofformats,noftranslations)
- end
-end
-
-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.is_command(str)
- return (str and str ~= "" and token.csname_name(token.create(str)) ~= "") or false -- there will be a proper function for this
-end
-
-function interfaces.interfacedcommand(name)
- local command = complete.commands[name]
- return command and command[currentinterface] or name
-end
-
--- interface
-
-function commands.writestatus(category,message,...)
- local r = reporters[category]
- if r then
- r(message,...)
- end
-end
-
-commands.registernamespace = interfaces.registernamespace
-commands.setinterfaceconstant = interfaces.setconstant
-commands.setinterfacevariable = interfaces.setvariable
-commands.setinterfaceelement = interfaces.setelement
-commands.setinterfacemessage = interfaces.setmessage
-commands.setinterfacemessages = interfaces.setmessages
-commands.showmessage = interfaces.showmessage
-
-function commands.doifelsemessage(category,tag)
- commands.doifelse(interfaces.doifelsemessage(category,tag))
-end
-
-function commands.getmessage(category,tag,default)
- context(interfaces.getmessage(category,tag,default))
-end
-
-function commands.showassignerror(namespace,key,value,line)
- local ns, instance = match(namespace,"^(%d+)[^%a]+(%a+)")
- if ns then
- namespace = corenamespaces[tonumber(ns)] or ns
- end
- if 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
-
--- 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
+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 = string.format, string.gmatch, string.match
+local lpegmatch = lpeg.match
+local serialize = table.serialize
+
+local allocate = utilities.storage.allocate
+local mark = utilities.storage.mark
+local prtcatcodes = catcodes.numbers.prtcatcodes
+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.corenamespaces = mark(interfaces.corenamespaces 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 corenamespaces = interfaces.corenamespaces
+local reporters = { } -- just an optimization
+
+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/corenamespaces", corenamespaces, "interfaces.corenamespaces")
+
+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
+
+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)
+
+function interfaces.registernamespace(n,namespace)
+ corenamespaces[n] = namespace
+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
+
+-- 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 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)
+
+-- initialization
+
+function interfaces.setuserinterface(interface,response)
+ sharedstorage.currentinterface, currentinterface = interface, interface
+ sharedstorage.currentresponse, currentresponse = response, response
+ if environment.initex then
+ local nofconstants = 0
+ for given, constant in next, complete.constants do
+ constant = constant[interface] or constant.en or given
+ constants[constant] = given -- breedte -> width
+ contextsprint(prtcatcodes,"\\ui_c{",given,"}{",constant,"}") -- user interface constant
+ nofconstants = nofconstants + 1
+ end
+ local nofvariables = 0
+ for given, variable in next, complete.variables do
+ variable = variable[interface] or variable.en or given
+ variables[given] = variable -- ja -> yes
+ contextsprint(prtcatcodes,"\\ui_v{",given,"}{",variable,"}") -- user interface variable
+ nofvariables = nofvariables + 1
+ end
+ local nofelements = 0
+ for given, element in next, complete.elements do
+ element = element[interface] or element.en or given
+ elements[element] = given
+ contextsprint(prtcatcodes,"\\ui_e{",given,"}{",element,"}") -- user interface element
+ nofelements = nofelements + 1
+ end
+ local nofcommands = 0
+ for given, command in next, complete.commands do
+ command = command[interface] or command.en or given
+ if command ~= given then
+ contextsprint(prtcatcodes,"\\ui_m{",given,"}{",command,"}") -- user interface macro
+ end
+ nofcommands = nofcommands + 1
+ end
+ local nofformats = 0
+ for given, format in next, complete.messages.formats do
+ formats[given] = format[interface] or format.en or given
+ nofformats = nofformats + 1
+ end
+ local noftranslations = 0
+ for given, translation in next, complete.messages.translations do
+ translations[given] = translation[interface] or translation.en or given
+ noftranslations = noftranslations + 1
+ end
+ report_interface("definitions: %a constants, %a variables, %a elements, %a commands, %a formats, %a translations",
+ nofconstants,nofvariables,nofelements,nofcommands,nofformats,noftranslations)
+ end
+end
+
+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.is_command(str)
+ return (str and str ~= "" and token.csname_name(token.create(str)) ~= "") or false -- there will be a proper function for this
+end
+
+function interfaces.interfacedcommand(name)
+ local command = complete.commands[name]
+ return command and command[currentinterface] or name
+end
+
+-- interface
+
+function commands.writestatus(category,message,...)
+ local r = reporters[category]
+ if r then
+ r(message,...)
+ end
+end
+
+commands.registernamespace = interfaces.registernamespace
+commands.setinterfaceconstant = interfaces.setconstant
+commands.setinterfacevariable = interfaces.setvariable
+commands.setinterfaceelement = interfaces.setelement
+commands.setinterfacemessage = interfaces.setmessage
+commands.setinterfacemessages = interfaces.setmessages
+commands.showmessage = interfaces.showmessage
+
+function commands.doifelsemessage(category,tag)
+ commands.doifelse(interfaces.doifelsemessage(category,tag))
+end
+
+function commands.getmessage(category,tag,default)
+ context(interfaces.getmessage(category,tag,default))
+end
+
+function commands.showassignerror(namespace,key,value,line)
+ local ns, instance = match(namespace,"^(%d+)[^%a]+(%a+)")
+ if ns then
+ namespace = corenamespaces[tonumber(ns)] or ns
+ end
+ if 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
+
+-- 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/tex/context/base/mult-low.lua b/tex/context/base/mult-low.lua
index 46c2c24d6..47e31978b 100644
--- a/tex/context/base/mult-low.lua
+++ b/tex/context/base/mult-low.lua
@@ -1,347 +1,347 @@
-if not modules then modules = { } end modules ['mult-low'] = {
- 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"
-}
-
--- for syntax highlighters, only the ones that are for users (boring to collect them)
-
-return {
- ["constants"] = {
- --
- "zerocount", "minusone", "minustwo", "plusone", "plustwo", "plusthree", "plusfour", "plusfive",
- "plussix", "plusseven", "pluseight", "plusnine", "plusten", "plussixteen", "plushundred",
- "plusthousand", "plustenthousand", "plustwentythousand", "medcard", "maxcard",
- "zeropoint", "onepoint", "halfapoint", "onebasepoint", "maxdimen", "scaledpoint", "thousandpoint",
- "points", "halfpoint",
- "zeroskip",
- "zeromuskip", "onemuskip",
- "pluscxxvii", "pluscxxviii", "pluscclv", "pluscclvi",
- "normalpagebox",
- -- --
- "endoflinetoken", "outputnewlinechar",
- --
- "emptytoks", "empty", "undefined",
- --
- "voidbox", "emptybox", "emptyvbox", "emptyhbox",
- --
- "bigskipamount", "medskipamount", "smallskipamount",
- --
- "fmtname", "fmtversion", "texengine", "texenginename", "texengineversion",
- "luatexengine", "pdftexengine", "xetexengine", "unknownengine",
- "etexversion", "pdftexversion", "xetexversion", "xetexrevision",
- --
- "activecatcode",
- --
- "bgroup", "egroup",
- "endline",
- --
- "conditionaltrue", "conditionalfalse",
- --
- "attributeunsetvalue",
- --
- "uprotationangle", "rightrotationangle", "downrotationangle", "leftrotationangle",
- --
- "inicatcodes",
- "ctxcatcodes", "texcatcodes", "notcatcodes", "txtcatcodes", "vrbcatcodes",
- "prtcatcodes", "nilcatcodes", "luacatcodes", "tpacatcodes", "tpbcatcodes",
- "xmlcatcodes",
- --
- "escapecatcode", "begingroupcatcode", "endgroupcatcode", "mathshiftcatcode", "alignmentcatcode",
- "endoflinecatcode", "parametercatcode", "superscriptcatcode", "subscriptcatcode", "ignorecatcode",
- "spacecatcode", "lettercatcode", "othercatcode", "activecatcode", "commentcatcode", "invalidcatcode",
- --
- "tabasciicode", "newlineasciicode", "formfeedasciicode", "endoflineasciicode", "endoffileasciicode",
- "spaceasciicode", "hashasciicode", "dollarasciicode", "commentasciicode", "ampersandasciicode",
- "colonasciicode", "backslashasciicode", "circumflexasciicode", "underscoreasciicode",
- "leftbraceasciicode", "barasciicode", "rightbraceasciicode", "tildeasciicode", "delasciicode",
- "lessthanasciicode", "morethanasciicode", "doublecommentsignal",
- "atsignasciicode", "exclamationmarkasciicode", "questionmarkasciicode",
- "doublequoteasciicode", "singlequoteasciicode", "forwardslashasciicode",
- "primeasciicode",
- --
- "activemathcharcode",
- --
- "activetabtoken", "activeformfeedtoken", "activeendoflinetoken",
- --
- "batchmodecode", "nonstopmodecode", "scrollmodecode", "errorstopmodecode",
- --
- "bottomlevelgroupcode", "simplegroupcode", "hboxgroupcode", "adjustedhboxgroupcode", "vboxgroupcode",
- "vtopgroupcode", "aligngroupcode", "noaligngroupcode", "outputgroupcode", "mathgroupcode",
- "discretionarygroupcode", "insertgroupcode", "vcentergroupcode", "mathchoicegroupcode",
- "semisimplegroupcode", "mathshiftgroupcode", "mathleftgroupcode", "vadjustgroupcode",
- --
- "charnodecode", "hlistnodecode", "vlistnodecode", "rulenodecode", "insertnodecode", "marknodecode",
- "adjustnodecode", "ligaturenodecode", "discretionarynodecode", "whatsitnodecode", "mathnodecode",
- "gluenodecode", "kernnodecode", "penaltynodecode", "unsetnodecode", "mathsnodecode",
- --
- "charifcode", "catifcode", "numifcode", "dimifcode", "oddifcode", "vmodeifcode", "hmodeifcode",
- "mmodeifcode", "innerifcode", "voidifcode", "hboxifcode", "vboxifcode", "xifcode", "eofifcode",
- "trueifcode", "falseifcode", "caseifcode", "definedifcode", "csnameifcode", "fontcharifcode",
- --
- "fontslantperpoint", "fontinterwordspace", "fontinterwordstretch", "fontinterwordshrink",
- "fontexheight", "fontemwidth", "fontextraspace", "slantperpoint",
- "interwordspace", "interwordstretch", "interwordshrink", "exheight", "emwidth", "extraspace",
- "mathsupdisplay", "mathsupnormal", "mathsupcramped", "mathsubnormal", "mathsubcombined", "mathaxisheight",
- --
- -- maybe a different class
- --
- "startmode", "stopmode", "startnotmode", "stopnotmode", "startmodeset", "stopmodeset",
- "doifmode", "doifmodeelse", "doifnotmode",
- "startallmodes", "stopallmodes", "startnotallmodes", "stopnotallmodes", "doifallmodes", "doifallmodeselse", "doifnotallmodes",
- "startenvironment", "stopenvironment", "environment",
- "startcomponent", "stopcomponent", "component",
- "startproduct", "stopproduct", "product",
- "startproject", "stopproject", "project",
- "starttext", "stoptext", "startnotext", "stopnotext","startdocument", "stopdocument", "documentvariable", "setupdocument",
- "startmodule", "stopmodule", "usemodule", "usetexmodule", "useluamodule",
- --
- "startTEXpage", "stopTEXpage",
- -- "startMPpage", "stopMPpage", -- already catched by nested lexer
- --
- "enablemode", "disablemode", "preventmode",
- "globalenablemode", "globaldisablemode", "globalpreventmode",
- "pushmode", "popmode",
- --
- "typescriptone", "typescripttwo", "typescriptthree", "mathsizesuffix",
- --
- "mathordcode", "mathopcode", "mathbincode", "mathrelcode", "mathopencode", "mathclosecode",
- "mathpunctcode", "mathalphacode", "mathinnercode", "mathnothingcode", "mathlimopcode",
- "mathnolopcode", "mathboxcode", "mathchoicecode", "mathaccentcode", "mathradicalcode",
- --
- "constantnumber", "constantnumberargument", "constantdimen", "constantdimenargument", "constantemptyargument",
- --
- "continueifinputfile",
- --
- "luastringsep", "!!bs", "!!es",
- },
- ["helpers"] = {
- --
- "startsetups", "stopsetups",
- "startxmlsetups", "stopxmlsetups",
- "startluasetups", "stopluasetups",
- "starttexsetups", "stoptexsetups",
- "startrawsetups", "stoprawsetups",
- "startlocalsetups", "stoplocalsetups",
- "starttexdefinition", "stoptexdefinition",
- "starttexcode", "stoptexcode",
- "startcontextcode", "stopcontextcode",
- --
- "doifsetupselse", "doifsetups", "doifnotsetups", "setup", "setups", "texsetup", "xmlsetup", "luasetup", "directsetup",
- "doifelsecommandhandler","doifnotcommandhandler","doifcommandhandler",
- --
- "newmode", "setmode", "resetmode",
- "newsystemmode", "setsystemmode", "resetsystemmode", "pushsystemmode", "popsystemmode",
- "booleanmodevalue",
- --
- "newcount", "newdimen", "newskip", "newmuskip", "newbox", "newtoks", "newread", "newwrite", "newmarks", "newinsert", "newattribute", "newif",
- "newlanguage", "newfamily", "newfam", "newhelp", -- not used
- --
- "then",
- "begcsname",
- --
- "strippedcsname",
- --
- "firstargumentfalse", "firstargumenttrue",
- "secondargumentfalse", "secondargumenttrue",
- "thirdargumentfalse", "thirdargumenttrue",
- "fourthargumentfalse", "fourthargumenttrue",
- "fifthargumentfalse", "fifthsargumenttrue",
- "sixthargumentfalse", "sixtsargumenttrue",
- --
- "doglobal", "dodoglobal", "redoglobal", "resetglobal",
- --
- "donothing", "dontcomplain", "forgetall",
- --
- "donetrue", "donefalse",
- --
- "htdp",
- "unvoidbox",
- "hfilll", "vfilll",
- --
- "mathbox", "mathlimop", "mathnolop", "mathnothing", "mathalpha",
- --
- "currentcatcodetable", "defaultcatcodetable", "catcodetablename",
- "newcatcodetable", "startcatcodetable", "stopcatcodetable", "startextendcatcodetable", "stopextendcatcodetable",
- "pushcatcodetable", "popcatcodetable", "restorecatcodes",
- "setcatcodetable", "letcatcodecommand", "defcatcodecommand", "uedcatcodecommand",
- --
- "hglue", "vglue", "hfillneg", "vfillneg", "hfilllneg", "vfilllneg",
- --
- "ruledhss", "ruledhfil", "ruledhfill", "ruledhfilneg", "ruledhfillneg", "normalhfillneg",
- "ruledvss", "ruledvfil", "ruledvfill", "ruledvfilneg", "ruledvfillneg", "normalvfillneg",
- "ruledhbox", "ruledvbox", "ruledvtop", "ruledvcenter",
- "ruledhskip", "ruledvskip", "ruledkern", "ruledmskip", "ruledmkern",
- "ruledhglue", "ruledvglue", "normalhglue", "normalvglue",
- "ruledpenalty",
- --
- "filledhboxb", "filledhboxr", "filledhboxg", "filledhboxc", "filledhboxm", "filledhboxy", "filledhboxk",
- --
- "scratchcounter", "globalscratchcounter",
- "scratchdimen", "globalscratchdimen",
- "scratchskip", "globalscratchskip",
- "scratchmuskip", "globalscratchmuskip",
- "scratchtoks", "globalscratchtoks",
- "scratchbox", "globalscratchbox",
- --
- "availablehsize", "localhsize", "setlocalhsize",
- --
- "nextbox", "dowithnextbox", "dowithnextboxcs", "dowithnextboxcontent", "dowithnextboxcontentcs",
- --
- "scratchwidth", "scratchheight", "scratchdepth", "scratchoffset", "scratchdistance",
- "scratchhsize", "scratchvsize",
- "scratchxoffset", "scratchyoffset", "scratchhoffset", "scratchvoffset",
- "scratchxposition", "scratchyposition",
- "scratchtopoffset", "scratchbottomoffset", "scratchleftoffset", "scratchrightoffset",
- --
- "scratchcounterone", "scratchcountertwo", "scratchcounterthree",
- "scratchdimenone", "scratchdimentwo", "scratchdimenthree",
- "scratchskipone", "scratchskiptwo", "scratchskipthree",
- "scratchmuskipone", "scratchmuskiptwo", "scratchmuskipthree",
- "scratchtoksone", "scratchtokstwo", "scratchtoksthree",
- "scratchboxone", "scratchboxtwo", "scratchboxthree",
- "scratchnx", "scratchny", "scratchmx", "scratchmy",
- "scratchunicode",
- --
- "scratchleftskip", "scratchrightskip", "scratchtopskip", "scratchbottomskip",
- --
- "doif", "doifnot", "doifelse",
- "doifinset", "doifnotinset", "doifinsetelse",
- "doifnextcharelse", "doifnextoptionalelse", "doifnextbgroupelse", "doifnextparenthesiselse", "doiffastoptionalcheckelse",
- "doifundefinedelse", "doifdefinedelse", "doifundefined", "doifdefined",
- "doifelsevalue", "doifvalue", "doifnotvalue",
- "doifnothing", "doifsomething", "doifelsenothing", "doifsomethingelse",
- "doifvaluenothing", "doifvaluesomething", "doifelsevaluenothing",
- "doifdimensionelse", "doifnumberelse", "doifnumber", "doifnotnumber",
- "doifcommonelse", "doifcommon", "doifnotcommon",
- "doifinstring", "doifnotinstring", "doifinstringelse",
- "doifassignmentelse", "docheckassignment",
- --
- "tracingall", "tracingnone", "loggingall",
- --
- "removetoks", "appendtoks", "prependtoks", "appendtotoks", "prependtotoks", "to",
- --
- "endgraf", "endpar", "everyendpar", "reseteverypar", "finishpar", "empty", "null", "space", "quad", "enspace",
- "obeyspaces", "obeylines", "obeyedspace", "obeyedline",
- "normalspace",
- --
- "executeifdefined",
- --
- "singleexpandafter", "doubleexpandafter", "tripleexpandafter",
- --
- "dontleavehmode", "removelastspace", "removeunwantedspaces", "keepunwantedspaces",
- --
- "wait", "writestatus", "define", "defineexpandable", "redefine",
- --
- "setmeasure", "setemeasure", "setgmeasure", "setxmeasure", "definemeasure", "freezemeasure", "measure", "measured",
- --
- "installcorenamespace",
- --
- "getvalue", "getuvalue", "setvalue", "setevalue", "setgvalue", "setxvalue", "letvalue", "letgvalue",
- "resetvalue", "undefinevalue", "ignorevalue",
- "setuvalue", "setuevalue", "setugvalue", "setuxvalue",
- --
- "globallet", "glet", "udef", "ugdef", "uedef", "uxdef", "checked", "unique",
- --
- "getparameters", "geteparameters", "getgparameters", "getxparameters", "forgetparameters", "copyparameters",
- --
- "getdummyparameters", "dummyparameter", "directdummyparameter", "setdummyparameter", "letdummyparameter",
- "usedummystyleandcolor", "usedummystyleparameter", "usedummycolorparameter",
- --
- "processcommalist", "processcommacommand", "quitcommalist", "quitprevcommalist",
- "processaction", "processallactions", "processfirstactioninset", "processallactionsinset",
- --
- "unexpanded", "expanded", "startexpanded", "stopexpanded", "protected", "protect", "unprotect",
- --
- "firstofoneargument",
- "firstoftwoarguments", "secondoftwoarguments",
- "firstofthreearguments", "secondofthreearguments", "thirdofthreearguments",
- "firstoffourarguments", "secondoffourarguments", "thirdoffourarguments", "fourthoffourarguments",
- "firstoffivearguments", "secondoffivearguments", "thirdoffivearguments", "fourthoffivearguments", "fifthoffivearguments",
- "firstofsixarguments", "secondofsixarguments", "thirdofsixarguments", "fourthofsixarguments", "fifthofsixarguments", "sixthofsixarguments",
- --
- "firstofoneunexpanded",
- --
- "gobbleoneargument", "gobbletwoarguments", "gobblethreearguments", "gobblefourarguments", "gobblefivearguments", "gobblesixarguments", "gobblesevenarguments", "gobbleeightarguments", "gobbleninearguments", "gobbletenarguments",
- "gobbleoneoptional", "gobbletwooptionals", "gobblethreeoptionals", "gobblefouroptionals", "gobblefiveoptionals",
- --
- "dorecurse", "doloop", "exitloop", "dostepwiserecurse", "recurselevel", "recursedepth", "dofastloopcs", "dowith",
- --
- "newconstant", "setnewconstant", "newconditional", "settrue", "setfalse", "setconstant",
- "newmacro", "setnewmacro", "newfraction",
- "newsignal",
- --
- "dosingleempty", "dodoubleempty", "dotripleempty", "doquadrupleempty", "doquintupleempty", "dosixtupleempty", "doseventupleempty",
- "dosingleargument", "dodoubleargument", "dotripleargument", "doquadrupleargument", "doquintupleargument", "dosixtupleargument", "doseventupleargument",
- "dosinglegroupempty", "dodoublegroupempty", "dotriplegroupempty", "doquadruplegroupempty", "doquintuplegroupempty",
- "permitspacesbetweengroups", "dontpermitspacesbetweengroups",
- --
- "nopdfcompression", "maximumpdfcompression", "normalpdfcompression",
- --
- "modulonumber", "dividenumber",
- --
- "getfirstcharacter", "doiffirstcharelse",
- --
- "startnointerference", "stopnointerference",
- --
- "twodigits","threedigits",
- --
- "strut", "setstrut", "strutbox", "strutht", "strutdp", "strutwd", "struthtdp", "begstrut", "endstrut", "lineheight",
- --
- "ordordspacing", "ordopspacing", "ordbinspacing", "ordrelspacing",
- "ordopenspacing", "ordclosespacing", "ordpunctspacing", "ordinnerspacing",
- --
- "opordspacing", "opopspacing", "opbinspacing", "oprelspacing",
- "opopenspacing", "opclosespacing", "oppunctspacing", "opinnerspacing",
- --
- "binordspacing", "binopspacing", "binbinspacing", "binrelspacing",
- "binopenspacing", "binclosespacing", "binpunctspacing", "bininnerspacing",
- --
- "relordspacing", "relopspacing", "relbinspacing", "relrelspacing",
- "relopenspacing", "relclosespacing", "relpunctspacing", "relinnerspacing",
- --
- "openordspacing", "openopspacing", "openbinspacing", "openrelspacing",
- "openopenspacing", "openclosespacing", "openpunctspacing", "openinnerspacing",
- --
- "closeordspacing", "closeopspacing", "closebinspacing", "closerelspacing",
- "closeopenspacing", "closeclosespacing", "closepunctspacing", "closeinnerspacing",
- --
- "punctordspacing", "punctopspacing", "punctbinspacing", "punctrelspacing",
- "punctopenspacing", "punctclosespacing", "punctpunctspacing", "punctinnerspacing",
- --
- "innerordspacing", "inneropspacing", "innerbinspacing", "innerrelspacing",
- "inneropenspacing", "innerclosespacing", "innerpunctspacing", "innerinnerspacing",
- --
- "normalreqno",
- --
- "startimath", "stopimath", "normalstartimath", "normalstopimath",
- "startdmath", "stopdmath", "normalstartdmath", "normalstopdmath",
- --
- "uncramped", "cramped", "triggermathstyle", "mathstylefont", "mathsmallstylefont", "mathstyleface", "mathsmallstyleface", "mathstylecommand", "mathpalette",
- "mathstylehbox", "mathstylevbox", "mathstylevcenter", "mathstylevcenteredhbox", "mathstylevcenteredvbox",
- "mathtext", "setmathsmalltextbox", "setmathtextbox",
- --
- "triggerdisplaystyle", "triggertextstyle", "triggerscriptstyle", "triggerscriptscriptstyle",
- "triggeruncrampedstyle", "triggercrampedstyle",
- "triggersmallstyle", "triggeruncrampedsmallstyle", "triggercrampedsmallstyle",
- "triggerbigstyle", "triggeruncrampedbigstyle", "triggercrampedbigstyle",
- --
- "luaexpr", "expdoifelse", "expdoif", "expdoifnot", "expdoifcommonelse", "expdoifinsetelse",
- --
- "ctxdirectlua", "ctxlatelua", "ctxsprint", "ctxwrite", "ctxcommand", "ctxdirectcommand", "ctxlatecommand", "ctxreport",
- "ctxlua", "luacode", "lateluacode", "directluacode",
- "registerctxluafile", "ctxloadluafile",
- "luaversion", "luamajorversion", "luaminorversion",
- "ctxluacode", "luaconditional", "luaexpanded",
- "startluaparameterset", "stopluaparameterset", "luaparameterset",
- "definenamedlua",
- "obeylualines", "obeyluatokens",
- "startluacode", "stopluacode", "startlua", "stoplua",
- --
- "carryoverpar",
- --
- "Umathbotaccent",
- }
-}
+if not modules then modules = { } end modules ['mult-low'] = {
+ 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"
+}
+
+-- for syntax highlighters, only the ones that are for users (boring to collect them)
+
+return {
+ ["constants"] = {
+ --
+ "zerocount", "minusone", "minustwo", "plusone", "plustwo", "plusthree", "plusfour", "plusfive",
+ "plussix", "plusseven", "pluseight", "plusnine", "plusten", "plussixteen", "plushundred",
+ "plusthousand", "plustenthousand", "plustwentythousand", "medcard", "maxcard",
+ "zeropoint", "onepoint", "halfapoint", "onebasepoint", "maxdimen", "scaledpoint", "thousandpoint",
+ "points", "halfpoint",
+ "zeroskip",
+ "zeromuskip", "onemuskip",
+ "pluscxxvii", "pluscxxviii", "pluscclv", "pluscclvi",
+ "normalpagebox",
+ -- --
+ "endoflinetoken", "outputnewlinechar",
+ --
+ "emptytoks", "empty", "undefined",
+ --
+ "voidbox", "emptybox", "emptyvbox", "emptyhbox",
+ --
+ "bigskipamount", "medskipamount", "smallskipamount",
+ --
+ "fmtname", "fmtversion", "texengine", "texenginename", "texengineversion",
+ "luatexengine", "pdftexengine", "xetexengine", "unknownengine",
+ "etexversion", "pdftexversion", "xetexversion", "xetexrevision",
+ --
+ "activecatcode",
+ --
+ "bgroup", "egroup",
+ "endline",
+ --
+ "conditionaltrue", "conditionalfalse",
+ --
+ "attributeunsetvalue",
+ --
+ "uprotationangle", "rightrotationangle", "downrotationangle", "leftrotationangle",
+ --
+ "inicatcodes",
+ "ctxcatcodes", "texcatcodes", "notcatcodes", "txtcatcodes", "vrbcatcodes",
+ "prtcatcodes", "nilcatcodes", "luacatcodes", "tpacatcodes", "tpbcatcodes",
+ "xmlcatcodes",
+ --
+ "escapecatcode", "begingroupcatcode", "endgroupcatcode", "mathshiftcatcode", "alignmentcatcode",
+ "endoflinecatcode", "parametercatcode", "superscriptcatcode", "subscriptcatcode", "ignorecatcode",
+ "spacecatcode", "lettercatcode", "othercatcode", "activecatcode", "commentcatcode", "invalidcatcode",
+ --
+ "tabasciicode", "newlineasciicode", "formfeedasciicode", "endoflineasciicode", "endoffileasciicode",
+ "spaceasciicode", "hashasciicode", "dollarasciicode", "commentasciicode", "ampersandasciicode",
+ "colonasciicode", "backslashasciicode", "circumflexasciicode", "underscoreasciicode",
+ "leftbraceasciicode", "barasciicode", "rightbraceasciicode", "tildeasciicode", "delasciicode",
+ "lessthanasciicode", "morethanasciicode", "doublecommentsignal",
+ "atsignasciicode", "exclamationmarkasciicode", "questionmarkasciicode",
+ "doublequoteasciicode", "singlequoteasciicode", "forwardslashasciicode",
+ "primeasciicode",
+ --
+ "activemathcharcode",
+ --
+ "activetabtoken", "activeformfeedtoken", "activeendoflinetoken",
+ --
+ "batchmodecode", "nonstopmodecode", "scrollmodecode", "errorstopmodecode",
+ --
+ "bottomlevelgroupcode", "simplegroupcode", "hboxgroupcode", "adjustedhboxgroupcode", "vboxgroupcode",
+ "vtopgroupcode", "aligngroupcode", "noaligngroupcode", "outputgroupcode", "mathgroupcode",
+ "discretionarygroupcode", "insertgroupcode", "vcentergroupcode", "mathchoicegroupcode",
+ "semisimplegroupcode", "mathshiftgroupcode", "mathleftgroupcode", "vadjustgroupcode",
+ --
+ "charnodecode", "hlistnodecode", "vlistnodecode", "rulenodecode", "insertnodecode", "marknodecode",
+ "adjustnodecode", "ligaturenodecode", "discretionarynodecode", "whatsitnodecode", "mathnodecode",
+ "gluenodecode", "kernnodecode", "penaltynodecode", "unsetnodecode", "mathsnodecode",
+ --
+ "charifcode", "catifcode", "numifcode", "dimifcode", "oddifcode", "vmodeifcode", "hmodeifcode",
+ "mmodeifcode", "innerifcode", "voidifcode", "hboxifcode", "vboxifcode", "xifcode", "eofifcode",
+ "trueifcode", "falseifcode", "caseifcode", "definedifcode", "csnameifcode", "fontcharifcode",
+ --
+ "fontslantperpoint", "fontinterwordspace", "fontinterwordstretch", "fontinterwordshrink",
+ "fontexheight", "fontemwidth", "fontextraspace", "slantperpoint",
+ "interwordspace", "interwordstretch", "interwordshrink", "exheight", "emwidth", "extraspace",
+ "mathsupdisplay", "mathsupnormal", "mathsupcramped", "mathsubnormal", "mathsubcombined", "mathaxisheight",
+ --
+ -- maybe a different class
+ --
+ "startmode", "stopmode", "startnotmode", "stopnotmode", "startmodeset", "stopmodeset",
+ "doifmode", "doifmodeelse", "doifnotmode",
+ "startallmodes", "stopallmodes", "startnotallmodes", "stopnotallmodes", "doifallmodes", "doifallmodeselse", "doifnotallmodes",
+ "startenvironment", "stopenvironment", "environment",
+ "startcomponent", "stopcomponent", "component",
+ "startproduct", "stopproduct", "product",
+ "startproject", "stopproject", "project",
+ "starttext", "stoptext", "startnotext", "stopnotext","startdocument", "stopdocument", "documentvariable", "setupdocument",
+ "startmodule", "stopmodule", "usemodule", "usetexmodule", "useluamodule",
+ --
+ "startTEXpage", "stopTEXpage",
+ -- "startMPpage", "stopMPpage", -- already catched by nested lexer
+ --
+ "enablemode", "disablemode", "preventmode",
+ "globalenablemode", "globaldisablemode", "globalpreventmode",
+ "pushmode", "popmode",
+ --
+ "typescriptone", "typescripttwo", "typescriptthree", "mathsizesuffix",
+ --
+ "mathordcode", "mathopcode", "mathbincode", "mathrelcode", "mathopencode", "mathclosecode",
+ "mathpunctcode", "mathalphacode", "mathinnercode", "mathnothingcode", "mathlimopcode",
+ "mathnolopcode", "mathboxcode", "mathchoicecode", "mathaccentcode", "mathradicalcode",
+ --
+ "constantnumber", "constantnumberargument", "constantdimen", "constantdimenargument", "constantemptyargument",
+ --
+ "continueifinputfile",
+ --
+ "luastringsep", "!!bs", "!!es",
+ },
+ ["helpers"] = {
+ --
+ "startsetups", "stopsetups",
+ "startxmlsetups", "stopxmlsetups",
+ "startluasetups", "stopluasetups",
+ "starttexsetups", "stoptexsetups",
+ "startrawsetups", "stoprawsetups",
+ "startlocalsetups", "stoplocalsetups",
+ "starttexdefinition", "stoptexdefinition",
+ "starttexcode", "stoptexcode",
+ "startcontextcode", "stopcontextcode",
+ --
+ "doifsetupselse", "doifsetups", "doifnotsetups", "setup", "setups", "texsetup", "xmlsetup", "luasetup", "directsetup",
+ "doifelsecommandhandler","doifnotcommandhandler","doifcommandhandler",
+ --
+ "newmode", "setmode", "resetmode",
+ "newsystemmode", "setsystemmode", "resetsystemmode", "pushsystemmode", "popsystemmode",
+ "booleanmodevalue",
+ --
+ "newcount", "newdimen", "newskip", "newmuskip", "newbox", "newtoks", "newread", "newwrite", "newmarks", "newinsert", "newattribute", "newif",
+ "newlanguage", "newfamily", "newfam", "newhelp", -- not used
+ --
+ "then",
+ "begcsname",
+ --
+ "strippedcsname",
+ --
+ "firstargumentfalse", "firstargumenttrue",
+ "secondargumentfalse", "secondargumenttrue",
+ "thirdargumentfalse", "thirdargumenttrue",
+ "fourthargumentfalse", "fourthargumenttrue",
+ "fifthargumentfalse", "fifthsargumenttrue",
+ "sixthargumentfalse", "sixtsargumenttrue",
+ --
+ "doglobal", "dodoglobal", "redoglobal", "resetglobal",
+ --
+ "donothing", "dontcomplain", "forgetall",
+ --
+ "donetrue", "donefalse",
+ --
+ "htdp",
+ "unvoidbox",
+ "hfilll", "vfilll",
+ --
+ "mathbox", "mathlimop", "mathnolop", "mathnothing", "mathalpha",
+ --
+ "currentcatcodetable", "defaultcatcodetable", "catcodetablename",
+ "newcatcodetable", "startcatcodetable", "stopcatcodetable", "startextendcatcodetable", "stopextendcatcodetable",
+ "pushcatcodetable", "popcatcodetable", "restorecatcodes",
+ "setcatcodetable", "letcatcodecommand", "defcatcodecommand", "uedcatcodecommand",
+ --
+ "hglue", "vglue", "hfillneg", "vfillneg", "hfilllneg", "vfilllneg",
+ --
+ "ruledhss", "ruledhfil", "ruledhfill", "ruledhfilneg", "ruledhfillneg", "normalhfillneg",
+ "ruledvss", "ruledvfil", "ruledvfill", "ruledvfilneg", "ruledvfillneg", "normalvfillneg",
+ "ruledhbox", "ruledvbox", "ruledvtop", "ruledvcenter",
+ "ruledhskip", "ruledvskip", "ruledkern", "ruledmskip", "ruledmkern",
+ "ruledhglue", "ruledvglue", "normalhglue", "normalvglue",
+ "ruledpenalty",
+ --
+ "filledhboxb", "filledhboxr", "filledhboxg", "filledhboxc", "filledhboxm", "filledhboxy", "filledhboxk",
+ --
+ "scratchcounter", "globalscratchcounter",
+ "scratchdimen", "globalscratchdimen",
+ "scratchskip", "globalscratchskip",
+ "scratchmuskip", "globalscratchmuskip",
+ "scratchtoks", "globalscratchtoks",
+ "scratchbox", "globalscratchbox",
+ --
+ "availablehsize", "localhsize", "setlocalhsize",
+ --
+ "nextbox", "dowithnextbox", "dowithnextboxcs", "dowithnextboxcontent", "dowithnextboxcontentcs",
+ --
+ "scratchwidth", "scratchheight", "scratchdepth", "scratchoffset", "scratchdistance",
+ "scratchhsize", "scratchvsize",
+ "scratchxoffset", "scratchyoffset", "scratchhoffset", "scratchvoffset",
+ "scratchxposition", "scratchyposition",
+ "scratchtopoffset", "scratchbottomoffset", "scratchleftoffset", "scratchrightoffset",
+ --
+ "scratchcounterone", "scratchcountertwo", "scratchcounterthree",
+ "scratchdimenone", "scratchdimentwo", "scratchdimenthree",
+ "scratchskipone", "scratchskiptwo", "scratchskipthree",
+ "scratchmuskipone", "scratchmuskiptwo", "scratchmuskipthree",
+ "scratchtoksone", "scratchtokstwo", "scratchtoksthree",
+ "scratchboxone", "scratchboxtwo", "scratchboxthree",
+ "scratchnx", "scratchny", "scratchmx", "scratchmy",
+ "scratchunicode",
+ --
+ "scratchleftskip", "scratchrightskip", "scratchtopskip", "scratchbottomskip",
+ --
+ "doif", "doifnot", "doifelse",
+ "doifinset", "doifnotinset", "doifinsetelse",
+ "doifnextcharelse", "doifnextoptionalelse", "doifnextbgroupelse", "doifnextparenthesiselse", "doiffastoptionalcheckelse",
+ "doifundefinedelse", "doifdefinedelse", "doifundefined", "doifdefined",
+ "doifelsevalue", "doifvalue", "doifnotvalue",
+ "doifnothing", "doifsomething", "doifelsenothing", "doifsomethingelse",
+ "doifvaluenothing", "doifvaluesomething", "doifelsevaluenothing",
+ "doifdimensionelse", "doifnumberelse", "doifnumber", "doifnotnumber",
+ "doifcommonelse", "doifcommon", "doifnotcommon",
+ "doifinstring", "doifnotinstring", "doifinstringelse",
+ "doifassignmentelse", "docheckassignment",
+ --
+ "tracingall", "tracingnone", "loggingall",
+ --
+ "removetoks", "appendtoks", "prependtoks", "appendtotoks", "prependtotoks", "to",
+ --
+ "endgraf", "endpar", "everyendpar", "reseteverypar", "finishpar", "empty", "null", "space", "quad", "enspace",
+ "obeyspaces", "obeylines", "obeyedspace", "obeyedline",
+ "normalspace",
+ --
+ "executeifdefined",
+ --
+ "singleexpandafter", "doubleexpandafter", "tripleexpandafter",
+ --
+ "dontleavehmode", "removelastspace", "removeunwantedspaces", "keepunwantedspaces",
+ --
+ "wait", "writestatus", "define", "defineexpandable", "redefine",
+ --
+ "setmeasure", "setemeasure", "setgmeasure", "setxmeasure", "definemeasure", "freezemeasure", "measure", "measured",
+ --
+ "installcorenamespace",
+ --
+ "getvalue", "getuvalue", "setvalue", "setevalue", "setgvalue", "setxvalue", "letvalue", "letgvalue",
+ "resetvalue", "undefinevalue", "ignorevalue",
+ "setuvalue", "setuevalue", "setugvalue", "setuxvalue",
+ --
+ "globallet", "glet", "udef", "ugdef", "uedef", "uxdef", "checked", "unique",
+ --
+ "getparameters", "geteparameters", "getgparameters", "getxparameters", "forgetparameters", "copyparameters",
+ --
+ "getdummyparameters", "dummyparameter", "directdummyparameter", "setdummyparameter", "letdummyparameter",
+ "usedummystyleandcolor", "usedummystyleparameter", "usedummycolorparameter",
+ --
+ "processcommalist", "processcommacommand", "quitcommalist", "quitprevcommalist",
+ "processaction", "processallactions", "processfirstactioninset", "processallactionsinset",
+ --
+ "unexpanded", "expanded", "startexpanded", "stopexpanded", "protected", "protect", "unprotect",
+ --
+ "firstofoneargument",
+ "firstoftwoarguments", "secondoftwoarguments",
+ "firstofthreearguments", "secondofthreearguments", "thirdofthreearguments",
+ "firstoffourarguments", "secondoffourarguments", "thirdoffourarguments", "fourthoffourarguments",
+ "firstoffivearguments", "secondoffivearguments", "thirdoffivearguments", "fourthoffivearguments", "fifthoffivearguments",
+ "firstofsixarguments", "secondofsixarguments", "thirdofsixarguments", "fourthofsixarguments", "fifthofsixarguments", "sixthofsixarguments",
+ --
+ "firstofoneunexpanded",
+ --
+ "gobbleoneargument", "gobbletwoarguments", "gobblethreearguments", "gobblefourarguments", "gobblefivearguments", "gobblesixarguments", "gobblesevenarguments", "gobbleeightarguments", "gobbleninearguments", "gobbletenarguments",
+ "gobbleoneoptional", "gobbletwooptionals", "gobblethreeoptionals", "gobblefouroptionals", "gobblefiveoptionals",
+ --
+ "dorecurse", "doloop", "exitloop", "dostepwiserecurse", "recurselevel", "recursedepth", "dofastloopcs", "dowith",
+ --
+ "newconstant", "setnewconstant", "newconditional", "settrue", "setfalse", "setconstant",
+ "newmacro", "setnewmacro", "newfraction",
+ "newsignal",
+ --
+ "dosingleempty", "dodoubleempty", "dotripleempty", "doquadrupleempty", "doquintupleempty", "dosixtupleempty", "doseventupleempty",
+ "dosingleargument", "dodoubleargument", "dotripleargument", "doquadrupleargument", "doquintupleargument", "dosixtupleargument", "doseventupleargument",
+ "dosinglegroupempty", "dodoublegroupempty", "dotriplegroupempty", "doquadruplegroupempty", "doquintuplegroupempty",
+ "permitspacesbetweengroups", "dontpermitspacesbetweengroups",
+ --
+ "nopdfcompression", "maximumpdfcompression", "normalpdfcompression",
+ --
+ "modulonumber", "dividenumber",
+ --
+ "getfirstcharacter", "doiffirstcharelse",
+ --
+ "startnointerference", "stopnointerference",
+ --
+ "twodigits","threedigits",
+ --
+ "strut", "setstrut", "strutbox", "strutht", "strutdp", "strutwd", "struthtdp", "begstrut", "endstrut", "lineheight",
+ --
+ "ordordspacing", "ordopspacing", "ordbinspacing", "ordrelspacing",
+ "ordopenspacing", "ordclosespacing", "ordpunctspacing", "ordinnerspacing",
+ --
+ "opordspacing", "opopspacing", "opbinspacing", "oprelspacing",
+ "opopenspacing", "opclosespacing", "oppunctspacing", "opinnerspacing",
+ --
+ "binordspacing", "binopspacing", "binbinspacing", "binrelspacing",
+ "binopenspacing", "binclosespacing", "binpunctspacing", "bininnerspacing",
+ --
+ "relordspacing", "relopspacing", "relbinspacing", "relrelspacing",
+ "relopenspacing", "relclosespacing", "relpunctspacing", "relinnerspacing",
+ --
+ "openordspacing", "openopspacing", "openbinspacing", "openrelspacing",
+ "openopenspacing", "openclosespacing", "openpunctspacing", "openinnerspacing",
+ --
+ "closeordspacing", "closeopspacing", "closebinspacing", "closerelspacing",
+ "closeopenspacing", "closeclosespacing", "closepunctspacing", "closeinnerspacing",
+ --
+ "punctordspacing", "punctopspacing", "punctbinspacing", "punctrelspacing",
+ "punctopenspacing", "punctclosespacing", "punctpunctspacing", "punctinnerspacing",
+ --
+ "innerordspacing", "inneropspacing", "innerbinspacing", "innerrelspacing",
+ "inneropenspacing", "innerclosespacing", "innerpunctspacing", "innerinnerspacing",
+ --
+ "normalreqno",
+ --
+ "startimath", "stopimath", "normalstartimath", "normalstopimath",
+ "startdmath", "stopdmath", "normalstartdmath", "normalstopdmath",
+ --
+ "uncramped", "cramped", "triggermathstyle", "mathstylefont", "mathsmallstylefont", "mathstyleface", "mathsmallstyleface", "mathstylecommand", "mathpalette",
+ "mathstylehbox", "mathstylevbox", "mathstylevcenter", "mathstylevcenteredhbox", "mathstylevcenteredvbox",
+ "mathtext", "setmathsmalltextbox", "setmathtextbox",
+ --
+ "triggerdisplaystyle", "triggertextstyle", "triggerscriptstyle", "triggerscriptscriptstyle",
+ "triggeruncrampedstyle", "triggercrampedstyle",
+ "triggersmallstyle", "triggeruncrampedsmallstyle", "triggercrampedsmallstyle",
+ "triggerbigstyle", "triggeruncrampedbigstyle", "triggercrampedbigstyle",
+ --
+ "luaexpr", "expdoifelse", "expdoif", "expdoifnot", "expdoifcommonelse", "expdoifinsetelse",
+ --
+ "ctxdirectlua", "ctxlatelua", "ctxsprint", "ctxwrite", "ctxcommand", "ctxdirectcommand", "ctxlatecommand", "ctxreport",
+ "ctxlua", "luacode", "lateluacode", "directluacode",
+ "registerctxluafile", "ctxloadluafile",
+ "luaversion", "luamajorversion", "luaminorversion",
+ "ctxluacode", "luaconditional", "luaexpanded",
+ "startluaparameterset", "stopluaparameterset", "luaparameterset",
+ "definenamedlua",
+ "obeylualines", "obeyluatokens",
+ "startluacode", "stopluacode", "startlua", "stoplua",
+ --
+ "carryoverpar",
+ --
+ "Umathbotaccent",
+ }
+}
diff --git a/tex/context/base/mult-mps.lua b/tex/context/base/mult-mps.lua
index f599111e8..59411cd97 100644
--- a/tex/context/base/mult-mps.lua
+++ b/tex/context/base/mult-mps.lua
@@ -1,115 +1,115 @@
-return {
- tex = {
- "btex", "etex", "verbatimtex",
- },
- shortcuts = {
- "..", "...", "--", "---", "&",
- },
- primitives = { -- to be checked
- "charcode", "day", "linecap", "linejoin", "miterlimit", "month", "pausing",
- "prologues", "showstopping", "time", "tracingcapsules", "tracingchoices", "mpprocset",
- "tracingcommands", "tracingequations", "tracinglostchars",
- "tracingmacros", "tracingonline", "tracingoutput", "tracingrestores",
- "tracingspecs", "tracingstats", "tracingtitles", "truecorners",
- "warningcheck", "year",
- "false", "nullpicture", "pencircle", "true",
- "and", "angle", "arclength", "arctime", "ASCII", "boolean", "bot",
- "char", "color", "cosd", "cycle", "decimal", "directiontime", "floor", "fontsize",
- "hex", "infont", "intersectiontimes", "known", "length", "llcorner",
- "lrcorner", "makepath", "makepen", "mexp", "mlog", "normaldeviate", "not",
- "numeric", "oct", "odd", "or", "path", "pair", "pen", "penoffset", "picture", "point",
- "postcontrol", "precontrol", "reverse", "rotated", "scaled",
- "shifted", "sind", "slanted", "sqrt", "str", "string", "subpath", "substring",
- "transform", "transformed", "ulcorner", "uniformdeviate", "unknown",
- "urcorner", "xpart", "xscaled", "xxpart", "xypart", "ypart", "yscaled", "yxpart",
- "yypart", "zscaled",
- "addto", "clip", "input", "interim", "let", "newinternal", "save", "setbounds",
- "shipout", "show", "showdependencies", "showtoken", "showvariable",
- "special",
- "begingroup", "endgroup", "of", "curl", "tension", "and", "controls",
- "interpath", "on", "off",
- "def", "vardef", "enddef", "expr", "suffix", "text", "primary", "secondary",
- "tertiary", "primarydef", "secondarydef", "tertiarydef",
- "randomseed", "also", "contour", "doublepath",
- "withcolor", "withpen", "dashed", "if", "else", "elseif", "fi", "for", "endfor", "forever", "exitif", "within",
- "forsuffixes", "downto", "upto", "step", "until",
- "charlist", "extensible", "fontdimen", "headerbyte", "kern", "ligtable",
- "boundarychar", "chardp", "charext", "charht", "charic", "charwd", "designsize",
- "fontmaking", "charexists",
- "cullit", "currenttransform", "gfcorners", "grayfont", "hround",
- "imagerules", "lowres_fix", "nodisplays", "notransforms", "openit",
- "displaying", "currentwindow", "screen_rows", "screen_cols",
- "pixels_per_inch", "cull", "display", "openwindow", "numspecial",
- "totalweight", "autorounding", "fillin", "proofing", "tracingpens",
- "xoffset", "chardx", "granularity", "smoothing", "turningcheck", "yoffset",
- "chardy", "hppp", "tracingedges", "vppp",
- "extra_beginfig", "extra_endfig", "mpxbreak",
- "endinput",
- "message", "delimiters", "turningnumber", "errmessage",
- "readstring", "scantokens", "end", "outer", "inner", "write", "to", "readfrom",
- "withprescript", "withpostscript",
- "top", "bot", "lft", "rt", "ulft", "urt", "llft", "lrt",
- --
- "redpart", "greenpart", "bluepart", "cyanpart", "magentapart", "yellowpart", "blackpart", "greypart",
- "rgbcolor", "cmykcolor", "greycolor", "graycolor",
- "colormodel", "graypart",
- "dashpart", "penpart",
--- "colorpart",
- "stroked", "filled", "textual", "clipped", "bounded",
- "expandafter",
- },
- commands = {
- "beginfig", "endfig",
- "rotatedaround", "reflectedabout",
- "arrowhead",
- "currentpen", "currentpicture", "cuttings",
- "defaultfont", "extra_beginfig", "extra_endfig",
- "ditto", "EOF", "down",
- "evenly", "fullcircle", "halfcircle", "identity", "in", "left",
- "origin", "pensquare", "quartercircle", "right",
- "unitsquare", "up", "withdots",
- "abs", "bbox", "ceiling", "center", "cutafter", "cutbefore", "dir",
- "directionpoint", "div", "dotprod", "intersectionpoint", "inverse", "mod",
- "round", "unitvector", "whatever",
- "cutdraw", "draw", "drawarrow", "drawdblarrow", "fill", "filldraw", "drawdot",
- "loggingall", "interact", "tracingall", "tracingnone",
- "pickup",
- "undraw", "unfill", "unfilldraw",
- "buildcycle", "dashpattern", "decr", "dotlabel", "dotlabels", "drawoptions",
- "incr", "label", "labels", "max", "min", "thelabel", "z",
- "beginchar", "blacker", "capsule_end", "change_width",
- "define_blacker_pixels", "define_corrected_pixels",
- "define_good_x_pixels", "define_good_y_pixels",
- "define_horizontal_corrected_pixels", "define_pixels",
- "define_whole_blacker_pixels", "define_whole_pixels",
- "define_whole_vertical_blacker_pixels",
- "define_whole_vertical_pixels", "endchar", "extra_beginchar",
- "extra_endchar", "extra_setup", "font_coding_scheme",
- "clearxy", "clearit", "clearpen", "shipit",
- "font_extra_space",
- "exitunless",
- "relax", "hide", "gobble", "gobbled", "stop",
- "blankpicture",
- "counterclockwise", "tensepath", "takepower", "direction",
- "softjoin", -- "magstep",
- "makelabel", -- "laboff",
- "rotatedabout", "flex", "superellipse", "erase", "image",
- "nullpen", "savepen", "clearpen", "penpos", "penlabels", -- "clear_pen_memory",
- "range", "numtok", "thru",
- "z", "laboff",
- "bye",
- --
- "red", "green", "blue", "cyan", "magenta", "yellow", "black", "white", "background",
- "graypart", "graycolor",
- --
- "mm", "pt", "dd", "bp", "cm", "pc", "cc", "in",
- },
- internals = { -- we need to remove duplicates above
- --
- "mitered", "rounded", "beveled", "butt", "squared",
- "eps", "epsilon", "infinity",
- "bboxmargin", "ahlength", "ahangle", "labeloffset", "dotlabeldiam", "defaultpen", "defaultscale", "join_radius",
- --
- "pen_lft", "pen_rt", "pen_top", "pen_bot", -- "pen_count_",
- },
-}
+return {
+ tex = {
+ "btex", "etex", "verbatimtex",
+ },
+ shortcuts = {
+ "..", "...", "--", "---", "&",
+ },
+ primitives = { -- to be checked
+ "charcode", "day", "linecap", "linejoin", "miterlimit", "month", "pausing",
+ "prologues", "showstopping", "time", "tracingcapsules", "tracingchoices", "mpprocset",
+ "tracingcommands", "tracingequations", "tracinglostchars",
+ "tracingmacros", "tracingonline", "tracingoutput", "tracingrestores",
+ "tracingspecs", "tracingstats", "tracingtitles", "truecorners",
+ "warningcheck", "year",
+ "false", "nullpicture", "pencircle", "true",
+ "and", "angle", "arclength", "arctime", "ASCII", "boolean", "bot",
+ "char", "color", "cosd", "cycle", "decimal", "directiontime", "floor", "fontsize",
+ "hex", "infont", "intersectiontimes", "known", "length", "llcorner",
+ "lrcorner", "makepath", "makepen", "mexp", "mlog", "normaldeviate", "not",
+ "numeric", "oct", "odd", "or", "path", "pair", "pen", "penoffset", "picture", "point",
+ "postcontrol", "precontrol", "reverse", "rotated", "scaled",
+ "shifted", "sind", "slanted", "sqrt", "str", "string", "subpath", "substring",
+ "transform", "transformed", "ulcorner", "uniformdeviate", "unknown",
+ "urcorner", "xpart", "xscaled", "xxpart", "xypart", "ypart", "yscaled", "yxpart",
+ "yypart", "zscaled",
+ "addto", "clip", "input", "interim", "let", "newinternal", "save", "setbounds",
+ "shipout", "show", "showdependencies", "showtoken", "showvariable",
+ "special",
+ "begingroup", "endgroup", "of", "curl", "tension", "and", "controls",
+ "interpath", "on", "off",
+ "def", "vardef", "enddef", "expr", "suffix", "text", "primary", "secondary",
+ "tertiary", "primarydef", "secondarydef", "tertiarydef",
+ "randomseed", "also", "contour", "doublepath",
+ "withcolor", "withpen", "dashed", "if", "else", "elseif", "fi", "for", "endfor", "forever", "exitif", "within",
+ "forsuffixes", "downto", "upto", "step", "until",
+ "charlist", "extensible", "fontdimen", "headerbyte", "kern", "ligtable",
+ "boundarychar", "chardp", "charext", "charht", "charic", "charwd", "designsize",
+ "fontmaking", "charexists",
+ "cullit", "currenttransform", "gfcorners", "grayfont", "hround",
+ "imagerules", "lowres_fix", "nodisplays", "notransforms", "openit",
+ "displaying", "currentwindow", "screen_rows", "screen_cols",
+ "pixels_per_inch", "cull", "display", "openwindow", "numspecial",
+ "totalweight", "autorounding", "fillin", "proofing", "tracingpens",
+ "xoffset", "chardx", "granularity", "smoothing", "turningcheck", "yoffset",
+ "chardy", "hppp", "tracingedges", "vppp",
+ "extra_beginfig", "extra_endfig", "mpxbreak",
+ "endinput",
+ "message", "delimiters", "turningnumber", "errmessage",
+ "readstring", "scantokens", "end", "outer", "inner", "write", "to", "readfrom",
+ "withprescript", "withpostscript",
+ "top", "bot", "lft", "rt", "ulft", "urt", "llft", "lrt",
+ --
+ "redpart", "greenpart", "bluepart", "cyanpart", "magentapart", "yellowpart", "blackpart", "greypart",
+ "rgbcolor", "cmykcolor", "greycolor", "graycolor",
+ "colormodel", "graypart",
+ "dashpart", "penpart",
+-- "colorpart",
+ "stroked", "filled", "textual", "clipped", "bounded",
+ "expandafter",
+ },
+ commands = {
+ "beginfig", "endfig",
+ "rotatedaround", "reflectedabout",
+ "arrowhead",
+ "currentpen", "currentpicture", "cuttings",
+ "defaultfont", "extra_beginfig", "extra_endfig",
+ "ditto", "EOF", "down",
+ "evenly", "fullcircle", "halfcircle", "identity", "in", "left",
+ "origin", "pensquare", "quartercircle", "right",
+ "unitsquare", "up", "withdots",
+ "abs", "bbox", "ceiling", "center", "cutafter", "cutbefore", "dir",
+ "directionpoint", "div", "dotprod", "intersectionpoint", "inverse", "mod",
+ "round", "unitvector", "whatever",
+ "cutdraw", "draw", "drawarrow", "drawdblarrow", "fill", "filldraw", "drawdot",
+ "loggingall", "interact", "tracingall", "tracingnone",
+ "pickup",
+ "undraw", "unfill", "unfilldraw",
+ "buildcycle", "dashpattern", "decr", "dotlabel", "dotlabels", "drawoptions",
+ "incr", "label", "labels", "max", "min", "thelabel", "z",
+ "beginchar", "blacker", "capsule_end", "change_width",
+ "define_blacker_pixels", "define_corrected_pixels",
+ "define_good_x_pixels", "define_good_y_pixels",
+ "define_horizontal_corrected_pixels", "define_pixels",
+ "define_whole_blacker_pixels", "define_whole_pixels",
+ "define_whole_vertical_blacker_pixels",
+ "define_whole_vertical_pixels", "endchar", "extra_beginchar",
+ "extra_endchar", "extra_setup", "font_coding_scheme",
+ "clearxy", "clearit", "clearpen", "shipit",
+ "font_extra_space",
+ "exitunless",
+ "relax", "hide", "gobble", "gobbled", "stop",
+ "blankpicture",
+ "counterclockwise", "tensepath", "takepower", "direction",
+ "softjoin", -- "magstep",
+ "makelabel", -- "laboff",
+ "rotatedabout", "flex", "superellipse", "erase", "image",
+ "nullpen", "savepen", "clearpen", "penpos", "penlabels", -- "clear_pen_memory",
+ "range", "numtok", "thru",
+ "z", "laboff",
+ "bye",
+ --
+ "red", "green", "blue", "cyan", "magenta", "yellow", "black", "white", "background",
+ "graypart", "graycolor",
+ --
+ "mm", "pt", "dd", "bp", "cm", "pc", "cc", "in",
+ },
+ internals = { -- we need to remove duplicates above
+ --
+ "mitered", "rounded", "beveled", "butt", "squared",
+ "eps", "epsilon", "infinity",
+ "bboxmargin", "ahlength", "ahangle", "labeloffset", "dotlabeldiam", "defaultpen", "defaultscale", "join_radius",
+ --
+ "pen_lft", "pen_rt", "pen_top", "pen_bot", -- "pen_count_",
+ },
+}
diff --git a/tex/context/base/node-acc.lua b/tex/context/base/node-acc.lua
index c2675b970..4380ec3a4 100644
--- a/tex/context/base/node-acc.lua
+++ b/tex/context/base/node-acc.lua
@@ -1,140 +1,140 @@
-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"
-}
-
-local nodes, node = nodes, node
-
-local nodecodes = nodes.nodecodes
-local tasks = nodes.tasks
-
-local traverse_nodes = node.traverse
-local traverse_id = node.traverse_id
-local copy_node = node.copy
-local free_nodelist = node.flush_list
-
-local glue_code = nodecodes.glue
-local kern_code = nodecodes.kern
-local glyph_code = nodecodes.glyph
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-
-local a_characters = attributes.private("characters")
-
-local threshold = 65536
-
--- todo: nbsp etc
--- todo: collapse kerns
-
-local function injectspaces(head)
- local p
- local n = head
- while n do
- local id = n.id
- if id == glue_code then -- todo: check for subtype related to spacing (13/14 but most seems to be 0)
---~ if n.spec.width > 0 then -- threshold
- if p and p.id == glyph_code then
- local g = copy_node(p)
- local c = g.components
- if c then -- it happens that we copied a ligature
- free_nodelist(c)
- g.components = nil
- g.subtype = 256
- end
- local a = n[a_characters]
- local s = copy_node(n.spec)
- g.char, n.spec = 32, s
- p.next, g.prev = g, p
- g.next, n.prev = n, g
- s.width = s.width - g.width
- if a then
- g[a_characters] = a
- end
- s[a_characters] = 0
- n[a_characters] = 0
- end
---~ end
- elseif id == hlist_code or id == vlist_code then
- injectspaces(n.list,attribute)
- -- elseif id == kern_code then -- the backend already collapses
- -- local first = n
- -- while true do
- -- local nn = n.next
- -- if nn and nn.id == kern_code then
- -- -- maybe we should delete kerns but who cares at this stage
- -- first.kern = first.kern + nn.kern
- -- nn.kern = 0
- -- n = nn
- -- else
- -- break
- -- end
- -- end
- end
- p = n
- n = n.next
- end
- return head, true
-end
-
-nodes.handlers.accessibility = injectspaces
-
--- todo:
-
---~ local a_hyphenated = attributes.private('hyphenated')
---~
---~ local hyphenated, codes = { }, { }
---~
---~ local function compact(n)
---~ local t = { }
---~ for n in traverse_id(glyph_code,n) do
---~ t[#t+1] = utfchar(n.char) -- check for unicode
---~ end
---~ return concat(t,"")
---~ end
---~
---~ local function injectspans(head)
---~ for n in traverse_nodes(head) do
---~ local id = n.id
---~ if id == disc then
---~ local r, p = n.replace, n.pre
---~ if r and p then
---~ local str = compact(r)
---~ local hsh = hyphenated[str]
---~ if not hsh then
---~ hsh = #codes + 1
---~ hyphenated[str] = hsh
---~ codes[hsh] = str
---~ end
---~ n[a_hyphenated] = hsh
---~ end
---~ elseif id == hlist_code or id == vlist_code then
---~ injectspans(n.list)
---~ end
---~ end
---~ return head, true
---~ end
---~
---~ nodes.injectspans = injectspans
---~
---~ tasks.appendaction("processors", "words", "nodes.injectspans")
---~
---~ local function injectspans(head)
---~ for n in traverse_nodes(head) do
---~ local id = n.id
---~ if id == disc then
---~ local a = n[a_hyphenated]
---~ if a then
---~ local str = codes[a]
---~ local b = new_pdfliteral(format("/Span << /ActualText %s >> BDC", lpdf.tosixteen(str)))
---~ local e = new_pdfliteral("EMC")
---~ node.insert_before(head,n,b)
---~ node.insert_after(head,n,e)
---~ end
---~ elseif id == hlist_code or id == vlist_code then
---~ injectspans(n.list)
---~ end
---~ end
---~ end
+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"
+}
+
+local nodes, node = nodes, node
+
+local nodecodes = nodes.nodecodes
+local tasks = nodes.tasks
+
+local traverse_nodes = node.traverse
+local traverse_id = node.traverse_id
+local copy_node = node.copy
+local free_nodelist = node.flush_list
+
+local glue_code = nodecodes.glue
+local kern_code = nodecodes.kern
+local glyph_code = nodecodes.glyph
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+
+local a_characters = attributes.private("characters")
+
+local threshold = 65536
+
+-- todo: nbsp etc
+-- todo: collapse kerns
+
+local function injectspaces(head)
+ local p
+ local n = head
+ while n do
+ local id = n.id
+ if id == glue_code then -- todo: check for subtype related to spacing (13/14 but most seems to be 0)
+--~ if n.spec.width > 0 then -- threshold
+ if p and p.id == glyph_code then
+ local g = copy_node(p)
+ local c = g.components
+ if c then -- it happens that we copied a ligature
+ free_nodelist(c)
+ g.components = nil
+ g.subtype = 256
+ end
+ local a = n[a_characters]
+ local s = copy_node(n.spec)
+ g.char, n.spec = 32, s
+ p.next, g.prev = g, p
+ g.next, n.prev = n, g
+ s.width = s.width - g.width
+ if a then
+ g[a_characters] = a
+ end
+ s[a_characters] = 0
+ n[a_characters] = 0
+ end
+--~ end
+ elseif id == hlist_code or id == vlist_code then
+ injectspaces(n.list,attribute)
+ -- elseif id == kern_code then -- the backend already collapses
+ -- local first = n
+ -- while true do
+ -- local nn = n.next
+ -- if nn and nn.id == kern_code then
+ -- -- maybe we should delete kerns but who cares at this stage
+ -- first.kern = first.kern + nn.kern
+ -- nn.kern = 0
+ -- n = nn
+ -- else
+ -- break
+ -- end
+ -- end
+ end
+ p = n
+ n = n.next
+ end
+ return head, true
+end
+
+nodes.handlers.accessibility = injectspaces
+
+-- todo:
+
+--~ local a_hyphenated = attributes.private('hyphenated')
+--~
+--~ local hyphenated, codes = { }, { }
+--~
+--~ local function compact(n)
+--~ local t = { }
+--~ for n in traverse_id(glyph_code,n) do
+--~ t[#t+1] = utfchar(n.char) -- check for unicode
+--~ end
+--~ return concat(t,"")
+--~ end
+--~
+--~ local function injectspans(head)
+--~ for n in traverse_nodes(head) do
+--~ local id = n.id
+--~ if id == disc then
+--~ local r, p = n.replace, n.pre
+--~ if r and p then
+--~ local str = compact(r)
+--~ local hsh = hyphenated[str]
+--~ if not hsh then
+--~ hsh = #codes + 1
+--~ hyphenated[str] = hsh
+--~ codes[hsh] = str
+--~ end
+--~ n[a_hyphenated] = hsh
+--~ end
+--~ elseif id == hlist_code or id == vlist_code then
+--~ injectspans(n.list)
+--~ end
+--~ end
+--~ return head, true
+--~ end
+--~
+--~ nodes.injectspans = injectspans
+--~
+--~ tasks.appendaction("processors", "words", "nodes.injectspans")
+--~
+--~ local function injectspans(head)
+--~ for n in traverse_nodes(head) do
+--~ local id = n.id
+--~ if id == disc then
+--~ local a = n[a_hyphenated]
+--~ if a then
+--~ local str = codes[a]
+--~ local b = new_pdfliteral(format("/Span << /ActualText %s >> BDC", lpdf.tosixteen(str)))
+--~ local e = new_pdfliteral("EMC")
+--~ node.insert_before(head,n,b)
+--~ node.insert_after(head,n,e)
+--~ end
+--~ elseif id == hlist_code or id == vlist_code then
+--~ injectspans(n.list)
+--~ end
+--~ end
+--~ end
diff --git a/tex/context/base/node-aux.lua b/tex/context/base/node-aux.lua
index 21737a43b..e3fc7ad6f 100644
--- a/tex/context/base/node-aux.lua
+++ b/tex/context/base/node-aux.lua
@@ -1,389 +1,389 @@
-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, node = nodes, node
-
-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 math_code = nodecodes.math
-
-local nodepool = nodes.pool
-
-local new_glue = nodepool.glue
-local new_glyph = nodepool.glyph
-
-local traverse_nodes = node.traverse
-local traverse_id = node.traverse_id
-local free_node = node.free
-local hpack_nodes = node.hpack
-local unset_attribute = node.unset_attribute
-local first_glyph = node.first_glyph or node.first_character
-local copy_node = node.copy
-local copy_node_list = node.copy_list
-local slide_nodes = node.slide
-local insert_node_after = node.insert_after
-local isnode = node.is_node
-
-local unsetvalue = attributes.unsetvalue
-
-local current_font = font.current
-
-local texbox = tex.box
-
-local report_error = logs.reporter("node-aux:error")
-
-function nodes.repackhlist(list,...)
---~ nodes.showsimplelist(list)
- local temp, b = hpack_nodes(list,...)
- list = temp.list
- temp.list = nil
- free_node(temp)
- return list, b
-end
-
-local function set_attributes(head,attr,value)
- for n in traverse_nodes(head) do
- n[attr] = value
- local id = n.id
- if id == hlist_node or id == vlist_node then
- set_attributes(n.list,attr,value)
- end
- end
-end
-
-local function set_unset_attributes(head,attr,value)
- for n in traverse_nodes(head) do
- if not n[attr] then
- n[attr] = value
- end
- local id = n.id
- if id == hlist_code or id == vlist_code then
- set_unset_attributes(n.list,attr,value)
- end
- end
-end
-
-local function unset_attributes(head,attr)
- for n in traverse_nodes(head) do
- n[attr] = unsetvalue
- local id = n.id
- if id == hlist_code or id == vlist_code then
- unset_attributes(n.list,attr)
- end
- end
-end
-
-nodes.setattribute = node.set_attribute
-nodes.getattribute = node.has_attribute
-nodes.unsetattribute = node.unset_attribute
-nodes.has_attribute = node.has_attribute
-
-nodes.firstglyph = first_glyph
-nodes.setattributes = set_attributes
-nodes.setunsetattributes = set_unset_attributes
-nodes.unsetattributes = unset_attributes
-
--- function nodes.is_skipable(a,id) -- skipable nodes at the margins during character protrusion
--- return (
--- id ~= glyph_node
--- or id == ins_node
--- or id == mark_node
--- or id == adjust_node
--- or id == penalty_node
--- or (id == glue_node and a.spec.writable)
--- or (id == disc_node and a.pre == nil and a.post == nil and a.replace == nil)
--- or (id == math_node and a.surround == 0)
--- or (id == kern_node and (a.kern == 0 or a.subtype == NORMAL))
--- or (id == hlist_node and a.width == 0 and a.height == 0 and a.depth == 0 and a.list == nil)
--- or (id == whatsit_node and a.subtype ~= pdf_refximage_node and a.subtype ~= pdf_refxform_node)
--- )
--- end
-
--- history:
---
---
--- local function glyph_width(a)
--- local ch = chardata[a.font][a.char]
--- return (ch and ch.width) or 0
--- end
---
--- local function glyph_total(a)
--- local ch = chardata[a.font][a.char]
--- return (ch and (ch.height+ch.depth)) or 0
--- end
---
--- local function non_discardable(a) -- inline
--- return a.id < math_node -- brrrr
--- end
---
--- local function calculate_badness(t,s)
--- if t == 0 then
--- return 0
--- elseif s <= 0 then
--- return INF_BAD
--- else
--- local r
--- if t <= 7230584 then
--- r = t * 297 / s
--- elseif s >= 1663497 then
--- r = t / floor(s / 297)
--- else
--- r = t
--- end
--- r = floor(r)
--- if r > 1290 then
--- return INF_BAD
--- else
--- return floor((r * r * r + 0x20000) / 0x40000) -- 0400000 / 01000000
--- end
--- end
--- end
---
--- left-overs
---
--- local function round_xn_over_d(x, n, d)
--- local positive -- was x >= 0
--- if x >= 0 then
--- positive = true
--- else
--- x = -x
--- positive = false
--- end
--- local t = floor(x % 0x8000) * n -- 0100000
--- local f = floor(t / 0x8000) -- 0100000
--- local u = floor(x / 0x8000) * n + f -- 0100000
--- local v = floor(u % d) * 0x8000 + f -- 0100000
--- if floor(u / d) >= 0x8000 then -- 0100000
--- report_parbuilders('arith_error')
--- else
--- u = 0x8000 * floor(u / d) + floor(v / d) -- 0100000
--- end
--- v = floor(v % d)
--- if 2*v >= d then
--- u = u + 1
--- end
--- if positive then
--- return u
--- else
--- return -u
--- end
--- end
-
-function nodes.firstcharacter(n,untagged) -- tagged == subtype > 255
- if untagged then
- return first_glyph(n)
- else
- for g in traverse_id(glyph_code,n) do
- return g
- end
- end
-end
-
-function nodes.firstcharinbox(n)
- local l = texbox[n].list
- if l then
- for g in traverse_id(glyph_code,l) do
- return g.char
- end
- end
- return 0
-end
-
-if not node.end_of_math then
- function node.end_of_math(n)
- for n in traverse_id(math_code,n.next) do
- return n
- end
- end
-end
-
-nodes.endofmath = node.end_of_math
-
--- local function firstline(n)
--- while n do
--- local id = n.id
--- if id == hlist_code then
--- if n.subtype == line_code then
--- return n
--- else
--- return firstline(n.list)
--- end
--- elseif id == vlist_code then
--- return firstline(n.list)
--- end
--- n = n.next
--- end
--- end
-
--- nodes.firstline = firstline
-
--- 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 fnt.id == "glyph" then
- fnt, template = nil, fnt
- -- else
- -- already a number
- 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)
- n.char = s
- else
- n = new_glyph(fnt,s)
- end
- if attr then -- normally false when template
- n.attr = copy_node_list(attr)
- end
- if head then
- insert_node_after(head,tail,n)
- else
- head = n
- end
- tail = n
- end
- return head, tail
-end
-
-nodes.tonodes = tonodes
-
-local function link(list,currentfont,currentattr,head,tail)
- for i=1,#list do
- local n = list[i]
- if n then
- local tn = isnode(n)
- if not tn then
- local tn = type(n)
- if tn == "number" then
- if not currentfont then
- currentfont = current_font()
- end
- local h, t = tonodes(tostring(n),currentfont,currentattr)
- if not h then
- -- skip
- elseif not head then
- head, tail = h, t
- else
- tail.next, h.prev, tail = h, t, t
- end
- elseif 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
- tail.next, h.prev, tail = h, t, 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
- end
- elseif not head then
- head = n
- if n.next then
- tail = slide_nodes(n)
- else
- tail = n
- end
- elseif n.id == 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,l.id == attributelist_code and "!" or ">",l)
- end
- os.exit()
- else
- tail.next = n
- n.prev = tail
- if n.next then
- tail = slide_nodes(n)
- else
- tail = n
- end
- end
- else
- -- permitting nil is convenient
- end
- end
- return head, tail
-end
-
-nodes.link = link
-
-local function locate(start,wantedid,wantedsubtype)
- for n in traverse_nodes(start) do
- local id = n.id
- if id == wantedid then
- if not wantedsubtype or n.subtype == wantedsubtype then
- return n
- end
- elseif id == hlist_code or id == vlist_code then
- local found = locate(n.list,wantedid,wantedsubtype)
- if found then
- return found
- end
- end
- end
-end
-
-nodes.locate = locate
-
-function nodes.concat(list)
- local head, tail
- for i=1,#list do
- local li = list[i]
- if not li then
- -- skip
- elseif head then
- tail.next = li
- li.prev = tail
- tail = li.next and slide_nodes(li) or li
- else
- head = li
- tail = li.next and slide_nodes(li) or li
- end
- end
- return head, tail
-end
+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, node = nodes, node
+
+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 math_code = nodecodes.math
+
+local nodepool = nodes.pool
+
+local new_glue = nodepool.glue
+local new_glyph = nodepool.glyph
+
+local traverse_nodes = node.traverse
+local traverse_id = node.traverse_id
+local free_node = node.free
+local hpack_nodes = node.hpack
+local unset_attribute = node.unset_attribute
+local first_glyph = node.first_glyph or node.first_character
+local copy_node = node.copy
+local copy_node_list = node.copy_list
+local slide_nodes = node.slide
+local insert_node_after = node.insert_after
+local isnode = node.is_node
+
+local unsetvalue = attributes.unsetvalue
+
+local current_font = font.current
+
+local texbox = tex.box
+
+local report_error = logs.reporter("node-aux:error")
+
+function nodes.repackhlist(list,...)
+--~ nodes.showsimplelist(list)
+ local temp, b = hpack_nodes(list,...)
+ list = temp.list
+ temp.list = nil
+ free_node(temp)
+ return list, b
+end
+
+local function set_attributes(head,attr,value)
+ for n in traverse_nodes(head) do
+ n[attr] = value
+ local id = n.id
+ if id == hlist_node or id == vlist_node then
+ set_attributes(n.list,attr,value)
+ end
+ end
+end
+
+local function set_unset_attributes(head,attr,value)
+ for n in traverse_nodes(head) do
+ if not n[attr] then
+ n[attr] = value
+ end
+ local id = n.id
+ if id == hlist_code or id == vlist_code then
+ set_unset_attributes(n.list,attr,value)
+ end
+ end
+end
+
+local function unset_attributes(head,attr)
+ for n in traverse_nodes(head) do
+ n[attr] = unsetvalue
+ local id = n.id
+ if id == hlist_code or id == vlist_code then
+ unset_attributes(n.list,attr)
+ end
+ end
+end
+
+nodes.setattribute = node.set_attribute
+nodes.getattribute = node.has_attribute
+nodes.unsetattribute = node.unset_attribute
+nodes.has_attribute = node.has_attribute
+
+nodes.firstglyph = first_glyph
+nodes.setattributes = set_attributes
+nodes.setunsetattributes = set_unset_attributes
+nodes.unsetattributes = unset_attributes
+
+-- function nodes.is_skipable(a,id) -- skipable nodes at the margins during character protrusion
+-- return (
+-- id ~= glyph_node
+-- or id == ins_node
+-- or id == mark_node
+-- or id == adjust_node
+-- or id == penalty_node
+-- or (id == glue_node and a.spec.writable)
+-- or (id == disc_node and a.pre == nil and a.post == nil and a.replace == nil)
+-- or (id == math_node and a.surround == 0)
+-- or (id == kern_node and (a.kern == 0 or a.subtype == NORMAL))
+-- or (id == hlist_node and a.width == 0 and a.height == 0 and a.depth == 0 and a.list == nil)
+-- or (id == whatsit_node and a.subtype ~= pdf_refximage_node and a.subtype ~= pdf_refxform_node)
+-- )
+-- end
+
+-- history:
+--
+--
+-- local function glyph_width(a)
+-- local ch = chardata[a.font][a.char]
+-- return (ch and ch.width) or 0
+-- end
+--
+-- local function glyph_total(a)
+-- local ch = chardata[a.font][a.char]
+-- return (ch and (ch.height+ch.depth)) or 0
+-- end
+--
+-- local function non_discardable(a) -- inline
+-- return a.id < math_node -- brrrr
+-- end
+--
+-- local function calculate_badness(t,s)
+-- if t == 0 then
+-- return 0
+-- elseif s <= 0 then
+-- return INF_BAD
+-- else
+-- local r
+-- if t <= 7230584 then
+-- r = t * 297 / s
+-- elseif s >= 1663497 then
+-- r = t / floor(s / 297)
+-- else
+-- r = t
+-- end
+-- r = floor(r)
+-- if r > 1290 then
+-- return INF_BAD
+-- else
+-- return floor((r * r * r + 0x20000) / 0x40000) -- 0400000 / 01000000
+-- end
+-- end
+-- end
+--
+-- left-overs
+--
+-- local function round_xn_over_d(x, n, d)
+-- local positive -- was x >= 0
+-- if x >= 0 then
+-- positive = true
+-- else
+-- x = -x
+-- positive = false
+-- end
+-- local t = floor(x % 0x8000) * n -- 0100000
+-- local f = floor(t / 0x8000) -- 0100000
+-- local u = floor(x / 0x8000) * n + f -- 0100000
+-- local v = floor(u % d) * 0x8000 + f -- 0100000
+-- if floor(u / d) >= 0x8000 then -- 0100000
+-- report_parbuilders('arith_error')
+-- else
+-- u = 0x8000 * floor(u / d) + floor(v / d) -- 0100000
+-- end
+-- v = floor(v % d)
+-- if 2*v >= d then
+-- u = u + 1
+-- end
+-- if positive then
+-- return u
+-- else
+-- return -u
+-- end
+-- end
+
+function nodes.firstcharacter(n,untagged) -- tagged == subtype > 255
+ if untagged then
+ return first_glyph(n)
+ else
+ for g in traverse_id(glyph_code,n) do
+ return g
+ end
+ end
+end
+
+function nodes.firstcharinbox(n)
+ local l = texbox[n].list
+ if l then
+ for g in traverse_id(glyph_code,l) do
+ return g.char
+ end
+ end
+ return 0
+end
+
+if not node.end_of_math then
+ function node.end_of_math(n)
+ for n in traverse_id(math_code,n.next) do
+ return n
+ end
+ end
+end
+
+nodes.endofmath = node.end_of_math
+
+-- local function firstline(n)
+-- while n do
+-- local id = n.id
+-- if id == hlist_code then
+-- if n.subtype == line_code then
+-- return n
+-- else
+-- return firstline(n.list)
+-- end
+-- elseif id == vlist_code then
+-- return firstline(n.list)
+-- end
+-- n = n.next
+-- end
+-- end
+
+-- nodes.firstline = firstline
+
+-- 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 fnt.id == "glyph" then
+ fnt, template = nil, fnt
+ -- else
+ -- already a number
+ 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)
+ n.char = s
+ else
+ n = new_glyph(fnt,s)
+ end
+ if attr then -- normally false when template
+ n.attr = copy_node_list(attr)
+ end
+ if head then
+ insert_node_after(head,tail,n)
+ else
+ head = n
+ end
+ tail = n
+ end
+ return head, tail
+end
+
+nodes.tonodes = tonodes
+
+local function link(list,currentfont,currentattr,head,tail)
+ for i=1,#list do
+ local n = list[i]
+ if n then
+ local tn = isnode(n)
+ if not tn then
+ local tn = type(n)
+ if tn == "number" then
+ if not currentfont then
+ currentfont = current_font()
+ end
+ local h, t = tonodes(tostring(n),currentfont,currentattr)
+ if not h then
+ -- skip
+ elseif not head then
+ head, tail = h, t
+ else
+ tail.next, h.prev, tail = h, t, t
+ end
+ elseif 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
+ tail.next, h.prev, tail = h, t, 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
+ end
+ elseif not head then
+ head = n
+ if n.next then
+ tail = slide_nodes(n)
+ else
+ tail = n
+ end
+ elseif n.id == 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,l.id == attributelist_code and "!" or ">",l)
+ end
+ os.exit()
+ else
+ tail.next = n
+ n.prev = tail
+ if n.next then
+ tail = slide_nodes(n)
+ else
+ tail = n
+ end
+ end
+ else
+ -- permitting nil is convenient
+ end
+ end
+ return head, tail
+end
+
+nodes.link = link
+
+local function locate(start,wantedid,wantedsubtype)
+ for n in traverse_nodes(start) do
+ local id = n.id
+ if id == wantedid then
+ if not wantedsubtype or n.subtype == wantedsubtype then
+ return n
+ end
+ elseif id == hlist_code or id == vlist_code then
+ local found = locate(n.list,wantedid,wantedsubtype)
+ if found then
+ return found
+ end
+ end
+ end
+end
+
+nodes.locate = locate
+
+function nodes.concat(list)
+ local head, tail
+ for i=1,#list do
+ local li = list[i]
+ if not li then
+ -- skip
+ elseif head then
+ tail.next = li
+ li.prev = tail
+ tail = li.next and slide_nodes(li) or li
+ else
+ head = li
+ tail = li.next and slide_nodes(li) or li
+ end
+ end
+ return head, tail
+end
diff --git a/tex/context/base/node-bck.lua b/tex/context/base/node-bck.lua
index 44fed5e17..feaa2c684 100644
--- a/tex/context/base/node-bck.lua
+++ b/tex/context/base/node-bck.lua
@@ -1,161 +1,161 @@
-if not modules then modules = { } end modules ['node-bck'] = {
- version = 1.001,
- 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
-
-local attributes, nodes, node = attributes, nodes, node
-
-local nodecodes = nodes.nodecodes
-local listcodes = nodes.listcodes
-
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-local glyph_code = nodecodes.glyph
-local cell_code = listcodes.cell
-
-local traverse = node.traverse
-local traverse_id = node.traverse_id
-
-local nodepool = nodes.pool
-local tasks = nodes.tasks
-
-local new_rule = nodepool.rule
-local new_glue = nodepool.glue
-
-local a_color = attributes.private('color')
-local a_transparency = attributes.private('transparency')
-local a_colorspace = attributes.private('colormodel')
-local a_background = attributes.private('background')
-local a_alignbackground = attributes.private('alignbackground')
-
-local function add_backgrounds(head) -- rather old code .. to be redone
- local current = head
- while current do
- local id = current.id
- if id == hlist_code or id == vlist_code then
- local list = current.list
- if list then
- local head = add_backgrounds(list)
- if head then
- current.list = head
- list = head
- end
- end
- local width = current.width
- if width > 0 then
- local background = current[a_background]
- if background then
- -- direct to hbox
- -- colorspace is already set so we can omit that and stick to color
- local mode = current[a_colorspace]
- if mode then
- local height = current.height
- local depth = current.depth
- local skip = id == hlist_code and width or (height + depth)
- local glue = new_glue(-skip)
- local rule = new_rule(width,height,depth)
- local color = current[a_color]
- local transparency = current[a_transparency]
- rule[a_colorspace] = mode
- if color then
- rule[a_color] = color
- end
- if transparency then
- rule[a_transparency] = transparency
- end
- rule.next = glue
- glue.prev = rule
- if list then
- glue.next = list
- list.prev = glue
- end
- current.list = rule
- end
- end
- end
- end
- current = current.next
- end
- return head, true
-end
-
-local function add_alignbackgrounds(head)
- local current = head
- while current do
- local id = current.id
- if id == hlist_code then
- local list = current.list
- if not list then
- -- no need to look
- elseif current.subtype == cell_code then
- local background = nil
- local found = nil
- -- for l in traverse(list) do
- -- background = l[a_alignbackground]
- -- if background then
- -- found = l
- -- break
- -- end
- -- end
- -- we know that it's a fake hlist (could be user node)
- -- but we cannot store tables in user nodes yet
- for l in traverse_id(hpack_code,list) do
- background = l[a_alignbackground]
- if background then
- found = l
- end
- break
- end
- --
- if background then
- -- current has subtype 5 (cell)
- local width = current.width
- if width > 0 then
- local mode = found[a_colorspace]
- if mode then
- local glue = new_glue(-width)
- local rule = new_rule(width,current.height,current.depth)
- local color = found[a_color]
- local transparency = found[a_transparency]
- rule[a_colorspace] = mode
- if color then
- rule[a_color] = color
- end
- if transparency then
- rule[a_transparency] = transparency
- end
- rule.next = glue
- glue.prev = rule
- if list then
- glue.next = list
- list.prev = glue
- end
- current.list = rule
- end
- end
- end
- else
- add_alignbackgrounds(list)
- end
- elseif id == vlist_code then
- local list = current.list
- if list then
- add_alignbackgrounds(list)
- end
- end
- current = current.next
- end
- return head, true
-end
-
-nodes.handlers.backgrounds = add_backgrounds
-nodes.handlers.alignbackgrounds = add_alignbackgrounds
-
-tasks.appendaction("shipouts","normalizers","nodes.handlers.backgrounds")
-tasks.appendaction("shipouts","normalizers","nodes.handlers.alignbackgrounds")
+if not modules then modules = { } end modules ['node-bck'] = {
+ version = 1.001,
+ 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
+
+local attributes, nodes, node = attributes, nodes, node
+
+local nodecodes = nodes.nodecodes
+local listcodes = nodes.listcodes
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local glyph_code = nodecodes.glyph
+local cell_code = listcodes.cell
+
+local traverse = node.traverse
+local traverse_id = node.traverse_id
+
+local nodepool = nodes.pool
+local tasks = nodes.tasks
+
+local new_rule = nodepool.rule
+local new_glue = nodepool.glue
+
+local a_color = attributes.private('color')
+local a_transparency = attributes.private('transparency')
+local a_colorspace = attributes.private('colormodel')
+local a_background = attributes.private('background')
+local a_alignbackground = attributes.private('alignbackground')
+
+local function add_backgrounds(head) -- rather old code .. to be redone
+ local current = head
+ while current do
+ local id = current.id
+ if id == hlist_code or id == vlist_code then
+ local list = current.list
+ if list then
+ local head = add_backgrounds(list)
+ if head then
+ current.list = head
+ list = head
+ end
+ end
+ local width = current.width
+ if width > 0 then
+ local background = current[a_background]
+ if background then
+ -- direct to hbox
+ -- colorspace is already set so we can omit that and stick to color
+ local mode = current[a_colorspace]
+ if mode then
+ local height = current.height
+ local depth = current.depth
+ local skip = id == hlist_code and width or (height + depth)
+ local glue = new_glue(-skip)
+ local rule = new_rule(width,height,depth)
+ local color = current[a_color]
+ local transparency = current[a_transparency]
+ rule[a_colorspace] = mode
+ if color then
+ rule[a_color] = color
+ end
+ if transparency then
+ rule[a_transparency] = transparency
+ end
+ rule.next = glue
+ glue.prev = rule
+ if list then
+ glue.next = list
+ list.prev = glue
+ end
+ current.list = rule
+ end
+ end
+ end
+ end
+ current = current.next
+ end
+ return head, true
+end
+
+local function add_alignbackgrounds(head)
+ local current = head
+ while current do
+ local id = current.id
+ if id == hlist_code then
+ local list = current.list
+ if not list then
+ -- no need to look
+ elseif current.subtype == cell_code then
+ local background = nil
+ local found = nil
+ -- for l in traverse(list) do
+ -- background = l[a_alignbackground]
+ -- if background then
+ -- found = l
+ -- break
+ -- end
+ -- end
+ -- we know that it's a fake hlist (could be user node)
+ -- but we cannot store tables in user nodes yet
+ for l in traverse_id(hpack_code,list) do
+ background = l[a_alignbackground]
+ if background then
+ found = l
+ end
+ break
+ end
+ --
+ if background then
+ -- current has subtype 5 (cell)
+ local width = current.width
+ if width > 0 then
+ local mode = found[a_colorspace]
+ if mode then
+ local glue = new_glue(-width)
+ local rule = new_rule(width,current.height,current.depth)
+ local color = found[a_color]
+ local transparency = found[a_transparency]
+ rule[a_colorspace] = mode
+ if color then
+ rule[a_color] = color
+ end
+ if transparency then
+ rule[a_transparency] = transparency
+ end
+ rule.next = glue
+ glue.prev = rule
+ if list then
+ glue.next = list
+ list.prev = glue
+ end
+ current.list = rule
+ end
+ end
+ end
+ else
+ add_alignbackgrounds(list)
+ end
+ elseif id == vlist_code then
+ local list = current.list
+ if list then
+ add_alignbackgrounds(list)
+ end
+ end
+ current = current.next
+ end
+ return head, true
+end
+
+nodes.handlers.backgrounds = add_backgrounds
+nodes.handlers.alignbackgrounds = add_alignbackgrounds
+
+tasks.appendaction("shipouts","normalizers","nodes.handlers.backgrounds")
+tasks.appendaction("shipouts","normalizers","nodes.handlers.alignbackgrounds")
diff --git a/tex/context/base/node-dir.lua b/tex/context/base/node-dir.lua
index 9a1f4e30c..6ee5cd4b8 100644
--- a/tex/context/base/node-dir.lua
+++ b/tex/context/base/node-dir.lua
@@ -1,309 +1,309 @@
-if not modules then modules = { } end modules ['node-dir'] = {
- version = 1.001,
- comment = "companion to node-ini.mkiv",
- author = "Taco Hoekwater and Hans Hagen",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
---[[
-<p>In the process of cleaning up the lua variant of the parbuilder
-we ran into a couple of functions (translated c macros) that were
-somewhat inefficient. More convenient is to use hashes although at
-the c-end still macros are used. In the process directions.h was
-adapted and now has the mappings as comments. This lua file is
-based on that file.
-]]--
-
-local allocate = utilities.storage.allocate
-
-local nodes = nodes
-
-nodes.is_mirrored = allocate {
- -- TLT = false,
- -- TRT = false,
- -- LTL = false,
- -- RTT = false,
-}
-
-nodes.is_rotated = allocate {
- -- TLT = false,
- -- TRT = false,
- -- LTL = false,
- RTT = true, ["+RTT"] = true,
-}
-
-nodes.textdir_is_parallel = allocate {
- TLT = {
- TLT = true, ["+TLT"] = true,
- TRT = true, ["+TRT"] = true,
- -- LTL = false,
- -- RTT = false,
- },
- TRT= {
- TLT = true, ["+TLT"] = true,
- TRT = true, ["+TRT"] = true,
- -- LTL = false,
- -- RTT = false,
- },
- LTL = {
- -- TLT = false,
- -- TRT = false,
- LTL = true, ["+LTL"] = true,
- RTT = true, ["+RTT"] = true,
- },
- RTT = {
- -- TLT = false,
- -- TRT = false,
- LTL = true, ["+LTL"] = true,
- RTT = true, ["+RTT"] = true,
- }
-}
-
-nodes.pardir_is_parallel = allocate {
- TLT = {
- TLT = true, ["+TLT"] = true,
- TRT = true, ["+TRT"] = true,
- -- LTL = false,
- -- RTT = false,
- },
- TRT = {
- TLT = true, ["+TLT"] = true,
- TRT = true, ["+TRT"] = true,
- -- LTL = false,
- -- RTT = false,
- },
- LTL = {
- -- TLT = false,
- -- TRT = false,
- LTL = true, ["+LTL"] = true,
- RTT = true, ["+RTT"] = true,
- },
- RTT = {
- -- TLT = false,
- -- TRT = false,
- LTL = true, ["+LTL"] = true,
- RTT = true, ["+RTT"] = true,
- },
-}
-
-nodes.pardir_is_opposite = allocate {
- TLT = {
- -- TLT = false,
- -- TRT = false,
- -- LTL = false,
- -- RTT = false,
- },
- TRT = {
- -- TLT = false,
- -- TRT = false,
- -- LTL = false,
- -- RTT = false,
- },
- LTL = {
- -- TLT = false,
- -- TRT = false,
- -- LTL = false,
- RTT = true, ["+RTT"] = true,
- },
- RTT = {
- -- TLT = false,
- -- TRT = false,
- LTL = true, ["+LTL"] = true,
- -- RTT = false,
- },
-}
-
-nodes.textdir_is_opposite = allocate {
- TLT = {
- -- TLT = false,
- TRT = true, ["+TRT"] = true,
- -- LTL = false,
- -- RTT = false,
- },
- TRT= {
- TLT = true, ["+TLT"] = true,
- -- TRT = false,
- -- LTL = false,
- -- RTT = false,
- },
- LTL = {
- -- TLT = false,
- -- TRT = false,
- -- LTL = false,
- -- RTT = false,
- },
- RTT = {
- -- TLT = false,
- -- TRT = false,
- -- LTL = false,
- -- RTT = false,
- },
-}
-
-nodes.glyphdir_is_opposite = allocate {
- TLT = {
- -- TLT = false,
- -- TRT = false,
- -- LTL = false,
- -- RTT = false,
- },
- TRT= {
- -- TLT = false,
- -- TRT = false,
- -- LTL = false,
- -- RTT = false,
- },
- LTL = {
- -- TLT = false,
- -- TRT = false,
- -- LTL = false,
- -- RTT = false,
- },
- RTT = {
- -- TLT = false,
- -- TRT = false,
- -- LTL = false,
- -- RTT = false,
- },
-}
-
-nodes.pardir_is_equal = allocate {
- TLT = {
- TLT = true, ["+TLT"] = true,
- TRT = true, ["+TRT"] = true,
- -- LTL = false,
- -- RTT = false,
- },
- TRT= {
- TLT = true, ["+TLT"] = true,
- TRT = true, ["+TRT"] = true,
- -- LTL = false,
- -- RTT = false,
- },
- LTL= {
- -- TLT = false,
- -- TRT = false,
- LTL = true, ["+LTL"] = true,
- -- RTT = false,
- },
- RTT= {
- -- TLT = false,
- -- TRT = false,
- -- LTL = false,
- RTT = true, ["+RTT"] = true,
- },
-}
-
-nodes.textdir_is_equal = allocate {
- TLT = {
- TLT = true, ["+TLT"] = true,
- -- TRT = false,
- -- LTL = false,
- -- RTT = false,
- },
- TRT= {
- -- TLT = false,
- TRT = true, ["+TRT"] = true,
- -- LTL = false,
- -- RTT = false,
- },
- LTL = {
- -- TLT = false,
- -- TRT = false,
- LTL = true, ["+LTL"] = true,
- RTT = true, ["+RTT"] = true,
- },
- RTT = {
- -- TLT = false,
- -- TRT = false,
- LTL = true, ["+LTL"] = true,
- RTT = true, ["+RTT"] = true,
- },
-}
-
-nodes.glyphdir_is_equal = allocate {
- TLT = {
- TLT = true, ["+TLT"] = true,
- TRT = true, ["+TRT"] = true,
- -- LTL = false,
- RTT = true, ["+RTT"] = true,
- },
- TRT= {
- TLT = true, ["+TLT"] = true,
- TRT = true, ["+TRT"] = true,
- -- LTL = false,
- RTT = true, ["+RTT"] = true,
- },
- LTL = {
- -- TLT = false,
- -- TRT = false,
- LTL = true, ["+LTL"] = true,
- -- RTT = false,
- },
- RTT = {
- TLT = true, ["+TLT"] = true,
- TRT = true, ["+TRT"] = true,
- -- LTL = false,
- RTT = true, ["+RTT"] = true,
- },
-}
-
-nodes.partextdir_is_equal = allocate {
- TLT = {
- -- TLT = false,
- -- TRT = false,
- LTL = true, ["+LTL"] = true,
- RTT = true, ["+RTT"] = true,
- },
- TRT= {
- -- TLT = false,
- -- TRT = false,
- LTL = true, ["+LTL"] = true,
- RTT = true, ["+RTT"] = true,
- },
- LTL = {
- TLT = true, ["+TLT"] = true,
- -- TRT = false,
- -- LTL = false,
- -- RTT = false,
- },
- RTT = {
- -- TLT = false,
- TRT = true, ["+TRT"] = true,
- -- LTL = false,
- -- RTT = false,
- },
-}
-
-nodes.textdir_is_is = allocate {
- TLT = true, ["+TLT"] = true,
- -- TRT = false,
- -- LTL = false,
- -- RTT = false,
-}
-
-nodes.glyphdir_is_orthogonal = allocate {
- TLT = true, ["+TLT"] = true,
- TRT = true, ["+TRT"] = true,
- LTL = true, ["+LTL"] = true,
- -- RTT = false
-}
-
-nodes.dir_is_pop = allocate {
- ["-TRT"] = true,
- ["-TLT"] = true,
- ["-LTL"] = true,
- ["-RTT"] = true,
-}
-
-nodes.dir_negation = allocate {
- ["-TRT"] = "+TRT",
- ["-TLT"] = "+TLT",
- ["-LTL"] = "+LTL",
- ["-RTT"] = "+RTT",
- ["+TRT"] = "-TRT",
- ["+TLT"] = "-TLT",
- ["+LTL"] = "-LTL",
- ["+RTT"] = "-RTT",
-}
+if not modules then modules = { } end modules ['node-dir'] = {
+ version = 1.001,
+ comment = "companion to node-ini.mkiv",
+ author = "Taco Hoekwater and Hans Hagen",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[
+<p>In the process of cleaning up the lua variant of the parbuilder
+we ran into a couple of functions (translated c macros) that were
+somewhat inefficient. More convenient is to use hashes although at
+the c-end still macros are used. In the process directions.h was
+adapted and now has the mappings as comments. This lua file is
+based on that file.
+]]--
+
+local allocate = utilities.storage.allocate
+
+local nodes = nodes
+
+nodes.is_mirrored = allocate {
+ -- TLT = false,
+ -- TRT = false,
+ -- LTL = false,
+ -- RTT = false,
+}
+
+nodes.is_rotated = allocate {
+ -- TLT = false,
+ -- TRT = false,
+ -- LTL = false,
+ RTT = true, ["+RTT"] = true,
+}
+
+nodes.textdir_is_parallel = allocate {
+ TLT = {
+ TLT = true, ["+TLT"] = true,
+ TRT = true, ["+TRT"] = true,
+ -- LTL = false,
+ -- RTT = false,
+ },
+ TRT= {
+ TLT = true, ["+TLT"] = true,
+ TRT = true, ["+TRT"] = true,
+ -- LTL = false,
+ -- RTT = false,
+ },
+ LTL = {
+ -- TLT = false,
+ -- TRT = false,
+ LTL = true, ["+LTL"] = true,
+ RTT = true, ["+RTT"] = true,
+ },
+ RTT = {
+ -- TLT = false,
+ -- TRT = false,
+ LTL = true, ["+LTL"] = true,
+ RTT = true, ["+RTT"] = true,
+ }
+}
+
+nodes.pardir_is_parallel = allocate {
+ TLT = {
+ TLT = true, ["+TLT"] = true,
+ TRT = true, ["+TRT"] = true,
+ -- LTL = false,
+ -- RTT = false,
+ },
+ TRT = {
+ TLT = true, ["+TLT"] = true,
+ TRT = true, ["+TRT"] = true,
+ -- LTL = false,
+ -- RTT = false,
+ },
+ LTL = {
+ -- TLT = false,
+ -- TRT = false,
+ LTL = true, ["+LTL"] = true,
+ RTT = true, ["+RTT"] = true,
+ },
+ RTT = {
+ -- TLT = false,
+ -- TRT = false,
+ LTL = true, ["+LTL"] = true,
+ RTT = true, ["+RTT"] = true,
+ },
+}
+
+nodes.pardir_is_opposite = allocate {
+ TLT = {
+ -- TLT = false,
+ -- TRT = false,
+ -- LTL = false,
+ -- RTT = false,
+ },
+ TRT = {
+ -- TLT = false,
+ -- TRT = false,
+ -- LTL = false,
+ -- RTT = false,
+ },
+ LTL = {
+ -- TLT = false,
+ -- TRT = false,
+ -- LTL = false,
+ RTT = true, ["+RTT"] = true,
+ },
+ RTT = {
+ -- TLT = false,
+ -- TRT = false,
+ LTL = true, ["+LTL"] = true,
+ -- RTT = false,
+ },
+}
+
+nodes.textdir_is_opposite = allocate {
+ TLT = {
+ -- TLT = false,
+ TRT = true, ["+TRT"] = true,
+ -- LTL = false,
+ -- RTT = false,
+ },
+ TRT= {
+ TLT = true, ["+TLT"] = true,
+ -- TRT = false,
+ -- LTL = false,
+ -- RTT = false,
+ },
+ LTL = {
+ -- TLT = false,
+ -- TRT = false,
+ -- LTL = false,
+ -- RTT = false,
+ },
+ RTT = {
+ -- TLT = false,
+ -- TRT = false,
+ -- LTL = false,
+ -- RTT = false,
+ },
+}
+
+nodes.glyphdir_is_opposite = allocate {
+ TLT = {
+ -- TLT = false,
+ -- TRT = false,
+ -- LTL = false,
+ -- RTT = false,
+ },
+ TRT= {
+ -- TLT = false,
+ -- TRT = false,
+ -- LTL = false,
+ -- RTT = false,
+ },
+ LTL = {
+ -- TLT = false,
+ -- TRT = false,
+ -- LTL = false,
+ -- RTT = false,
+ },
+ RTT = {
+ -- TLT = false,
+ -- TRT = false,
+ -- LTL = false,
+ -- RTT = false,
+ },
+}
+
+nodes.pardir_is_equal = allocate {
+ TLT = {
+ TLT = true, ["+TLT"] = true,
+ TRT = true, ["+TRT"] = true,
+ -- LTL = false,
+ -- RTT = false,
+ },
+ TRT= {
+ TLT = true, ["+TLT"] = true,
+ TRT = true, ["+TRT"] = true,
+ -- LTL = false,
+ -- RTT = false,
+ },
+ LTL= {
+ -- TLT = false,
+ -- TRT = false,
+ LTL = true, ["+LTL"] = true,
+ -- RTT = false,
+ },
+ RTT= {
+ -- TLT = false,
+ -- TRT = false,
+ -- LTL = false,
+ RTT = true, ["+RTT"] = true,
+ },
+}
+
+nodes.textdir_is_equal = allocate {
+ TLT = {
+ TLT = true, ["+TLT"] = true,
+ -- TRT = false,
+ -- LTL = false,
+ -- RTT = false,
+ },
+ TRT= {
+ -- TLT = false,
+ TRT = true, ["+TRT"] = true,
+ -- LTL = false,
+ -- RTT = false,
+ },
+ LTL = {
+ -- TLT = false,
+ -- TRT = false,
+ LTL = true, ["+LTL"] = true,
+ RTT = true, ["+RTT"] = true,
+ },
+ RTT = {
+ -- TLT = false,
+ -- TRT = false,
+ LTL = true, ["+LTL"] = true,
+ RTT = true, ["+RTT"] = true,
+ },
+}
+
+nodes.glyphdir_is_equal = allocate {
+ TLT = {
+ TLT = true, ["+TLT"] = true,
+ TRT = true, ["+TRT"] = true,
+ -- LTL = false,
+ RTT = true, ["+RTT"] = true,
+ },
+ TRT= {
+ TLT = true, ["+TLT"] = true,
+ TRT = true, ["+TRT"] = true,
+ -- LTL = false,
+ RTT = true, ["+RTT"] = true,
+ },
+ LTL = {
+ -- TLT = false,
+ -- TRT = false,
+ LTL = true, ["+LTL"] = true,
+ -- RTT = false,
+ },
+ RTT = {
+ TLT = true, ["+TLT"] = true,
+ TRT = true, ["+TRT"] = true,
+ -- LTL = false,
+ RTT = true, ["+RTT"] = true,
+ },
+}
+
+nodes.partextdir_is_equal = allocate {
+ TLT = {
+ -- TLT = false,
+ -- TRT = false,
+ LTL = true, ["+LTL"] = true,
+ RTT = true, ["+RTT"] = true,
+ },
+ TRT= {
+ -- TLT = false,
+ -- TRT = false,
+ LTL = true, ["+LTL"] = true,
+ RTT = true, ["+RTT"] = true,
+ },
+ LTL = {
+ TLT = true, ["+TLT"] = true,
+ -- TRT = false,
+ -- LTL = false,
+ -- RTT = false,
+ },
+ RTT = {
+ -- TLT = false,
+ TRT = true, ["+TRT"] = true,
+ -- LTL = false,
+ -- RTT = false,
+ },
+}
+
+nodes.textdir_is_is = allocate {
+ TLT = true, ["+TLT"] = true,
+ -- TRT = false,
+ -- LTL = false,
+ -- RTT = false,
+}
+
+nodes.glyphdir_is_orthogonal = allocate {
+ TLT = true, ["+TLT"] = true,
+ TRT = true, ["+TRT"] = true,
+ LTL = true, ["+LTL"] = true,
+ -- RTT = false
+}
+
+nodes.dir_is_pop = allocate {
+ ["-TRT"] = true,
+ ["-TLT"] = true,
+ ["-LTL"] = true,
+ ["-RTT"] = true,
+}
+
+nodes.dir_negation = allocate {
+ ["-TRT"] = "+TRT",
+ ["-TLT"] = "+TLT",
+ ["-LTL"] = "+LTL",
+ ["-RTT"] = "+RTT",
+ ["+TRT"] = "-TRT",
+ ["+TLT"] = "-TLT",
+ ["+LTL"] = "-LTL",
+ ["+RTT"] = "-RTT",
+}
diff --git a/tex/context/base/node-ext.lua b/tex/context/base/node-ext.lua
index df2a37650..82ec04ee5 100644
--- a/tex/context/base/node-ext.lua
+++ b/tex/context/base/node-ext.lua
@@ -1,30 +1,30 @@
-if not modules then modules = { } end modules ['node-ext'] = {
- 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>Serializing nodes can be handy for tracing. Also, saving and
-loading node lists can come in handy as soon we are going to
-use external applications to process node lists.</p>
---ldx]]--
-
-function nodes.show(stack)
--- logs.writer(table.serialize(stack))
-end
-
-function nodes.save(stack,name) -- *.ltn : luatex node file
--- if name then
--- file.savedata(name,table.serialize(stack))
--- else
--- logs.writer(table.serialize(stack))
--- end
-end
-
-function nodes.load(name)
--- return file.loaddata(name)
--- -- todo
-end
+if not modules then modules = { } end modules ['node-ext'] = {
+ 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>Serializing nodes can be handy for tracing. Also, saving and
+loading node lists can come in handy as soon we are going to
+use external applications to process node lists.</p>
+--ldx]]--
+
+function nodes.show(stack)
+-- logs.writer(table.serialize(stack))
+end
+
+function nodes.save(stack,name) -- *.ltn : luatex node file
+-- if name then
+-- file.savedata(name,table.serialize(stack))
+-- else
+-- logs.writer(table.serialize(stack))
+-- end
+end
+
+function nodes.load(name)
+-- return file.loaddata(name)
+-- -- todo
+end
diff --git a/tex/context/base/node-fin.lua b/tex/context/base/node-fin.lua
index e95725d29..2e62ebcb5 100644
--- a/tex/context/base/node-fin.lua
+++ b/tex/context/base/node-fin.lua
@@ -1,1222 +1,1222 @@
-if not modules then modules = { } end modules ['node-fin'] = {
- version = 1.001,
- 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
-
-local next, type, format = next, type, string.format
-
-local attributes, nodes, node = attributes, nodes, node
-
-local copy_node = node.copy
-local find_tail = node.slide
-
-local nodecodes = nodes.nodecodes
-local whatcodes = nodes.whatcodes
-
-local glyph_code = nodecodes.glyph
-local disc_code = nodecodes.disc
-local glue_code = nodecodes.glue
-local rule_code = nodecodes.rule
-local whatsit_code = nodecodes.whatsit
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-
-local pdfliteral_code = whatcodes.pdfliteral
-
-local states = attributes.states
-local numbers = attributes.numbers
-local a_trigger = attributes.private('trigger')
-local triggering = false
-
-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
-
--- the following code is no longer needed due to the new backend
--- but we keep it around for a while as an example
---
--- states.collected = states.collected or { }
---
--- storage.register("states/collected", states.collected, "states.collected")
---
--- local collected = states.collected
---
--- function states.collect(str)
--- collected[#collected+1] = str
--- end
---
--- function states.flush()
--- if #collected > 0 then
--- for i=1,#collected do
--- context(collected[i]) -- we're in context mode anyway
--- end
--- collected = { }
--- states.collected = collected
--- end
--- end
---
--- function states.check()
--- logs.report("states",concat(collected,"\n"))
--- end
-
--- we used to do the main processor loop here and call processor for each node
--- but eventually this was too much a slow down (1 sec on 23 for 120 pages mk)
--- so that we moved looping to the processor itself; this may lead to a bit of
--- duplicate code once that we have more state handlers
-
--- local function process_attribute(head,plugin) -- head,attribute,enabled,initializer,resolver,processor,finalizer
--- local namespace = plugin.namespace
--- if namespace.enabled ~= false then -- this test will go away
--- starttiming(attributes) -- in principle we could delegate this to the main caller
--- local done, used, ok = false, nil, false
--- local attribute = namespace.attribute or numbers[plugin.name] -- todo: plugin.attribute
--- local processor = plugin.processor
--- if processor then
--- local initializer = plugin.initializer
--- local resolver = plugin.resolver
--- local inheritance = (resolver and resolver()) or nil -- -0x7FFFFFFF -- we can best use nil and skip !
--- if initializer then
--- initializer(namespace,attribute,head)
--- end
--- head, ok = processor(namespace,attribute,head,inheritance)
--- if ok then
--- local finalizer = plugin.finalizer
--- if finalizer then
--- head, ok, used = finalizer(namespace,attribute,head)
--- if used then
--- local flusher = plugin.flusher
--- if flusher then
--- head = flusher(namespace,attribute,head,used)
--- end
--- end
--- end
--- done = true
--- end
--- end
--- stoptiming(attributes)
--- return head, done
--- else
--- return head, false
--- end
--- end
---
--- function nodes.installattributehandler(plugin) -- we need to avoid this nested function
--- return function(head)
--- return process_attribute(head,plugin)
--- end
--- end
-
--- An experiment: lean and mean functions. It is not really faster but
--- with upcoming functionality it might make a difference, e.g. features
--- like 'casing' and 'italics' can be called a lot so there it makes sense.
-
-nodes.plugindata = nil
-
-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, false
- end
-elseif initializer or finalizer or resolver then
- return function(head)
- starttiming(attributes)
- local done, used, ok = false, nil, false
- local inheritance = (resolver and resolver()) or nil -- -0x7FFFFFFF -- we can best use nil and skip !
- if initializer then
- initializer(namespace,attribute,head)
- end
- head, ok = processor(namespace,attribute,head,inheritance)
- if ok then
- if finalizer then
- head, ok, used = finalizer(namespace,attribute,head)
- if used and flusher then
- head = flusher(namespace,attribute,head,used)
- end
- end
- done = true
- end
- stoptiming(attributes)
- return head, done
- end
-else
- return function(head)
- starttiming(attributes)
- local head, done = processor(namespace,attribute,head)
- stoptiming(attributes)
- return head, done
- end
-end
-nodes.plugindata = nil
-]]
-
-function nodes.installattributehandler(plugin)
- nodes.plugindata = plugin
- return loadstripped(template)()
-end
-
--- the injectors
-
-local insert_node_before = node.insert_before
-local insert_node_after = node.insert_after
-
-local nsdata, nsnone, nslistwise, nsforced, nsselector, nstrigger
-local current, current_selector, done = 0, 0, false -- nb, stack has a local current !
-local nsbegin, nsend
-
-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
- done = false -- todo: done cleanup
- nsstep = namespace.resolve_step
- if nsstep then
- 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 = head.id
- if id == hlist_code or id == vlist_code then
- local list = head.list
- if list then
- head.list = insert_node_before(list,list,copy_node(nsnone))
- end
- else
- head = insert_node_before(head,head,copy_node(nsnone))
- end
- return head, true, true
- end
- return head, false, false
-end
-
--- disc nodes can be ignored
--- we need to deal with literals too (reset as well as oval)
--- if id == glyph_code or (id == whatsit_code and stack.subtype == pdfliteral_code) or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then
-
--- local function process(namespace,attribute,head,inheritance,default) -- one attribute
--- local stack, done = head, false
--- while stack do
--- local id = stack.id
--- if id == glyph_code or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then -- or disc_code
--- local c = 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
--- done = true
--- end
--- elseif current ~= c then
--- head = insert_node_before(head,stack,copy_node(nsdata[c]))
--- current = c
--- done = true
--- end
--- -- here ? compare selective
--- if id == glue_code then --leader
--- -- same as *list
--- local content = stack.leader
--- if content then
--- local savedcurrent = current
--- local ci = content.id
--- 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
--- local ok = false
--- if nstrigger and stack[nstrigger] then
--- local outer = stack[attribute]
--- if outer ~= inheritance then
--- stack.leader, ok = process(namespace,attribute,content,inheritance,outer)
--- else
--- stack.leader, ok = process(namespace,attribute,content,inheritance,default)
--- end
--- else
--- stack.leader, ok = process(namespace,attribute,content,inheritance,default)
--- end
--- current = savedcurrent
--- done = done or ok
--- end
--- end
--- elseif default and inheritance then
--- if current ~= default then
--- head = insert_node_before(head,stack,copy_node(nsdata[default]))
--- current = default
--- done = true
--- end
--- elseif current > 0 then
--- head = insert_node_before(head,stack,copy_node(nsnone))
--- current = 0
--- done = true
--- end
--- elseif id == hlist_code or id == vlist_code then
--- local content = stack.list
--- if content then
--- local ok = false
--- if nstrigger and stack[nstrigger] then
--- local outer = stack[attribute]
--- if outer ~= inheritance then
--- stack.list, ok = process(namespace,attribute,content,inheritance,outer)
--- else
--- stack.list, ok = process(namespace,attribute,content,inheritance,default)
--- end
--- else
--- stack.list, ok = process(namespace,attribute,content,inheritance,default)
--- end
--- done = done or ok
--- end
--- end
--- stack = stack.next
--- end
--- return head, done
--- end
-
--- local function process(namespace,attribute,head,inheritance,default) -- one attribute
--- local stack, done = head, false
-
--- local function check()
--- local c = 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
--- done = true
--- end
--- elseif current ~= c then
--- head = insert_node_before(head,stack,copy_node(nsdata[c]))
--- current = c
--- done = true
--- end
--- elseif default and inheritance then
--- if current ~= default then
--- head = insert_node_before(head,stack,copy_node(nsdata[default]))
--- current = default
--- done = true
--- end
--- elseif current > 0 then
--- head = insert_node_before(head,stack,copy_node(nsnone))
--- current = 0
--- done = true
--- end
--- return c
--- end
-
--- local function nested(content)
--- if nstrigger and stack[nstrigger] then
--- local outer = stack[attribute]
--- if outer ~= inheritance then
--- return process(namespace,attribute,content,inheritance,outer)
--- else
--- return process(namespace,attribute,content,inheritance,default)
--- end
--- else
--- return process(namespace,attribute,content,inheritance,default)
--- end
--- end
-
--- while stack do
--- local id = stack.id
--- if id == glyph_code then
--- check()
--- elseif id == glue_code then
--- local content = stack.leader
--- if content and check() then
--- local savedcurrent = current
--- local ci = content.id
--- 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
-
--- local ok = false
--- stack.leader, ok = nested(content)
--- done = done or ok
-
--- current = savedcurrent
--- end
--- elseif id == hlist_code or id == vlist_code then
--- local content = stack.list
--- if content then
-
--- local ok = false
--- stack.list, ok = nested(content)
--- done = done or ok
-
--- end
--- elseif id == rule_code then
--- if stack.width ~= 0 then
--- check()
--- end
--- end
--- stack = stack.next
--- end
--- return head, done
--- end
-
--- local function process(namespace,attribute,head,inheritance,default) -- one attribute
--- local stack, done = head, false
--- while stack do
--- local id = stack.id
--- if id == glyph_code then
--- -- begin of check
--- local c = 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
--- done = true
--- end
--- elseif current ~= c then
--- head = insert_node_before(head,stack,copy_node(nsdata[c]))
--- current = c
--- done = true
--- end
--- elseif default and inheritance then
--- if current ~= default then
--- head = insert_node_before(head,stack,copy_node(nsdata[default]))
--- current = default
--- done = true
--- end
--- elseif current > 0 then
--- head = insert_node_before(head,stack,copy_node(nsnone))
--- current = 0
--- done = true
--- end
--- -- end of check
--- elseif id == glue_code then
--- local content = stack.leader
--- if content then
--- -- begin of check
--- local c = 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
--- done = true
--- end
--- elseif current ~= c then
--- head = insert_node_before(head,stack,copy_node(nsdata[c]))
--- current = c
--- done = true
--- end
--- -- begin special to this check
--- local savedcurrent = current
--- local ci = content.id
--- 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 ok = false
--- if nstrigger and stack[nstrigger] then
--- local outer = stack[attribute]
--- if outer ~= inheritance then
--- stack.leader, ok = process(namespace,attribute,content,inheritance,outer)
--- else
--- stack.leader, ok = process(namespace,attribute,content,inheritance,default)
--- end
--- else
--- stack.leader, ok = process(namespace,attribute,content,inheritance,default)
--- end
--- -- end nested --
--- done = done or ok
--- current = savedcurrent
--- -- end special to this check
--- elseif default and inheritance then
--- if current ~= default then
--- head = insert_node_before(head,stack,copy_node(nsdata[default]))
--- current = default
--- done = true
--- end
--- elseif current > 0 then
--- head = insert_node_before(head,stack,copy_node(nsnone))
--- current = 0
--- done = true
--- end
--- -- end of check
--- end
--- elseif id == hlist_code or id == vlist_code then
--- local content = stack.list
--- if content then
--- -- begin nested --
--- local ok
--- if nstrigger and stack[nstrigger] then
--- local outer = stack[attribute]
--- if outer ~= inheritance then
--- stack.list, ok = process(namespace,attribute,content,inheritance,outer)
--- else
--- stack.list, ok = process(namespace,attribute,content,inheritance,default)
--- end
--- else
--- stack.list, ok = process(namespace,attribute,content,inheritance,default)
--- end
--- -- end nested --
--- done = done or ok
--- end
--- elseif id == rule_code then
--- if stack.width ~= 0 then
--- -- begin of check
--- local c = 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
--- done = true
--- end
--- elseif current ~= c then
--- head = insert_node_before(head,stack,copy_node(nsdata[c]))
--- current = c
--- done = true
--- end
--- elseif default and inheritance then
--- if current ~= default then
--- head = insert_node_before(head,stack,copy_node(nsdata[default]))
--- current = default
--- done = true
--- end
--- elseif current > 0 then
--- head = insert_node_before(head,stack,copy_node(nsnone))
--- current = 0
--- done = true
--- end
--- -- end of check
--- end
--- end
--- stack = stack.next
--- end
--- return head, done
--- end
-
-local function process(namespace,attribute,head,inheritance,default) -- one attribute
- local stack = head
- local done = false
- local check = false
- local leader = nil
- while stack do
- local id = stack.id
- if id == glyph_code then
- check = true
- elseif id == glue_code then
- leader = stack.leader
- if leader then
- check = true
- end
- elseif id == hlist_code or id == vlist_code then
- local content = stack.list
- if content then
- -- begin nested --
- local ok
- if nstrigger and stack[nstrigger] then
- local outer = stack[attribute]
- if outer ~= inheritance then
- stack.list, ok = process(namespace,attribute,content,inheritance,outer)
- else
- stack.list, ok = process(namespace,attribute,content,inheritance,default)
- end
- else
- stack.list, ok = process(namespace,attribute,content,inheritance,default)
- end
- -- end nested --
- done = done or ok
- end
- elseif id == rule_code then
- check = stack.width ~= 0
- end
- -- much faster this way than using a check() and nested() function
- if check then
- local c = 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
- done = true
- end
- elseif current ~= c then
- head = insert_node_before(head,stack,copy_node(nsdata[c]))
- current = c
- done = true
- end
- if leader then
- local savedcurrent = current
- local ci = leader.id
- 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 ok = false
- if nstrigger and stack[nstrigger] then
- local outer = stack[attribute]
- if outer ~= inheritance then
- stack.leader, ok = process(namespace,attribute,leader,inheritance,outer)
- else
- stack.leader, ok = process(namespace,attribute,leader,inheritance,default)
- end
- else
- stack.leader, ok = process(namespace,attribute,leader,inheritance,default)
- end
- -- end nested --
- done = done or ok
- 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
- done = true
- end
- elseif current > 0 then
- head = insert_node_before(head,stack,copy_node(nsnone))
- current = 0
- done = true
- end
- check = false
- end
- stack = stack.next
- end
- return head, done
-end
-
-states.process = process
-
--- 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(namespace,attribute,head,inheritance,default) -- two attributes
--- local stack, done = head, false
--- while stack do
--- local id = stack.id
--- -- we need to deal with literals too (reset as well as oval)
--- -- if id == glyph_code or (id == whatsit_code and stack.subtype == pdfliteral_code) or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then -- or disc_code
--- if id == glyph_code -- or id == disc_code
--- or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then -- or disc_code
--- local c = 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 stack[nsselector] or nsselector]))
--- current = default
--- done = true
--- end
--- else
--- local s = 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 stack[nsselector] or nsselector]))
--- current = c
--- current_selector = s
--- done = true
--- 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 stack[nsselector] or nsselector]))
--- current = default
--- done = true
--- end
--- elseif current > 0 then
--- head = insert_node_before(head,stack,copy_node(nsnone))
--- current, current_selector, done = 0, 0, true
--- end
--- if id == glue_code then -- leader
--- -- same as *list
--- local content = stack.leader
--- if content then
--- local savedcurrent = current
--- local ci = content.id
--- 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
--- local ok = false
--- if nstrigger and stack[nstrigger] then
--- local outer = stack[attribute]
--- if outer ~= inheritance then
--- stack.leader, ok = selective(namespace,attribute,content,inheritance,outer)
--- else
--- stack.leader, ok = selective(namespace,attribute,content,inheritance,default)
--- end
--- else
--- stack.leader, ok = selective(namespace,attribute,content,inheritance,default)
--- end
--- current = savedcurrent
--- done = done or ok
--- end
--- end
--- elseif id == hlist_code or id == vlist_code then
--- local content = stack.list
--- if content then
--- local ok = false
--- if nstrigger and stack[nstrigger] then
--- local outer = stack[attribute]
--- if outer ~= inheritance then
--- stack.list, ok = selective(namespace,attribute,content,inheritance,outer)
--- else
--- stack.list, ok = selective(namespace,attribute,content,inheritance,default)
--- end
--- else
--- stack.list, ok = selective(namespace,attribute,content,inheritance,default)
--- end
--- done = done or ok
--- end
--- end
--- stack = stack.next
--- end
--- return head, done
--- end
-
--- local function selective(namespace,attribute,head,inheritance,default) -- two attributes
--- local stack, done = head, false
-
--- local function check()
--- local c = 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 stack[nsselector] or nsselector]))
--- current = default
--- done = true
--- end
--- else
--- local s = 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 stack[nsselector] or nsselector]))
--- current = c
--- current_selector = s
--- done = true
--- 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 stack[nsselector] or nsselector]))
--- current = default
--- done = true
--- end
--- elseif current > 0 then
--- head = insert_node_before(head,stack,copy_node(nsnone))
--- current, current_selector, done = 0, 0, true
--- end
--- return c
--- end
-
--- local function nested(content)
--- if nstrigger and stack[nstrigger] then
--- local outer = stack[attribute]
--- if outer ~= inheritance then
--- return selective(namespace,attribute,content,inheritance,outer)
--- else
--- return selective(namespace,attribute,content,inheritance,default)
--- end
--- else
--- return selective(namespace,attribute,content,inheritance,default)
--- end
--- end
-
--- while stack do
--- local id = stack.id
--- if id == glyph_code then
--- check()
--- elseif id == glue_code then
--- local content = stack.leader
--- if content and check() then
--- -- local savedcurrent = current
--- -- local ci = content.id
--- -- 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
-
--- local ok = false
--- stack.leader, ok = nested(content)
--- done = done or ok
-
--- -- current = savedcurrent
--- end
--- elseif id == hlist_code or id == vlist_code then
--- local content = stack.list
--- if content then
-
--- local ok = false
--- stack.list, ok = nested(content)
--- done = done or ok
-
--- end
--- elseif id == rule_code then
--- if stack.width ~= 0 then
--- check()
--- end
--- end
--- stack = stack.next
--- end
--- return head, done
--- end
-
-local function selective(namespace,attribute,head,inheritance,default) -- two attributes
- local stack = head
- local done = false
- local check = false
- local leader = nil
- while stack do
- local id = stack.id
- if id == glyph_code then
- check = true
- elseif id == glue_code then
- leader = stack.leader
- if leader then
- check = true
- end
- elseif id == hlist_code or id == vlist_code then
- local content = stack.list
- if content then
- local ok = false
- -- begin nested
- if nstrigger and stack[nstrigger] then
- local outer = stack[attribute]
- if outer ~= inheritance then
- stack.list, ok = selective(namespace,attribute,content,inheritance,outer)
- else
- stack.list, ok = selective(namespace,attribute,content,inheritance,default)
- end
- else
- stack.list, ok = selective(namespace,attribute,content,inheritance,default)
- end
- -- end nested
- done = done or ok
- end
- elseif id == rule_code then
- check = stack.width ~= 0
- end
-
- if check then
- local c = 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 stack[nsselector] or nsselector]))
- current = default
- done = true
- end
- else
- local s = 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 stack[nsselector] or nsselector]))
- current = c
- current_selector = s
- done = true
- end
- end
- if leader then
- local ok = false
- -- begin nested
- if nstrigger and stack[nstrigger] then
- local outer = stack[attribute]
- if outer ~= inheritance then
- stack.leader, ok = selective(namespace,attribute,leader,inheritance,outer)
- else
- stack.leader, ok = selective(namespace,attribute,leader,inheritance,default)
- end
- else
- stack.leader, ok = selective(namespace,attribute,leader,inheritance,default)
- end
- -- end nested
- done = done or ok
- 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 stack[nsselector] or nsselector]))
- current = default
- done = true
- end
- elseif current > 0 then
- head = insert_node_before(head,stack,copy_node(nsnone))
- current, current_selector, done = 0, 0, true
- end
- check = false
- end
-
- stack = stack.next
- end
- return head, done
-end
-
-states.selective = selective
-
--- 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(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
--- local stack, done = head, false
--- local current, depth = default or 0, 0
---
--- local function check()
--- local a = stack[attribute]
--- if a then
--- if current ~= a then
--- head = insert_node_before(head,stack,copy_node(nsdata[a]))
--- depth = depth + 1
--- current, done = a, true
--- end
--- elseif default > 0 then
--- --
--- elseif current > 0 then
--- head = insert_node_before(head,stack,copy_node(nsnone))
--- depth = depth - 1
--- current, done = 0, true
--- end
--- return a
--- end
---
--- while stack do
--- local id = stack.id
--- if id == glyph_code then
--- check()
--- elseif id == glue_code then
--- local content = stack.leader
--- if content and check() then
--- local ok = false
--- stack.leader, ok = stacked(namespace,attribute,content,current)
--- done = done or ok
--- end
--- elseif id == hlist_code or id == vlist_code then
--- local content = stack.list
--- if content then
--- -- the problem is that broken lines gets the attribute which can be a later one
--- if nslistwise then
--- local a = stack[attribute]
--- if a and current ~= a and nslistwise[a] then -- viewerlayer / needs checking, see below
--- local p = current
--- current, done = a, true
--- head = insert_node_before(head,stack,copy_node(nsdata[a]))
--- stack.list = stacked(namespace,attribute,content,current)
--- head, stack = insert_node_after(head,stack,copy_node(nsnone))
--- current = p
--- else
--- local ok = false
--- stack.list, ok = stacked(namespace,attribute,content,current)
--- done = done or ok
--- end
--- else
--- local ok = false
--- stack.list, ok = stacked(namespace,attribute,content,current)
--- done = done or ok
--- end
--- end
--- elseif id == rule_code then
--- if stack.width ~= 0 then
--- check()
--- end
--- end
--- stack = stack.next
--- end
--- while depth > 0 do
--- head = insert_node_after(head,stack,copy_node(nsnone))
--- depth = depth - 1
--- end
--- return head, done
--- end
-
-local function stacked(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
- local stack = head
- local done = false
- local current = default or 0
- local depth = 0
- local check = false
- local leader = false
- while stack do
- local id = stack.id
- if id == glyph_code then
- check = true
- elseif id == glue_code then
- leader = stack.leader
- if leader then
- check = true
- end
- elseif id == hlist_code or id == vlist_code then
- local content = stack.list
- if content then
- -- the problem is that broken lines gets the attribute which can be a later one
- if nslistwise then
- local a = stack[attribute]
- if a and current ~= a and nslistwise[a] then -- viewerlayer / needs checking, see below
- local p = current
- current, done = a, true
- head = insert_node_before(head,stack,copy_node(nsdata[a]))
- stack.list = stacked(namespace,attribute,content,current)
- head, stack = insert_node_after(head,stack,copy_node(nsnone))
- current = p
- else
- local ok = false
- stack.list, ok = stacked(namespace,attribute,content,current)
- done = done or ok
- end
- else
- local ok = false
- stack.list, ok = stacked(namespace,attribute,content,current)
- done = done or ok
- end
- end
- elseif id == rule_code then
- check = stack.width ~= 0
- end
-
- if check then
- local a = stack[attribute]
- if a then
- if current ~= a then
- head = insert_node_before(head,stack,copy_node(nsdata[a]))
- depth = depth + 1
- current, done = a, true
- end
- if leader then
- local ok = false
- stack.leader, ok = stacked(namespace,attribute,content,current)
- done = done or ok
- leader = false
- end
- elseif default > 0 then
- --
- elseif current > 0 then
- head = insert_node_before(head,stack,copy_node(nsnone))
- depth = depth - 1
- current, done = 0, true
- end
- check = false
- end
-
- stack = stack.next
- end
- while depth > 0 do
- head = insert_node_after(head,stack,copy_node(nsnone))
- depth = depth - 1
- end
- return head, done
-end
-
-states.stacked = stacked
-
--- experimental
-
--- local function stacker(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
--- nsbegin()
--- local current, previous, done, okay = head, head, false, false
--- local attrib = default or unsetvalue
---
--- local function check()
--- local a = current[attribute] or unsetvalue
--- if a ~= attrib then
--- local n = nsstep(a)
--- if n then
--- -- !!!! TEST CODE !!!!
--- -- head = insert_node_before(head,current,copy_node(nsdata[tonumber(n)])) -- a
--- head = insert_node_before(head,current,n) -- a
--- end
--- attrib, done, okay = a, true, true
--- end
--- return a
--- end
---
--- while current do
--- local id = current.id
--- if id == glyph_code then
--- check()
--- elseif id == glue_code then
--- local content = current.leader
--- if content and check() then
--- -- tricky as a leader has to be a list so we cannot inject before
--- local _, ok = stacker(namespace,attribute,content,attrib)
--- done = done or ok
--- end
--- elseif id == hlist_code or id == vlist_code then
--- local content = current.list
--- if not content then
--- -- skip
--- elseif nslistwise then
--- local a = current[attribute]
--- if a and attrib ~= a and nslistwise[a] then -- viewerlayer
--- done = true
--- head = insert_node_before(head,current,copy_node(nsdata[a]))
--- current.list = stacker(namespace,attribute,content,a)
--- head, current = insert_node_after(head,current,copy_node(nsnone))
--- else
--- local ok = false
--- current.list, ok = stacker(namespace,attribute,content,attrib)
--- done = done or ok
--- end
--- else
--- local ok = false
--- current.list, ok = stacker(namespace,attribute,content,default)
--- done = done or ok
--- end
--- elseif id == rule_code then
--- if current.width ~= 0 then
--- check()
--- end
--- end
--- previous = current
--- current = current.next
--- end
--- if okay then
--- local n = nsend()
--- if n then
--- -- !!!! TEST CODE !!!!
--- -- head = insert_node_after(head,previous,copy_node(nsdata[tostring(n)]))
--- head = insert_node_after(head,previous,n)
--- end
--- end
--- return head, done
--- end
-
-local function stacker(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
- nsbegin()
- local current = head
- local previous = head
- local done = false
- local okay = false
- local attrib = default or unsetvalue
- local check = false
- local leader = false
- while current do
- local id = current.id
- if id == glyph_code then
- check = true
- elseif id == glue_code then
- leader = current.leader
- if leader then
- check = true
- end
- elseif id == hlist_code or id == vlist_code then
- local content = current.list
- if not content then
- -- skip
- elseif nslistwise then
- local a = current[attribute]
- if a and attrib ~= a and nslistwise[a] then -- viewerlayer
- done = true
- head = insert_node_before(head,current,copy_node(nsdata[a]))
- current.list = stacker(namespace,attribute,content,a)
- head, current = insert_node_after(head,current,copy_node(nsnone))
- else
- local ok = false
- current.list, ok = stacker(namespace,attribute,content,attrib)
- done = done or ok
- end
- else
- local ok = false
- current.list, ok = stacker(namespace,attribute,content,default)
- done = done or ok
- end
- elseif id == rule_code then
- check = current.width ~= 0
- end
-
- if check then
- local a = current[attribute] or unsetvalue
- if a ~= attrib then
- local n = nsstep(a)
- if n then
- -- !!!! TEST CODE !!!!
- -- head = insert_node_before(head,current,copy_node(nsdata[tonumber(n)])) -- a
- head = insert_node_before(head,current,n) -- a
- end
- attrib, done, okay = a, true, true
- if leader then
- -- tricky as a leader has to be a list so we cannot inject before
- local _, ok = stacker(namespace,attribute,leader,attrib)
- done = done or ok
- leader = false
- end
- end
- check = false
- end
-
- previous = current
- current = current.next
- end
- if okay then
- local n = nsend()
- if n then
- -- !!!! TEST CODE !!!!
- -- head = insert_node_after(head,previous,copy_node(nsdata[tostring(n)]))
- head = insert_node_after(head,previous,n)
- end
- end
- return head, done
-end
-
-states.stacker = stacker
-
--- -- --
-
-statistics.register("attribute processing time", function()
- return statistics.elapsedseconds(attributes,"front- and backend")
-end)
+if not modules then modules = { } end modules ['node-fin'] = {
+ version = 1.001,
+ 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
+
+local next, type, format = next, type, string.format
+
+local attributes, nodes, node = attributes, nodes, node
+
+local copy_node = node.copy
+local find_tail = node.slide
+
+local nodecodes = nodes.nodecodes
+local whatcodes = nodes.whatcodes
+
+local glyph_code = nodecodes.glyph
+local disc_code = nodecodes.disc
+local glue_code = nodecodes.glue
+local rule_code = nodecodes.rule
+local whatsit_code = nodecodes.whatsit
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+
+local pdfliteral_code = whatcodes.pdfliteral
+
+local states = attributes.states
+local numbers = attributes.numbers
+local a_trigger = attributes.private('trigger')
+local triggering = false
+
+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
+
+-- the following code is no longer needed due to the new backend
+-- but we keep it around for a while as an example
+--
+-- states.collected = states.collected or { }
+--
+-- storage.register("states/collected", states.collected, "states.collected")
+--
+-- local collected = states.collected
+--
+-- function states.collect(str)
+-- collected[#collected+1] = str
+-- end
+--
+-- function states.flush()
+-- if #collected > 0 then
+-- for i=1,#collected do
+-- context(collected[i]) -- we're in context mode anyway
+-- end
+-- collected = { }
+-- states.collected = collected
+-- end
+-- end
+--
+-- function states.check()
+-- logs.report("states",concat(collected,"\n"))
+-- end
+
+-- we used to do the main processor loop here and call processor for each node
+-- but eventually this was too much a slow down (1 sec on 23 for 120 pages mk)
+-- so that we moved looping to the processor itself; this may lead to a bit of
+-- duplicate code once that we have more state handlers
+
+-- local function process_attribute(head,plugin) -- head,attribute,enabled,initializer,resolver,processor,finalizer
+-- local namespace = plugin.namespace
+-- if namespace.enabled ~= false then -- this test will go away
+-- starttiming(attributes) -- in principle we could delegate this to the main caller
+-- local done, used, ok = false, nil, false
+-- local attribute = namespace.attribute or numbers[plugin.name] -- todo: plugin.attribute
+-- local processor = plugin.processor
+-- if processor then
+-- local initializer = plugin.initializer
+-- local resolver = plugin.resolver
+-- local inheritance = (resolver and resolver()) or nil -- -0x7FFFFFFF -- we can best use nil and skip !
+-- if initializer then
+-- initializer(namespace,attribute,head)
+-- end
+-- head, ok = processor(namespace,attribute,head,inheritance)
+-- if ok then
+-- local finalizer = plugin.finalizer
+-- if finalizer then
+-- head, ok, used = finalizer(namespace,attribute,head)
+-- if used then
+-- local flusher = plugin.flusher
+-- if flusher then
+-- head = flusher(namespace,attribute,head,used)
+-- end
+-- end
+-- end
+-- done = true
+-- end
+-- end
+-- stoptiming(attributes)
+-- return head, done
+-- else
+-- return head, false
+-- end
+-- end
+--
+-- function nodes.installattributehandler(plugin) -- we need to avoid this nested function
+-- return function(head)
+-- return process_attribute(head,plugin)
+-- end
+-- end
+
+-- An experiment: lean and mean functions. It is not really faster but
+-- with upcoming functionality it might make a difference, e.g. features
+-- like 'casing' and 'italics' can be called a lot so there it makes sense.
+
+nodes.plugindata = nil
+
+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, false
+ end
+elseif initializer or finalizer or resolver then
+ return function(head)
+ starttiming(attributes)
+ local done, used, ok = false, nil, false
+ local inheritance = (resolver and resolver()) or nil -- -0x7FFFFFFF -- we can best use nil and skip !
+ if initializer then
+ initializer(namespace,attribute,head)
+ end
+ head, ok = processor(namespace,attribute,head,inheritance)
+ if ok then
+ if finalizer then
+ head, ok, used = finalizer(namespace,attribute,head)
+ if used and flusher then
+ head = flusher(namespace,attribute,head,used)
+ end
+ end
+ done = true
+ end
+ stoptiming(attributes)
+ return head, done
+ end
+else
+ return function(head)
+ starttiming(attributes)
+ local head, done = processor(namespace,attribute,head)
+ stoptiming(attributes)
+ return head, done
+ end
+end
+nodes.plugindata = nil
+]]
+
+function nodes.installattributehandler(plugin)
+ nodes.plugindata = plugin
+ return loadstripped(template)()
+end
+
+-- the injectors
+
+local insert_node_before = node.insert_before
+local insert_node_after = node.insert_after
+
+local nsdata, nsnone, nslistwise, nsforced, nsselector, nstrigger
+local current, current_selector, done = 0, 0, false -- nb, stack has a local current !
+local nsbegin, nsend
+
+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
+ done = false -- todo: done cleanup
+ nsstep = namespace.resolve_step
+ if nsstep then
+ 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 = head.id
+ if id == hlist_code or id == vlist_code then
+ local list = head.list
+ if list then
+ head.list = insert_node_before(list,list,copy_node(nsnone))
+ end
+ else
+ head = insert_node_before(head,head,copy_node(nsnone))
+ end
+ return head, true, true
+ end
+ return head, false, false
+end
+
+-- disc nodes can be ignored
+-- we need to deal with literals too (reset as well as oval)
+-- if id == glyph_code or (id == whatsit_code and stack.subtype == pdfliteral_code) or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then
+
+-- local function process(namespace,attribute,head,inheritance,default) -- one attribute
+-- local stack, done = head, false
+-- while stack do
+-- local id = stack.id
+-- if id == glyph_code or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then -- or disc_code
+-- local c = 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
+-- done = true
+-- end
+-- elseif current ~= c then
+-- head = insert_node_before(head,stack,copy_node(nsdata[c]))
+-- current = c
+-- done = true
+-- end
+-- -- here ? compare selective
+-- if id == glue_code then --leader
+-- -- same as *list
+-- local content = stack.leader
+-- if content then
+-- local savedcurrent = current
+-- local ci = content.id
+-- 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
+-- local ok = false
+-- if nstrigger and stack[nstrigger] then
+-- local outer = stack[attribute]
+-- if outer ~= inheritance then
+-- stack.leader, ok = process(namespace,attribute,content,inheritance,outer)
+-- else
+-- stack.leader, ok = process(namespace,attribute,content,inheritance,default)
+-- end
+-- else
+-- stack.leader, ok = process(namespace,attribute,content,inheritance,default)
+-- end
+-- current = savedcurrent
+-- done = done or ok
+-- end
+-- end
+-- elseif default and inheritance then
+-- if current ~= default then
+-- head = insert_node_before(head,stack,copy_node(nsdata[default]))
+-- current = default
+-- done = true
+-- end
+-- elseif current > 0 then
+-- head = insert_node_before(head,stack,copy_node(nsnone))
+-- current = 0
+-- done = true
+-- end
+-- elseif id == hlist_code or id == vlist_code then
+-- local content = stack.list
+-- if content then
+-- local ok = false
+-- if nstrigger and stack[nstrigger] then
+-- local outer = stack[attribute]
+-- if outer ~= inheritance then
+-- stack.list, ok = process(namespace,attribute,content,inheritance,outer)
+-- else
+-- stack.list, ok = process(namespace,attribute,content,inheritance,default)
+-- end
+-- else
+-- stack.list, ok = process(namespace,attribute,content,inheritance,default)
+-- end
+-- done = done or ok
+-- end
+-- end
+-- stack = stack.next
+-- end
+-- return head, done
+-- end
+
+-- local function process(namespace,attribute,head,inheritance,default) -- one attribute
+-- local stack, done = head, false
+
+-- local function check()
+-- local c = 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
+-- done = true
+-- end
+-- elseif current ~= c then
+-- head = insert_node_before(head,stack,copy_node(nsdata[c]))
+-- current = c
+-- done = true
+-- end
+-- elseif default and inheritance then
+-- if current ~= default then
+-- head = insert_node_before(head,stack,copy_node(nsdata[default]))
+-- current = default
+-- done = true
+-- end
+-- elseif current > 0 then
+-- head = insert_node_before(head,stack,copy_node(nsnone))
+-- current = 0
+-- done = true
+-- end
+-- return c
+-- end
+
+-- local function nested(content)
+-- if nstrigger and stack[nstrigger] then
+-- local outer = stack[attribute]
+-- if outer ~= inheritance then
+-- return process(namespace,attribute,content,inheritance,outer)
+-- else
+-- return process(namespace,attribute,content,inheritance,default)
+-- end
+-- else
+-- return process(namespace,attribute,content,inheritance,default)
+-- end
+-- end
+
+-- while stack do
+-- local id = stack.id
+-- if id == glyph_code then
+-- check()
+-- elseif id == glue_code then
+-- local content = stack.leader
+-- if content and check() then
+-- local savedcurrent = current
+-- local ci = content.id
+-- 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
+
+-- local ok = false
+-- stack.leader, ok = nested(content)
+-- done = done or ok
+
+-- current = savedcurrent
+-- end
+-- elseif id == hlist_code or id == vlist_code then
+-- local content = stack.list
+-- if content then
+
+-- local ok = false
+-- stack.list, ok = nested(content)
+-- done = done or ok
+
+-- end
+-- elseif id == rule_code then
+-- if stack.width ~= 0 then
+-- check()
+-- end
+-- end
+-- stack = stack.next
+-- end
+-- return head, done
+-- end
+
+-- local function process(namespace,attribute,head,inheritance,default) -- one attribute
+-- local stack, done = head, false
+-- while stack do
+-- local id = stack.id
+-- if id == glyph_code then
+-- -- begin of check
+-- local c = 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
+-- done = true
+-- end
+-- elseif current ~= c then
+-- head = insert_node_before(head,stack,copy_node(nsdata[c]))
+-- current = c
+-- done = true
+-- end
+-- elseif default and inheritance then
+-- if current ~= default then
+-- head = insert_node_before(head,stack,copy_node(nsdata[default]))
+-- current = default
+-- done = true
+-- end
+-- elseif current > 0 then
+-- head = insert_node_before(head,stack,copy_node(nsnone))
+-- current = 0
+-- done = true
+-- end
+-- -- end of check
+-- elseif id == glue_code then
+-- local content = stack.leader
+-- if content then
+-- -- begin of check
+-- local c = 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
+-- done = true
+-- end
+-- elseif current ~= c then
+-- head = insert_node_before(head,stack,copy_node(nsdata[c]))
+-- current = c
+-- done = true
+-- end
+-- -- begin special to this check
+-- local savedcurrent = current
+-- local ci = content.id
+-- 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 ok = false
+-- if nstrigger and stack[nstrigger] then
+-- local outer = stack[attribute]
+-- if outer ~= inheritance then
+-- stack.leader, ok = process(namespace,attribute,content,inheritance,outer)
+-- else
+-- stack.leader, ok = process(namespace,attribute,content,inheritance,default)
+-- end
+-- else
+-- stack.leader, ok = process(namespace,attribute,content,inheritance,default)
+-- end
+-- -- end nested --
+-- done = done or ok
+-- current = savedcurrent
+-- -- end special to this check
+-- elseif default and inheritance then
+-- if current ~= default then
+-- head = insert_node_before(head,stack,copy_node(nsdata[default]))
+-- current = default
+-- done = true
+-- end
+-- elseif current > 0 then
+-- head = insert_node_before(head,stack,copy_node(nsnone))
+-- current = 0
+-- done = true
+-- end
+-- -- end of check
+-- end
+-- elseif id == hlist_code or id == vlist_code then
+-- local content = stack.list
+-- if content then
+-- -- begin nested --
+-- local ok
+-- if nstrigger and stack[nstrigger] then
+-- local outer = stack[attribute]
+-- if outer ~= inheritance then
+-- stack.list, ok = process(namespace,attribute,content,inheritance,outer)
+-- else
+-- stack.list, ok = process(namespace,attribute,content,inheritance,default)
+-- end
+-- else
+-- stack.list, ok = process(namespace,attribute,content,inheritance,default)
+-- end
+-- -- end nested --
+-- done = done or ok
+-- end
+-- elseif id == rule_code then
+-- if stack.width ~= 0 then
+-- -- begin of check
+-- local c = 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
+-- done = true
+-- end
+-- elseif current ~= c then
+-- head = insert_node_before(head,stack,copy_node(nsdata[c]))
+-- current = c
+-- done = true
+-- end
+-- elseif default and inheritance then
+-- if current ~= default then
+-- head = insert_node_before(head,stack,copy_node(nsdata[default]))
+-- current = default
+-- done = true
+-- end
+-- elseif current > 0 then
+-- head = insert_node_before(head,stack,copy_node(nsnone))
+-- current = 0
+-- done = true
+-- end
+-- -- end of check
+-- end
+-- end
+-- stack = stack.next
+-- end
+-- return head, done
+-- end
+
+local function process(namespace,attribute,head,inheritance,default) -- one attribute
+ local stack = head
+ local done = false
+ local check = false
+ local leader = nil
+ while stack do
+ local id = stack.id
+ if id == glyph_code then
+ check = true
+ elseif id == glue_code then
+ leader = stack.leader
+ if leader then
+ check = true
+ end
+ elseif id == hlist_code or id == vlist_code then
+ local content = stack.list
+ if content then
+ -- begin nested --
+ local ok
+ if nstrigger and stack[nstrigger] then
+ local outer = stack[attribute]
+ if outer ~= inheritance then
+ stack.list, ok = process(namespace,attribute,content,inheritance,outer)
+ else
+ stack.list, ok = process(namespace,attribute,content,inheritance,default)
+ end
+ else
+ stack.list, ok = process(namespace,attribute,content,inheritance,default)
+ end
+ -- end nested --
+ done = done or ok
+ end
+ elseif id == rule_code then
+ check = stack.width ~= 0
+ end
+ -- much faster this way than using a check() and nested() function
+ if check then
+ local c = 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
+ done = true
+ end
+ elseif current ~= c then
+ head = insert_node_before(head,stack,copy_node(nsdata[c]))
+ current = c
+ done = true
+ end
+ if leader then
+ local savedcurrent = current
+ local ci = leader.id
+ 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 ok = false
+ if nstrigger and stack[nstrigger] then
+ local outer = stack[attribute]
+ if outer ~= inheritance then
+ stack.leader, ok = process(namespace,attribute,leader,inheritance,outer)
+ else
+ stack.leader, ok = process(namespace,attribute,leader,inheritance,default)
+ end
+ else
+ stack.leader, ok = process(namespace,attribute,leader,inheritance,default)
+ end
+ -- end nested --
+ done = done or ok
+ 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
+ done = true
+ end
+ elseif current > 0 then
+ head = insert_node_before(head,stack,copy_node(nsnone))
+ current = 0
+ done = true
+ end
+ check = false
+ end
+ stack = stack.next
+ end
+ return head, done
+end
+
+states.process = process
+
+-- 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(namespace,attribute,head,inheritance,default) -- two attributes
+-- local stack, done = head, false
+-- while stack do
+-- local id = stack.id
+-- -- we need to deal with literals too (reset as well as oval)
+-- -- if id == glyph_code or (id == whatsit_code and stack.subtype == pdfliteral_code) or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then -- or disc_code
+-- if id == glyph_code -- or id == disc_code
+-- or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then -- or disc_code
+-- local c = 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 stack[nsselector] or nsselector]))
+-- current = default
+-- done = true
+-- end
+-- else
+-- local s = 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 stack[nsselector] or nsselector]))
+-- current = c
+-- current_selector = s
+-- done = true
+-- 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 stack[nsselector] or nsselector]))
+-- current = default
+-- done = true
+-- end
+-- elseif current > 0 then
+-- head = insert_node_before(head,stack,copy_node(nsnone))
+-- current, current_selector, done = 0, 0, true
+-- end
+-- if id == glue_code then -- leader
+-- -- same as *list
+-- local content = stack.leader
+-- if content then
+-- local savedcurrent = current
+-- local ci = content.id
+-- 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
+-- local ok = false
+-- if nstrigger and stack[nstrigger] then
+-- local outer = stack[attribute]
+-- if outer ~= inheritance then
+-- stack.leader, ok = selective(namespace,attribute,content,inheritance,outer)
+-- else
+-- stack.leader, ok = selective(namespace,attribute,content,inheritance,default)
+-- end
+-- else
+-- stack.leader, ok = selective(namespace,attribute,content,inheritance,default)
+-- end
+-- current = savedcurrent
+-- done = done or ok
+-- end
+-- end
+-- elseif id == hlist_code or id == vlist_code then
+-- local content = stack.list
+-- if content then
+-- local ok = false
+-- if nstrigger and stack[nstrigger] then
+-- local outer = stack[attribute]
+-- if outer ~= inheritance then
+-- stack.list, ok = selective(namespace,attribute,content,inheritance,outer)
+-- else
+-- stack.list, ok = selective(namespace,attribute,content,inheritance,default)
+-- end
+-- else
+-- stack.list, ok = selective(namespace,attribute,content,inheritance,default)
+-- end
+-- done = done or ok
+-- end
+-- end
+-- stack = stack.next
+-- end
+-- return head, done
+-- end
+
+-- local function selective(namespace,attribute,head,inheritance,default) -- two attributes
+-- local stack, done = head, false
+
+-- local function check()
+-- local c = 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 stack[nsselector] or nsselector]))
+-- current = default
+-- done = true
+-- end
+-- else
+-- local s = 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 stack[nsselector] or nsselector]))
+-- current = c
+-- current_selector = s
+-- done = true
+-- 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 stack[nsselector] or nsselector]))
+-- current = default
+-- done = true
+-- end
+-- elseif current > 0 then
+-- head = insert_node_before(head,stack,copy_node(nsnone))
+-- current, current_selector, done = 0, 0, true
+-- end
+-- return c
+-- end
+
+-- local function nested(content)
+-- if nstrigger and stack[nstrigger] then
+-- local outer = stack[attribute]
+-- if outer ~= inheritance then
+-- return selective(namespace,attribute,content,inheritance,outer)
+-- else
+-- return selective(namespace,attribute,content,inheritance,default)
+-- end
+-- else
+-- return selective(namespace,attribute,content,inheritance,default)
+-- end
+-- end
+
+-- while stack do
+-- local id = stack.id
+-- if id == glyph_code then
+-- check()
+-- elseif id == glue_code then
+-- local content = stack.leader
+-- if content and check() then
+-- -- local savedcurrent = current
+-- -- local ci = content.id
+-- -- 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
+
+-- local ok = false
+-- stack.leader, ok = nested(content)
+-- done = done or ok
+
+-- -- current = savedcurrent
+-- end
+-- elseif id == hlist_code or id == vlist_code then
+-- local content = stack.list
+-- if content then
+
+-- local ok = false
+-- stack.list, ok = nested(content)
+-- done = done or ok
+
+-- end
+-- elseif id == rule_code then
+-- if stack.width ~= 0 then
+-- check()
+-- end
+-- end
+-- stack = stack.next
+-- end
+-- return head, done
+-- end
+
+local function selective(namespace,attribute,head,inheritance,default) -- two attributes
+ local stack = head
+ local done = false
+ local check = false
+ local leader = nil
+ while stack do
+ local id = stack.id
+ if id == glyph_code then
+ check = true
+ elseif id == glue_code then
+ leader = stack.leader
+ if leader then
+ check = true
+ end
+ elseif id == hlist_code or id == vlist_code then
+ local content = stack.list
+ if content then
+ local ok = false
+ -- begin nested
+ if nstrigger and stack[nstrigger] then
+ local outer = stack[attribute]
+ if outer ~= inheritance then
+ stack.list, ok = selective(namespace,attribute,content,inheritance,outer)
+ else
+ stack.list, ok = selective(namespace,attribute,content,inheritance,default)
+ end
+ else
+ stack.list, ok = selective(namespace,attribute,content,inheritance,default)
+ end
+ -- end nested
+ done = done or ok
+ end
+ elseif id == rule_code then
+ check = stack.width ~= 0
+ end
+
+ if check then
+ local c = 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 stack[nsselector] or nsselector]))
+ current = default
+ done = true
+ end
+ else
+ local s = 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 stack[nsselector] or nsselector]))
+ current = c
+ current_selector = s
+ done = true
+ end
+ end
+ if leader then
+ local ok = false
+ -- begin nested
+ if nstrigger and stack[nstrigger] then
+ local outer = stack[attribute]
+ if outer ~= inheritance then
+ stack.leader, ok = selective(namespace,attribute,leader,inheritance,outer)
+ else
+ stack.leader, ok = selective(namespace,attribute,leader,inheritance,default)
+ end
+ else
+ stack.leader, ok = selective(namespace,attribute,leader,inheritance,default)
+ end
+ -- end nested
+ done = done or ok
+ 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 stack[nsselector] or nsselector]))
+ current = default
+ done = true
+ end
+ elseif current > 0 then
+ head = insert_node_before(head,stack,copy_node(nsnone))
+ current, current_selector, done = 0, 0, true
+ end
+ check = false
+ end
+
+ stack = stack.next
+ end
+ return head, done
+end
+
+states.selective = selective
+
+-- 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(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
+-- local stack, done = head, false
+-- local current, depth = default or 0, 0
+--
+-- local function check()
+-- local a = stack[attribute]
+-- if a then
+-- if current ~= a then
+-- head = insert_node_before(head,stack,copy_node(nsdata[a]))
+-- depth = depth + 1
+-- current, done = a, true
+-- end
+-- elseif default > 0 then
+-- --
+-- elseif current > 0 then
+-- head = insert_node_before(head,stack,copy_node(nsnone))
+-- depth = depth - 1
+-- current, done = 0, true
+-- end
+-- return a
+-- end
+--
+-- while stack do
+-- local id = stack.id
+-- if id == glyph_code then
+-- check()
+-- elseif id == glue_code then
+-- local content = stack.leader
+-- if content and check() then
+-- local ok = false
+-- stack.leader, ok = stacked(namespace,attribute,content,current)
+-- done = done or ok
+-- end
+-- elseif id == hlist_code or id == vlist_code then
+-- local content = stack.list
+-- if content then
+-- -- the problem is that broken lines gets the attribute which can be a later one
+-- if nslistwise then
+-- local a = stack[attribute]
+-- if a and current ~= a and nslistwise[a] then -- viewerlayer / needs checking, see below
+-- local p = current
+-- current, done = a, true
+-- head = insert_node_before(head,stack,copy_node(nsdata[a]))
+-- stack.list = stacked(namespace,attribute,content,current)
+-- head, stack = insert_node_after(head,stack,copy_node(nsnone))
+-- current = p
+-- else
+-- local ok = false
+-- stack.list, ok = stacked(namespace,attribute,content,current)
+-- done = done or ok
+-- end
+-- else
+-- local ok = false
+-- stack.list, ok = stacked(namespace,attribute,content,current)
+-- done = done or ok
+-- end
+-- end
+-- elseif id == rule_code then
+-- if stack.width ~= 0 then
+-- check()
+-- end
+-- end
+-- stack = stack.next
+-- end
+-- while depth > 0 do
+-- head = insert_node_after(head,stack,copy_node(nsnone))
+-- depth = depth - 1
+-- end
+-- return head, done
+-- end
+
+local function stacked(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
+ local stack = head
+ local done = false
+ local current = default or 0
+ local depth = 0
+ local check = false
+ local leader = false
+ while stack do
+ local id = stack.id
+ if id == glyph_code then
+ check = true
+ elseif id == glue_code then
+ leader = stack.leader
+ if leader then
+ check = true
+ end
+ elseif id == hlist_code or id == vlist_code then
+ local content = stack.list
+ if content then
+ -- the problem is that broken lines gets the attribute which can be a later one
+ if nslistwise then
+ local a = stack[attribute]
+ if a and current ~= a and nslistwise[a] then -- viewerlayer / needs checking, see below
+ local p = current
+ current, done = a, true
+ head = insert_node_before(head,stack,copy_node(nsdata[a]))
+ stack.list = stacked(namespace,attribute,content,current)
+ head, stack = insert_node_after(head,stack,copy_node(nsnone))
+ current = p
+ else
+ local ok = false
+ stack.list, ok = stacked(namespace,attribute,content,current)
+ done = done or ok
+ end
+ else
+ local ok = false
+ stack.list, ok = stacked(namespace,attribute,content,current)
+ done = done or ok
+ end
+ end
+ elseif id == rule_code then
+ check = stack.width ~= 0
+ end
+
+ if check then
+ local a = stack[attribute]
+ if a then
+ if current ~= a then
+ head = insert_node_before(head,stack,copy_node(nsdata[a]))
+ depth = depth + 1
+ current, done = a, true
+ end
+ if leader then
+ local ok = false
+ stack.leader, ok = stacked(namespace,attribute,content,current)
+ done = done or ok
+ leader = false
+ end
+ elseif default > 0 then
+ --
+ elseif current > 0 then
+ head = insert_node_before(head,stack,copy_node(nsnone))
+ depth = depth - 1
+ current, done = 0, true
+ end
+ check = false
+ end
+
+ stack = stack.next
+ end
+ while depth > 0 do
+ head = insert_node_after(head,stack,copy_node(nsnone))
+ depth = depth - 1
+ end
+ return head, done
+end
+
+states.stacked = stacked
+
+-- experimental
+
+-- local function stacker(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
+-- nsbegin()
+-- local current, previous, done, okay = head, head, false, false
+-- local attrib = default or unsetvalue
+--
+-- local function check()
+-- local a = current[attribute] or unsetvalue
+-- if a ~= attrib then
+-- local n = nsstep(a)
+-- if n then
+-- -- !!!! TEST CODE !!!!
+-- -- head = insert_node_before(head,current,copy_node(nsdata[tonumber(n)])) -- a
+-- head = insert_node_before(head,current,n) -- a
+-- end
+-- attrib, done, okay = a, true, true
+-- end
+-- return a
+-- end
+--
+-- while current do
+-- local id = current.id
+-- if id == glyph_code then
+-- check()
+-- elseif id == glue_code then
+-- local content = current.leader
+-- if content and check() then
+-- -- tricky as a leader has to be a list so we cannot inject before
+-- local _, ok = stacker(namespace,attribute,content,attrib)
+-- done = done or ok
+-- end
+-- elseif id == hlist_code or id == vlist_code then
+-- local content = current.list
+-- if not content then
+-- -- skip
+-- elseif nslistwise then
+-- local a = current[attribute]
+-- if a and attrib ~= a and nslistwise[a] then -- viewerlayer
+-- done = true
+-- head = insert_node_before(head,current,copy_node(nsdata[a]))
+-- current.list = stacker(namespace,attribute,content,a)
+-- head, current = insert_node_after(head,current,copy_node(nsnone))
+-- else
+-- local ok = false
+-- current.list, ok = stacker(namespace,attribute,content,attrib)
+-- done = done or ok
+-- end
+-- else
+-- local ok = false
+-- current.list, ok = stacker(namespace,attribute,content,default)
+-- done = done or ok
+-- end
+-- elseif id == rule_code then
+-- if current.width ~= 0 then
+-- check()
+-- end
+-- end
+-- previous = current
+-- current = current.next
+-- end
+-- if okay then
+-- local n = nsend()
+-- if n then
+-- -- !!!! TEST CODE !!!!
+-- -- head = insert_node_after(head,previous,copy_node(nsdata[tostring(n)]))
+-- head = insert_node_after(head,previous,n)
+-- end
+-- end
+-- return head, done
+-- end
+
+local function stacker(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
+ nsbegin()
+ local current = head
+ local previous = head
+ local done = false
+ local okay = false
+ local attrib = default or unsetvalue
+ local check = false
+ local leader = false
+ while current do
+ local id = current.id
+ if id == glyph_code then
+ check = true
+ elseif id == glue_code then
+ leader = current.leader
+ if leader then
+ check = true
+ end
+ elseif id == hlist_code or id == vlist_code then
+ local content = current.list
+ if not content then
+ -- skip
+ elseif nslistwise then
+ local a = current[attribute]
+ if a and attrib ~= a and nslistwise[a] then -- viewerlayer
+ done = true
+ head = insert_node_before(head,current,copy_node(nsdata[a]))
+ current.list = stacker(namespace,attribute,content,a)
+ head, current = insert_node_after(head,current,copy_node(nsnone))
+ else
+ local ok = false
+ current.list, ok = stacker(namespace,attribute,content,attrib)
+ done = done or ok
+ end
+ else
+ local ok = false
+ current.list, ok = stacker(namespace,attribute,content,default)
+ done = done or ok
+ end
+ elseif id == rule_code then
+ check = current.width ~= 0
+ end
+
+ if check then
+ local a = current[attribute] or unsetvalue
+ if a ~= attrib then
+ local n = nsstep(a)
+ if n then
+ -- !!!! TEST CODE !!!!
+ -- head = insert_node_before(head,current,copy_node(nsdata[tonumber(n)])) -- a
+ head = insert_node_before(head,current,n) -- a
+ end
+ attrib, done, okay = a, true, true
+ if leader then
+ -- tricky as a leader has to be a list so we cannot inject before
+ local _, ok = stacker(namespace,attribute,leader,attrib)
+ done = done or ok
+ leader = false
+ end
+ end
+ check = false
+ end
+
+ previous = current
+ current = current.next
+ end
+ if okay then
+ local n = nsend()
+ if n then
+ -- !!!! TEST CODE !!!!
+ -- head = insert_node_after(head,previous,copy_node(nsdata[tostring(n)]))
+ head = insert_node_after(head,previous,n)
+ end
+ end
+ return head, done
+end
+
+states.stacker = stacker
+
+-- -- --
+
+statistics.register("attribute processing time", function()
+ return statistics.elapsedseconds(attributes,"front- and backend")
+end)
diff --git a/tex/context/base/node-fnt.lua b/tex/context/base/node-fnt.lua
index edc1c990e..54359117e 100644
--- a/tex/context/base/node-fnt.lua
+++ b/tex/context/base/node-fnt.lua
@@ -1,226 +1,226 @@
-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 report_fonts = logs.reporter("fonts","processing")
-
-local fonthashes = fonts.hashes
-local fontdata = fonthashes.identifiers
-
-local otf = fonts.handlers.otf
-
-local traverse_id = node.traverse_id
-local starttiming = statistics.starttiming
-local stoptiming = statistics.stoptiming
-local nodecodes = nodes.nodecodes
-local handlers = nodes.handlers
-
-local glyph_code = nodecodes.glyph
-
-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
-
--- potential speedup: check for subtype < 256 so that we can remove that test
--- elsewhere, danger: injected nodes will not be dealt with but that does not
--- happen often; we could consider processing sublists but that might need more
--- checking later on; the current approach also permits variants
-
-local run = 0
-
-local setfontdynamics = { }
-local fontprocesses = { }
-
-setmetatableindex(setfontdynamics, function(t,font)
- local tfmdata = fontdata[font]
- local shared = tfmdata.shared
- local v = shared and shared.dynamics and otf.setdynamics or false
- t[font] = v
- return v
-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
-
-function handlers.characters(head)
- -- either next or not, but definitely no already processed list
- starttiming(nodes)
- local usedfonts, attrfonts, done = { }, { }, false
- local a, u, prevfont, prevattr = 0, 0, nil, 0
- if trace_fontrun then
- run = run + 1
- report_fonts()
- report_fonts("checking node list, run %s",run)
- report_fonts()
- local n = head
- while n do
- local id = n.id
- if id == glyph_code then
- local font = n.font
- local attr = n[0] or 0
- report_fonts("font %03i, dynamic %03i, glyph %s",font,attr,utf.char(n.char))
- else
- report_fonts("[%s]",nodecodes[n.id])
- end
- n = n.next
- end
- end
- for n in traverse_id(glyph_code,head) do
- -- if n.subtype<256 then -- all are 1
- local font = n.font
- local attr = n[0] or 0 -- zero attribute is reserved for fonts in context
- if font ~= prevfont or attr ~= prevattr then
- if attr > 0 then
- local used = attrfonts[font]
- if not used then
- used = { }
- attrfonts[font] = used
- end
- if not used[attr] then
- local sd = setfontdynamics[font]
- if sd then -- always true ?
- local d = sd(font,attr) -- can we cache this one?
- if d then
- used[attr] = d
- a = a + 1
- else
- -- can't happen ... otherwise best use nil/false distinction
- end
- end
- end
- else
- local used = usedfonts[font]
- if not used then
- local fp = fontprocesses[font]
- if fp then
- usedfonts[font] = fp
- u = u + 1
- else
- -- can't happen ... otherwise best use nil/false distinction
- end
- end
- end
- prevfont = font
- prevattr = attr
- end
- -- end
- end
- if trace_fontrun then
- report_fonts()
- report_fonts("statics : %s",(u > 0 and concat(keys(usedfonts)," ")) or "none")
- report_fonts("dynamics: %s",(a > 0 and concat(keys(attrfonts)," ")) or "none")
- report_fonts()
- end
- if u == 0 then
- -- skip
- elseif u == 1 then
- local font, processors = next(usedfonts)
- local n = #processors
- if n > 0 then
- local h, d = processors[1](head,font,0)
- head = h or head
- done = done or d
- if n > 1 then
- for i=2,n do
- local h, d = processors[i](head,font,0)
- head = h or head
- done = done or d
- end
- end
- end
- else
- for font, processors in next, usedfonts do
- local n = #processors
- local h, d = processors[1](head,font,0)
- head = h or head
- done = done or d
- if n > 1 then
- for i=2,n do
- local h, d = processors[i](head,font,0)
- head = h or head
- done = done or d
- end
- end
- end
- end
- if a == 0 then
- -- skip
- elseif a == 1 then
- local font, dynamics = next(attrfonts)
- for attribute, processors in next, dynamics do -- attr can switch in between
- local n = #processors
- if n == 0 then
- report_fonts("no processors associated with dynamic %s",attribute)
- else
- local h, d = processors[1](head,font,attribute)
- head = h or head
- done = done or d
- if n > 1 then
- for i=2,n do
- local h, d = processors[i](head,font,attribute)
- head = h or head
- done = done or d
- end
- end
- end
- end
- else
- for font, dynamics in next, attrfonts do
- for attribute, processors in next, dynamics do -- attr can switch in between
- local n = #processors
- if n == 0 then
- report_fonts("no processors associated with dynamic %s",attribute)
- else
- local h, d = processors[1](head,font,attribute)
- head = h or head
- done = done or d
- if n > 1 then
- for i=2,n do
- local h, d = processors[i](head,font,attribute)
- head = h or head
- done = done or d
- end
- end
- end
- end
- end
- end
- stoptiming(nodes)
- if trace_characters then
- nodes.report(head,done)
- end
- return head, true
-end
-
-handlers.protectglyphs = node.protect_glyphs
-handlers.unprotectglyphs = node.unprotect_glyphs
+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 report_fonts = logs.reporter("fonts","processing")
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+
+local otf = fonts.handlers.otf
+
+local traverse_id = node.traverse_id
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+local nodecodes = nodes.nodecodes
+local handlers = nodes.handlers
+
+local glyph_code = nodecodes.glyph
+
+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
+
+-- potential speedup: check for subtype < 256 so that we can remove that test
+-- elsewhere, danger: injected nodes will not be dealt with but that does not
+-- happen often; we could consider processing sublists but that might need more
+-- checking later on; the current approach also permits variants
+
+local run = 0
+
+local setfontdynamics = { }
+local fontprocesses = { }
+
+setmetatableindex(setfontdynamics, function(t,font)
+ local tfmdata = fontdata[font]
+ local shared = tfmdata.shared
+ local v = shared and shared.dynamics and otf.setdynamics or false
+ t[font] = v
+ return v
+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
+
+function handlers.characters(head)
+ -- either next or not, but definitely no already processed list
+ starttiming(nodes)
+ local usedfonts, attrfonts, done = { }, { }, false
+ local a, u, prevfont, prevattr = 0, 0, nil, 0
+ if trace_fontrun then
+ run = run + 1
+ report_fonts()
+ report_fonts("checking node list, run %s",run)
+ report_fonts()
+ local n = head
+ while n do
+ local id = n.id
+ if id == glyph_code then
+ local font = n.font
+ local attr = n[0] or 0
+ report_fonts("font %03i, dynamic %03i, glyph %s",font,attr,utf.char(n.char))
+ else
+ report_fonts("[%s]",nodecodes[n.id])
+ end
+ n = n.next
+ end
+ end
+ for n in traverse_id(glyph_code,head) do
+ -- if n.subtype<256 then -- all are 1
+ local font = n.font
+ local attr = n[0] or 0 -- zero attribute is reserved for fonts in context
+ if font ~= prevfont or attr ~= prevattr then
+ if attr > 0 then
+ local used = attrfonts[font]
+ if not used then
+ used = { }
+ attrfonts[font] = used
+ end
+ if not used[attr] then
+ local sd = setfontdynamics[font]
+ if sd then -- always true ?
+ local d = sd(font,attr) -- can we cache this one?
+ if d then
+ used[attr] = d
+ a = a + 1
+ else
+ -- can't happen ... otherwise best use nil/false distinction
+ end
+ end
+ end
+ else
+ local used = usedfonts[font]
+ if not used then
+ local fp = fontprocesses[font]
+ if fp then
+ usedfonts[font] = fp
+ u = u + 1
+ else
+ -- can't happen ... otherwise best use nil/false distinction
+ end
+ end
+ end
+ prevfont = font
+ prevattr = attr
+ end
+ -- end
+ end
+ if trace_fontrun then
+ report_fonts()
+ report_fonts("statics : %s",(u > 0 and concat(keys(usedfonts)," ")) or "none")
+ report_fonts("dynamics: %s",(a > 0 and concat(keys(attrfonts)," ")) or "none")
+ report_fonts()
+ end
+ if u == 0 then
+ -- skip
+ elseif u == 1 then
+ local font, processors = next(usedfonts)
+ local n = #processors
+ if n > 0 then
+ local h, d = processors[1](head,font,0)
+ head = h or head
+ done = done or d
+ if n > 1 then
+ for i=2,n do
+ local h, d = processors[i](head,font,0)
+ head = h or head
+ done = done or d
+ end
+ end
+ end
+ else
+ for font, processors in next, usedfonts do
+ local n = #processors
+ local h, d = processors[1](head,font,0)
+ head = h or head
+ done = done or d
+ if n > 1 then
+ for i=2,n do
+ local h, d = processors[i](head,font,0)
+ head = h or head
+ done = done or d
+ end
+ end
+ end
+ end
+ if a == 0 then
+ -- skip
+ elseif a == 1 then
+ local font, dynamics = next(attrfonts)
+ for attribute, processors in next, dynamics do -- attr can switch in between
+ local n = #processors
+ if n == 0 then
+ report_fonts("no processors associated with dynamic %s",attribute)
+ else
+ local h, d = processors[1](head,font,attribute)
+ head = h or head
+ done = done or d
+ if n > 1 then
+ for i=2,n do
+ local h, d = processors[i](head,font,attribute)
+ head = h or head
+ done = done or d
+ end
+ end
+ end
+ end
+ else
+ for font, dynamics in next, attrfonts do
+ for attribute, processors in next, dynamics do -- attr can switch in between
+ local n = #processors
+ if n == 0 then
+ report_fonts("no processors associated with dynamic %s",attribute)
+ else
+ local h, d = processors[1](head,font,attribute)
+ head = h or head
+ done = done or d
+ if n > 1 then
+ for i=2,n do
+ local h, d = processors[i](head,font,attribute)
+ head = h or head
+ done = done or d
+ end
+ end
+ end
+ end
+ end
+ end
+ stoptiming(nodes)
+ if trace_characters then
+ nodes.report(head,done)
+ end
+ return head, true
+end
+
+handlers.protectglyphs = node.protect_glyphs
+handlers.unprotectglyphs = node.unprotect_glyphs
diff --git a/tex/context/base/node-ini.lua b/tex/context/base/node-ini.lua
index 1de6fbddd..5a3986c3a 100644
--- a/tex/context/base/node-ini.lua
+++ b/tex/context/base/node-ini.lua
@@ -1,421 +1,421 @@
-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]]--
-
--- this module is being reconstructed
-
-local next, type = next, type
-local format, match, gsub = string.format, string.match, string.gsub
-local concat, remove = table.concat, table.remove
-local sortedhash, sortedkeys, swapped, tohash = table.sortedhash, table.sortedkeys, table.swapped, table.tohash
-local utfchar = utf.char
-local lpegmatch = lpeg.match
-local formatcolumns = utilities.formatters.formatcolumns
-
---[[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]]--
-
---[[ldx--
-<p>When manipulating node lists in <l n='context'/>, we will remove
-nodes and insert new ones. While node access was implemented, we did
-quite some experiments in order to find out if manipulating nodes
-in <l n='lua'/> was feasible from the perspective of performance.</p>
-
-<p>First of all, we noticed that the bottleneck is more with excessive
-callbacks (some gets called very often) and the conversion from and to
-<l n='tex'/>'s datastructures. However, at the <l n='lua'/> end, we
-found that inserting and deleting nodes in a table could become a
-bottleneck.</p>
-
-<p>This resulted in two special situations in passing nodes back to
-<l n='tex'/>: a table entry with value <type>false</type> is ignored,
-and when instead of a table <type>true</type> is returned, the
-original table is used.</p>
-
-<p>Insertion is handled (at least in <l n='context'/> as follows. When
-we need to insert a node at a certain position, we change the node at
-that position by a dummy node, tagged <type>inline</type> which itself
-has_attribute the original node and one or more new nodes. Before we pass
-back the list we collapse the list. Of course collapsing could be built
-into the <l n='tex'/> engine, but this is a not so natural extension.</p>
-
-<p>When we collapse (something that we only do when really needed), we
-also ignore the empty nodes. [This is obsolete!]</p>
---ldx]]--
-
-local traverse = node.traverse
-local traverse_id = node.traverse_id
-local free_node = node.free
-local remove_node = node.remove
-local insert_node_before = node.insert_before
-local insert_node_after = node.insert_after
-local node_fields = node.fields
-
-local allocate = utilities.storage.allocate
-
-nodes = nodes or { }
-local nodes = nodes
-
-nodes.handlers = nodes.handlers or { }
-
--- there will be more of this:
-
-local skipcodes = allocate {
- [ 0] = "userskip",
- [ 1] = "lineskip",
- [ 2] = "baselineskip",
- [ 3] = "parskip",
- [ 4] = "abovedisplayskip",
- [ 5] = "belowdisplayskip",
- [ 6] = "abovedisplayshortskip",
- [ 7] = "belowdisplayshortskip",
- [ 8] = "leftskip",
- [ 9] = "rightskip",
- [ 10] = "topskip",
- [ 11] = "splittopskip",
- [ 12] = "tabskip",
- [ 13] = "spaceskip",
- [ 14] = "xspaceskip",
- [ 15] = "parfillskip",
- [ 16] = "thinmuskip",
- [ 17] = "medmuskip",
- [ 18] = "thickmuskip",
- [100] = "leaders",
- [101] = "cleaders",
- [102] = "xleaders",
- [103] = "gleaders",
-}
-
-local penaltycodes = allocate { -- unfortunately not used
- [ 0] = "userpenalty",
-}
-
-table.setmetatableindex(penaltycodes,function(t,k) return "userpenalty" end) -- not used anyway
-
-local noadcodes = allocate {
- [ 0] = "ord",
- [ 1] = "opdisplaylimits",
- [ 2] = "oplimits",
- [ 3] = "opnolimits",
- [ 4] = "bin",
- [ 5] = "rel",
- [ 6] = "open",
- [ 7] = "close",
- [ 8] = "punct",
- [ 9] = "inner",
- [10] = "under",
- [11] = "over",
- [12] = "vcenter",
-}
-
-local listcodes = allocate {
- [ 0] = "unknown",
- [ 1] = "line",
- [ 2] = "box",
- [ 3] = "indent",
- [ 4] = "alignment", -- row or column
- [ 5] = "cell",
-}
-
-local glyphcodes = allocate {
- [0] = "character",
- [1] = "glyph",
- [2] = "ligature",
- [3] = "ghost",
- [4] = "left",
- [5] = "right",
-}
-
-local kerncodes = allocate {
- [0] = "fontkern",
- [1] = "userkern",
- [2] = "accentkern",
-}
-
-local mathcodes = allocate {
- [0] = "beginmath",
- [1] = "endmath",
-}
-
-local fillcodes = allocate {
- [0] = "stretch",
- [1] = "fi",
- [2] = "fil",
- [3] = "fill",
- [4] = "filll",
-}
-
-local margincodes = allocate {
- [0] = "left",
- [1] = "right",
-}
-
-local disccodes = allocate {
- [0] = "discretionary", -- \discretionary
- [1] = "explicit", -- \-
- [2] = "automatic", -- following a -
- [3] = "regular", -- simple
- [4] = "first", -- hard first item
- [5] = "second", -- hard second item
-}
-
-local function simplified(t)
- local r = { }
- for k, v in next, t do
- r[k] = gsub(v,"_","")
- end
- return r
-end
-
-local nodecodes = simplified(node.types())
-local whatcodes = simplified(node.whatsits())
-
-skipcodes = allocate(swapped(skipcodes,skipcodes))
-noadcodes = allocate(swapped(noadcodes,noadcodes))
-nodecodes = allocate(swapped(nodecodes,nodecodes))
-whatcodes = allocate(swapped(whatcodes,whatcodes))
-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))
-fillcodes = allocate(swapped(fillcodes,fillcodes))
-margincodes = allocate(swapped(margincodes,margincodes))
-disccodes = allocate(swapped(disccodes,disccodes))
-
-nodes.skipcodes = skipcodes nodes.gluecodes = skipcodes -- more official
-nodes.noadcodes = noadcodes
-nodes.nodecodes = nodecodes
-nodes.whatcodes = whatcodes nodes.whatsitcodes = whatcodes -- more official
-nodes.listcodes = listcodes
-nodes.glyphcodes = glyphcodes
-nodes.kerncodes = kerncodes
-nodes.penaltycodes = kerncodes
-nodes.mathcodes = mathcodes
-nodes.fillcodes = fillcodes
-nodes.margincodes = margincodes
-nodes.disccodes = disccodes nodes.discretionarycodes = disccodes
-
-listcodes.row = listcodes.alignment
-listcodes.column = listcodes.alignment
-
-kerncodes.italiccorrection = kerncodes.userkern
-kerncodes.kerning = kerncodes.fontkern
-
-nodes.codes = allocate { -- mostly for listing
- glue = skipcodes,
- noad = noadcodes,
- node = nodecodes,
- hlist = listcodes,
- vlist = listcodes,
- glyph = glyphcodes,
- kern = kerncodes,
- penalty = penaltycodes,
- math = mathnodes,
- fill = fillcodes,
- margin = margincodes,
- disc = disccodes,
- whatsit = whatcodes,
-}
-
-local report_codes = logs.reporter("nodes","codes")
-
-function nodes.showcodes()
- local t = { }
- for name, codes in sortedhash(nodes.codes) do
- local sorted = sortedkeys(codes)
- for i=1,#sorted do
- local s = sorted[i]
- if type(s) ~= "number" then
- t[#t+1] = { name, s, codes[s] }
- end
- end
- end
- formatcolumns(t)
- for k=1,#t do
- report_codes (t[k])
- end
-end
-
-local whatsit_node = nodecodes.whatsit
-
-local messyhack = tohash { -- temporary solution
- nodecodes.attributelist,
- nodecodes.attribute,
- nodecodes.gluespec,
- nodecodes.action,
-}
-
-function nodes.fields(n)
- local id = n.id
- if id == whatsit_node then
- return node_fields(id,n.subtype)
- else
- local t = node_fields(id)
- if messyhack[id] then
- for i=1,#t do
- if t[i] == "subtype" then
- remove(t,i)
- break
- end
- end
- end
- return t
- end
-end
-
-trackers.register("system.showcodes", nodes.showcodes)
-
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-local glue_code = nodecodes.glue
-
--- if t.id == glue_code then
--- local s = t.spec
--- print(t)
--- print(s,s and s.writable)
--- if s and s.writable then
--- free_node(s)
--- end
--- t.spec = nil
--- end
-
-local function remove(head, current, free_too)
- local t = current
- head, current = remove_node(head,current)
- if t then
- if free_too then
- free_node(t)
- t = nil
- else
- t.next = nil
- t.prev = nil
- end
- end
- return head, current, t
-end
-
-nodes.remove = remove
-
-function nodes.delete(head,current)
- return remove(head,current,true)
-end
-
-nodes.before = insert_node_before
-nodes.after = insert_node_after
-
--- we need to test this, as it might be fixed now
-
-function nodes.before(h,c,n)
- if c then
- if c == h then
- n.next = h
- n.prev = nil
- h.prev = n
- else
- local cp = c.prev
- n.next = c
- n.prev = cp
- if cp then
- cp.next = n
- end
- c.prev = n
- return h, n
- end
- end
- return n, n
-end
-
-function nodes.after(h,c,n)
- if c then
- local cn = c.next
- if cn then
- n.next = cn
- cn.prev = n
- else
- n.next = nil
- end
- c.next = n
- n.prev = c
- return h, n
- end
- return n, n
-end
-
--- local h, c = nodes.replace(head,current,new)
--- local c = nodes.replace(false,current,new)
--- local c = nodes.replace(current,new)
-
-function nodes.replace(head,current,new) -- no head returned if false
- if not new then
- head, current, new = false, head, current
- end
- local prev, next = current.prev, current.next
- if next then
- new.next = next
- next.prev = new
- end
- if prev then
- new.prev = prev
- prev.next = new
- end
- if head then
- if head == current then
- head = new
- end
- free_node(current)
- return head, new
- else
- free_node(current)
- return new
- end
-end
-
--- will move
-
-local function count(stack,flat)
- local n = 0
- while stack do
- local id = stack.id
- if not flat and id == hlist_code or id == vlist_code then
- local list = stack.list
- if list then
- n = n + 1 + count(list) -- self counts too
- else
- n = n + 1
- end
- else
- n = n + 1
- end
- stack = stack.next
- end
- return n
-end
-
-nodes.count = count
-
-local left, space = lpeg.P("<"), lpeg.P(" ")
-
-local reference = left * (1-left)^0 * left * space^0 * lpeg.C((1-space)^0)
-
-function nodes.reference(n)
- return lpegmatch(reference,tostring(n))
-end
-
-if not node.next then
-
- function node.next(n) return n and n.next end
- function node.prev(n) return n and n.prev end
-
-end
+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]]--
+
+-- this module is being reconstructed
+
+local next, type = next, type
+local format, match, gsub = string.format, string.match, string.gsub
+local concat, remove = table.concat, table.remove
+local sortedhash, sortedkeys, swapped, tohash = table.sortedhash, table.sortedkeys, table.swapped, table.tohash
+local utfchar = utf.char
+local lpegmatch = lpeg.match
+local formatcolumns = utilities.formatters.formatcolumns
+
+--[[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]]--
+
+--[[ldx--
+<p>When manipulating node lists in <l n='context'/>, we will remove
+nodes and insert new ones. While node access was implemented, we did
+quite some experiments in order to find out if manipulating nodes
+in <l n='lua'/> was feasible from the perspective of performance.</p>
+
+<p>First of all, we noticed that the bottleneck is more with excessive
+callbacks (some gets called very often) and the conversion from and to
+<l n='tex'/>'s datastructures. However, at the <l n='lua'/> end, we
+found that inserting and deleting nodes in a table could become a
+bottleneck.</p>
+
+<p>This resulted in two special situations in passing nodes back to
+<l n='tex'/>: a table entry with value <type>false</type> is ignored,
+and when instead of a table <type>true</type> is returned, the
+original table is used.</p>
+
+<p>Insertion is handled (at least in <l n='context'/> as follows. When
+we need to insert a node at a certain position, we change the node at
+that position by a dummy node, tagged <type>inline</type> which itself
+has_attribute the original node and one or more new nodes. Before we pass
+back the list we collapse the list. Of course collapsing could be built
+into the <l n='tex'/> engine, but this is a not so natural extension.</p>
+
+<p>When we collapse (something that we only do when really needed), we
+also ignore the empty nodes. [This is obsolete!]</p>
+--ldx]]--
+
+local traverse = node.traverse
+local traverse_id = node.traverse_id
+local free_node = node.free
+local remove_node = node.remove
+local insert_node_before = node.insert_before
+local insert_node_after = node.insert_after
+local node_fields = node.fields
+
+local allocate = utilities.storage.allocate
+
+nodes = nodes or { }
+local nodes = nodes
+
+nodes.handlers = nodes.handlers or { }
+
+-- there will be more of this:
+
+local skipcodes = allocate {
+ [ 0] = "userskip",
+ [ 1] = "lineskip",
+ [ 2] = "baselineskip",
+ [ 3] = "parskip",
+ [ 4] = "abovedisplayskip",
+ [ 5] = "belowdisplayskip",
+ [ 6] = "abovedisplayshortskip",
+ [ 7] = "belowdisplayshortskip",
+ [ 8] = "leftskip",
+ [ 9] = "rightskip",
+ [ 10] = "topskip",
+ [ 11] = "splittopskip",
+ [ 12] = "tabskip",
+ [ 13] = "spaceskip",
+ [ 14] = "xspaceskip",
+ [ 15] = "parfillskip",
+ [ 16] = "thinmuskip",
+ [ 17] = "medmuskip",
+ [ 18] = "thickmuskip",
+ [100] = "leaders",
+ [101] = "cleaders",
+ [102] = "xleaders",
+ [103] = "gleaders",
+}
+
+local penaltycodes = allocate { -- unfortunately not used
+ [ 0] = "userpenalty",
+}
+
+table.setmetatableindex(penaltycodes,function(t,k) return "userpenalty" end) -- not used anyway
+
+local noadcodes = allocate {
+ [ 0] = "ord",
+ [ 1] = "opdisplaylimits",
+ [ 2] = "oplimits",
+ [ 3] = "opnolimits",
+ [ 4] = "bin",
+ [ 5] = "rel",
+ [ 6] = "open",
+ [ 7] = "close",
+ [ 8] = "punct",
+ [ 9] = "inner",
+ [10] = "under",
+ [11] = "over",
+ [12] = "vcenter",
+}
+
+local listcodes = allocate {
+ [ 0] = "unknown",
+ [ 1] = "line",
+ [ 2] = "box",
+ [ 3] = "indent",
+ [ 4] = "alignment", -- row or column
+ [ 5] = "cell",
+}
+
+local glyphcodes = allocate {
+ [0] = "character",
+ [1] = "glyph",
+ [2] = "ligature",
+ [3] = "ghost",
+ [4] = "left",
+ [5] = "right",
+}
+
+local kerncodes = allocate {
+ [0] = "fontkern",
+ [1] = "userkern",
+ [2] = "accentkern",
+}
+
+local mathcodes = allocate {
+ [0] = "beginmath",
+ [1] = "endmath",
+}
+
+local fillcodes = allocate {
+ [0] = "stretch",
+ [1] = "fi",
+ [2] = "fil",
+ [3] = "fill",
+ [4] = "filll",
+}
+
+local margincodes = allocate {
+ [0] = "left",
+ [1] = "right",
+}
+
+local disccodes = allocate {
+ [0] = "discretionary", -- \discretionary
+ [1] = "explicit", -- \-
+ [2] = "automatic", -- following a -
+ [3] = "regular", -- simple
+ [4] = "first", -- hard first item
+ [5] = "second", -- hard second item
+}
+
+local function simplified(t)
+ local r = { }
+ for k, v in next, t do
+ r[k] = gsub(v,"_","")
+ end
+ return r
+end
+
+local nodecodes = simplified(node.types())
+local whatcodes = simplified(node.whatsits())
+
+skipcodes = allocate(swapped(skipcodes,skipcodes))
+noadcodes = allocate(swapped(noadcodes,noadcodes))
+nodecodes = allocate(swapped(nodecodes,nodecodes))
+whatcodes = allocate(swapped(whatcodes,whatcodes))
+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))
+fillcodes = allocate(swapped(fillcodes,fillcodes))
+margincodes = allocate(swapped(margincodes,margincodes))
+disccodes = allocate(swapped(disccodes,disccodes))
+
+nodes.skipcodes = skipcodes nodes.gluecodes = skipcodes -- more official
+nodes.noadcodes = noadcodes
+nodes.nodecodes = nodecodes
+nodes.whatcodes = whatcodes nodes.whatsitcodes = whatcodes -- more official
+nodes.listcodes = listcodes
+nodes.glyphcodes = glyphcodes
+nodes.kerncodes = kerncodes
+nodes.penaltycodes = kerncodes
+nodes.mathcodes = mathcodes
+nodes.fillcodes = fillcodes
+nodes.margincodes = margincodes
+nodes.disccodes = disccodes nodes.discretionarycodes = disccodes
+
+listcodes.row = listcodes.alignment
+listcodes.column = listcodes.alignment
+
+kerncodes.italiccorrection = kerncodes.userkern
+kerncodes.kerning = kerncodes.fontkern
+
+nodes.codes = allocate { -- mostly for listing
+ glue = skipcodes,
+ noad = noadcodes,
+ node = nodecodes,
+ hlist = listcodes,
+ vlist = listcodes,
+ glyph = glyphcodes,
+ kern = kerncodes,
+ penalty = penaltycodes,
+ math = mathnodes,
+ fill = fillcodes,
+ margin = margincodes,
+ disc = disccodes,
+ whatsit = whatcodes,
+}
+
+local report_codes = logs.reporter("nodes","codes")
+
+function nodes.showcodes()
+ local t = { }
+ for name, codes in sortedhash(nodes.codes) do
+ local sorted = sortedkeys(codes)
+ for i=1,#sorted do
+ local s = sorted[i]
+ if type(s) ~= "number" then
+ t[#t+1] = { name, s, codes[s] }
+ end
+ end
+ end
+ formatcolumns(t)
+ for k=1,#t do
+ report_codes (t[k])
+ end
+end
+
+local whatsit_node = nodecodes.whatsit
+
+local messyhack = tohash { -- temporary solution
+ nodecodes.attributelist,
+ nodecodes.attribute,
+ nodecodes.gluespec,
+ nodecodes.action,
+}
+
+function nodes.fields(n)
+ local id = n.id
+ if id == whatsit_node then
+ return node_fields(id,n.subtype)
+ else
+ local t = node_fields(id)
+ if messyhack[id] then
+ for i=1,#t do
+ if t[i] == "subtype" then
+ remove(t,i)
+ break
+ end
+ end
+ end
+ return t
+ end
+end
+
+trackers.register("system.showcodes", nodes.showcodes)
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local glue_code = nodecodes.glue
+
+-- if t.id == glue_code then
+-- local s = t.spec
+-- print(t)
+-- print(s,s and s.writable)
+-- if s and s.writable then
+-- free_node(s)
+-- end
+-- t.spec = nil
+-- end
+
+local function remove(head, current, free_too)
+ local t = current
+ head, current = remove_node(head,current)
+ if t then
+ if free_too then
+ free_node(t)
+ t = nil
+ else
+ t.next = nil
+ t.prev = nil
+ end
+ end
+ return head, current, t
+end
+
+nodes.remove = remove
+
+function nodes.delete(head,current)
+ return remove(head,current,true)
+end
+
+nodes.before = insert_node_before
+nodes.after = insert_node_after
+
+-- we need to test this, as it might be fixed now
+
+function nodes.before(h,c,n)
+ if c then
+ if c == h then
+ n.next = h
+ n.prev = nil
+ h.prev = n
+ else
+ local cp = c.prev
+ n.next = c
+ n.prev = cp
+ if cp then
+ cp.next = n
+ end
+ c.prev = n
+ return h, n
+ end
+ end
+ return n, n
+end
+
+function nodes.after(h,c,n)
+ if c then
+ local cn = c.next
+ if cn then
+ n.next = cn
+ cn.prev = n
+ else
+ n.next = nil
+ end
+ c.next = n
+ n.prev = c
+ return h, n
+ end
+ return n, n
+end
+
+-- local h, c = nodes.replace(head,current,new)
+-- local c = nodes.replace(false,current,new)
+-- local c = nodes.replace(current,new)
+
+function nodes.replace(head,current,new) -- no head returned if false
+ if not new then
+ head, current, new = false, head, current
+ end
+ local prev, next = current.prev, current.next
+ if next then
+ new.next = next
+ next.prev = new
+ end
+ if prev then
+ new.prev = prev
+ prev.next = new
+ end
+ if head then
+ if head == current then
+ head = new
+ end
+ free_node(current)
+ return head, new
+ else
+ free_node(current)
+ return new
+ end
+end
+
+-- will move
+
+local function count(stack,flat)
+ local n = 0
+ while stack do
+ local id = stack.id
+ if not flat and id == hlist_code or id == vlist_code then
+ local list = stack.list
+ if list then
+ n = n + 1 + count(list) -- self counts too
+ else
+ n = n + 1
+ end
+ else
+ n = n + 1
+ end
+ stack = stack.next
+ end
+ return n
+end
+
+nodes.count = count
+
+local left, space = lpeg.P("<"), lpeg.P(" ")
+
+local reference = left * (1-left)^0 * left * space^0 * lpeg.C((1-space)^0)
+
+function nodes.reference(n)
+ return lpegmatch(reference,tostring(n))
+end
+
+if not node.next then
+
+ function node.next(n) return n and n.next end
+ function node.prev(n) return n and n.prev end
+
+end
diff --git a/tex/context/base/node-inj.lua b/tex/context/base/node-inj.lua
index d6a851cfb..697370cfb 100644
--- a/tex/context/base/node-inj.lua
+++ b/tex/context/base/node-inj.lua
@@ -1,519 +1,519 @@
-if not modules then modules = { } end modules ['node-inj'] = {
- 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 very experimental (this will change when we have luatex > .50 and
--- a few pending thingies are available. Also, Idris needs to make a few more
--- test fonts. Btw, future versions of luatex will have extended glyph properties
--- that can be of help. Some optimizations can go away when we have faster machines.
-
-local next = next
-local utfchar = utf.char
-
-local trace_injections = false trackers.register("nodes.injections", function(v) trace_injections = v end)
-
-local report_injections = logs.reporter("nodes","injections")
-
-local attributes, nodes, node = attributes, nodes, node
-
-fonts = fonts
-local fontdata = fonts.hashes.identifiers
-
-nodes.injections = nodes.injections or { }
-local injections = nodes.injections
-
-local nodecodes = nodes.nodecodes
-local glyph_code = nodecodes.glyph
-local kern_code = nodecodes.kern
-local nodepool = nodes.pool
-local newkern = nodepool.kern
-
-local traverse_id = node.traverse_id
-local insert_node_before = node.insert_before
-local insert_node_after = node.insert_after
-
-local a_kernpair = attributes.private('kernpair')
-local a_ligacomp = attributes.private('ligacomp')
-local a_markbase = attributes.private('markbase')
-local a_markmark = attributes.private('markmark')
-local a_markdone = attributes.private('markdone')
-local a_cursbase = attributes.private('cursbase')
-local a_curscurs = attributes.private('curscurs')
-local a_cursdone = attributes.private('cursdone')
-
--- This injector has been tested by Idris Samawi Hamid (several arabic fonts as well as
--- the rather demanding Husayni font), Khaled Hosny (latin and arabic) and Kaj Eigner
--- (arabic, hebrew and thai) and myself (whatever font I come across). I'm pretty sure
--- that this code is not 100% okay but examples are needed to figure things out.
-
-function injections.installnewkern(nk)
- newkern = nk or newkern
-end
-
-local cursives = { }
-local marks = { }
-local kerns = { }
-
--- Currently we do gpos/kern in a bit inofficial way but when we have the extra fields in
--- glyphnodes to manipulate ht/dp/wd explicitly I will provide an alternative; also, we
--- can share tables.
-
--- For the moment we pass the r2l key ... volt/arabtype tests .. idris: this needs
--- checking with husayni (volt and fontforge).
-
-function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext)
- local dx, dy = factor*(exit[1]-entry[1]), factor*(exit[2]-entry[2])
- local ws, wn = tfmstart.width, tfmnext.width
- local bound = #cursives + 1
- start[a_cursbase] = bound
- nxt[a_curscurs] = bound
- cursives[bound] = { rlmode, dx, dy, ws, wn }
- return dx, dy, bound
-end
-
-function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr)
- local x, y, w, h = factor*spec[1], factor*spec[2], factor*spec[3], factor*spec[4]
- -- dy = y - h
- if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then
- local bound = current[a_kernpair]
- if bound then
- local kb = kerns[bound]
- -- inefficient but singles have less, but weird anyway, needs checking
- kb[2], kb[3], kb[4], kb[5] = (kb[2] or 0) + x, (kb[3] or 0) + y, (kb[4] or 0)+ w, (kb[5] or 0) + h
- else
- bound = #kerns + 1
- current[a_kernpair] = bound
- kerns[bound] = { rlmode, x, y, w, h, r2lflag, tfmchr.width }
- end
- return x, y, w, h, bound
- end
- return x, y, w, h -- no bound
-end
-
-function injections.setkern(current,factor,rlmode,x,tfmchr)
- local dx = factor*x
- if dx ~= 0 then
- local bound = #kerns + 1
- current[a_kernpair] = bound
- kerns[bound] = { rlmode, dx }
- return dx, bound
- else
- return 0, 0
- end
-end
-
-function injections.setmark(start,base,factor,rlmode,ba,ma,index) -- ba=baseanchor, ma=markanchor
- local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2]) -- the index argument is no longer used but when this
- local bound = base[a_markbase] -- fails again we should pass it
- local index = 1
- if bound then
- local mb = marks[bound]
- if mb then
- -- if not index then index = #mb + 1 end
- index = #mb + 1
- mb[index] = { dx, dy, rlmode }
- start[a_markmark] = bound
- start[a_markdone] = index
- return dx, dy, bound
- else
- report_injections("possible problem, %U is base mark without data (id %a)",base.char,bound)
- end
- end
--- index = index or 1
- index = index or 1
- bound = #marks + 1
- base[a_markbase] = bound
- start[a_markmark] = bound
- start[a_markdone] = index
- marks[bound] = { [index] = { dx, dy, rlmode } }
- return dx, dy, bound
-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 trace(head)
- report_injections("begin run")
- for n in traverse_id(glyph_code,head) do
- if n.subtype < 256 then
- local kp = n[a_kernpair]
- local mb = n[a_markbase]
- local mm = n[a_markmark]
- local md = n[a_markdone]
- local cb = n[a_cursbase]
- local cc = n[a_curscurs]
- local char = n.char
- report_injections("font %s, char %U, glyph %c",n.font,char,char)
- if kp then
- local k = kerns[kp]
- if k[3] then
- report_injections(" pairkern: dir %a, x %p, y %p, w %p, h %p",dir(k[1]),k[2],k[3],k[4],k[5])
- else
- report_injections(" kern: dir %a, dx %p",dir(k[1]),k[2])
- end
- end
- if mb then
- report_injections(" markbase: bound %a",mb)
- end
- if mm then
- local m = marks[mm]
- if mb then
- local m = m[mb]
- if m then
- report_injections(" markmark: bound %a, index %a, dx %p, dy %p",mm,md,m[1],m[2])
- else
- report_injections(" markmark: bound %a, missing index",mm)
- end
- else
- m = m[1]
- report_injections(" markmark: bound %a, dx %p, dy %p",mm,m and m[1],m and m[2])
- end
- end
- if cb then
- report_injections(" cursbase: bound %a",cb)
- end
- if cc then
- local c = cursives[cc]
- report_injections(" curscurs: bound %a, dir %a, dx %p, dy %p",cc,dir(c[1]),c[2],c[3])
- end
- end
- end
- report_injections("end run")
-end
-
--- todo: reuse tables (i.e. no collection), but will be extra fields anyway
--- todo: check for attribute
-
--- We can have a fast test on a font being processed, so we can check faster for marks etc
--- but I'll make a context variant anyway.
-
-local function show_result(head)
- local current = head
- local skipping = false
- while current do
- local id = current.id
- if id == glyph_code then
- report_injections("char: %C, width %p, xoffset %p, yoffset %p",current.char,current.width,current.xoffset,current.yoffset)
- skipping = false
- elseif id == kern_code then
- report_injections("kern: %p",current.kern)
- skipping = false
- elseif not skipping then
- report_injections()
- skipping = true
- end
- current = current.next
- end
-end
-
-function injections.handler(head,where,keep)
- local has_marks, has_cursives, has_kerns = next(marks), next(cursives), next(kerns)
- if has_marks or has_cursives then
- if trace_injections then
- trace(head)
- end
- -- in the future variant we will not copy items but refs to tables
- local done, ky, rl, valid, cx, wx, mk, nofvalid = false, { }, { }, { }, { }, { }, { }, 0
- if has_kerns then -- move outside loop
- local nf, tm = nil, nil
- for n in traverse_id(glyph_code,head) do -- only needed for relevant fonts
- if n.subtype < 256 then
- nofvalid = nofvalid + 1
- valid[nofvalid] = n
- if n.font ~= nf then
- nf = n.font
- tm = fontdata[nf].resources.marks
- end
- if tm then
- mk[n] = tm[n.char]
- end
- local k = n[a_kernpair]
- if k then
- local kk = kerns[k]
- if kk then
- local x, y, w, h = kk[2] or 0, kk[3] or 0, kk[4] or 0, kk[5] or 0
- local dy = y - h
- if dy ~= 0 then
- ky[n] = dy
- end
- if w ~= 0 or x ~= 0 then
- wx[n] = kk
- end
- rl[n] = kk[1] -- could move in test
- end
- end
- end
- end
- else
- local nf, tm = nil, nil
- for n in traverse_id(glyph_code,head) do
- if n.subtype < 256 then
- nofvalid = nofvalid + 1
- valid[nofvalid] = n
- if n.font ~= nf then
- nf = n.font
- tm = fontdata[nf].resources.marks
- end
- if tm then
- mk[n] = tm[n.char]
- end
- end
- end
- end
- if nofvalid > 0 then
- -- we can assume done == true because we have cursives and marks
- local cx = { }
- if has_kerns and next(ky) then
- for n, k in next, ky do
- n.yoffset = k
- end
- end
- -- todo: reuse t and use maxt
- if has_cursives then
- local p_cursbase, p = nil, nil
- -- since we need valid[n+1] we can also use a "while true do"
- local t, d, maxt = { }, { }, 0
- for i=1,nofvalid do -- valid == glyphs
- local n = valid[i]
- if not mk[n] then
- local n_cursbase = n[a_cursbase]
- if p_cursbase then
- local n_curscurs = n[a_curscurs]
- if p_cursbase == n_curscurs then
- local c = cursives[n_curscurs]
- if c then
- local rlmode, dx, dy, ws, wn = c[1], c[2], c[3], c[4], c[5]
- if rlmode >= 0 then
- dx = dx - ws
- else
- dx = dx + wn
- end
- if dx ~= 0 then
- cx[n] = dx
- rl[n] = rlmode
- end
- -- if rlmode and rlmode < 0 then
- dy = -dy
- -- end
- maxt = maxt + 1
- t[maxt] = p
- d[maxt] = dy
- else
- maxt = 0
- end
- end
- elseif maxt > 0 then
- local ny = n.yoffset
- for i=maxt,1,-1 do
- ny = ny + d[i]
- local ti = t[i]
- ti.yoffset = ti.yoffset + ny
- end
- maxt = 0
- end
- if not n_cursbase and maxt > 0 then
- local ny = n.yoffset
- for i=maxt,1,-1 do
- ny = ny + d[i]
- local ti = t[i]
- ti.yoffset = ny
- end
- maxt = 0
- end
- p_cursbase, p = n_cursbase, n
- end
- end
- if maxt > 0 then
- local ny = n.yoffset
- for i=maxt,1,-1 do
- ny = ny + d[i]
- local ti = t[i]
- ti.yoffset = ny
- end
- maxt = 0
- end
- if not keep then
- cursives = { }
- end
- end
- if has_marks then
- for i=1,nofvalid do
- local p = valid[i]
- local p_markbase = p[a_markbase]
- if p_markbase then
- local mrks = marks[p_markbase]
- local nofmarks = #mrks
- for n in traverse_id(glyph_code,p.next) do
- local n_markmark = n[a_markmark]
- if p_markbase == n_markmark then
- local index = n[a_markdone] or 1
- local d = mrks[index]
- if d then
- local rlmode = d[3]
- --
- local k = wx[p]
- if k then
- local x = k[2]
- local w = k[4]
- if w then
- if rlmode and rlmode >= 0 then
- -- kern(x) glyph(p) kern(w-x) mark(n)
- n.xoffset = p.xoffset - p.width + d[1] - (w-x)
- else
- -- kern(w-x) glyph(p) kern(x) mark(n)
- n.xoffset = p.xoffset - d[1] - x
- end
- else
- if rlmode and rlmode >= 0 then
- -- okay for husayni
- n.xoffset = p.xoffset - p.width + d[1]
- else
- -- needs checking: is x ok here?
- n.xoffset = p.xoffset - d[1] - x
- end
- end
- else
- if rlmode and rlmode >= 0 then
- n.xoffset = p.xoffset - p.width + d[1]
- else
- n.xoffset = p.xoffset - d[1]
- end
- end
- -- --
- if mk[p] then
- n.yoffset = p.yoffset + d[2]
- else
- n.yoffset = n.yoffset + p.yoffset + d[2]
- end
- --
- if nofmarks == 1 then
- break
- else
- nofmarks = nofmarks - 1
- end
- end
- else
- -- KE: there can be <mark> <mkmk> <mark> sequences in ligatures
- end
- end
- end
- end
- if not keep then
- marks = { }
- end
- end
- -- todo : combine
- if next(wx) then
- for n, k in next, wx do
- -- only w can be nil (kernclasses), can be sped up when w == nil
- local x = k[2]
- local w = k[4]
- if w then
- local rl = k[1] -- r2l = k[6]
- local wx = w - x
- if rl < 0 then -- KE: don't use r2l here
- if wx ~= 0 then
- insert_node_before(head,n,newkern(wx)) -- type 0/2
- end
- if x ~= 0 then
- insert_node_after (head,n,newkern(x)) -- type 0/2
- end
- else
- if x ~= 0 then
- insert_node_before(head,n,newkern(x)) -- type 0/2
- end
- if wx ~= 0 then
- insert_node_after (head,n,newkern(wx)) -- type 0/2
- end
- end
- elseif x ~= 0 then
- -- this needs checking for rl < 0 but it is unlikely that a r2l script
- -- uses kernclasses between glyphs so we're probably safe (KE has a
- -- problematic font where marks interfere with rl < 0 in the previous
- -- case)
- insert_node_before(head,n,newkern(x)) -- a real font kern, type 0
- end
- end
- end
- if next(cx) then
- for n, k in next, cx do
- if k ~= 0 then
- local rln = rl[n]
- if rln and rln < 0 then
- insert_node_before(head,n,newkern(-k)) -- type 0/2
- else
- insert_node_before(head,n,newkern(k)) -- type 0/2
- end
- end
- end
- end
- if not keep then
- kerns = { }
- end
- -- if trace_injections then
- -- show_result(head)
- -- end
- return head, true
- elseif not keep then
- kerns, cursives, marks = { }, { }, { }
- end
- elseif has_kerns then
- if trace_injections then
- trace(head)
- end
- for n in traverse_id(glyph_code,head) do
- if n.subtype < 256 then
- local k = n[a_kernpair]
- if k then
- local kk = kerns[k]
- if kk then
- local rl, x, y, w = kk[1], kk[2] or 0, kk[3], kk[4]
- if y and y ~= 0 then
- n.yoffset = y -- todo: h ?
- end
- if w then
- -- copied from above
- -- local r2l = kk[6]
- local wx = w - x
- if rl < 0 then -- KE: don't use r2l here
- if wx ~= 0 then
- insert_node_before(head,n,newkern(wx))
- end
- if x ~= 0 then
- insert_node_after (head,n,newkern(x))
- end
- else
- if x ~= 0 then
- insert_node_before(head,n,newkern(x))
- end
- if wx ~= 0 then
- insert_node_after(head,n,newkern(wx))
- end
- end
- else
- -- simple (e.g. kernclass kerns)
- if x ~= 0 then
- insert_node_before(head,n,newkern(x))
- end
- end
- end
- end
- end
- end
- if not keep then
- kerns = { }
- end
- -- if trace_injections then
- -- show_result(head)
- -- end
- return head, true
- else
- -- no tracing needed
- end
- return head, false
-end
+if not modules then modules = { } end modules ['node-inj'] = {
+ 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 very experimental (this will change when we have luatex > .50 and
+-- a few pending thingies are available. Also, Idris needs to make a few more
+-- test fonts. Btw, future versions of luatex will have extended glyph properties
+-- that can be of help. Some optimizations can go away when we have faster machines.
+
+local next = next
+local utfchar = utf.char
+
+local trace_injections = false trackers.register("nodes.injections", function(v) trace_injections = v end)
+
+local report_injections = logs.reporter("nodes","injections")
+
+local attributes, nodes, node = attributes, nodes, node
+
+fonts = fonts
+local fontdata = fonts.hashes.identifiers
+
+nodes.injections = nodes.injections or { }
+local injections = nodes.injections
+
+local nodecodes = nodes.nodecodes
+local glyph_code = nodecodes.glyph
+local kern_code = nodecodes.kern
+local nodepool = nodes.pool
+local newkern = nodepool.kern
+
+local traverse_id = node.traverse_id
+local insert_node_before = node.insert_before
+local insert_node_after = node.insert_after
+
+local a_kernpair = attributes.private('kernpair')
+local a_ligacomp = attributes.private('ligacomp')
+local a_markbase = attributes.private('markbase')
+local a_markmark = attributes.private('markmark')
+local a_markdone = attributes.private('markdone')
+local a_cursbase = attributes.private('cursbase')
+local a_curscurs = attributes.private('curscurs')
+local a_cursdone = attributes.private('cursdone')
+
+-- This injector has been tested by Idris Samawi Hamid (several arabic fonts as well as
+-- the rather demanding Husayni font), Khaled Hosny (latin and arabic) and Kaj Eigner
+-- (arabic, hebrew and thai) and myself (whatever font I come across). I'm pretty sure
+-- that this code is not 100% okay but examples are needed to figure things out.
+
+function injections.installnewkern(nk)
+ newkern = nk or newkern
+end
+
+local cursives = { }
+local marks = { }
+local kerns = { }
+
+-- Currently we do gpos/kern in a bit inofficial way but when we have the extra fields in
+-- glyphnodes to manipulate ht/dp/wd explicitly I will provide an alternative; also, we
+-- can share tables.
+
+-- For the moment we pass the r2l key ... volt/arabtype tests .. idris: this needs
+-- checking with husayni (volt and fontforge).
+
+function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext)
+ local dx, dy = factor*(exit[1]-entry[1]), factor*(exit[2]-entry[2])
+ local ws, wn = tfmstart.width, tfmnext.width
+ local bound = #cursives + 1
+ start[a_cursbase] = bound
+ nxt[a_curscurs] = bound
+ cursives[bound] = { rlmode, dx, dy, ws, wn }
+ return dx, dy, bound
+end
+
+function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr)
+ local x, y, w, h = factor*spec[1], factor*spec[2], factor*spec[3], factor*spec[4]
+ -- dy = y - h
+ if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then
+ local bound = current[a_kernpair]
+ if bound then
+ local kb = kerns[bound]
+ -- inefficient but singles have less, but weird anyway, needs checking
+ kb[2], kb[3], kb[4], kb[5] = (kb[2] or 0) + x, (kb[3] or 0) + y, (kb[4] or 0)+ w, (kb[5] or 0) + h
+ else
+ bound = #kerns + 1
+ current[a_kernpair] = bound
+ kerns[bound] = { rlmode, x, y, w, h, r2lflag, tfmchr.width }
+ end
+ return x, y, w, h, bound
+ end
+ return x, y, w, h -- no bound
+end
+
+function injections.setkern(current,factor,rlmode,x,tfmchr)
+ local dx = factor*x
+ if dx ~= 0 then
+ local bound = #kerns + 1
+ current[a_kernpair] = bound
+ kerns[bound] = { rlmode, dx }
+ return dx, bound
+ else
+ return 0, 0
+ end
+end
+
+function injections.setmark(start,base,factor,rlmode,ba,ma,index) -- ba=baseanchor, ma=markanchor
+ local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2]) -- the index argument is no longer used but when this
+ local bound = base[a_markbase] -- fails again we should pass it
+ local index = 1
+ if bound then
+ local mb = marks[bound]
+ if mb then
+ -- if not index then index = #mb + 1 end
+ index = #mb + 1
+ mb[index] = { dx, dy, rlmode }
+ start[a_markmark] = bound
+ start[a_markdone] = index
+ return dx, dy, bound
+ else
+ report_injections("possible problem, %U is base mark without data (id %a)",base.char,bound)
+ end
+ end
+-- index = index or 1
+ index = index or 1
+ bound = #marks + 1
+ base[a_markbase] = bound
+ start[a_markmark] = bound
+ start[a_markdone] = index
+ marks[bound] = { [index] = { dx, dy, rlmode } }
+ return dx, dy, bound
+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 trace(head)
+ report_injections("begin run")
+ for n in traverse_id(glyph_code,head) do
+ if n.subtype < 256 then
+ local kp = n[a_kernpair]
+ local mb = n[a_markbase]
+ local mm = n[a_markmark]
+ local md = n[a_markdone]
+ local cb = n[a_cursbase]
+ local cc = n[a_curscurs]
+ local char = n.char
+ report_injections("font %s, char %U, glyph %c",n.font,char,char)
+ if kp then
+ local k = kerns[kp]
+ if k[3] then
+ report_injections(" pairkern: dir %a, x %p, y %p, w %p, h %p",dir(k[1]),k[2],k[3],k[4],k[5])
+ else
+ report_injections(" kern: dir %a, dx %p",dir(k[1]),k[2])
+ end
+ end
+ if mb then
+ report_injections(" markbase: bound %a",mb)
+ end
+ if mm then
+ local m = marks[mm]
+ if mb then
+ local m = m[mb]
+ if m then
+ report_injections(" markmark: bound %a, index %a, dx %p, dy %p",mm,md,m[1],m[2])
+ else
+ report_injections(" markmark: bound %a, missing index",mm)
+ end
+ else
+ m = m[1]
+ report_injections(" markmark: bound %a, dx %p, dy %p",mm,m and m[1],m and m[2])
+ end
+ end
+ if cb then
+ report_injections(" cursbase: bound %a",cb)
+ end
+ if cc then
+ local c = cursives[cc]
+ report_injections(" curscurs: bound %a, dir %a, dx %p, dy %p",cc,dir(c[1]),c[2],c[3])
+ end
+ end
+ end
+ report_injections("end run")
+end
+
+-- todo: reuse tables (i.e. no collection), but will be extra fields anyway
+-- todo: check for attribute
+
+-- We can have a fast test on a font being processed, so we can check faster for marks etc
+-- but I'll make a context variant anyway.
+
+local function show_result(head)
+ local current = head
+ local skipping = false
+ while current do
+ local id = current.id
+ if id == glyph_code then
+ report_injections("char: %C, width %p, xoffset %p, yoffset %p",current.char,current.width,current.xoffset,current.yoffset)
+ skipping = false
+ elseif id == kern_code then
+ report_injections("kern: %p",current.kern)
+ skipping = false
+ elseif not skipping then
+ report_injections()
+ skipping = true
+ end
+ current = current.next
+ end
+end
+
+function injections.handler(head,where,keep)
+ local has_marks, has_cursives, has_kerns = next(marks), next(cursives), next(kerns)
+ if has_marks or has_cursives then
+ if trace_injections then
+ trace(head)
+ end
+ -- in the future variant we will not copy items but refs to tables
+ local done, ky, rl, valid, cx, wx, mk, nofvalid = false, { }, { }, { }, { }, { }, { }, 0
+ if has_kerns then -- move outside loop
+ local nf, tm = nil, nil
+ for n in traverse_id(glyph_code,head) do -- only needed for relevant fonts
+ if n.subtype < 256 then
+ nofvalid = nofvalid + 1
+ valid[nofvalid] = n
+ if n.font ~= nf then
+ nf = n.font
+ tm = fontdata[nf].resources.marks
+ end
+ if tm then
+ mk[n] = tm[n.char]
+ end
+ local k = n[a_kernpair]
+ if k then
+ local kk = kerns[k]
+ if kk then
+ local x, y, w, h = kk[2] or 0, kk[3] or 0, kk[4] or 0, kk[5] or 0
+ local dy = y - h
+ if dy ~= 0 then
+ ky[n] = dy
+ end
+ if w ~= 0 or x ~= 0 then
+ wx[n] = kk
+ end
+ rl[n] = kk[1] -- could move in test
+ end
+ end
+ end
+ end
+ else
+ local nf, tm = nil, nil
+ for n in traverse_id(glyph_code,head) do
+ if n.subtype < 256 then
+ nofvalid = nofvalid + 1
+ valid[nofvalid] = n
+ if n.font ~= nf then
+ nf = n.font
+ tm = fontdata[nf].resources.marks
+ end
+ if tm then
+ mk[n] = tm[n.char]
+ end
+ end
+ end
+ end
+ if nofvalid > 0 then
+ -- we can assume done == true because we have cursives and marks
+ local cx = { }
+ if has_kerns and next(ky) then
+ for n, k in next, ky do
+ n.yoffset = k
+ end
+ end
+ -- todo: reuse t and use maxt
+ if has_cursives then
+ local p_cursbase, p = nil, nil
+ -- since we need valid[n+1] we can also use a "while true do"
+ local t, d, maxt = { }, { }, 0
+ for i=1,nofvalid do -- valid == glyphs
+ local n = valid[i]
+ if not mk[n] then
+ local n_cursbase = n[a_cursbase]
+ if p_cursbase then
+ local n_curscurs = n[a_curscurs]
+ if p_cursbase == n_curscurs then
+ local c = cursives[n_curscurs]
+ if c then
+ local rlmode, dx, dy, ws, wn = c[1], c[2], c[3], c[4], c[5]
+ if rlmode >= 0 then
+ dx = dx - ws
+ else
+ dx = dx + wn
+ end
+ if dx ~= 0 then
+ cx[n] = dx
+ rl[n] = rlmode
+ end
+ -- if rlmode and rlmode < 0 then
+ dy = -dy
+ -- end
+ maxt = maxt + 1
+ t[maxt] = p
+ d[maxt] = dy
+ else
+ maxt = 0
+ end
+ end
+ elseif maxt > 0 then
+ local ny = n.yoffset
+ for i=maxt,1,-1 do
+ ny = ny + d[i]
+ local ti = t[i]
+ ti.yoffset = ti.yoffset + ny
+ end
+ maxt = 0
+ end
+ if not n_cursbase and maxt > 0 then
+ local ny = n.yoffset
+ for i=maxt,1,-1 do
+ ny = ny + d[i]
+ local ti = t[i]
+ ti.yoffset = ny
+ end
+ maxt = 0
+ end
+ p_cursbase, p = n_cursbase, n
+ end
+ end
+ if maxt > 0 then
+ local ny = n.yoffset
+ for i=maxt,1,-1 do
+ ny = ny + d[i]
+ local ti = t[i]
+ ti.yoffset = ny
+ end
+ maxt = 0
+ end
+ if not keep then
+ cursives = { }
+ end
+ end
+ if has_marks then
+ for i=1,nofvalid do
+ local p = valid[i]
+ local p_markbase = p[a_markbase]
+ if p_markbase then
+ local mrks = marks[p_markbase]
+ local nofmarks = #mrks
+ for n in traverse_id(glyph_code,p.next) do
+ local n_markmark = n[a_markmark]
+ if p_markbase == n_markmark then
+ local index = n[a_markdone] or 1
+ local d = mrks[index]
+ if d then
+ local rlmode = d[3]
+ --
+ local k = wx[p]
+ if k then
+ local x = k[2]
+ local w = k[4]
+ if w then
+ if rlmode and rlmode >= 0 then
+ -- kern(x) glyph(p) kern(w-x) mark(n)
+ n.xoffset = p.xoffset - p.width + d[1] - (w-x)
+ else
+ -- kern(w-x) glyph(p) kern(x) mark(n)
+ n.xoffset = p.xoffset - d[1] - x
+ end
+ else
+ if rlmode and rlmode >= 0 then
+ -- okay for husayni
+ n.xoffset = p.xoffset - p.width + d[1]
+ else
+ -- needs checking: is x ok here?
+ n.xoffset = p.xoffset - d[1] - x
+ end
+ end
+ else
+ if rlmode and rlmode >= 0 then
+ n.xoffset = p.xoffset - p.width + d[1]
+ else
+ n.xoffset = p.xoffset - d[1]
+ end
+ end
+ -- --
+ if mk[p] then
+ n.yoffset = p.yoffset + d[2]
+ else
+ n.yoffset = n.yoffset + p.yoffset + d[2]
+ end
+ --
+ if nofmarks == 1 then
+ break
+ else
+ nofmarks = nofmarks - 1
+ end
+ end
+ else
+ -- KE: there can be <mark> <mkmk> <mark> sequences in ligatures
+ end
+ end
+ end
+ end
+ if not keep then
+ marks = { }
+ end
+ end
+ -- todo : combine
+ if next(wx) then
+ for n, k in next, wx do
+ -- only w can be nil (kernclasses), can be sped up when w == nil
+ local x = k[2]
+ local w = k[4]
+ if w then
+ local rl = k[1] -- r2l = k[6]
+ local wx = w - x
+ if rl < 0 then -- KE: don't use r2l here
+ if wx ~= 0 then
+ insert_node_before(head,n,newkern(wx)) -- type 0/2
+ end
+ if x ~= 0 then
+ insert_node_after (head,n,newkern(x)) -- type 0/2
+ end
+ else
+ if x ~= 0 then
+ insert_node_before(head,n,newkern(x)) -- type 0/2
+ end
+ if wx ~= 0 then
+ insert_node_after (head,n,newkern(wx)) -- type 0/2
+ end
+ end
+ elseif x ~= 0 then
+ -- this needs checking for rl < 0 but it is unlikely that a r2l script
+ -- uses kernclasses between glyphs so we're probably safe (KE has a
+ -- problematic font where marks interfere with rl < 0 in the previous
+ -- case)
+ insert_node_before(head,n,newkern(x)) -- a real font kern, type 0
+ end
+ end
+ end
+ if next(cx) then
+ for n, k in next, cx do
+ if k ~= 0 then
+ local rln = rl[n]
+ if rln and rln < 0 then
+ insert_node_before(head,n,newkern(-k)) -- type 0/2
+ else
+ insert_node_before(head,n,newkern(k)) -- type 0/2
+ end
+ end
+ end
+ end
+ if not keep then
+ kerns = { }
+ end
+ -- if trace_injections then
+ -- show_result(head)
+ -- end
+ return head, true
+ elseif not keep then
+ kerns, cursives, marks = { }, { }, { }
+ end
+ elseif has_kerns then
+ if trace_injections then
+ trace(head)
+ end
+ for n in traverse_id(glyph_code,head) do
+ if n.subtype < 256 then
+ local k = n[a_kernpair]
+ if k then
+ local kk = kerns[k]
+ if kk then
+ local rl, x, y, w = kk[1], kk[2] or 0, kk[3], kk[4]
+ if y and y ~= 0 then
+ n.yoffset = y -- todo: h ?
+ end
+ if w then
+ -- copied from above
+ -- local r2l = kk[6]
+ local wx = w - x
+ if rl < 0 then -- KE: don't use r2l here
+ if wx ~= 0 then
+ insert_node_before(head,n,newkern(wx))
+ end
+ if x ~= 0 then
+ insert_node_after (head,n,newkern(x))
+ end
+ else
+ if x ~= 0 then
+ insert_node_before(head,n,newkern(x))
+ end
+ if wx ~= 0 then
+ insert_node_after(head,n,newkern(wx))
+ end
+ end
+ else
+ -- simple (e.g. kernclass kerns)
+ if x ~= 0 then
+ insert_node_before(head,n,newkern(x))
+ end
+ end
+ end
+ end
+ end
+ end
+ if not keep then
+ kerns = { }
+ end
+ -- if trace_injections then
+ -- show_result(head)
+ -- end
+ return head, true
+ else
+ -- no tracing needed
+ end
+ return head, false
+end
diff --git a/tex/context/base/node-mig.lua b/tex/context/base/node-mig.lua
index fd14fc43f..9fc35a048 100644
--- a/tex/context/base/node-mig.lua
+++ b/tex/context/base/node-mig.lua
@@ -1,138 +1,138 @@
-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"
-}
-
-local format = string.format
-
-local attributes, nodes, node = attributes, nodes, node
-
-local remove_nodes = nodes.remove
-
-local nodecodes = nodes.nodecodes
-local tasks = nodes.tasks
-
-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 trace_migrations = false trackers.register("nodes.migrations", function(v) trace_migrations = v end)
-
-local report_nodes = logs.reporter("nodes","migrations")
-
-local migrate_inserts, migrate_marks, inserts_too
-
-local t_inserts, t_marks, t_sweeps = 0, 0, 0
-
-local function locate(head,first,last,ni,nm)
- local current = head
- while current do
- local id = current.id
- if id == vlist_code or id == hlist_code then
- current.list, first, last, ni, nm = locate(current.list,first,last,ni,nm)
- current = current.next
- elseif migrate_inserts and id == insert_code then
- local insert
- head, current, insert = remove_nodes(head,current)
- insert.next = nil
- if first then
- insert.prev, last.next = last, insert
- else
- insert.prev, first = nil, insert
- end
- last, ni = insert, ni + 1
- elseif migrate_marks and id == mark_code then
- local mark
- head, current, mark = remove_nodes(head,current)
- mark.next = nil
- if first then
- mark.prev, last.next = last, mark
- else
- mark.prev, first = nil, mark
- end
- last, nm = mark, nm + 1
- else
- current= current.next
- end
- end
- return head, first, last, ni, nm
-end
-
-function nodes.handlers.migrate(head,where)
- local done = false
- if head then
- if trace_migrations then
- report_nodes("migration sweep %a",where)
- end
- local current = head
- while current do
- local id = current.id
- -- inserts_too is a temp hack, we should only do them when it concerns
- -- newly placed (flushed) inserts
- if id == vlist_code or id == hlist_code or (inserts_too and id == insert_code) and not current[a_migrated] then
- current[a_migrated] = 1
- t_sweeps = t_sweeps + 1
- local h = current.list
- local first, last, ni, nm
- while h do
- local id = h.id
- if id == vlist_code or id == hlist_code then
- h, first, last, ni, nm = locate(h,first,last,0,0)
- end
- h = h.next
- end
- if first then
- t_inserts, t_marks = t_inserts + ni, t_marks + nm
- if trace_migrations and (ni > 0 or nm > 0) then
- report_nodes("sweep %a, container %a, %s inserts and %s marks migrated outwards during %a",
- t_sweeps,nodecodes[id],ni,nm,where)
- end
- -- inserts after head
- local n = current.next
- if n then
- last.next, n.prev = n, last
- end
- current.next, first.prev = first, current
- done, current = true, last
- end
- end
- current = current.next
- end
- return head, done
- end
-end
-
--- for the moment this way, this will disappear
-
-experiments.register("marks.migrate", function(v)
- if v then
- tasks.enableaction("mvlbuilders", "nodes.handlers.migrate")
- end
- migrate_marks = v
-end)
-
-experiments.register("inserts.migrate", function(v)
- if v then
- tasks.enableaction("mvlbuilders", "nodes.handlers.migrate")
- end
- migrate_inserts = v
-end)
-
-experiments.register("inserts.migrate.nested", function(v)
- if v then
- tasks.enableaction("mvlbuilders", "nodes.handlers.migrate")
- end
- inserts_too = v
-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)
+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"
+}
+
+local format = string.format
+
+local attributes, nodes, node = attributes, nodes, node
+
+local remove_nodes = nodes.remove
+
+local nodecodes = nodes.nodecodes
+local tasks = nodes.tasks
+
+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 trace_migrations = false trackers.register("nodes.migrations", function(v) trace_migrations = v end)
+
+local report_nodes = logs.reporter("nodes","migrations")
+
+local migrate_inserts, migrate_marks, inserts_too
+
+local t_inserts, t_marks, t_sweeps = 0, 0, 0
+
+local function locate(head,first,last,ni,nm)
+ local current = head
+ while current do
+ local id = current.id
+ if id == vlist_code or id == hlist_code then
+ current.list, first, last, ni, nm = locate(current.list,first,last,ni,nm)
+ current = current.next
+ elseif migrate_inserts and id == insert_code then
+ local insert
+ head, current, insert = remove_nodes(head,current)
+ insert.next = nil
+ if first then
+ insert.prev, last.next = last, insert
+ else
+ insert.prev, first = nil, insert
+ end
+ last, ni = insert, ni + 1
+ elseif migrate_marks and id == mark_code then
+ local mark
+ head, current, mark = remove_nodes(head,current)
+ mark.next = nil
+ if first then
+ mark.prev, last.next = last, mark
+ else
+ mark.prev, first = nil, mark
+ end
+ last, nm = mark, nm + 1
+ else
+ current= current.next
+ end
+ end
+ return head, first, last, ni, nm
+end
+
+function nodes.handlers.migrate(head,where)
+ local done = false
+ if head then
+ if trace_migrations then
+ report_nodes("migration sweep %a",where)
+ end
+ local current = head
+ while current do
+ local id = current.id
+ -- inserts_too is a temp hack, we should only do them when it concerns
+ -- newly placed (flushed) inserts
+ if id == vlist_code or id == hlist_code or (inserts_too and id == insert_code) and not current[a_migrated] then
+ current[a_migrated] = 1
+ t_sweeps = t_sweeps + 1
+ local h = current.list
+ local first, last, ni, nm
+ while h do
+ local id = h.id
+ if id == vlist_code or id == hlist_code then
+ h, first, last, ni, nm = locate(h,first,last,0,0)
+ end
+ h = h.next
+ end
+ if first then
+ t_inserts, t_marks = t_inserts + ni, t_marks + nm
+ if trace_migrations and (ni > 0 or nm > 0) then
+ report_nodes("sweep %a, container %a, %s inserts and %s marks migrated outwards during %a",
+ t_sweeps,nodecodes[id],ni,nm,where)
+ end
+ -- inserts after head
+ local n = current.next
+ if n then
+ last.next, n.prev = n, last
+ end
+ current.next, first.prev = first, current
+ done, current = true, last
+ end
+ end
+ current = current.next
+ end
+ return head, done
+ end
+end
+
+-- for the moment this way, this will disappear
+
+experiments.register("marks.migrate", function(v)
+ if v then
+ tasks.enableaction("mvlbuilders", "nodes.handlers.migrate")
+ end
+ migrate_marks = v
+end)
+
+experiments.register("inserts.migrate", function(v)
+ if v then
+ tasks.enableaction("mvlbuilders", "nodes.handlers.migrate")
+ end
+ migrate_inserts = v
+end)
+
+experiments.register("inserts.migrate.nested", function(v)
+ if v then
+ tasks.enableaction("mvlbuilders", "nodes.handlers.migrate")
+ end
+ inserts_too = v
+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)
diff --git a/tex/context/base/node-pag.lua b/tex/context/base/node-pag.lua
index 47eba4eeb..9b8202042 100644
--- a/tex/context/base/node-pag.lua
+++ b/tex/context/base/node-pag.lua
@@ -1,30 +1,30 @@
-if not modules then modules = { } end modules ['node-pag'] = {
- version = 1.001,
- comment = "companion to node-pag.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- this callback might disappear and come back in the same way
--- as par builders
-
-pagebuilders = pagebuilders or { }
-
-local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming
-
-local actions = nodes.tasks.actions("pagebuilders")
-
-local function processor(head,groupcode,size,packtype,maxdepth,direction)
- starttiming(pagebuilders)
- local _, done = actions(head,groupcode,size,packtype,maxdepth,direction)
- stoptiming(pagebuilders)
- return (done and head) or true
--- return vpack(head)
-end
-
---~ callbacks.register('pre_output_filter', processor, "preparing output box")
-
---~ statistics.register("output preparation time", function()
---~ return statistics.elapsedseconds(pagebuilders)
---~ end)
+if not modules then modules = { } end modules ['node-pag'] = {
+ version = 1.001,
+ comment = "companion to node-pag.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- this callback might disappear and come back in the same way
+-- as par builders
+
+pagebuilders = pagebuilders or { }
+
+local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming
+
+local actions = nodes.tasks.actions("pagebuilders")
+
+local function processor(head,groupcode,size,packtype,maxdepth,direction)
+ starttiming(pagebuilders)
+ local _, done = actions(head,groupcode,size,packtype,maxdepth,direction)
+ stoptiming(pagebuilders)
+ return (done and head) or true
+-- return vpack(head)
+end
+
+--~ callbacks.register('pre_output_filter', processor, "preparing output box")
+
+--~ statistics.register("output preparation time", function()
+--~ return statistics.elapsedseconds(pagebuilders)
+--~ end)
diff --git a/tex/context/base/node-pro.lua b/tex/context/base/node-pro.lua
index 6b0829e5e..60f2d8a72 100644
--- a/tex/context/base/node-pro.lua
+++ b/tex/context/base/node-pro.lua
@@ -1,165 +1,165 @@
-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 utfchar = utf.char
-local format, concat = string.format, table.concat
-
-local trace_callbacks = false trackers.register("nodes.callbacks", function(v) trace_callbacks = v end)
-
-local report_nodes = logs.reporter("nodes","processors")
-
-local nodes, node = nodes, node
-
-local nodecodes = nodes.nodecodes
-local glyph_code = nodecodes.glyph
-local tasks = nodes.tasks
-
-local free_node = node.free
-local first_glyph = node.first_glyph or node.first_character
-local has_attribute = node.has_attribute
-
-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")
-
-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 id = h.id
- if id == glyph_code then -- todo: disc etc
- t[n] = utfchar(h.char)
- else
- t[n] = "[]"
- end
- h = h.next
- end
- return concat(t)
-end
-
-local function tracer(what,state,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, state %a, group %a, # before %a, # after %s, stream: %s",what,n,state,groupcode,before,after,reconstruct(head))
- else
- report_nodes("%s: location %a, state %a, group %a, # before %a, # after %s",what,n,state,groupcode,before,after)
- end
-end
-
-processors.tracer = tracer
-
-processors.enabled = true -- this will become a proper state (like trackers)
-
-function processors.pre_linebreak_filter(head,groupcode,size,packtype,direction)
- local first, found = first_glyph(head) -- they really need to be glyphs
- if found then
- if trace_callbacks then
- local before = nodes.count(head,true)
- local head, done = actions(head,groupcode,size,packtype,direction) -- todo : pass first
- local after = nodes.count(head,true)
- if done then
- tracer("pre_linebreak","changed",head,groupcode,before,after,true)
- else
- tracer("pre_linebreak","unchanged",head,groupcode,before,after,true)
- end
- return done and head or true
- else
- local head, done = actions(head,groupcode,size,packtype,direction) -- todo : pass first
- return done and head or true
- end
- elseif trace_callbacks then
- local n = nodes.count(head,false)
- tracer("pre_linebreak","no chars",head,groupcode,n,n)
- end
- return true
-end
-
-local enabled = true
-
-function processors.hpack_filter(head,groupcode,size,packtype,direction)
- if enabled then
- local first, found = first_glyph(head) -- they really need to be glyphs
- if found then
- if trace_callbacks then
- local before = nodes.count(head,true)
- local head, done = actions(head,groupcode,size,packtype,direction)
- local after = nodes.count(head,true)
- if done then
- tracer("hpack","changed",head,groupcode,before,after,true)
- else
- tracer("hpack","unchanged",head,groupcode,before,after,true)
- end
- return done and head or true
- else
- local head, done = actions(head,groupcode,size,packtype,direction)
- return done and head or true
- end
- elseif trace_callbacks then
- local n = nodes.count(head,false)
- tracer("hpack","no chars",head,groupcode,n,n)
- end
- end
- return true
-end
-
-local hpack = node.hpack
-
-function nodes.fasthpack(...) -- todo: pass explicit arguments
- enabled = false
- local hp, b = hpack(...)
- hp.prev = nil
- hp.next = nil
- enabled = true
- return hp, b
-end
-
-callbacks.register('pre_linebreak_filter', processors.pre_linebreak_filter, "all kind of horizontal manipulations (before par break)")
-callbacks.register('hpack_filter' , processors.hpack_filter, "all kind of horizontal manipulations (before hbox creation)")
-
-local actions = tasks.actions("finalizers") -- head, where
-
--- beware, these are packaged boxes so no first_glyph test
--- maybe some day a hash with valid groupcodes
---
--- beware, much can pass twice, for instance vadjust passes two times
---
--- something weird here .. group mvl when making a vbox
-
-function processors.post_linebreak_filter(head,groupcode)
- if trace_callbacks then
- local before = nodes.count(head,true)
- local head, done = actions(head,groupcode)
- local after = nodes.count(head,true)
- if done then
- tracer("post_linebreak","changed",head,groupcode,before,after,true)
- else
- tracer("post_linebreak","unchanged",head,groupcode,before,after,true)
- end
- return done and head or true
- else
- local head, done = actions(head,groupcode)
- return done and head or true
- end
-end
-
-callbacks.register('post_linebreak_filter', processors.post_linebreak_filter,"all kind of horizontal manipulations (after par break)")
-
-statistics.register("h-node processing time", function()
- return statistics.elapsedseconds(nodes,"including kernel") -- hm, ok here?
-end)
+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 utfchar = utf.char
+local format, concat = string.format, table.concat
+
+local trace_callbacks = false trackers.register("nodes.callbacks", function(v) trace_callbacks = v end)
+
+local report_nodes = logs.reporter("nodes","processors")
+
+local nodes, node = nodes, node
+
+local nodecodes = nodes.nodecodes
+local glyph_code = nodecodes.glyph
+local tasks = nodes.tasks
+
+local free_node = node.free
+local first_glyph = node.first_glyph or node.first_character
+local has_attribute = node.has_attribute
+
+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")
+
+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 id = h.id
+ if id == glyph_code then -- todo: disc etc
+ t[n] = utfchar(h.char)
+ else
+ t[n] = "[]"
+ end
+ h = h.next
+ end
+ return concat(t)
+end
+
+local function tracer(what,state,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, state %a, group %a, # before %a, # after %s, stream: %s",what,n,state,groupcode,before,after,reconstruct(head))
+ else
+ report_nodes("%s: location %a, state %a, group %a, # before %a, # after %s",what,n,state,groupcode,before,after)
+ end
+end
+
+processors.tracer = tracer
+
+processors.enabled = true -- this will become a proper state (like trackers)
+
+function processors.pre_linebreak_filter(head,groupcode,size,packtype,direction)
+ local first, found = first_glyph(head) -- they really need to be glyphs
+ if found then
+ if trace_callbacks then
+ local before = nodes.count(head,true)
+ local head, done = actions(head,groupcode,size,packtype,direction) -- todo : pass first
+ local after = nodes.count(head,true)
+ if done then
+ tracer("pre_linebreak","changed",head,groupcode,before,after,true)
+ else
+ tracer("pre_linebreak","unchanged",head,groupcode,before,after,true)
+ end
+ return done and head or true
+ else
+ local head, done = actions(head,groupcode,size,packtype,direction) -- todo : pass first
+ return done and head or true
+ end
+ elseif trace_callbacks then
+ local n = nodes.count(head,false)
+ tracer("pre_linebreak","no chars",head,groupcode,n,n)
+ end
+ return true
+end
+
+local enabled = true
+
+function processors.hpack_filter(head,groupcode,size,packtype,direction)
+ if enabled then
+ local first, found = first_glyph(head) -- they really need to be glyphs
+ if found then
+ if trace_callbacks then
+ local before = nodes.count(head,true)
+ local head, done = actions(head,groupcode,size,packtype,direction)
+ local after = nodes.count(head,true)
+ if done then
+ tracer("hpack","changed",head,groupcode,before,after,true)
+ else
+ tracer("hpack","unchanged",head,groupcode,before,after,true)
+ end
+ return done and head or true
+ else
+ local head, done = actions(head,groupcode,size,packtype,direction)
+ return done and head or true
+ end
+ elseif trace_callbacks then
+ local n = nodes.count(head,false)
+ tracer("hpack","no chars",head,groupcode,n,n)
+ end
+ end
+ return true
+end
+
+local hpack = node.hpack
+
+function nodes.fasthpack(...) -- todo: pass explicit arguments
+ enabled = false
+ local hp, b = hpack(...)
+ hp.prev = nil
+ hp.next = nil
+ enabled = true
+ return hp, b
+end
+
+callbacks.register('pre_linebreak_filter', processors.pre_linebreak_filter, "all kind of horizontal manipulations (before par break)")
+callbacks.register('hpack_filter' , processors.hpack_filter, "all kind of horizontal manipulations (before hbox creation)")
+
+local actions = tasks.actions("finalizers") -- head, where
+
+-- beware, these are packaged boxes so no first_glyph test
+-- maybe some day a hash with valid groupcodes
+--
+-- beware, much can pass twice, for instance vadjust passes two times
+--
+-- something weird here .. group mvl when making a vbox
+
+function processors.post_linebreak_filter(head,groupcode)
+ if trace_callbacks then
+ local before = nodes.count(head,true)
+ local head, done = actions(head,groupcode)
+ local after = nodes.count(head,true)
+ if done then
+ tracer("post_linebreak","changed",head,groupcode,before,after,true)
+ else
+ tracer("post_linebreak","unchanged",head,groupcode,before,after,true)
+ end
+ return done and head or true
+ else
+ local head, done = actions(head,groupcode)
+ return done and head or true
+ end
+end
+
+callbacks.register('post_linebreak_filter', processors.post_linebreak_filter,"all kind of horizontal manipulations (after par break)")
+
+statistics.register("h-node processing time", function()
+ return statistics.elapsedseconds(nodes,"including kernel") -- hm, ok here?
+end)
diff --git a/tex/context/base/node-ref.lua b/tex/context/base/node-ref.lua
index cd46cd2dd..09e066434 100644
--- a/tex/context/base/node-ref.lua
+++ b/tex/context/base/node-ref.lua
@@ -1,585 +1,585 @@
-if not modules then modules = { } end modules ['node-ref'] = {
- version = 1.001,
- 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 format = string.format
-
-local allocate, mark = utilities.storage.allocate, utilities.storage.mark
-
-local cleanupreferences, cleanupdestinations = false, true
-
-local attributes, nodes, node = attributes, nodes, node
-
-local nodeinjections = backends.nodeinjections
-local codeinjections = backends.codeinjections
-
-local transparencies = attributes.transparencies
-local colors = attributes.colors
-local references = structures.references
-local tasks = nodes.tasks
-
-local hpack_list = node.hpack
-local list_dimensions = node.dimensions
-
--- current.glue_set current.glue_sign
-
-local trace_backend = false trackers.register("nodes.backend", function(v) trace_backend = v end)
-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 report_reference = logs.reporter("backend","references")
-local report_destination = logs.reporter("backend","destinations")
-local report_area = logs.reporter("backend","areas")
-
-local nodecodes = nodes.nodecodes
-local skipcodes = nodes.skipcodes
-local whatcodes = nodes.whatcodes
-local listcodes = nodes.listcodes
-
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-local glue_code = nodecodes.glue
-local whatsit_code = nodecodes.whatsit
-
-local leftskip_code = skipcodes.leftskip
-local rightskip_code = skipcodes.rightskip
-local parfillskip_code = skipcodes.parfillskip
-
-local localpar_code = whatcodes.localpar
-local dir_code = whatcodes.dir
-
-local line_code = listcodes.line
-
-local nodepool = nodes.pool
-
-local new_kern = nodepool.kern
-
-local traverse = node.traverse
-local find_node_tail = node.tail or node.slide
-local tosequence = nodes.tosequence
-
--- local function dimensions(parent,start,stop)
--- stop = stop and stop.next
--- if parent then
--- if stop then
--- return list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,start,stop)
--- else
--- return list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,start)
--- end
--- else
--- if stop then
--- return list_dimensions(start,stop)
--- else
--- return list_dimensions(start)
--- end
--- end
--- end
---
--- -- more compact
-
-local function dimensions(parent,start,stop)
- if parent then
- return list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,start,stop and stop.next)
- else
- return list_dimensions(start,stop and stop.next)
- end
-end
-
--- is pardir important at all?
-
-local function inject_range(head,first,last,reference,make,stack,parent,pardir,txtdir)
- local width, height, depth = dimensions(parent,first,last)
- if txtdir == "+TRT" or (txtdir == "===" and pardir == "TRT") then -- KH: textdir == "===" test added
- width = - width
- end
- local result, resolved = make(width,height,depth,reference)
- if result and resolved then
- if head == first then
- if trace_backend then
- report_area("head: %04i %s %s %s => w=%p, h=%p, d=%p, c=%s",reference,pardir or "---",txtdir or "----",tosequence(first,last,true),width,height,depth,resolved)
- end
- result.next = first
- first.prev = result
- return result, last
- else
- if trace_backend then
- report_area("middle: %04i %s %s => w=%p, h=%p, d=%p, c=%s",reference,pardir or "---",txtdir or "----",tosequence(first,last,true),width,height,depth,resolved)
- end
- local prev = first.prev
- if prev then
- result.next = first
- result.prev = prev
- prev.next = result
- first.prev = result
- else
- result.next = first
- first.prev = result
- end
- if first == head.next then
- head.next = result -- hm, weird
- end
- return head, last
- end
- else
- return head, last
- end
-end
-
-local function inject_list(id,current,reference,make,stack,pardir,txtdir)
- local width, height, depth, correction = current.width, current.height, current.depth, 0
- local moveright = false
- local first = current.list
- if id == hlist_code then -- box_code line_code
- -- can be either an explicit hbox or a line and there is no way
- -- to recognize this; anyway only if ht/dp (then inline)
- local sr = stack[reference]
- if first then
- if sr and sr[2] then
- local last = find_node_tail(first)
- if last.id == glue_code and last.subtype == rightskip_code then
- local prev = last.prev
- moveright = first.id == glue_code and first.subtype == leftskip_code
- if prev and prev.id == glue_code and prev.subtype == parfillskip_code then
- width = dimensions(current,first,prev.prev) -- maybe not current as we already take care of it
- else
- if moveright and first.writable then
- width = width - first.spec.stretch*current.glue_set * current.glue_sign
- end
- if last.writable then
- width = width - last.spec.stretch*current.glue_set * current.glue_sign
- end
- end
- end
- else
- -- also weird
- end
- else
- -- ok
- end
- correction = width
- else
- correction = height + depth
- height, depth = depth, height -- ugly hack, needed because pdftex backend does something funny
- end
- if pardir == "TRT" then
- width = - width
- end
- local result, resolved = make(width,height,depth,reference)
- -- todo: only when width is ok
- if result and resolved then
- if trace_backend then
- report_area("box: %04i %s %s: w=%p, h=%p, d=%p, c=%s",reference,pardir or "---",txtdir or "----",width,height,depth,resolved)
- end
- if not first then
- current.list = result
- elseif moveright then -- brr no prevs done
- -- result after first
- local n = first.next
- result.next = n
- first.next = result
- result.prev = first
- if n then n.prev = result end
- else
- -- first after result
- result.next = first
- first.prev = result
- current.list = result
- end
- end
-end
-
--- skip is somewhat messy
-
-local function inject_areas(head,attribute,make,stack,done,skip,parent,pardir,txtdir) -- main
- if head then
- local current, first, last, firstdir, reference = head, nil, nil, nil, nil
- pardir = pardir or "==="
- txtdir = txtdir or "==="
- while current do
- local id = current.id
- if id == hlist_code or id == vlist_code then
- local r = current[attribute]
- -- somehow reference is true so the following fails (second one not done) in
- -- test \goto{test}[page(2)] test \gotobox{test}[page(2)]
- -- so let's wait till this fails again
- -- if not reference and r and (not skip or r > skip) then -- > or ~=
- if r and (not skip or r > skip) then -- > or ~=
- inject_list(id,current,r,make,stack,pardir,txtdir)
- end
- if r then
- done[r] = (done[r] or 0) + 1
- end
- local list = current.list
- if list then
- local _
- current.list, _, pardir, txtdir = inject_areas(list,attribute,make,stack,done,r or skip or 0,current,pardir,txtdir)
- end
- if r then
- done[r] = done[r] - 1
- end
- elseif id == whatsit_code then
- local subtype = current.subtype
- if subtype == localpar_code then
- pardir = current.dir
- elseif subtype == dir_code then
- txtdir = current.dir
- end
- elseif id == glue_code and current.subtype == leftskip_code then -- any glue at the left?
- --
- else
- local r = current[attribute]
- if not r then
- -- just go on, can be kerns
- elseif not reference then
- reference, first, last, firstdir = r, current, current, txtdir
- elseif r == reference then
- last = current
- elseif (done[reference] or 0) == 0 then -- or id == glue_code and current.subtype == right_skip_code
- if not skip or r > skip then -- maybe no > test
- head, current = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir)
- reference, first, last, firstdir = nil, nil, nil, nil
- end
- else
- reference, first, last, firstdir = r, current, current, txtdir
- end
- end
- current = current.next
- end
- if reference and (done[reference] or 0) == 0 then
- head = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir)
- end
- end
- return head, true, pardir, txtdir
-end
-
-local function inject_area(head,attribute,make,stack,done,parent,pardir,txtdir) -- singular !
- if head then
- pardir = pardir or "==="
- txtdir = txtdir or "==="
- local current = head
- while current do
- local id = current.id
- if id == hlist_code or id == vlist_code then
- local r = current[attribute]
- if r and not done[r] then
- done[r] = true
- inject_list(id,current,r,make,stack,pardir,txtdir)
- end
- local list = current.list
- if list then
- current.list = inject_area(list,attribute,make,stack,done,current,pardir,txtdir)
- end
- elseif id == whatsit_code then
- local subtype = current.subtype
- if subtype == localpar_code then
- pardir = current.dir
- elseif subtype == dir_code then
- txtdir = current.dir
- end
- else
- local r = current[attribute]
- if r and not done[r] then
- done[r] = true
- head, current = inject_range(head,current,current,r,make,stack,parent,pardir,txtdir)
- end
- end
- current = current.next
- end
- end
- return head, true
-end
-
--- tracing
-
-local nodepool = nodes.pool
-
-local new_rule = nodepool.rule
-local new_kern = nodepool.kern
-
-local set_attribute = node.set_attribute
-local register_color = colors.register
-
-local a_color = attributes.private('color')
-local a_colormodel = attributes.private('colormodel')
-local a_transparency = attributes.private('transparency')
-local u_transparency = nil
-local u_colors = { }
-local force_gray = true
-
-local function colorize(width,height,depth,n,reference,what)
- 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 = new_rule(width,height,depth)
- rule[a_colormodel] = 1 -- gray color model
- rule[a_color] = u_color
- rule[a_transparency] = u_transparency
- if width < 0 then
- local kern = new_kern(width)
- rule.width = -width
- kern.next = rule
- rule.prev = kern
- return kern
- else
- return rule
- end
-end
-
-local nodepool = nodes.pool
-
-local new_kern = nodepool.kern
-
-local texattribute = tex.attribute
-local texcount = tex.count
-
--- references:
-
-local stack = { }
-local done = { }
-local attribute = attributes.private('reference')
-local nofreferences = 0
-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)
- 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, d, codeinjections.prerollreference(r) }
- -- texattribute[attribute] = topofstack -- todo -> at tex end
- texcount.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)
- local sr = stack[reference]
- if sr then
- if trace_references then
- report_reference("resolving attribute %a",reference)
- end
- local resolved, ht, dp, set, n = sr[1], sr[2], sr[3], sr[4], sr[5]
- if ht then
- if height < ht then height = ht end
- if depth < dp then depth = dp end
- end
- local annot = nodeinjections.reference(width,height,depth,set)
- if annot then
- nofreferences = nofreferences + 1
- local result, current
- if trace_references then
- local step = 65536
- result = hpack_list(colorize(width,height-step,depth-step,2,reference,"reference")) -- step subtracted so that we can see seperate links
- result.width = 0
- current = result
- end
- if current then
- current.next = annot
- else
- result = annot
- end
- references.registerpage(n)
- result = hpack_list(result,0)
- result.width, result.height, result.depth = 0, 0, 0
- 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 topofstack > 0 then
- return inject_areas(head,attribute,makereference,stack,done)
- else
- return head, false
- end
-end
-
--- destinations (we can clean up once set, unless tagging!)
-
-local stack = { }
-local done = { }
-local attribute = attributes.private('destination')
-local nofdestinations = 0
-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, ht, dp, name, view = sr[1], sr[2], sr[3], sr[4], sr[5]
- if ht then
- if height < ht then height = ht end
- if depth < dp then depth = dp end
- end
- local result, current
- if trace_destinations then
- local step = 0
- if width == 0 then
- step = 4*65536
- width, height, depth = 5*step, 5*step, 0
- end
- for n=1,#name do
- local rule = hpack_list(colorize(width,height,depth,3,reference,"destination"))
- rule.width = 0
- if not result then
- result, current = rule, rule
- else
- current.next = rule
- rule.prev = current
- current = rule
- end
- width, height = width - step, height - step
- end
- end
- nofdestinations = nofdestinations + 1
- for n=1,#name do
- local annot = nodeinjections.destination(width,height,depth,name[n],view)
- if not result then
- result = annot
- else
- current.next = annot
- annot.prev = current
- end
- current = find_node_tail(annot)
- end
- if result then
- -- some internal error
- result = hpack_list(result,0)
- result.width, result.height, result.depth = 0, 0, 0
- 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 topofstack > 0 then
- return inject_area(head,attribute,makedestination,stack,done) -- singular
- else
- return head, false
- end
-end
-
--- will move
-
-function references.mark(reference,h,d,view)
- return setdestination(tex.currentgrouplevel,h,d,reference,view)
-end
-
-function references.inject(prefix,reference,h,d,highlight,newwindow,layer) -- 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
- -- check
- set.highlight, set.newwindow,set.layer = highlight, newwindow, layer
- setreference(h,d,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
-
-commands.injectreference = references.inject
-commands.injectcurrentreference = references.injectcurrentset
-
---
-
-local function checkboth(open,close)
- if open and open ~= "" then
- local set, bug = references.identify("",open)
- open = not bug and #set > 0 and set
- end
- if close and close ~= "" then
- local set, bug = references.identify("",close)
- close = not bug and #set > 0 and set
- end
- return open, close
-end
-
--- end temp hack
-
-statistics.register("interactive elements", function()
- if nofreferences > 0 or nofdestinations > 0 then
- return format("%s references, %s destinations",nofreferences,nofdestinations)
- else
- return nil
- end
-end)
-
-function references.enableinteraction()
- tasks.enableaction("shipouts","nodes.references.handler")
- tasks.enableaction("shipouts","nodes.destinations.handler")
-end
+if not modules then modules = { } end modules ['node-ref'] = {
+ version = 1.001,
+ 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 format = string.format
+
+local allocate, mark = utilities.storage.allocate, utilities.storage.mark
+
+local cleanupreferences, cleanupdestinations = false, true
+
+local attributes, nodes, node = attributes, nodes, node
+
+local nodeinjections = backends.nodeinjections
+local codeinjections = backends.codeinjections
+
+local transparencies = attributes.transparencies
+local colors = attributes.colors
+local references = structures.references
+local tasks = nodes.tasks
+
+local hpack_list = node.hpack
+local list_dimensions = node.dimensions
+
+-- current.glue_set current.glue_sign
+
+local trace_backend = false trackers.register("nodes.backend", function(v) trace_backend = v end)
+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 report_reference = logs.reporter("backend","references")
+local report_destination = logs.reporter("backend","destinations")
+local report_area = logs.reporter("backend","areas")
+
+local nodecodes = nodes.nodecodes
+local skipcodes = nodes.skipcodes
+local whatcodes = nodes.whatcodes
+local listcodes = nodes.listcodes
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local glue_code = nodecodes.glue
+local whatsit_code = nodecodes.whatsit
+
+local leftskip_code = skipcodes.leftskip
+local rightskip_code = skipcodes.rightskip
+local parfillskip_code = skipcodes.parfillskip
+
+local localpar_code = whatcodes.localpar
+local dir_code = whatcodes.dir
+
+local line_code = listcodes.line
+
+local nodepool = nodes.pool
+
+local new_kern = nodepool.kern
+
+local traverse = node.traverse
+local find_node_tail = node.tail or node.slide
+local tosequence = nodes.tosequence
+
+-- local function dimensions(parent,start,stop)
+-- stop = stop and stop.next
+-- if parent then
+-- if stop then
+-- return list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,start,stop)
+-- else
+-- return list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,start)
+-- end
+-- else
+-- if stop then
+-- return list_dimensions(start,stop)
+-- else
+-- return list_dimensions(start)
+-- end
+-- end
+-- end
+--
+-- -- more compact
+
+local function dimensions(parent,start,stop)
+ if parent then
+ return list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,start,stop and stop.next)
+ else
+ return list_dimensions(start,stop and stop.next)
+ end
+end
+
+-- is pardir important at all?
+
+local function inject_range(head,first,last,reference,make,stack,parent,pardir,txtdir)
+ local width, height, depth = dimensions(parent,first,last)
+ if txtdir == "+TRT" or (txtdir == "===" and pardir == "TRT") then -- KH: textdir == "===" test added
+ width = - width
+ end
+ local result, resolved = make(width,height,depth,reference)
+ if result and resolved then
+ if head == first then
+ if trace_backend then
+ report_area("head: %04i %s %s %s => w=%p, h=%p, d=%p, c=%s",reference,pardir or "---",txtdir or "----",tosequence(first,last,true),width,height,depth,resolved)
+ end
+ result.next = first
+ first.prev = result
+ return result, last
+ else
+ if trace_backend then
+ report_area("middle: %04i %s %s => w=%p, h=%p, d=%p, c=%s",reference,pardir or "---",txtdir or "----",tosequence(first,last,true),width,height,depth,resolved)
+ end
+ local prev = first.prev
+ if prev then
+ result.next = first
+ result.prev = prev
+ prev.next = result
+ first.prev = result
+ else
+ result.next = first
+ first.prev = result
+ end
+ if first == head.next then
+ head.next = result -- hm, weird
+ end
+ return head, last
+ end
+ else
+ return head, last
+ end
+end
+
+local function inject_list(id,current,reference,make,stack,pardir,txtdir)
+ local width, height, depth, correction = current.width, current.height, current.depth, 0
+ local moveright = false
+ local first = current.list
+ if id == hlist_code then -- box_code line_code
+ -- can be either an explicit hbox or a line and there is no way
+ -- to recognize this; anyway only if ht/dp (then inline)
+ local sr = stack[reference]
+ if first then
+ if sr and sr[2] then
+ local last = find_node_tail(first)
+ if last.id == glue_code and last.subtype == rightskip_code then
+ local prev = last.prev
+ moveright = first.id == glue_code and first.subtype == leftskip_code
+ if prev and prev.id == glue_code and prev.subtype == parfillskip_code then
+ width = dimensions(current,first,prev.prev) -- maybe not current as we already take care of it
+ else
+ if moveright and first.writable then
+ width = width - first.spec.stretch*current.glue_set * current.glue_sign
+ end
+ if last.writable then
+ width = width - last.spec.stretch*current.glue_set * current.glue_sign
+ end
+ end
+ end
+ else
+ -- also weird
+ end
+ else
+ -- ok
+ end
+ correction = width
+ else
+ correction = height + depth
+ height, depth = depth, height -- ugly hack, needed because pdftex backend does something funny
+ end
+ if pardir == "TRT" then
+ width = - width
+ end
+ local result, resolved = make(width,height,depth,reference)
+ -- todo: only when width is ok
+ if result and resolved then
+ if trace_backend then
+ report_area("box: %04i %s %s: w=%p, h=%p, d=%p, c=%s",reference,pardir or "---",txtdir or "----",width,height,depth,resolved)
+ end
+ if not first then
+ current.list = result
+ elseif moveright then -- brr no prevs done
+ -- result after first
+ local n = first.next
+ result.next = n
+ first.next = result
+ result.prev = first
+ if n then n.prev = result end
+ else
+ -- first after result
+ result.next = first
+ first.prev = result
+ current.list = result
+ end
+ end
+end
+
+-- skip is somewhat messy
+
+local function inject_areas(head,attribute,make,stack,done,skip,parent,pardir,txtdir) -- main
+ if head then
+ local current, first, last, firstdir, reference = head, nil, nil, nil, nil
+ pardir = pardir or "==="
+ txtdir = txtdir or "==="
+ while current do
+ local id = current.id
+ if id == hlist_code or id == vlist_code then
+ local r = current[attribute]
+ -- somehow reference is true so the following fails (second one not done) in
+ -- test \goto{test}[page(2)] test \gotobox{test}[page(2)]
+ -- so let's wait till this fails again
+ -- if not reference and r and (not skip or r > skip) then -- > or ~=
+ if r and (not skip or r > skip) then -- > or ~=
+ inject_list(id,current,r,make,stack,pardir,txtdir)
+ end
+ if r then
+ done[r] = (done[r] or 0) + 1
+ end
+ local list = current.list
+ if list then
+ local _
+ current.list, _, pardir, txtdir = inject_areas(list,attribute,make,stack,done,r or skip or 0,current,pardir,txtdir)
+ end
+ if r then
+ done[r] = done[r] - 1
+ end
+ elseif id == whatsit_code then
+ local subtype = current.subtype
+ if subtype == localpar_code then
+ pardir = current.dir
+ elseif subtype == dir_code then
+ txtdir = current.dir
+ end
+ elseif id == glue_code and current.subtype == leftskip_code then -- any glue at the left?
+ --
+ else
+ local r = current[attribute]
+ if not r then
+ -- just go on, can be kerns
+ elseif not reference then
+ reference, first, last, firstdir = r, current, current, txtdir
+ elseif r == reference then
+ last = current
+ elseif (done[reference] or 0) == 0 then -- or id == glue_code and current.subtype == right_skip_code
+ if not skip or r > skip then -- maybe no > test
+ head, current = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir)
+ reference, first, last, firstdir = nil, nil, nil, nil
+ end
+ else
+ reference, first, last, firstdir = r, current, current, txtdir
+ end
+ end
+ current = current.next
+ end
+ if reference and (done[reference] or 0) == 0 then
+ head = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir)
+ end
+ end
+ return head, true, pardir, txtdir
+end
+
+local function inject_area(head,attribute,make,stack,done,parent,pardir,txtdir) -- singular !
+ if head then
+ pardir = pardir or "==="
+ txtdir = txtdir or "==="
+ local current = head
+ while current do
+ local id = current.id
+ if id == hlist_code or id == vlist_code then
+ local r = current[attribute]
+ if r and not done[r] then
+ done[r] = true
+ inject_list(id,current,r,make,stack,pardir,txtdir)
+ end
+ local list = current.list
+ if list then
+ current.list = inject_area(list,attribute,make,stack,done,current,pardir,txtdir)
+ end
+ elseif id == whatsit_code then
+ local subtype = current.subtype
+ if subtype == localpar_code then
+ pardir = current.dir
+ elseif subtype == dir_code then
+ txtdir = current.dir
+ end
+ else
+ local r = current[attribute]
+ if r and not done[r] then
+ done[r] = true
+ head, current = inject_range(head,current,current,r,make,stack,parent,pardir,txtdir)
+ end
+ end
+ current = current.next
+ end
+ end
+ return head, true
+end
+
+-- tracing
+
+local nodepool = nodes.pool
+
+local new_rule = nodepool.rule
+local new_kern = nodepool.kern
+
+local set_attribute = node.set_attribute
+local register_color = colors.register
+
+local a_color = attributes.private('color')
+local a_colormodel = attributes.private('colormodel')
+local a_transparency = attributes.private('transparency')
+local u_transparency = nil
+local u_colors = { }
+local force_gray = true
+
+local function colorize(width,height,depth,n,reference,what)
+ 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 = new_rule(width,height,depth)
+ rule[a_colormodel] = 1 -- gray color model
+ rule[a_color] = u_color
+ rule[a_transparency] = u_transparency
+ if width < 0 then
+ local kern = new_kern(width)
+ rule.width = -width
+ kern.next = rule
+ rule.prev = kern
+ return kern
+ else
+ return rule
+ end
+end
+
+local nodepool = nodes.pool
+
+local new_kern = nodepool.kern
+
+local texattribute = tex.attribute
+local texcount = tex.count
+
+-- references:
+
+local stack = { }
+local done = { }
+local attribute = attributes.private('reference')
+local nofreferences = 0
+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)
+ 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, d, codeinjections.prerollreference(r) }
+ -- texattribute[attribute] = topofstack -- todo -> at tex end
+ texcount.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)
+ local sr = stack[reference]
+ if sr then
+ if trace_references then
+ report_reference("resolving attribute %a",reference)
+ end
+ local resolved, ht, dp, set, n = sr[1], sr[2], sr[3], sr[4], sr[5]
+ if ht then
+ if height < ht then height = ht end
+ if depth < dp then depth = dp end
+ end
+ local annot = nodeinjections.reference(width,height,depth,set)
+ if annot then
+ nofreferences = nofreferences + 1
+ local result, current
+ if trace_references then
+ local step = 65536
+ result = hpack_list(colorize(width,height-step,depth-step,2,reference,"reference")) -- step subtracted so that we can see seperate links
+ result.width = 0
+ current = result
+ end
+ if current then
+ current.next = annot
+ else
+ result = annot
+ end
+ references.registerpage(n)
+ result = hpack_list(result,0)
+ result.width, result.height, result.depth = 0, 0, 0
+ 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 topofstack > 0 then
+ return inject_areas(head,attribute,makereference,stack,done)
+ else
+ return head, false
+ end
+end
+
+-- destinations (we can clean up once set, unless tagging!)
+
+local stack = { }
+local done = { }
+local attribute = attributes.private('destination')
+local nofdestinations = 0
+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, ht, dp, name, view = sr[1], sr[2], sr[3], sr[4], sr[5]
+ if ht then
+ if height < ht then height = ht end
+ if depth < dp then depth = dp end
+ end
+ local result, current
+ if trace_destinations then
+ local step = 0
+ if width == 0 then
+ step = 4*65536
+ width, height, depth = 5*step, 5*step, 0
+ end
+ for n=1,#name do
+ local rule = hpack_list(colorize(width,height,depth,3,reference,"destination"))
+ rule.width = 0
+ if not result then
+ result, current = rule, rule
+ else
+ current.next = rule
+ rule.prev = current
+ current = rule
+ end
+ width, height = width - step, height - step
+ end
+ end
+ nofdestinations = nofdestinations + 1
+ for n=1,#name do
+ local annot = nodeinjections.destination(width,height,depth,name[n],view)
+ if not result then
+ result = annot
+ else
+ current.next = annot
+ annot.prev = current
+ end
+ current = find_node_tail(annot)
+ end
+ if result then
+ -- some internal error
+ result = hpack_list(result,0)
+ result.width, result.height, result.depth = 0, 0, 0
+ 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 topofstack > 0 then
+ return inject_area(head,attribute,makedestination,stack,done) -- singular
+ else
+ return head, false
+ end
+end
+
+-- will move
+
+function references.mark(reference,h,d,view)
+ return setdestination(tex.currentgrouplevel,h,d,reference,view)
+end
+
+function references.inject(prefix,reference,h,d,highlight,newwindow,layer) -- 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
+ -- check
+ set.highlight, set.newwindow,set.layer = highlight, newwindow, layer
+ setreference(h,d,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
+
+commands.injectreference = references.inject
+commands.injectcurrentreference = references.injectcurrentset
+
+--
+
+local function checkboth(open,close)
+ if open and open ~= "" then
+ local set, bug = references.identify("",open)
+ open = not bug and #set > 0 and set
+ end
+ if close and close ~= "" then
+ local set, bug = references.identify("",close)
+ close = not bug and #set > 0 and set
+ end
+ return open, close
+end
+
+-- end temp hack
+
+statistics.register("interactive elements", function()
+ if nofreferences > 0 or nofdestinations > 0 then
+ return format("%s references, %s destinations",nofreferences,nofdestinations)
+ else
+ return nil
+ end
+end)
+
+function references.enableinteraction()
+ tasks.enableaction("shipouts","nodes.references.handler")
+ tasks.enableaction("shipouts","nodes.destinations.handler")
+end
diff --git a/tex/context/base/node-res.lua b/tex/context/base/node-res.lua
index 6ec6895c8..768aac404 100644
--- a/tex/context/base/node-res.lua
+++ b/tex/context/base/node-res.lua
@@ -1,406 +1,406 @@
-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 gmatch, format = string.gmatch, string.format
-local tonumber, round = tonumber, math.round
-
---[[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 report_nodes = logs.reporter("nodes","housekeeping")
-
-local nodes, node = nodes, node
-
-local copy_node = node.copy
-local free_node = node.free
-local free_list = node.flush_list
-local new_node = node.new
-
-nodes.pool = nodes.pool or { }
-local pool = nodes.pool
-
-local whatsitcodes = nodes.whatsitcodes
-local skipcodes = nodes.skipcodes
-local kerncodes = nodes.kerncodes
-local nodecodes = nodes.nodecodes
-
-local glyph_code = nodecodes.glyph
-
-local allocate = utilities.storage.allocate
-
-local reserved, nofreserved = { }, 0
-
-local function register_node(n)
- nofreserved = nofreserved + 1
- reserved[nofreserved] = n
- return n
-end
-
-pool.register = register_node
-
-function pool.cleanup(nofboxes) -- todo
- if nodes.tracers.steppers then -- to be resolved
- nodes.tracers.steppers.reset() -- todo: make a registration subsystem
- end
- local nl, nr = 0, nofreserved
- for i=1,nofreserved do
- local ri = reserved[i]
- -- if not (ri.id == glue_spec and not ri.is_writable) then
- free_node(reserved[i])
- -- end
- end
- if nofboxes then
- local tb = tex.box
- for i=0,nofboxes do
- local l = tb[i]
- if l then
- free_node(tb[i])
- nl = nl + 1
- end
- end
- end
- reserved = { }
- nofreserved = 0
- return nr, nl, nofboxes -- can be nil
-end
-
-function pool.usage()
- local t = { }
- for n, tag in gmatch(status.node_mem_usage,"(%d+) ([a-z_]+)") do
- t[tag] = n
- end
- return t
-end
-
-local disc = register_node(new_node("disc"))
-local kern = register_node(new_node("kern",kerncodes.userkern))
-local fontkern = register_node(new_node("kern",kerncodes.fontkern))
-local penalty = register_node(new_node("penalty"))
-local glue = register_node(new_node("glue")) -- glue.spec = nil
-local glue_spec = register_node(new_node("glue_spec"))
-local glyph = register_node(new_node("glyph",0))
-local textdir = register_node(new_node("whatsit",whatsitcodes.dir))
-local latelua = register_node(new_node("whatsit",whatsitcodes.latelua))
-local special = register_node(new_node("whatsit",whatsitcodes.special))
-local user_n = register_node(new_node("whatsit",whatsitcodes.userdefined)) user_n.type = 100 -- 44
-local user_l = register_node(new_node("whatsit",whatsitcodes.userdefined)) user_l.type = 110 -- 44
-local user_s = register_node(new_node("whatsit",whatsitcodes.userdefined)) user_s.type = 115 -- 44
-local user_t = register_node(new_node("whatsit",whatsitcodes.userdefined)) user_t.type = 116 -- 44
-local left_margin_kern = register_node(new_node("margin_kern",0))
-local right_margin_kern = register_node(new_node("margin_kern",1))
-local lineskip = register_node(new_node("glue",skipcodes.lineskip))
-local baselineskip = register_node(new_node("glue",skipcodes.baselineskip))
-local leftskip = register_node(new_node("glue",skipcodes.leftskip))
-local rightskip = register_node(new_node("glue",skipcodes.rightskip))
-local temp = register_node(new_node("temp",0))
-local noad = register_node(new_node("noad"))
-
--- the dir field needs to be set otherwise crash:
-
-local rule = register_node(new_node("rule")) rule .dir = "TLT"
-local hlist = register_node(new_node("hlist")) hlist.dir = "TLT"
-local vlist = register_node(new_node("vlist")) vlist.dir = "TLT"
-
-function pool.zeroglue(n)
- local s = n.spec
- return not writable or (
- s.width == 0
- and s.stretch == 0
- and s.shrink == 0
- and s.stretch_order == 0
- and s.shrink_order == 0
- )
-end
-
-function pool.glyph(fnt,chr)
- local n = copy_node(glyph)
- if fnt then n.font = fnt end
- if chr then n.char = chr end
- return n
-end
-
-function pool.penalty(p)
- local n = copy_node(penalty)
- n.penalty = p
- return n
-end
-
-function pool.kern(k)
- local n = copy_node(kern)
- n.kern = k
- return n
-end
-
-function pool.fontkern(k)
- local n = copy_node(fontkern)
- n.kern = k
- return n
-end
-
-function pool.gluespec(width,stretch,shrink,stretch_order,shrink_order)
- local s = copy_node(glue_spec)
- if width then s.width = width end
- if stretch then s.stretch = stretch end
- if shrink then s.shrink = shrink end
- if stretch_order then s.stretch_order = stretch_order end
- if shrink_order then s.shrink_order = shrink_order end
- return s
-end
-
-local function someskip(skip,width,stretch,shrink,stretch_order,shrink_order)
- local n = copy_node(skip)
- if not width then
- -- no spec
- elseif width == false or tonumber(width) then
- local s = copy_node(glue_spec)
- if width then s.width = width end
- if stretch then s.stretch = stretch end
- if shrink then s.shrink = shrink end
- if stretch_order then s.stretch_order = stretch_order end
- if shrink_order then s.shrink_order = shrink_order end
- n.spec = s
- else
- -- shared
- n.spec = copy_node(width)
- end
- return n
-end
-
-function pool.stretch(a,b)
- local n = copy_node(glue)
- local s = copy_node(glue_spec)
- if b then
- s.stretch = a
- s.stretch_order = b
- else
- s.stretch = 1
- s.stretch_order = a or 1
- end
- n.spec = s
- return n
-end
-
-function pool.shrink(a,b)
- local n = copy_node(glue)
- local s = copy_node(glue_spec)
- if b then
- s.shrink = a
- s.shrink_order = b
- else
- s.shrink = 1
- s.shrink_order = a or 1
- end
- n.spec = s
- return n
-end
-
-
-function pool.glue(width,stretch,shrink,stretch_order,shrink_order)
- return someskip(glue,width,stretch,shrink,stretch_order,shrink_order)
-end
-
-function pool.leftskip(width,stretch,shrink,stretch_order,shrink_order)
- return someskip(leftskip,width,stretch,shrink,stretch_order,shrink_order)
-end
-
-function pool.rightskip(width,stretch,shrink,stretch_order,shrink_order)
- return someskip(rightskip,width,stretch,shrink,stretch_order,shrink_order)
-end
-
-function pool.lineskip(width,stretch,shrink,stretch_order,shrink_order)
- return someskip(lineskip,width,stretch,shrink,stretch_order,shrink_order)
-end
-
-function pool.baselineskip(width,stretch,shrink)
- return someskip(baselineskip,width,stretch,shrink)
-end
-
-function pool.disc()
- return copy_node(disc)
-end
-
-function pool.textdir(dir)
- local t = copy_node(textdir)
- t.dir = dir
- return t
-end
-
-function pool.rule(width,height,depth,dir) -- w/h/d == nil will let them adapt
- local n = copy_node(rule)
- if width then n.width = width end
- if height then n.height = height end
- if depth then n.depth = depth end
- if dir then n.dir = dir end
- return n
-end
-
-if node.has_field(latelua,'string') then
- function pool.latelua(code)
- local n = copy_node(latelua)
- n.string = code
- return n
- end
-else
- function pool.latelua(code)
- local n = copy_node(latelua)
- n.data = code
- return n
- end
-end
-
-function pool.leftmarginkern(glyph,width)
- local n = copy_node(left_margin_kern)
- if not glyph then
- report_nodes("invalid pointer to left margin glyph node")
- elseif glyph.id ~= glyph_code then
- report_nodes("invalid node type %a for %s margin glyph node",nodecodes[glyph],"left")
- else
- n.glyph = glyph
- end
- if width then
- n.width = width
- end
- return n
-end
-
-function pool.rightmarginkern(glyph,width)
- local n = copy_node(right_margin_kern)
- if not glyph then
- report_nodes("invalid pointer to right margin glyph node")
- elseif glyph.id ~= glyph_code then
- report_nodes("invalid node type %a for %s margin glyph node",nodecodes[p],"right")
- else
- n.glyph = glyph
- end
- if width then
- n.width = width
- end
- return n
-end
-
-function pool.temp()
- return copy_node(temp)
-end
-
-function pool.noad()
- return copy_node(noad)
-end
-
-function pool.hlist()
- return copy_node(hlist)
-end
-
-function pool.vlist()
- return copy_node(vlist)
-end
-
---[[
-<p>At some point we ran into a problem that the glue specification
-of the zeropoint dimension was overwritten when adapting a glue spec
-node. This is a side effect of glue specs being shared. After a
-couple of hours tracing and debugging Taco and I came to the
-conclusion that it made no sense to complicate the spec allocator
-and settled on a writable flag. This all is a side effect of the
-fact that some glues use reserved memory slots (with the zeropoint
-glue being a noticeable one). So, next we wrap this into a function
-and hide it for the user. And yes, LuaTeX now gives a warning as
-well.</p>
-]]--
-
-function nodes.writable_spec(n) -- not pool
- local spec = n.spec
- if not spec then
- spec = copy_node(glue_spec)
- n.spec = spec
- elseif not spec.writable then
- spec = copy_node(spec)
- n.spec = spec
- end
- return spec
-end
-
--- local num = userids["my id"]
--- local str = userids[num]
-
-local userids = allocate() pool.userids = userids
-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 pool.usernumber(id,num)
- local n = copy_node(user_n)
- if num then
- n.user_id, n.value = id, num
- elseif id then
- n.value = id
- end
- return n
-end
-
-function pool.userlist(id,list)
- local n = copy_node(user_l)
- if list then
- n.user_id, n.value = id, list
- else
- n.value = id
- end
- return n
-end
-
-function pool.userstring(id,str)
- local n = copy_node(user_s)
- if str then
- n.user_id, n.value = id, str
- else
- n.value = id
- end
- return n
-end
-
-function pool.usertokens(id,tokens)
- local n = copy_node(user_t)
- if tokens then
- n.user_id, n.value = id, tokens
- else
- n.value = id
- end
- return n
-end
-
-function pool.special(str)
- local n = copy_node(special)
- n.data = str
- return n
-end
-
-statistics.register("cleaned up reserved nodes", function()
- return format("%s nodes, %s lists of %s", pool.cleanup(tex.count["c_syst_last_allocated_box"]))
-end) -- \topofboxstack
-
-statistics.register("node memory usage", function() -- comes after cleanup !
- return status.node_mem_usage
-end)
-
-lua.registerfinalizer(pool.cleanup, "cleanup reserved nodes")
+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 gmatch, format = string.gmatch, string.format
+local tonumber, round = tonumber, math.round
+
+--[[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 report_nodes = logs.reporter("nodes","housekeeping")
+
+local nodes, node = nodes, node
+
+local copy_node = node.copy
+local free_node = node.free
+local free_list = node.flush_list
+local new_node = node.new
+
+nodes.pool = nodes.pool or { }
+local pool = nodes.pool
+
+local whatsitcodes = nodes.whatsitcodes
+local skipcodes = nodes.skipcodes
+local kerncodes = nodes.kerncodes
+local nodecodes = nodes.nodecodes
+
+local glyph_code = nodecodes.glyph
+
+local allocate = utilities.storage.allocate
+
+local reserved, nofreserved = { }, 0
+
+local function register_node(n)
+ nofreserved = nofreserved + 1
+ reserved[nofreserved] = n
+ return n
+end
+
+pool.register = register_node
+
+function pool.cleanup(nofboxes) -- todo
+ if nodes.tracers.steppers then -- to be resolved
+ nodes.tracers.steppers.reset() -- todo: make a registration subsystem
+ end
+ local nl, nr = 0, nofreserved
+ for i=1,nofreserved do
+ local ri = reserved[i]
+ -- if not (ri.id == glue_spec and not ri.is_writable) then
+ free_node(reserved[i])
+ -- end
+ end
+ if nofboxes then
+ local tb = tex.box
+ for i=0,nofboxes do
+ local l = tb[i]
+ if l then
+ free_node(tb[i])
+ nl = nl + 1
+ end
+ end
+ end
+ reserved = { }
+ nofreserved = 0
+ return nr, nl, nofboxes -- can be nil
+end
+
+function pool.usage()
+ local t = { }
+ for n, tag in gmatch(status.node_mem_usage,"(%d+) ([a-z_]+)") do
+ t[tag] = n
+ end
+ return t
+end
+
+local disc = register_node(new_node("disc"))
+local kern = register_node(new_node("kern",kerncodes.userkern))
+local fontkern = register_node(new_node("kern",kerncodes.fontkern))
+local penalty = register_node(new_node("penalty"))
+local glue = register_node(new_node("glue")) -- glue.spec = nil
+local glue_spec = register_node(new_node("glue_spec"))
+local glyph = register_node(new_node("glyph",0))
+local textdir = register_node(new_node("whatsit",whatsitcodes.dir))
+local latelua = register_node(new_node("whatsit",whatsitcodes.latelua))
+local special = register_node(new_node("whatsit",whatsitcodes.special))
+local user_n = register_node(new_node("whatsit",whatsitcodes.userdefined)) user_n.type = 100 -- 44
+local user_l = register_node(new_node("whatsit",whatsitcodes.userdefined)) user_l.type = 110 -- 44
+local user_s = register_node(new_node("whatsit",whatsitcodes.userdefined)) user_s.type = 115 -- 44
+local user_t = register_node(new_node("whatsit",whatsitcodes.userdefined)) user_t.type = 116 -- 44
+local left_margin_kern = register_node(new_node("margin_kern",0))
+local right_margin_kern = register_node(new_node("margin_kern",1))
+local lineskip = register_node(new_node("glue",skipcodes.lineskip))
+local baselineskip = register_node(new_node("glue",skipcodes.baselineskip))
+local leftskip = register_node(new_node("glue",skipcodes.leftskip))
+local rightskip = register_node(new_node("glue",skipcodes.rightskip))
+local temp = register_node(new_node("temp",0))
+local noad = register_node(new_node("noad"))
+
+-- the dir field needs to be set otherwise crash:
+
+local rule = register_node(new_node("rule")) rule .dir = "TLT"
+local hlist = register_node(new_node("hlist")) hlist.dir = "TLT"
+local vlist = register_node(new_node("vlist")) vlist.dir = "TLT"
+
+function pool.zeroglue(n)
+ local s = n.spec
+ return not writable or (
+ s.width == 0
+ and s.stretch == 0
+ and s.shrink == 0
+ and s.stretch_order == 0
+ and s.shrink_order == 0
+ )
+end
+
+function pool.glyph(fnt,chr)
+ local n = copy_node(glyph)
+ if fnt then n.font = fnt end
+ if chr then n.char = chr end
+ return n
+end
+
+function pool.penalty(p)
+ local n = copy_node(penalty)
+ n.penalty = p
+ return n
+end
+
+function pool.kern(k)
+ local n = copy_node(kern)
+ n.kern = k
+ return n
+end
+
+function pool.fontkern(k)
+ local n = copy_node(fontkern)
+ n.kern = k
+ return n
+end
+
+function pool.gluespec(width,stretch,shrink,stretch_order,shrink_order)
+ local s = copy_node(glue_spec)
+ if width then s.width = width end
+ if stretch then s.stretch = stretch end
+ if shrink then s.shrink = shrink end
+ if stretch_order then s.stretch_order = stretch_order end
+ if shrink_order then s.shrink_order = shrink_order end
+ return s
+end
+
+local function someskip(skip,width,stretch,shrink,stretch_order,shrink_order)
+ local n = copy_node(skip)
+ if not width then
+ -- no spec
+ elseif width == false or tonumber(width) then
+ local s = copy_node(glue_spec)
+ if width then s.width = width end
+ if stretch then s.stretch = stretch end
+ if shrink then s.shrink = shrink end
+ if stretch_order then s.stretch_order = stretch_order end
+ if shrink_order then s.shrink_order = shrink_order end
+ n.spec = s
+ else
+ -- shared
+ n.spec = copy_node(width)
+ end
+ return n
+end
+
+function pool.stretch(a,b)
+ local n = copy_node(glue)
+ local s = copy_node(glue_spec)
+ if b then
+ s.stretch = a
+ s.stretch_order = b
+ else
+ s.stretch = 1
+ s.stretch_order = a or 1
+ end
+ n.spec = s
+ return n
+end
+
+function pool.shrink(a,b)
+ local n = copy_node(glue)
+ local s = copy_node(glue_spec)
+ if b then
+ s.shrink = a
+ s.shrink_order = b
+ else
+ s.shrink = 1
+ s.shrink_order = a or 1
+ end
+ n.spec = s
+ return n
+end
+
+
+function pool.glue(width,stretch,shrink,stretch_order,shrink_order)
+ return someskip(glue,width,stretch,shrink,stretch_order,shrink_order)
+end
+
+function pool.leftskip(width,stretch,shrink,stretch_order,shrink_order)
+ return someskip(leftskip,width,stretch,shrink,stretch_order,shrink_order)
+end
+
+function pool.rightskip(width,stretch,shrink,stretch_order,shrink_order)
+ return someskip(rightskip,width,stretch,shrink,stretch_order,shrink_order)
+end
+
+function pool.lineskip(width,stretch,shrink,stretch_order,shrink_order)
+ return someskip(lineskip,width,stretch,shrink,stretch_order,shrink_order)
+end
+
+function pool.baselineskip(width,stretch,shrink)
+ return someskip(baselineskip,width,stretch,shrink)
+end
+
+function pool.disc()
+ return copy_node(disc)
+end
+
+function pool.textdir(dir)
+ local t = copy_node(textdir)
+ t.dir = dir
+ return t
+end
+
+function pool.rule(width,height,depth,dir) -- w/h/d == nil will let them adapt
+ local n = copy_node(rule)
+ if width then n.width = width end
+ if height then n.height = height end
+ if depth then n.depth = depth end
+ if dir then n.dir = dir end
+ return n
+end
+
+if node.has_field(latelua,'string') then
+ function pool.latelua(code)
+ local n = copy_node(latelua)
+ n.string = code
+ return n
+ end
+else
+ function pool.latelua(code)
+ local n = copy_node(latelua)
+ n.data = code
+ return n
+ end
+end
+
+function pool.leftmarginkern(glyph,width)
+ local n = copy_node(left_margin_kern)
+ if not glyph then
+ report_nodes("invalid pointer to left margin glyph node")
+ elseif glyph.id ~= glyph_code then
+ report_nodes("invalid node type %a for %s margin glyph node",nodecodes[glyph],"left")
+ else
+ n.glyph = glyph
+ end
+ if width then
+ n.width = width
+ end
+ return n
+end
+
+function pool.rightmarginkern(glyph,width)
+ local n = copy_node(right_margin_kern)
+ if not glyph then
+ report_nodes("invalid pointer to right margin glyph node")
+ elseif glyph.id ~= glyph_code then
+ report_nodes("invalid node type %a for %s margin glyph node",nodecodes[p],"right")
+ else
+ n.glyph = glyph
+ end
+ if width then
+ n.width = width
+ end
+ return n
+end
+
+function pool.temp()
+ return copy_node(temp)
+end
+
+function pool.noad()
+ return copy_node(noad)
+end
+
+function pool.hlist()
+ return copy_node(hlist)
+end
+
+function pool.vlist()
+ return copy_node(vlist)
+end
+
+--[[
+<p>At some point we ran into a problem that the glue specification
+of the zeropoint dimension was overwritten when adapting a glue spec
+node. This is a side effect of glue specs being shared. After a
+couple of hours tracing and debugging Taco and I came to the
+conclusion that it made no sense to complicate the spec allocator
+and settled on a writable flag. This all is a side effect of the
+fact that some glues use reserved memory slots (with the zeropoint
+glue being a noticeable one). So, next we wrap this into a function
+and hide it for the user. And yes, LuaTeX now gives a warning as
+well.</p>
+]]--
+
+function nodes.writable_spec(n) -- not pool
+ local spec = n.spec
+ if not spec then
+ spec = copy_node(glue_spec)
+ n.spec = spec
+ elseif not spec.writable then
+ spec = copy_node(spec)
+ n.spec = spec
+ end
+ return spec
+end
+
+-- local num = userids["my id"]
+-- local str = userids[num]
+
+local userids = allocate() pool.userids = userids
+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 pool.usernumber(id,num)
+ local n = copy_node(user_n)
+ if num then
+ n.user_id, n.value = id, num
+ elseif id then
+ n.value = id
+ end
+ return n
+end
+
+function pool.userlist(id,list)
+ local n = copy_node(user_l)
+ if list then
+ n.user_id, n.value = id, list
+ else
+ n.value = id
+ end
+ return n
+end
+
+function pool.userstring(id,str)
+ local n = copy_node(user_s)
+ if str then
+ n.user_id, n.value = id, str
+ else
+ n.value = id
+ end
+ return n
+end
+
+function pool.usertokens(id,tokens)
+ local n = copy_node(user_t)
+ if tokens then
+ n.user_id, n.value = id, tokens
+ else
+ n.value = id
+ end
+ return n
+end
+
+function pool.special(str)
+ local n = copy_node(special)
+ n.data = str
+ return n
+end
+
+statistics.register("cleaned up reserved nodes", function()
+ return format("%s nodes, %s lists of %s", pool.cleanup(tex.count["c_syst_last_allocated_box"]))
+end) -- \topofboxstack
+
+statistics.register("node memory usage", function() -- comes after cleanup !
+ return status.node_mem_usage
+end)
+
+lua.registerfinalizer(pool.cleanup, "cleanup reserved nodes")
diff --git a/tex/context/base/node-rul.lua b/tex/context/base/node-rul.lua
index 00039550c..09300964e 100644
--- a/tex/context/base/node-rul.lua
+++ b/tex/context/base/node-rul.lua
@@ -1,389 +1,389 @@
-if not modules then modules = { } end modules ['node-rul'] = {
- version = 1.001,
- 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
-
-local attributes, nodes, node = attributes, nodes, node
-
-local nodecodes = nodes.nodecodes
-local tasks = nodes.tasks
-
-local glyph_code = nodecodes.glyph
-local disc_code = nodecodes.disc
-local rule_code = nodecodes.rule
-
-function nodes.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 = first.id
- if id == glyph_code or id == disc_code then -- or id == rule_code
- break
- else
- first = first.next
- 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 = last.id
- if id == glyph_code or id == disc_code then -- or id == rule_code
- break
- else
- local prev = last.prev -- luatex < 0.70 has italic correction kern not prev'd
- if prev then
- last = last.prev
- else
- break
- end
- end
- end
- if not last then
- return nil, nil
- end
- end
- return first, last
-end
-
--- todo: order and maybe other dimensions
-
-local floor = math.floor
-
-local trace_ruled = false trackers.register("nodes.rules", function(v) trace_ruled = v end)
-local report_ruled = logs.reporter("nodes","rules")
-
-local n_tostring = nodes.idstostring
-local n_tosequence = nodes.tosequence
-
-local a_ruled = attributes.private('ruled')
-local a_color = attributes.private('color')
-local a_transparency = attributes.private('transparency')
-local a_colorspace = attributes.private('colormodel')
-
-local insert_node_before = node.insert_before
-local insert_node_after = node.insert_after
-local striprange = nodes.striprange
-local list_dimensions = node.dimensions
-
-local hpack_nodes = node.hpack
-
-local fontdata = fonts.hashes.identifiers
-local variables = interfaces.variables
-local dimenfactor = fonts.helpers.dimenfactor
-local splitdimen = number.splitdimen
-
-local nodecodes = nodes.nodecodes
-local skipcodes = nodes.skipcodes
-local whatcodes = nodes.whatcodes
-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
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-local rule_code = nodecodes.rule
-local whatsit_code = nodecodes.whatsit
-
-local userskip_code = skipcodes.userskip
-local spaceskip_code = skipcodes.spaceskip
-local xspaceskip_code = skipcodes.xspaceskip
-
-local dir_code = whatcodes.dir
-
-local kerning_code = kerncodes.kern
-
-local nodepool = nodes.pool
-
-local new_rule = nodepool.rule
-local new_kern = nodepool.kern
-local new_glue = nodepool.glue
-
--- we can use this one elsewhere too
---
--- todo: functions: word, sentence
---
--- glyph rule unset whatsit glue margin_kern kern math disc
-
-local checkdir = true
-
--- 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
-
-local function processwords(attribute,data,flush,head,parent) -- we have hlistdir and local dir
- local n = head
- if n then
- local f, l, a, d, i, class
- local continue, done, strip, level = false, false, true, -1
- while n do
- local id = n.id
- if id == glyph_code or id == rule_code then
- local aa = n[attribute]
- if aa 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
---~ 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]
- continue = d.continue == variables.yes
- end
- else
- if f then
- head, done = flush(head,f,l,d,level,parent,strip), true
- end
- f, l, a = nil, nil, nil
- end
- elseif f and (id == disc_code or (id == kern_code and n.subtype == kerning_code)) then
- l = n
- 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 = n.list
- if list then
- n.list = processwords(attribute,data,flush,list,n)
- end
- elseif checkdir and id == whatsit_code and n.subtype == dir_code then -- only changes in dir, we assume proper boundaries
- if f and a 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 = n.subtype
- if continue and n[attribute] and
- (subtype == userskip_code or subtype == spaceskip_code or subskip == xspaceskip_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 = n.next
- 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
-
-nodes.processwords = processwords
-
---
-
-nodes.rules = nodes.rules or { }
-nodes.rules.data = nodes.rules.data or { }
-
-storage.register("nodes/rules/data", nodes.rules.data, "nodes.rules.data")
-
-local data = nodes.rules.data
-
-function nodes.rules.define(settings)
- data[#data+1] = settings
- context(#data)
-end
-
-local a_viewerlayer = attributes.private("viewerlayer")
-
-local function flush_ruled(head,f,l,d,level,parent,strip) -- not that fast but acceptable for this purpose
--- check for f and l
- if f.id ~= glyph_code 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 w = list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,f,l.next)
- local method, offset, continue, dy, order, max = d.method, d.offset, d.continue, d.dy, d.order, d.max
- local rulethickness, unit = d.rulethickness, d.unit
- local ma, ca, ta = d.ma, d.ca, d.ta
- local colorspace = (ma > 0 and ma) or f[a_colorspace] or 1
- local color = (ca > 0 and ca) or f[a_color]
- local transparency = (ta > 0 and ta) or f[a_transparency]
- local foreground = order == variables.foreground
-
- local e = dimenfactor(unit,fontdata[f.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[f.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+rulethickness)*e/2
- m = (offset+(level-1)*dy)*e/2 + rulethickness/2
- else
- m = 0
- end
- for i=1,level do
--- local ht = (offset+(i-1)*dy+rulethickness)*e - m
--- local dp = -(offset+(i-1)*dy-rulethickness)*e + m
- local ht = (offset+(i-1)*dy)*e + rulethickness - m
- local dp = -(offset+(i-1)*dy)*e + rulethickness + m
- local r = new_rule(w,ht,dp)
- local v = f[a_viewerlayer]
- -- quick hack
- if v then
- r[a_viewerlayer] = v
- end
- --
- if color then
- r[a_colorspace] = colorspace
- r[a_color] = color
- end
- if transparency then
- r[a_transparency] = transparency
- end
- local k = new_kern(-w)
- 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
- if trace_ruled then
- report_ruled("level %a, width %p, height %p, depth %p, nodes %a, text %a",
- level,w,ht,dp,n_tostring(f,l),n_tosequence(f,l,true))
- end
- end
- return head
-end
-
-local process = nodes.processwords
-
-nodes.rules.handler = function(head) return process(a_ruled,data,flush_ruled,head) end
-
-function nodes.rules.enable()
- tasks.enableaction("shipouts","nodes.rules.handler")
-end
-
--- elsewhere:
---
--- tasks.appendaction ("shipouts", "normalizers", "nodes.rules.handler")
--- tasks.disableaction("shipouts", "nodes.rules.handler") -- only kick in when used
-
-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')
-
-nodes.shifts = nodes.shifts or { }
-nodes.shifts.data = nodes.shifts.data or { }
-
-storage.register("nodes/shifts/data", nodes.shifts.data, "nodes.shifts.data")
-
-local data = nodes.shifts.data
-
-function nodes.shifts.define(settings)
- data[#data+1] = settings
- context(#data)
-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, next = first.prev, last.next
- first.prev, last.next = nil, nil
- local width, height, depth = list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,first,next)
- local list = hpack_nodes(first,width,"exactly")
- if first == head then
- head = list
- end
- if prev then
- prev.next, list.prev = list, prev
- end
- if next then
- next.prev, list.next = list, next
- end
- local raise = data.dy * dimenfactor(data.unit,fontdata[first.font])
- list.shift, list.height, list.depth = raise, 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
-
-local process = nodes.processwords
-
-nodes.shifts.handler = function(head) return process(a_shifted,data,flush_shifted,head) end
-
-function nodes.shifts.enable()
- tasks.enableaction("shipouts","nodes.shifts.handler")
-end
+if not modules then modules = { } end modules ['node-rul'] = {
+ version = 1.001,
+ 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
+
+local attributes, nodes, node = attributes, nodes, node
+
+local nodecodes = nodes.nodecodes
+local tasks = nodes.tasks
+
+local glyph_code = nodecodes.glyph
+local disc_code = nodecodes.disc
+local rule_code = nodecodes.rule
+
+function nodes.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 = first.id
+ if id == glyph_code or id == disc_code then -- or id == rule_code
+ break
+ else
+ first = first.next
+ 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 = last.id
+ if id == glyph_code or id == disc_code then -- or id == rule_code
+ break
+ else
+ local prev = last.prev -- luatex < 0.70 has italic correction kern not prev'd
+ if prev then
+ last = last.prev
+ else
+ break
+ end
+ end
+ end
+ if not last then
+ return nil, nil
+ end
+ end
+ return first, last
+end
+
+-- todo: order and maybe other dimensions
+
+local floor = math.floor
+
+local trace_ruled = false trackers.register("nodes.rules", function(v) trace_ruled = v end)
+local report_ruled = logs.reporter("nodes","rules")
+
+local n_tostring = nodes.idstostring
+local n_tosequence = nodes.tosequence
+
+local a_ruled = attributes.private('ruled')
+local a_color = attributes.private('color')
+local a_transparency = attributes.private('transparency')
+local a_colorspace = attributes.private('colormodel')
+
+local insert_node_before = node.insert_before
+local insert_node_after = node.insert_after
+local striprange = nodes.striprange
+local list_dimensions = node.dimensions
+
+local hpack_nodes = node.hpack
+
+local fontdata = fonts.hashes.identifiers
+local variables = interfaces.variables
+local dimenfactor = fonts.helpers.dimenfactor
+local splitdimen = number.splitdimen
+
+local nodecodes = nodes.nodecodes
+local skipcodes = nodes.skipcodes
+local whatcodes = nodes.whatcodes
+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
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local rule_code = nodecodes.rule
+local whatsit_code = nodecodes.whatsit
+
+local userskip_code = skipcodes.userskip
+local spaceskip_code = skipcodes.spaceskip
+local xspaceskip_code = skipcodes.xspaceskip
+
+local dir_code = whatcodes.dir
+
+local kerning_code = kerncodes.kern
+
+local nodepool = nodes.pool
+
+local new_rule = nodepool.rule
+local new_kern = nodepool.kern
+local new_glue = nodepool.glue
+
+-- we can use this one elsewhere too
+--
+-- todo: functions: word, sentence
+--
+-- glyph rule unset whatsit glue margin_kern kern math disc
+
+local checkdir = true
+
+-- 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
+
+local function processwords(attribute,data,flush,head,parent) -- we have hlistdir and local dir
+ local n = head
+ if n then
+ local f, l, a, d, i, class
+ local continue, done, strip, level = false, false, true, -1
+ while n do
+ local id = n.id
+ if id == glyph_code or id == rule_code then
+ local aa = n[attribute]
+ if aa 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
+--~ 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]
+ continue = d.continue == variables.yes
+ end
+ else
+ if f then
+ head, done = flush(head,f,l,d,level,parent,strip), true
+ end
+ f, l, a = nil, nil, nil
+ end
+ elseif f and (id == disc_code or (id == kern_code and n.subtype == kerning_code)) then
+ l = n
+ 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 = n.list
+ if list then
+ n.list = processwords(attribute,data,flush,list,n)
+ end
+ elseif checkdir and id == whatsit_code and n.subtype == dir_code then -- only changes in dir, we assume proper boundaries
+ if f and a 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 = n.subtype
+ if continue and n[attribute] and
+ (subtype == userskip_code or subtype == spaceskip_code or subskip == xspaceskip_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 = n.next
+ 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
+
+nodes.processwords = processwords
+
+--
+
+nodes.rules = nodes.rules or { }
+nodes.rules.data = nodes.rules.data or { }
+
+storage.register("nodes/rules/data", nodes.rules.data, "nodes.rules.data")
+
+local data = nodes.rules.data
+
+function nodes.rules.define(settings)
+ data[#data+1] = settings
+ context(#data)
+end
+
+local a_viewerlayer = attributes.private("viewerlayer")
+
+local function flush_ruled(head,f,l,d,level,parent,strip) -- not that fast but acceptable for this purpose
+-- check for f and l
+ if f.id ~= glyph_code 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 w = list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,f,l.next)
+ local method, offset, continue, dy, order, max = d.method, d.offset, d.continue, d.dy, d.order, d.max
+ local rulethickness, unit = d.rulethickness, d.unit
+ local ma, ca, ta = d.ma, d.ca, d.ta
+ local colorspace = (ma > 0 and ma) or f[a_colorspace] or 1
+ local color = (ca > 0 and ca) or f[a_color]
+ local transparency = (ta > 0 and ta) or f[a_transparency]
+ local foreground = order == variables.foreground
+
+ local e = dimenfactor(unit,fontdata[f.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[f.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+rulethickness)*e/2
+ m = (offset+(level-1)*dy)*e/2 + rulethickness/2
+ else
+ m = 0
+ end
+ for i=1,level do
+-- local ht = (offset+(i-1)*dy+rulethickness)*e - m
+-- local dp = -(offset+(i-1)*dy-rulethickness)*e + m
+ local ht = (offset+(i-1)*dy)*e + rulethickness - m
+ local dp = -(offset+(i-1)*dy)*e + rulethickness + m
+ local r = new_rule(w,ht,dp)
+ local v = f[a_viewerlayer]
+ -- quick hack
+ if v then
+ r[a_viewerlayer] = v
+ end
+ --
+ if color then
+ r[a_colorspace] = colorspace
+ r[a_color] = color
+ end
+ if transparency then
+ r[a_transparency] = transparency
+ end
+ local k = new_kern(-w)
+ 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
+ if trace_ruled then
+ report_ruled("level %a, width %p, height %p, depth %p, nodes %a, text %a",
+ level,w,ht,dp,n_tostring(f,l),n_tosequence(f,l,true))
+ end
+ end
+ return head
+end
+
+local process = nodes.processwords
+
+nodes.rules.handler = function(head) return process(a_ruled,data,flush_ruled,head) end
+
+function nodes.rules.enable()
+ tasks.enableaction("shipouts","nodes.rules.handler")
+end
+
+-- elsewhere:
+--
+-- tasks.appendaction ("shipouts", "normalizers", "nodes.rules.handler")
+-- tasks.disableaction("shipouts", "nodes.rules.handler") -- only kick in when used
+
+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')
+
+nodes.shifts = nodes.shifts or { }
+nodes.shifts.data = nodes.shifts.data or { }
+
+storage.register("nodes/shifts/data", nodes.shifts.data, "nodes.shifts.data")
+
+local data = nodes.shifts.data
+
+function nodes.shifts.define(settings)
+ data[#data+1] = settings
+ context(#data)
+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, next = first.prev, last.next
+ first.prev, last.next = nil, nil
+ local width, height, depth = list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,first,next)
+ local list = hpack_nodes(first,width,"exactly")
+ if first == head then
+ head = list
+ end
+ if prev then
+ prev.next, list.prev = list, prev
+ end
+ if next then
+ next.prev, list.next = list, next
+ end
+ local raise = data.dy * dimenfactor(data.unit,fontdata[first.font])
+ list.shift, list.height, list.depth = raise, 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
+
+local process = nodes.processwords
+
+nodes.shifts.handler = function(head) return process(a_shifted,data,flush_shifted,head) end
+
+function nodes.shifts.enable()
+ tasks.enableaction("shipouts","nodes.shifts.handler")
+end
diff --git a/tex/context/base/node-ser.lua b/tex/context/base/node-ser.lua
index f4ae1e2b2..b0a6e9952 100644
--- a/tex/context/base/node-ser.lua
+++ b/tex/context/base/node-ser.lua
@@ -1,286 +1,286 @@
-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"
-}
-
--- beware, some field names will change in a next releases
--- of luatex; this is pretty old code that needs an overhaul
-
-local type, format, rep = type, string.format, string.rep
-local concat, tohash, sortedkeys, printtable = table.concat, table.tohash, table.sortedkeys, table.print
-
-local allocate = utilities.storage.allocate
-
-local nodes, node = nodes, node
-
-local traverse = node.traverse
-local is_node = node.is_node
-
-local nodecodes = nodes.nodecodes
-local noadcodes = nodes.noadcodes
-local nodefields = nodes.fields
-
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-
-local expand = allocate ( tohash {
- "list", -- list_ptr & ins_ptr & adjust_ptr
- "pre", --
- "post", --
- "spec", -- glue_ptr
- "top_skip", --
- "attr", --
- "replace", -- nobreak
- "components", -- lig_ptr
- "box_left", --
- "box_right", --
- "glyph", -- margin_char
- "leader", -- leader_ptr
- "action", -- action_ptr
- "value", -- user_defined nodes with subtype 'a' en 'n'
- "head",
-} )
-
--- page_insert: "height", "last_ins_ptr", "best_ins_ptr"
--- split_insert: "height", "last_ins_ptr", "best_ins_ptr", "broken_ptr", "broken_ins"
-
-local ignore = allocate ( tohash {
- "page_insert",
- "split_insert",
- "ref_count",
-} )
-
-local dimension = allocate ( tohash {
- "width", "height", "depth", "shift",
- "stretch", "shrink",
- "xoffset", "yoffset",
- "surround",
- "kern",
- "box_left_width", "box_right_width"
-} )
-
--- flat: don't use next, but indexes
--- verbose: also add type
--- can be sped up
-
-nodes.dimensionfields = dimension
-nodes.listablefields = expand
-nodes.ignorablefields = ignore
-
--- not ok yet:
-
-local function astable(n,sparse) -- not yet ok
- local f, t = nodefields(n), { }
- for i=1,#f do
- local v = f[i]
- local d = n[v]
- if d then
- if ignore[v] or v == "id" then
- -- skip
- elseif expand[v] then -- or: type(n[v]) ~= "string" or type(n[v]) ~= "number" or type(n[v]) ~= "table"
- t[v] = "pointer to list"
- elseif sparse then
- if (type(d) == "number" and d ~= 0) or (type(d) == "string" and d ~= "") then
- t[v] = d
- end
- else
- t[v] = d
- end
- end
- end
- t.type = nodecodes[n.id]
- return t
-end
-
-nodes.astable = astable
-
-setinspector(function(v) if is_node(v) then printtable(astable(v),tostring(v)) return true end end)
-
--- under construction:
-
-local function totable(n,flat,verbose,noattributes)
- -- todo: no local function
- local function to_table(n,flat,verbose,noattributes) -- no need to pass
- local f = nodefields(n)
- local tt = { }
- for k=1,#f do
- local v = f[k]
- local nv = v and n[v]
- if nv then
- if ignore[v] then
- -- skip
- elseif noattributes and v == "attr" then
- -- skip
- elseif expand[v] then
- if type(nv) == "number" or type(nv) == "string" then
- tt[v] = nv
- else
- tt[v] = totable(nv,flat,verbose)
- end
- elseif type(nv) == "table" then
- tt[v] = nv -- totable(nv,flat,verbose) -- data
- else
- tt[v] = nv
- end
- end
- end
- if verbose then
- tt.type = nodecodes[tt.id]
- end
- return tt
- end
- if n then
- if flat then
- local t, tn = { }, 0
- while n do
- tn = tn + 1
- t[tn] = to_table(n,flat,verbose,noattributes)
- n = n.next
- end
- return t
- else
- local t = to_table(n)
- if n.next then
- t.next = totable(n.next,flat,verbose,noattributes)
- end
- return t
- end
- else
- return { }
- end
-end
-
-nodes.totable = totable
-
-local function key(k)
- return ((type(k) == "number") and "["..k.."]") or k
-end
-
--- not ok yet; this will become a module
-
--- todo: adapt to nodecodes etc
-
-local function serialize(root,name,handle,depth,m,noattributes)
- handle = handle or print
- if depth then
- depth = depth .. " "
- handle(format("%s%s={",depth,key(name)))
- else
- depth = ""
- local tname = type(name)
- if tname == "string" then
- if name == "return" then
- handle("return {")
- else
- handle(name .. "={")
- end
- elseif tname == "number" then
- handle("[" .. name .. "]={")
- else
- handle("t={")
- end
- end
- if root then
- local fld
- if root.id then
- fld = nodefields(root) -- we can cache these (todo)
- else
- fld = sortedkeys(root)
- end
- if type(root) == 'table' and root['type'] then -- userdata or table
- handle(format("%s %s=%q,",depth,'type',root['type']))
- end
- for f=1,#fld do
- local k = fld[f]
- if k == "ref_count" then
- -- skip
- elseif noattributes and k == "attr" then
- -- skip
- elseif k == "id" then
- local v = root[k]
- handle(format("%s id=%s,",depth,nodecodes[v] or noadcodes[v] or v))
- elseif k then
- local v = root[k]
- local t = type(v)
- if t == "number" then
- if v == 0 then
- -- skip
- else
- handle(format("%s %s=%s,",depth,key(k),v))
- end
- elseif t == "string" then
- if v == "" then
- -- skip
- else
- handle(format("%s %s=%q,",depth,key(k),v))
- end
- elseif t == "boolean" then
- handle(format("%s %s=%q,",depth,key(k),tostring(v)))
- elseif v then -- userdata or table
- serialize(v,k,handle,depth,m+1,noattributes)
- end
- end
- end
- if root['next'] then -- userdata or table
- serialize(root['next'],'next',handle,depth,m+1,noattributes)
- end
- end
- if m and m > 0 then
- handle(format("%s},",depth))
- else
- handle(format("%s}",depth))
- end
-end
-
-function nodes.serialize(root,name,noattributes)
- local t, n = { }, 0
- local function flush(s)
- n = n + 1
- t[n] = s
- end
- serialize(root,name,flush,nil,0,noattributes)
- return concat(t,"\n")
-end
-
-function nodes.serializebox(n,flat,verbose,name)
- return nodes.serialize(nodes.totable(tex.box[n],flat,verbose),name)
-end
-
-function nodes.visualizebox(...) -- to be checked .. will move to module anyway
- context.starttyping()
- context.pushcatcodes("verbatim")
- context(nodes.serializebox(...))
- context.stoptyping()
- context.popcatcodes()
-end
-
-function nodes.list(head,n) -- name might change to nodes.type -- to be checked .. will move to module anyway
- 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)
- 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
+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"
+}
+
+-- beware, some field names will change in a next releases
+-- of luatex; this is pretty old code that needs an overhaul
+
+local type, format, rep = type, string.format, string.rep
+local concat, tohash, sortedkeys, printtable = table.concat, table.tohash, table.sortedkeys, table.print
+
+local allocate = utilities.storage.allocate
+
+local nodes, node = nodes, node
+
+local traverse = node.traverse
+local is_node = node.is_node
+
+local nodecodes = nodes.nodecodes
+local noadcodes = nodes.noadcodes
+local nodefields = nodes.fields
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+
+local expand = allocate ( tohash {
+ "list", -- list_ptr & ins_ptr & adjust_ptr
+ "pre", --
+ "post", --
+ "spec", -- glue_ptr
+ "top_skip", --
+ "attr", --
+ "replace", -- nobreak
+ "components", -- lig_ptr
+ "box_left", --
+ "box_right", --
+ "glyph", -- margin_char
+ "leader", -- leader_ptr
+ "action", -- action_ptr
+ "value", -- user_defined nodes with subtype 'a' en 'n'
+ "head",
+} )
+
+-- page_insert: "height", "last_ins_ptr", "best_ins_ptr"
+-- split_insert: "height", "last_ins_ptr", "best_ins_ptr", "broken_ptr", "broken_ins"
+
+local ignore = allocate ( tohash {
+ "page_insert",
+ "split_insert",
+ "ref_count",
+} )
+
+local dimension = allocate ( tohash {
+ "width", "height", "depth", "shift",
+ "stretch", "shrink",
+ "xoffset", "yoffset",
+ "surround",
+ "kern",
+ "box_left_width", "box_right_width"
+} )
+
+-- flat: don't use next, but indexes
+-- verbose: also add type
+-- can be sped up
+
+nodes.dimensionfields = dimension
+nodes.listablefields = expand
+nodes.ignorablefields = ignore
+
+-- not ok yet:
+
+local function astable(n,sparse) -- not yet ok
+ local f, t = nodefields(n), { }
+ for i=1,#f do
+ local v = f[i]
+ local d = n[v]
+ if d then
+ if ignore[v] or v == "id" then
+ -- skip
+ elseif expand[v] then -- or: type(n[v]) ~= "string" or type(n[v]) ~= "number" or type(n[v]) ~= "table"
+ t[v] = "pointer to list"
+ elseif sparse then
+ if (type(d) == "number" and d ~= 0) or (type(d) == "string" and d ~= "") then
+ t[v] = d
+ end
+ else
+ t[v] = d
+ end
+ end
+ end
+ t.type = nodecodes[n.id]
+ return t
+end
+
+nodes.astable = astable
+
+setinspector(function(v) if is_node(v) then printtable(astable(v),tostring(v)) return true end end)
+
+-- under construction:
+
+local function totable(n,flat,verbose,noattributes)
+ -- todo: no local function
+ local function to_table(n,flat,verbose,noattributes) -- no need to pass
+ local f = nodefields(n)
+ local tt = { }
+ for k=1,#f do
+ local v = f[k]
+ local nv = v and n[v]
+ if nv then
+ if ignore[v] then
+ -- skip
+ elseif noattributes and v == "attr" then
+ -- skip
+ elseif expand[v] then
+ if type(nv) == "number" or type(nv) == "string" then
+ tt[v] = nv
+ else
+ tt[v] = totable(nv,flat,verbose)
+ end
+ elseif type(nv) == "table" then
+ tt[v] = nv -- totable(nv,flat,verbose) -- data
+ else
+ tt[v] = nv
+ end
+ end
+ end
+ if verbose then
+ tt.type = nodecodes[tt.id]
+ end
+ return tt
+ end
+ if n then
+ if flat then
+ local t, tn = { }, 0
+ while n do
+ tn = tn + 1
+ t[tn] = to_table(n,flat,verbose,noattributes)
+ n = n.next
+ end
+ return t
+ else
+ local t = to_table(n)
+ if n.next then
+ t.next = totable(n.next,flat,verbose,noattributes)
+ end
+ return t
+ end
+ else
+ return { }
+ end
+end
+
+nodes.totable = totable
+
+local function key(k)
+ return ((type(k) == "number") and "["..k.."]") or k
+end
+
+-- not ok yet; this will become a module
+
+-- todo: adapt to nodecodes etc
+
+local function serialize(root,name,handle,depth,m,noattributes)
+ handle = handle or print
+ if depth then
+ depth = depth .. " "
+ handle(format("%s%s={",depth,key(name)))
+ else
+ depth = ""
+ local tname = type(name)
+ if tname == "string" then
+ if name == "return" then
+ handle("return {")
+ else
+ handle(name .. "={")
+ end
+ elseif tname == "number" then
+ handle("[" .. name .. "]={")
+ else
+ handle("t={")
+ end
+ end
+ if root then
+ local fld
+ if root.id then
+ fld = nodefields(root) -- we can cache these (todo)
+ else
+ fld = sortedkeys(root)
+ end
+ if type(root) == 'table' and root['type'] then -- userdata or table
+ handle(format("%s %s=%q,",depth,'type',root['type']))
+ end
+ for f=1,#fld do
+ local k = fld[f]
+ if k == "ref_count" then
+ -- skip
+ elseif noattributes and k == "attr" then
+ -- skip
+ elseif k == "id" then
+ local v = root[k]
+ handle(format("%s id=%s,",depth,nodecodes[v] or noadcodes[v] or v))
+ elseif k then
+ local v = root[k]
+ local t = type(v)
+ if t == "number" then
+ if v == 0 then
+ -- skip
+ else
+ handle(format("%s %s=%s,",depth,key(k),v))
+ end
+ elseif t == "string" then
+ if v == "" then
+ -- skip
+ else
+ handle(format("%s %s=%q,",depth,key(k),v))
+ end
+ elseif t == "boolean" then
+ handle(format("%s %s=%q,",depth,key(k),tostring(v)))
+ elseif v then -- userdata or table
+ serialize(v,k,handle,depth,m+1,noattributes)
+ end
+ end
+ end
+ if root['next'] then -- userdata or table
+ serialize(root['next'],'next',handle,depth,m+1,noattributes)
+ end
+ end
+ if m and m > 0 then
+ handle(format("%s},",depth))
+ else
+ handle(format("%s}",depth))
+ end
+end
+
+function nodes.serialize(root,name,noattributes)
+ local t, n = { }, 0
+ local function flush(s)
+ n = n + 1
+ t[n] = s
+ end
+ serialize(root,name,flush,nil,0,noattributes)
+ return concat(t,"\n")
+end
+
+function nodes.serializebox(n,flat,verbose,name)
+ return nodes.serialize(nodes.totable(tex.box[n],flat,verbose),name)
+end
+
+function nodes.visualizebox(...) -- to be checked .. will move to module anyway
+ context.starttyping()
+ context.pushcatcodes("verbatim")
+ context(nodes.serializebox(...))
+ context.stoptyping()
+ context.popcatcodes()
+end
+
+function nodes.list(head,n) -- name might change to nodes.type -- to be checked .. will move to module anyway
+ 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)
+ 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
diff --git a/tex/context/base/node-shp.lua b/tex/context/base/node-shp.lua
index 42084a135..8f7a411a7 100644
--- a/tex/context/base/node-shp.lua
+++ b/tex/context/base/node-shp.lua
@@ -1,148 +1,148 @@
-if not modules then modules = { } end modules ['node-shp'] = {
- 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 nodes, node = nodes, node
-
-local next, type = next, type
-local format = string.format
-local concat, sortedpairs = table.concat, table.sortedpairs
-local setmetatableindex = table.setmetatableindex
-
-local nodecodes = nodes.nodecodes
-local tasks = nodes.tasks
-local handlers = nodes.handlers
-
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-local disc_code = nodecodes.disc
-local mark_code = nodecodes.mark
-local kern_code = nodecodes.kern
-local glue_code = nodecodes.glue
-
-local texbox = tex.box
-
-local free_node = node.free
-local remove_node = node.remove
-local traverse_nodes = node.traverse
-
-local function cleanup(head) -- rough
- local start = head
- while start do
- local id = start.id
- if id == disc_code or (id == glue_code and not start.writable) or (id == kern_code and start.kern == 0) or id == mark_code then
- head, start, tmp = remove_node(head,start)
- free_node(tmp)
- elseif id == hlist_code or id == vlist_code then
- local sl = start.list
- if sl then
- start.list = cleanup(sl)
- start = start.next
- else
- head, start, tmp = remove_node(head,start)
- free_node(tmp)
- end
- else
- start = start.next
- end
- end
- return head
-end
-
-directives.register("backend.cleanup", function()
- tasks.enableaction("shipouts","nodes.handlers.cleanuppage")
-end)
-
-function handlers.cleanuppage(head)
- -- about 10% of the nodes make no sense for the backend
- return cleanup(head), true
-end
-
-local actions = tasks.actions("shipouts") -- no extra arguments
-
-function handlers.finalize(head) -- problem, attr loaded before node, todo ...
- return actions(head)
-end
-
--- handlers.finalize = actions
-
--- interface
-
-function commands.finalizebox(n)
- actions(texbox[n])
-end
-
--- 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 in traverse_nodes(head) do
- local id = n.id
- local dn = data[nodecodes[n.id]]
- dn[subcategory] = dn[subcategory] + 1
- if id == hlist_code or id == vlist_code then
- count(n.list,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)
+if not modules then modules = { } end modules ['node-shp'] = {
+ 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 nodes, node = nodes, node
+
+local next, type = next, type
+local format = string.format
+local concat, sortedpairs = table.concat, table.sortedpairs
+local setmetatableindex = table.setmetatableindex
+
+local nodecodes = nodes.nodecodes
+local tasks = nodes.tasks
+local handlers = nodes.handlers
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local disc_code = nodecodes.disc
+local mark_code = nodecodes.mark
+local kern_code = nodecodes.kern
+local glue_code = nodecodes.glue
+
+local texbox = tex.box
+
+local free_node = node.free
+local remove_node = node.remove
+local traverse_nodes = node.traverse
+
+local function cleanup(head) -- rough
+ local start = head
+ while start do
+ local id = start.id
+ if id == disc_code or (id == glue_code and not start.writable) or (id == kern_code and start.kern == 0) or id == mark_code then
+ head, start, tmp = remove_node(head,start)
+ free_node(tmp)
+ elseif id == hlist_code or id == vlist_code then
+ local sl = start.list
+ if sl then
+ start.list = cleanup(sl)
+ start = start.next
+ else
+ head, start, tmp = remove_node(head,start)
+ free_node(tmp)
+ end
+ else
+ start = start.next
+ end
+ end
+ return head
+end
+
+directives.register("backend.cleanup", function()
+ tasks.enableaction("shipouts","nodes.handlers.cleanuppage")
+end)
+
+function handlers.cleanuppage(head)
+ -- about 10% of the nodes make no sense for the backend
+ return cleanup(head), true
+end
+
+local actions = tasks.actions("shipouts") -- no extra arguments
+
+function handlers.finalize(head) -- problem, attr loaded before node, todo ...
+ return actions(head)
+end
+
+-- handlers.finalize = actions
+
+-- interface
+
+function commands.finalizebox(n)
+ actions(texbox[n])
+end
+
+-- 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 in traverse_nodes(head) do
+ local id = n.id
+ local dn = data[nodecodes[n.id]]
+ dn[subcategory] = dn[subcategory] + 1
+ if id == hlist_code or id == vlist_code then
+ count(n.list,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/tex/context/base/node-snp.lua b/tex/context/base/node-snp.lua
index 3a764e90a..31c7771ac 100644
--- a/tex/context/base/node-snp.lua
+++ b/tex/context/base/node-snp.lua
@@ -1,66 +1,66 @@
-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 nodeusage = nodes.pool and nodes.pool.usage
-local clock = os.gettimeofday or os.clock -- should go in environment
-local lasttime = clock()
-local samples = { }
-
-local parameters = {
- "cs_count",
- "dyn_used",
- "elapsed_time",
- "luabytecode_bytes",
- "luastate_bytes",
- "max_buf_stack",
- "obj_ptr",
- "pdf_mem_ptr",
- "pdf_mem_size",
- "pdf_os_cntr",
--- "pool_ptr", -- obsolete
- "str_ptr",
-}
-
-function snapshots.takesample(comment)
- if nodeusage then
- local c = clock()
- local t = {
- elapsed_time = c - lasttime,
- node_memory = nodeusage(),
- comment = comment,
- }
- for i=1,#parameters do
- local parameter = parameters[i]
- local ps = status[parameter]
- if ps then
- t[parameter] = ps
- end
- end
- samples[#samples+1] = t
- lasttime = c
- end
-end
-
-function snapshots.getsamples()
- return samples -- one return value !
-end
-
-function snapshots.resetsamples()
- samples = { }
-end
-
-function snapshots.getparameters()
- return parameters
-end
+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 nodeusage = nodes.pool and nodes.pool.usage
+local clock = os.gettimeofday or os.clock -- should go in environment
+local lasttime = clock()
+local samples = { }
+
+local parameters = {
+ "cs_count",
+ "dyn_used",
+ "elapsed_time",
+ "luabytecode_bytes",
+ "luastate_bytes",
+ "max_buf_stack",
+ "obj_ptr",
+ "pdf_mem_ptr",
+ "pdf_mem_size",
+ "pdf_os_cntr",
+-- "pool_ptr", -- obsolete
+ "str_ptr",
+}
+
+function snapshots.takesample(comment)
+ if nodeusage then
+ local c = clock()
+ local t = {
+ elapsed_time = c - lasttime,
+ node_memory = nodeusage(),
+ comment = comment,
+ }
+ for i=1,#parameters do
+ local parameter = parameters[i]
+ local ps = status[parameter]
+ if ps then
+ t[parameter] = ps
+ end
+ end
+ samples[#samples+1] = t
+ lasttime = c
+ end
+end
+
+function snapshots.getsamples()
+ return samples -- one return value !
+end
+
+function snapshots.resetsamples()
+ samples = { }
+end
+
+function snapshots.getparameters()
+ return parameters
+end
diff --git a/tex/context/base/node-tex.lua b/tex/context/base/node-tex.lua
index 9393eaf79..2170e0603 100644
--- a/tex/context/base/node-tex.lua
+++ b/tex/context/base/node-tex.lua
@@ -1,41 +1,41 @@
-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"
-}
-
-local format = string.format
-
-builders = builders or { }
-builders.kernel = builders.kernel or { }
-local kernel = builders.kernel
-
-local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming
-local hyphenate, ligaturing, kerning = lang.hyphenate, node.ligaturing, node.kerning
-
-function kernel.hyphenation(head)
- -- starttiming(kernel)
- local done = hyphenate(head)
- -- stoptiming(kernel)
- return head, done
-end
-
-function kernel.ligaturing(head)
- -- starttiming(kernel)
- local head, tail, done = ligaturing(head) -- todo: check what is returned
- -- stoptiming(kernel)
- return head, done
-end
-
-function kernel.kerning(head)
- -- starttiming(kernel)
- local head, tail, done = kerning(head) -- todo: check what is returned
- -- stoptiming(kernel)
- return head, done
-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")
+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"
+}
+
+local format = string.format
+
+builders = builders or { }
+builders.kernel = builders.kernel or { }
+local kernel = builders.kernel
+
+local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming
+local hyphenate, ligaturing, kerning = lang.hyphenate, node.ligaturing, node.kerning
+
+function kernel.hyphenation(head)
+ -- starttiming(kernel)
+ local done = hyphenate(head)
+ -- stoptiming(kernel)
+ return head, done
+end
+
+function kernel.ligaturing(head)
+ -- starttiming(kernel)
+ local head, tail, done = ligaturing(head) -- todo: check what is returned
+ -- stoptiming(kernel)
+ return head, done
+end
+
+function kernel.kerning(head)
+ -- starttiming(kernel)
+ local head, tail, done = kerning(head) -- todo: check what is returned
+ -- stoptiming(kernel)
+ return head, done
+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/tex/context/base/node-tra.lua b/tex/context/base/node-tra.lua
index f194239bb..916b2143d 100644
--- a/tex/context/base/node-tra.lua
+++ b/tex/context/base/node-tra.lua
@@ -1,529 +1,529 @@
-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 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")
-
-nodes = nodes or { }
-
-local nodes, node, context = nodes, node, context
-
-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 traverse_nodes = node.traverse
-local traverse_by_id = node.traverse_id
-local count_nodes = nodes.count
-
-local nodecodes = nodes.nodecodes
-local whatcodes = nodes.whatcodes
-local skipcodes = nodes.skipcodes
-local fillcodes = nodes.fillcodes
-
-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 whatsit_code = nodecodes.whatsit
-local spec_code = nodecodes.glue_spec
-
-local localpar_code = whatcodes.localpar
-local dir_code = whatcodes.dir
-
-local nodepool = nodes.pool
-
-local dimenfactors = number.dimenfactors
-local formatters = string.formatters
-
--- this will be reorganized:
-
-function nodes.showlist(head, message)
- if message then
- report_nodes(message)
- end
- for n in traverse_nodes(head) do
- report_nodes(tostring(n))
- end
-end
-
-function nodes.handlers.checkglyphs(head,message)
- local t = { }
- for g in traverse_by_id(glyph_code,head) do
- t[#t+1] = formatters["%U:%s"](g.char,g.subtype)
- end
- if #t > 0 then
- if message and message ~= "" then
- report_nodes("%s, %s glyphs: % t",message,#t,t)
- else
- report_nodes("%s glyphs: % t",#t,t)
- end
- end
- return false
-end
-
-function nodes.handlers.checkforleaks(sparse)
- local l = { }
- local q = node.usedlist()
- for p in traverse(q) do
- local s = table.serialize(nodes.astable(p,sparse),nodecodes[p.id])
- l[s] = (l[s] or 0) + 1
- end
- node.flush_list(q)
- for k, v in next, l do
- write_nl(formatters["%s * %s"](v,k))
- end
-end
-
-local f_sequence = formatters["U+%04X:%s"]
-
-local function tosequence(start,stop,compact)
- if start then
- local t = { }
- while start do
- local id = start.id
- if id == glyph_code then
- local c = start.char
- if compact then
- if start.components then
- t[#t+1] = tosequence(start.components,nil,compact)
- else
- t[#t+1] = utfchar(c)
- end
- else
- t[#t+1] = f_sequence(c,utfchar(c))
- end
- elseif id == whatsit_code and start.subtype == localpar_code or start.subtype == dir_code then
- t[#t+1] = "[" .. start.dir .. "]"
- elseif id == rule_code then
- if compact then
- t[#t+1] = "|"
- else
- t[#t+1] = nodecodes[id]
- end
- else
- if compact then
- t[#t+1] = "[]"
- else
- t[#t+1] = nodecodes[id]
- end
- end
- if start == stop then
- break
- else
- start = start.next
- end
- end
- if compact then
- return concat(t)
- else
- return concat(t," ")
- end
- else
- return "[empty]"
- end
-end
-
-nodes.tosequence = tosequence
-
-function nodes.report(t,done)
- report_nodes("output %a, %changed %a, %s nodes",status.output_active,done,count_nodes(t))
-end
-
-function nodes.packlist(head)
- local t = { }
- for n in traverse(head) do
- t[#t+1] = tostring(n)
- end
- return t
-end
-
-function nodes.idstostring(head,tail)
- local t, last_id, last_n = { }, nil, 0
- for n in traverse_nodes(head,tail) do -- hm, does not stop at tail
- 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 == tail then
- break
- end
- 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 concat(t," ")
-end
-
--- function nodes.xidstostring(head,tail) -- only for special tracing of backlinks
--- 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 = n.prev
--- 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)
- while h do
- write_nl(rep(" ",n) .. tostring(h))
- if not depth or n < depth then
- local id = h.id
- if id == hlist_code or id == vlist_code then
- showsimplelist(h.list,depth,n+1)
- end
- end
- h = h.next
- end
-end
-
---~ \startluacode
---~ callback.register('buildpage_filter',function() nodes.show_simple_list(tex.lists.contrib_head) end)
---~ \stopluacode
---~ \vbox{b\footnote{n}a}
---~ \startluacode
---~ callback.register('buildpage_filter',nil)
---~ \stopluacode
-
-nodes.showsimplelist = function(h,depth) showsimplelist(h,depth,0) end
-
-local function listtoutf(h,joiner,textonly,last)
- local joiner = (joiner == true and utfchar(0x200C)) or joiner -- zwnj
- local w = { }
- while h do
- local id = h.id
- if id == glyph_code then -- always true
- w[#w+1] = utfchar(h.char)
- if joiner then
- w[#w+1] = joiner
- end
- elseif id == disc_code then
- local pre, rep, pos = h.pre, h.replace, h.post
- w[#w+1] = formatters["[%s|%s|%s]"] (
- pre and listtoutf(pre,joiner,textonly) or "",
- rep and listtoutf(rep,joiner,textonly) or "",
- mid and listtoutf(mid,joiner,textonly) or ""
- )
- elseif textonly then
- if id == glue_code and h.spec and h.spec.width > 0 then
- w[#w+1] = " "
- end
- else
- w[#w+1] = "[-]"
- end
- if h == last then
- break
- else
- h = h.next
- end
- end
- return concat(w)
-end
-
-nodes.listtoutf = listtoutf
-
-local what = { [0] = "unknown", "line", "box", "indent", "row", "cell" }
-
-local function showboxes(n,symbol,depth)
- depth, symbol = depth or 0, symbol or "."
- for n in traverse_nodes(n) do
- local id = n.id
- if id == hlist_code or id == vlist_code then
- local s = n.subtype
- report_nodes(rep(symbol,depth) .. what[s] or s)
- showboxes(n.list,symbol,depth+1)
- end
- end
-end
-
-nodes.showboxes = showboxes
-
-local ptfactor = dimenfactors.pt
-local bpfactor = dimenfactors.bp
-local stripper = lpeg.patterns.stripzeros
-
--- start redefinition
---
--- -- if fmt then
--- -- return formatters[fmt](n*dimenfactors[unit],unit)
--- -- else
--- -- return match(formatters["%.20f"](n*dimenfactors[unit]),"(.-0?)0*$") .. unit
--- -- end
---
--- redefined:
-
-local dimenfactors = number.dimenfactors
-
-local function numbertodimen(d,unit,fmt,strip)
- if not d then
- local str = formatters[fmt](0,unit)
- return strip and lpegmatch(stripper,str) or str
- end
- local t = type(d)
- if t == 'string' then
- return d
- end
- if unit == true then
- unit = "pt"
- fmt = "%0.5f%s"
- else
- unit = unit or 'pt'
- if not fmt then
- fmt = "%s%s"
- elseif fmt == true then
- fmt = "%0.5f%s"
- end
- end
- if t == "number" then
- local str = formatters[fmt](d*dimenfactors[unit],unit)
- return strip and lpegmatch(stripper,str) or str
- end
- local id = node.id
- if id == kern_code then
- local str = formatters[fmt](d.width*dimenfactors[unit],unit)
- return strip and lpegmatch(stripper,str) or str
- end
- if id == glue_code then
- d = d.spec
- end
- if not d or not d.id == spec_code then
- local str = formatters[fmt](0,unit)
- return strip and lpegmatch(stripper,str) or str
- end
- local width = d.width
- local plus = d.stretch_order
- local minus = d.shrink_order
- local stretch = d.stretch
- local shrink = d.shrink
- if plus ~= 0 then
- plus = " plus " .. stretch/65536 .. fillcodes[plus]
- elseif stretch ~= 0 then
- plus = formatters[fmt](stretch*dimenfactors[unit],unit)
- plus = " plus " .. (strip and lpegmatch(stripper,plus) or plus)
- else
- plus = ""
- end
- if minus ~= 0 then
- minus = " minus " .. shrink/65536 .. fillcodes[minus]
- elseif shrink ~= 0 then
- minus = formatters[fmt](shrink*dimenfactors[unit],unit)
- minus = " minus " .. (strip and lpegmatch(stripper,minus) or minus)
- else
- minus = ""
- end
- local str = formatters[fmt](d.width*dimenfactors[unit],unit)
- return (strip and lpegmatch(stripper,str) or str) .. plus .. minus
-end
-
-number.todimen = numbertodimen
-
-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
-function number.tonewdidots (n,fmt) return numbertodimen(n,"nd",fmt) end
-function number.tonewciceros (n,fmt) return numbertodimen(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",true,true) -- 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",true,true) -- 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",true) -- 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
-
-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)
- local mc = m_color[c]
- if not mc then
- n[a_color] = unsetvalue
- else
- if not n[a_colormodel] then
- n[a_colormodel] = s or 1
- end
- n[a_color] = mc
- end
- return n
-end
-
-function colors.setlist(n,c,s)
- local f = n
- while n do
- local mc = m_color[c]
- if not mc then
- n[a_color] = unsetvalue
- else
- if not n[a_colormodel] then
- n[a_colormodel] = s or 1
- end
- n[a_color] = mc
- end
- n = n.next
- end
- return f
-end
-
-function colors.reset(n)
- 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)
- local mt = m_transparency[t]
- if not mt then
- n[a_transparency] = unsetvalue
- else
- n[a_transparency] = mt
- end
- return n
-end
-
-function transparencies.setlist(n,c,s)
- local f = n
- while n do
- local mt = m_transparency[c]
- if not mt then
- n[a_transparency] = unsetvalue
- else
- n[a_transparency] = mt
- end
- n = n.next
- end
- return f
-end
-
-function transparencies.reset(n)
- n[a_transparency] = unsetvalue
- return n
-end
-
--- for the moment here
-
-nodes.visualizers = { }
-
-function nodes.visualizers.handler(head)
- return head, false
-end
+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 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")
+
+nodes = nodes or { }
+
+local nodes, node, context = nodes, node, context
+
+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 traverse_nodes = node.traverse
+local traverse_by_id = node.traverse_id
+local count_nodes = nodes.count
+
+local nodecodes = nodes.nodecodes
+local whatcodes = nodes.whatcodes
+local skipcodes = nodes.skipcodes
+local fillcodes = nodes.fillcodes
+
+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 whatsit_code = nodecodes.whatsit
+local spec_code = nodecodes.glue_spec
+
+local localpar_code = whatcodes.localpar
+local dir_code = whatcodes.dir
+
+local nodepool = nodes.pool
+
+local dimenfactors = number.dimenfactors
+local formatters = string.formatters
+
+-- this will be reorganized:
+
+function nodes.showlist(head, message)
+ if message then
+ report_nodes(message)
+ end
+ for n in traverse_nodes(head) do
+ report_nodes(tostring(n))
+ end
+end
+
+function nodes.handlers.checkglyphs(head,message)
+ local t = { }
+ for g in traverse_by_id(glyph_code,head) do
+ t[#t+1] = formatters["%U:%s"](g.char,g.subtype)
+ end
+ if #t > 0 then
+ if message and message ~= "" then
+ report_nodes("%s, %s glyphs: % t",message,#t,t)
+ else
+ report_nodes("%s glyphs: % t",#t,t)
+ end
+ end
+ return false
+end
+
+function nodes.handlers.checkforleaks(sparse)
+ local l = { }
+ local q = node.usedlist()
+ for p in traverse(q) do
+ local s = table.serialize(nodes.astable(p,sparse),nodecodes[p.id])
+ l[s] = (l[s] or 0) + 1
+ end
+ node.flush_list(q)
+ for k, v in next, l do
+ write_nl(formatters["%s * %s"](v,k))
+ end
+end
+
+local f_sequence = formatters["U+%04X:%s"]
+
+local function tosequence(start,stop,compact)
+ if start then
+ local t = { }
+ while start do
+ local id = start.id
+ if id == glyph_code then
+ local c = start.char
+ if compact then
+ if start.components then
+ t[#t+1] = tosequence(start.components,nil,compact)
+ else
+ t[#t+1] = utfchar(c)
+ end
+ else
+ t[#t+1] = f_sequence(c,utfchar(c))
+ end
+ elseif id == whatsit_code and start.subtype == localpar_code or start.subtype == dir_code then
+ t[#t+1] = "[" .. start.dir .. "]"
+ elseif id == rule_code then
+ if compact then
+ t[#t+1] = "|"
+ else
+ t[#t+1] = nodecodes[id]
+ end
+ else
+ if compact then
+ t[#t+1] = "[]"
+ else
+ t[#t+1] = nodecodes[id]
+ end
+ end
+ if start == stop then
+ break
+ else
+ start = start.next
+ end
+ end
+ if compact then
+ return concat(t)
+ else
+ return concat(t," ")
+ end
+ else
+ return "[empty]"
+ end
+end
+
+nodes.tosequence = tosequence
+
+function nodes.report(t,done)
+ report_nodes("output %a, %changed %a, %s nodes",status.output_active,done,count_nodes(t))
+end
+
+function nodes.packlist(head)
+ local t = { }
+ for n in traverse(head) do
+ t[#t+1] = tostring(n)
+ end
+ return t
+end
+
+function nodes.idstostring(head,tail)
+ local t, last_id, last_n = { }, nil, 0
+ for n in traverse_nodes(head,tail) do -- hm, does not stop at tail
+ 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 == tail then
+ break
+ end
+ 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 concat(t," ")
+end
+
+-- function nodes.xidstostring(head,tail) -- only for special tracing of backlinks
+-- 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 = n.prev
+-- 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)
+ while h do
+ write_nl(rep(" ",n) .. tostring(h))
+ if not depth or n < depth then
+ local id = h.id
+ if id == hlist_code or id == vlist_code then
+ showsimplelist(h.list,depth,n+1)
+ end
+ end
+ h = h.next
+ end
+end
+
+--~ \startluacode
+--~ callback.register('buildpage_filter',function() nodes.show_simple_list(tex.lists.contrib_head) end)
+--~ \stopluacode
+--~ \vbox{b\footnote{n}a}
+--~ \startluacode
+--~ callback.register('buildpage_filter',nil)
+--~ \stopluacode
+
+nodes.showsimplelist = function(h,depth) showsimplelist(h,depth,0) end
+
+local function listtoutf(h,joiner,textonly,last)
+ local joiner = (joiner == true and utfchar(0x200C)) or joiner -- zwnj
+ local w = { }
+ while h do
+ local id = h.id
+ if id == glyph_code then -- always true
+ w[#w+1] = utfchar(h.char)
+ if joiner then
+ w[#w+1] = joiner
+ end
+ elseif id == disc_code then
+ local pre, rep, pos = h.pre, h.replace, h.post
+ w[#w+1] = formatters["[%s|%s|%s]"] (
+ pre and listtoutf(pre,joiner,textonly) or "",
+ rep and listtoutf(rep,joiner,textonly) or "",
+ mid and listtoutf(mid,joiner,textonly) or ""
+ )
+ elseif textonly then
+ if id == glue_code and h.spec and h.spec.width > 0 then
+ w[#w+1] = " "
+ end
+ else
+ w[#w+1] = "[-]"
+ end
+ if h == last then
+ break
+ else
+ h = h.next
+ end
+ end
+ return concat(w)
+end
+
+nodes.listtoutf = listtoutf
+
+local what = { [0] = "unknown", "line", "box", "indent", "row", "cell" }
+
+local function showboxes(n,symbol,depth)
+ depth, symbol = depth or 0, symbol or "."
+ for n in traverse_nodes(n) do
+ local id = n.id
+ if id == hlist_code or id == vlist_code then
+ local s = n.subtype
+ report_nodes(rep(symbol,depth) .. what[s] or s)
+ showboxes(n.list,symbol,depth+1)
+ end
+ end
+end
+
+nodes.showboxes = showboxes
+
+local ptfactor = dimenfactors.pt
+local bpfactor = dimenfactors.bp
+local stripper = lpeg.patterns.stripzeros
+
+-- start redefinition
+--
+-- -- if fmt then
+-- -- return formatters[fmt](n*dimenfactors[unit],unit)
+-- -- else
+-- -- return match(formatters["%.20f"](n*dimenfactors[unit]),"(.-0?)0*$") .. unit
+-- -- end
+--
+-- redefined:
+
+local dimenfactors = number.dimenfactors
+
+local function numbertodimen(d,unit,fmt,strip)
+ if not d then
+ local str = formatters[fmt](0,unit)
+ return strip and lpegmatch(stripper,str) or str
+ end
+ local t = type(d)
+ if t == 'string' then
+ return d
+ end
+ if unit == true then
+ unit = "pt"
+ fmt = "%0.5f%s"
+ else
+ unit = unit or 'pt'
+ if not fmt then
+ fmt = "%s%s"
+ elseif fmt == true then
+ fmt = "%0.5f%s"
+ end
+ end
+ if t == "number" then
+ local str = formatters[fmt](d*dimenfactors[unit],unit)
+ return strip and lpegmatch(stripper,str) or str
+ end
+ local id = node.id
+ if id == kern_code then
+ local str = formatters[fmt](d.width*dimenfactors[unit],unit)
+ return strip and lpegmatch(stripper,str) or str
+ end
+ if id == glue_code then
+ d = d.spec
+ end
+ if not d or not d.id == spec_code then
+ local str = formatters[fmt](0,unit)
+ return strip and lpegmatch(stripper,str) or str
+ end
+ local width = d.width
+ local plus = d.stretch_order
+ local minus = d.shrink_order
+ local stretch = d.stretch
+ local shrink = d.shrink
+ if plus ~= 0 then
+ plus = " plus " .. stretch/65536 .. fillcodes[plus]
+ elseif stretch ~= 0 then
+ plus = formatters[fmt](stretch*dimenfactors[unit],unit)
+ plus = " plus " .. (strip and lpegmatch(stripper,plus) or plus)
+ else
+ plus = ""
+ end
+ if minus ~= 0 then
+ minus = " minus " .. shrink/65536 .. fillcodes[minus]
+ elseif shrink ~= 0 then
+ minus = formatters[fmt](shrink*dimenfactors[unit],unit)
+ minus = " minus " .. (strip and lpegmatch(stripper,minus) or minus)
+ else
+ minus = ""
+ end
+ local str = formatters[fmt](d.width*dimenfactors[unit],unit)
+ return (strip and lpegmatch(stripper,str) or str) .. plus .. minus
+end
+
+number.todimen = numbertodimen
+
+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
+function number.tonewdidots (n,fmt) return numbertodimen(n,"nd",fmt) end
+function number.tonewciceros (n,fmt) return numbertodimen(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",true,true) -- 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",true,true) -- 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",true) -- 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
+
+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)
+ local mc = m_color[c]
+ if not mc then
+ n[a_color] = unsetvalue
+ else
+ if not n[a_colormodel] then
+ n[a_colormodel] = s or 1
+ end
+ n[a_color] = mc
+ end
+ return n
+end
+
+function colors.setlist(n,c,s)
+ local f = n
+ while n do
+ local mc = m_color[c]
+ if not mc then
+ n[a_color] = unsetvalue
+ else
+ if not n[a_colormodel] then
+ n[a_colormodel] = s or 1
+ end
+ n[a_color] = mc
+ end
+ n = n.next
+ end
+ return f
+end
+
+function colors.reset(n)
+ 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)
+ local mt = m_transparency[t]
+ if not mt then
+ n[a_transparency] = unsetvalue
+ else
+ n[a_transparency] = mt
+ end
+ return n
+end
+
+function transparencies.setlist(n,c,s)
+ local f = n
+ while n do
+ local mt = m_transparency[c]
+ if not mt then
+ n[a_transparency] = unsetvalue
+ else
+ n[a_transparency] = mt
+ end
+ n = n.next
+ end
+ return f
+end
+
+function transparencies.reset(n)
+ n[a_transparency] = unsetvalue
+ return n
+end
+
+-- for the moment here
+
+nodes.visualizers = { }
+
+function nodes.visualizers.handler(head)
+ return head, false
+end
diff --git a/tex/context/base/node-tsk.lua b/tex/context/base/node-tsk.lua
index d2686d4d8..596ac765a 100644
--- a/tex/context/base/node-tsk.lua
+++ b/tex/context/base/node-tsk.lua
@@ -1,402 +1,402 @@
-if not modules then modules = { } end modules ['node-tsk'] = {
- 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 might move to task-* and become less code as in sequencers
--- we already have dirty flags as well. On the other hand, nodes are
--- rather specialized and here we focus on node related tasks.
-
-local format = string.format
-
-local trace_tasks = false trackers.register("tasks.creation", function(v) trace_tasks = v end)
-
-local report_tasks = logs.reporter("tasks")
-
-local allocate = utilities.storage.allocate
-
-local nodes = nodes
-
-nodes.tasks = nodes.tasks or { }
-local tasks = nodes.tasks
-
-local tasksdata = { } -- no longer public
-
-local sequencers = utilities.sequencers
-local compile = sequencers.compile
-local nodeprocessor = sequencers.nodeprocessor
-
-local frozengroups = "no"
-
-function tasks.freeze(kind)
- frozengroups = kind or "tolerant" -- todo: hook into jobname
-end
-
-function tasks.new(specification) -- was: name,arguments,list
- local name = specification.name
- local arguments = specification.arguments or 0
- local sequence = specification.sequence
- if name and sequence then
- local tasklist = sequencers.new {
- -- we can move more to the sequencer now .. todo
- }
- tasksdata[name] = {
- list = tasklist,
- runner = false,
- arguments = arguments,
- -- sequence = sequence,
- frozen = { },
- processor = specification.processor or nodeprocessor
- }
- for l=1,#sequence do
- sequencers.appendgroup(tasklist,sequence[l])
- end
- end
-end
-
-local function valid(name)
- local data = tasksdata[name]
- if not data then
- report_tasks("unknown task %a",name)
- else
- return data
- end
-end
-
-local function validgroup(name,group,what)
- local data = tasksdata[name]
- if not data then
- report_tasks("unknown task %a",name)
- else
- local frozen = data.frozen[group]
- if frozen then
- if frozengroup == "no" then
- -- default
- elseif frozengroup == "strict" then
- report_tasks("warning: group %a of task %a is frozen, %a applied but not supported",group,name,what)
- return
- else -- if frozengroup == "tolerant" then
- report_tasks("warning: group %a of task %a is frozen, %a ignored",group,name,what)
- end
- end
- return data
- end
-end
-
-function tasks.freezegroup(name,group)
- local data = valid(name)
- if data then
- data.frozen[group] = true
- end
-end
-
-function tasks.restart(name)
- local data = valid(name)
- if data then
- data.runner = false
- end
-end
-
-function tasks.enableaction(name,action)
- local data = valid(name)
- if data then
- sequencers.enableaction(data.list,action)
- data.runner = false
- end
-end
-
-function tasks.disableaction(name,action)
- local data = valid(name)
- if data then
- sequencers.disableaction(data.list,action)
- data.runner = false
- end
-end
-
-function tasks.enablegroup(name,group)
- local data = validgroup(name,"enable group")
- if data then
- sequencers.enablegroup(data.list,group)
- data.runner = false
- end
-end
-
-function tasks.disablegroup(name,group)
- local data = validgroup(name,"disable group")
- if data then
- sequencers.disablegroup(data.list,group)
- data.runner = false
- end
-end
-
-function tasks.appendaction(name,group,action,where,kind)
- local data = validgroup(name,"append action")
- if data then
- sequencers.appendaction(data.list,group,action,where,kind)
- data.runner = false
- end
-end
-
-function tasks.prependaction(name,group,action,where,kind)
- local data = validgroup(name,"prepend action")
- if data then
- sequencers.prependaction(data.list,group,action,where,kind)
- data.runner = false
- end
-end
-
-function tasks.removeaction(name,group,action)
- local data = validgroup(name,"remove action")
- if data then
- sequencers.removeaction(data.list,group,action)
- data.runner = false
- end
-end
-
-function tasks.showactions(name,group,action,where,kind)
- local data = valid(name)
- if data then
- report_tasks("task %a, list:\n%s",name,nodeprocessor(data.list))
- end
-end
-
--- Optimizing for the number of arguments makes sense, but getting rid of
--- the nested call (no problem but then we also need to register the
--- callback with this mechanism so that it gets updated) does not save
--- much time (24K calls on mk.tex).
-
-local created, total = 0, 0
-
-statistics.register("node list callback tasks", function()
- if total > 0 then
- return format("%s unique task lists, %s instances (re)created, %s calls",table.count(tasksdata),created,total)
- else
- return nil
- end
-end)
-
-function tasks.actions(name) -- we optimize for the number or arguments (no ...)
- local data = tasksdata[name]
- if data then
- local n = data.arguments or 0
- if n == 0 then
- return function(head)
- total = total + 1 -- will go away
- local runner = data.runner
- if not runner then
- created = created + 1
- if trace_tasks then
- report_tasks("creating runner %a",name)
- end
- runner = compile(data.list,data.processor,0)
- data.runner = runner
- end
- return runner(head)
- end
- elseif n == 1 then
- return function(head,one)
- total = total + 1 -- will go away
- local runner = data.runner
- if not runner then
- created = created + 1
- if trace_tasks then
- report_tasks("creating runner %a with %s extra arguments",name,1)
- end
- runner = compile(data.list,data.processor,1)
- data.runner = runner
- end
- return runner(head,one)
- end
- elseif n == 2 then
- return function(head,one,two)
- total = total + 1 -- will go away
- local runner = data.runner
- if not runner then
- created = created + 1
- if trace_tasks then
- report_tasks("creating runner %a with %s extra arguments",name,2)
- end
- runner = compile(data.list,data.processor,2)
- data.runner = runner
- end
- return runner(head,one,two)
- end
- elseif n == 3 then
- return function(head,one,two,three)
- total = total + 1 -- will go away
- local runner = data.runner
- if not runner then
- created = created + 1
- if trace_tasks then
- report_tasks("creating runner %a with %s extra arguments",name,3)
- end
- runner = compile(data.list,data.processor,3)
- data.runner = runner
- end
- return runner(head,one,two,three)
- end
- elseif n == 4 then
- return function(head,one,two,three,four)
- total = total + 1 -- will go away
- local runner = data.runner
- if not runner then
- created = created + 1
- if trace_tasks then
- report_tasks("creating runner %a with %s extra arguments",name,4)
- end
- runner = compile(data.list,data.processor,4)
- data.runner = runner
- end
- return runner(head,one,two,three,four)
- end
- elseif n == 5 then
- return function(head,one,two,three,four,five)
- total = total + 1 -- will go away
- local runner = data.runner
- if not runner then
- created = created + 1
- if trace_tasks then
- report_tasks("creating runner %a with %s extra arguments",name,5)
- end
- runner = compile(data.list,data.processor,5)
- data.runner = runner
- end
- return runner(head,one,two,three,four,five)
- end
- else
- return function(head,...)
- total = total + 1 -- will go away
- local runner = data.runner
- if not runner then
- created = created + 1
- if trace_tasks then
- report_tasks("creating runner %a with %s extra arguments",name,n)
- end
- runner = compile(data.list,data.processor,"n")
- data.runner = runner
- end
- return runner(head,...)
- end
- end
- else
- return nil
- end
-end
-
-function tasks.table(name) --maybe move this to task-deb.lua
- local tsk = tasksdata[name]
- local lst = tsk and tsk.list
- local HL, NC, NR, bold, type = context.HL, context.NC, context.NR, context.bold, context.type
- if lst then
- local list, order = lst.list, lst.order
- if list and order then
- context.starttabulate { "|l|l|" }
- NC() bold("category") NC() bold("function") NC() NR()
- for i=1,#order do
- HL()
- local o = order[i]
- local l = list[o]
- if #l == 0 then
- NC() type(o) NC() context("unset") NC() NR()
- else
- local done = false
- for k, v in table.sortedhash(l) do
- NC() if not done then type(o) done = true end NC() type(v) NC() NR()
- end
- end
- end
- context.stoptabulate()
- end
- end
-end
-
--- this will move
-
-tasks.new {
- name = "processors",
- arguments = 4,
- processor = nodeprocessor,
- sequence = {
- "before", -- for users
- "normalizers",
- "characters",
- "words",
- "fonts",
- "lists",
- "after", -- for users
- }
-}
-
-tasks.new {
- name = "finalizers",
- arguments = 1,
- processor = nodeprocessor,
- sequence = {
- "before", -- for users
- "normalizers",
--- "characters",
--- "finishers",
- "fonts",
- "lists",
- "after", -- for users
- }
-}
-
-tasks.new {
- name = "shipouts",
- arguments = 0,
- processor = nodeprocessor,
- sequence = {
- "before", -- for users
- "normalizers",
- "finishers",
- "after", -- for users
- }
-}
-
-tasks.new {
- name = "mvlbuilders",
- arguments = 1,
- processor = nodeprocessor,
- sequence = {
- "before", -- for users
- "normalizers",
- "after", -- for users
- }
-}
-
-tasks.new {
- name = "vboxbuilders",
- arguments = 5,
- processor = nodeprocessor,
- sequence = {
- "before", -- for users
- "normalizers",
- "after", -- for users
- }
-}
-
--- tasks.new {
--- name = "parbuilders",
--- arguments = 1,
--- processor = nodeprocessor,
--- sequence = {
--- "before", -- for users
--- "lists",
--- "after", -- for users
--- }
--- }
-
--- tasks.new {
--- name = "pagebuilders",
--- arguments = 5,
--- processor = nodeprocessor,
--- sequence = {
--- "before", -- for users
--- "lists",
--- "after", -- for users
--- }
--- }
+if not modules then modules = { } end modules ['node-tsk'] = {
+ 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 might move to task-* and become less code as in sequencers
+-- we already have dirty flags as well. On the other hand, nodes are
+-- rather specialized and here we focus on node related tasks.
+
+local format = string.format
+
+local trace_tasks = false trackers.register("tasks.creation", function(v) trace_tasks = v end)
+
+local report_tasks = logs.reporter("tasks")
+
+local allocate = utilities.storage.allocate
+
+local nodes = nodes
+
+nodes.tasks = nodes.tasks or { }
+local tasks = nodes.tasks
+
+local tasksdata = { } -- no longer public
+
+local sequencers = utilities.sequencers
+local compile = sequencers.compile
+local nodeprocessor = sequencers.nodeprocessor
+
+local frozengroups = "no"
+
+function tasks.freeze(kind)
+ frozengroups = kind or "tolerant" -- todo: hook into jobname
+end
+
+function tasks.new(specification) -- was: name,arguments,list
+ local name = specification.name
+ local arguments = specification.arguments or 0
+ local sequence = specification.sequence
+ if name and sequence then
+ local tasklist = sequencers.new {
+ -- we can move more to the sequencer now .. todo
+ }
+ tasksdata[name] = {
+ list = tasklist,
+ runner = false,
+ arguments = arguments,
+ -- sequence = sequence,
+ frozen = { },
+ processor = specification.processor or nodeprocessor
+ }
+ for l=1,#sequence do
+ sequencers.appendgroup(tasklist,sequence[l])
+ end
+ end
+end
+
+local function valid(name)
+ local data = tasksdata[name]
+ if not data then
+ report_tasks("unknown task %a",name)
+ else
+ return data
+ end
+end
+
+local function validgroup(name,group,what)
+ local data = tasksdata[name]
+ if not data then
+ report_tasks("unknown task %a",name)
+ else
+ local frozen = data.frozen[group]
+ if frozen then
+ if frozengroup == "no" then
+ -- default
+ elseif frozengroup == "strict" then
+ report_tasks("warning: group %a of task %a is frozen, %a applied but not supported",group,name,what)
+ return
+ else -- if frozengroup == "tolerant" then
+ report_tasks("warning: group %a of task %a is frozen, %a ignored",group,name,what)
+ end
+ end
+ return data
+ end
+end
+
+function tasks.freezegroup(name,group)
+ local data = valid(name)
+ if data then
+ data.frozen[group] = true
+ end
+end
+
+function tasks.restart(name)
+ local data = valid(name)
+ if data then
+ data.runner = false
+ end
+end
+
+function tasks.enableaction(name,action)
+ local data = valid(name)
+ if data then
+ sequencers.enableaction(data.list,action)
+ data.runner = false
+ end
+end
+
+function tasks.disableaction(name,action)
+ local data = valid(name)
+ if data then
+ sequencers.disableaction(data.list,action)
+ data.runner = false
+ end
+end
+
+function tasks.enablegroup(name,group)
+ local data = validgroup(name,"enable group")
+ if data then
+ sequencers.enablegroup(data.list,group)
+ data.runner = false
+ end
+end
+
+function tasks.disablegroup(name,group)
+ local data = validgroup(name,"disable group")
+ if data then
+ sequencers.disablegroup(data.list,group)
+ data.runner = false
+ end
+end
+
+function tasks.appendaction(name,group,action,where,kind)
+ local data = validgroup(name,"append action")
+ if data then
+ sequencers.appendaction(data.list,group,action,where,kind)
+ data.runner = false
+ end
+end
+
+function tasks.prependaction(name,group,action,where,kind)
+ local data = validgroup(name,"prepend action")
+ if data then
+ sequencers.prependaction(data.list,group,action,where,kind)
+ data.runner = false
+ end
+end
+
+function tasks.removeaction(name,group,action)
+ local data = validgroup(name,"remove action")
+ if data then
+ sequencers.removeaction(data.list,group,action)
+ data.runner = false
+ end
+end
+
+function tasks.showactions(name,group,action,where,kind)
+ local data = valid(name)
+ if data then
+ report_tasks("task %a, list:\n%s",name,nodeprocessor(data.list))
+ end
+end
+
+-- Optimizing for the number of arguments makes sense, but getting rid of
+-- the nested call (no problem but then we also need to register the
+-- callback with this mechanism so that it gets updated) does not save
+-- much time (24K calls on mk.tex).
+
+local created, total = 0, 0
+
+statistics.register("node list callback tasks", function()
+ if total > 0 then
+ return format("%s unique task lists, %s instances (re)created, %s calls",table.count(tasksdata),created,total)
+ else
+ return nil
+ end
+end)
+
+function tasks.actions(name) -- we optimize for the number or arguments (no ...)
+ local data = tasksdata[name]
+ if data then
+ local n = data.arguments or 0
+ if n == 0 then
+ return function(head)
+ total = total + 1 -- will go away
+ local runner = data.runner
+ if not runner then
+ created = created + 1
+ if trace_tasks then
+ report_tasks("creating runner %a",name)
+ end
+ runner = compile(data.list,data.processor,0)
+ data.runner = runner
+ end
+ return runner(head)
+ end
+ elseif n == 1 then
+ return function(head,one)
+ total = total + 1 -- will go away
+ local runner = data.runner
+ if not runner then
+ created = created + 1
+ if trace_tasks then
+ report_tasks("creating runner %a with %s extra arguments",name,1)
+ end
+ runner = compile(data.list,data.processor,1)
+ data.runner = runner
+ end
+ return runner(head,one)
+ end
+ elseif n == 2 then
+ return function(head,one,two)
+ total = total + 1 -- will go away
+ local runner = data.runner
+ if not runner then
+ created = created + 1
+ if trace_tasks then
+ report_tasks("creating runner %a with %s extra arguments",name,2)
+ end
+ runner = compile(data.list,data.processor,2)
+ data.runner = runner
+ end
+ return runner(head,one,two)
+ end
+ elseif n == 3 then
+ return function(head,one,two,three)
+ total = total + 1 -- will go away
+ local runner = data.runner
+ if not runner then
+ created = created + 1
+ if trace_tasks then
+ report_tasks("creating runner %a with %s extra arguments",name,3)
+ end
+ runner = compile(data.list,data.processor,3)
+ data.runner = runner
+ end
+ return runner(head,one,two,three)
+ end
+ elseif n == 4 then
+ return function(head,one,two,three,four)
+ total = total + 1 -- will go away
+ local runner = data.runner
+ if not runner then
+ created = created + 1
+ if trace_tasks then
+ report_tasks("creating runner %a with %s extra arguments",name,4)
+ end
+ runner = compile(data.list,data.processor,4)
+ data.runner = runner
+ end
+ return runner(head,one,two,three,four)
+ end
+ elseif n == 5 then
+ return function(head,one,two,three,four,five)
+ total = total + 1 -- will go away
+ local runner = data.runner
+ if not runner then
+ created = created + 1
+ if trace_tasks then
+ report_tasks("creating runner %a with %s extra arguments",name,5)
+ end
+ runner = compile(data.list,data.processor,5)
+ data.runner = runner
+ end
+ return runner(head,one,two,three,four,five)
+ end
+ else
+ return function(head,...)
+ total = total + 1 -- will go away
+ local runner = data.runner
+ if not runner then
+ created = created + 1
+ if trace_tasks then
+ report_tasks("creating runner %a with %s extra arguments",name,n)
+ end
+ runner = compile(data.list,data.processor,"n")
+ data.runner = runner
+ end
+ return runner(head,...)
+ end
+ end
+ else
+ return nil
+ end
+end
+
+function tasks.table(name) --maybe move this to task-deb.lua
+ local tsk = tasksdata[name]
+ local lst = tsk and tsk.list
+ local HL, NC, NR, bold, type = context.HL, context.NC, context.NR, context.bold, context.type
+ if lst then
+ local list, order = lst.list, lst.order
+ if list and order then
+ context.starttabulate { "|l|l|" }
+ NC() bold("category") NC() bold("function") NC() NR()
+ for i=1,#order do
+ HL()
+ local o = order[i]
+ local l = list[o]
+ if #l == 0 then
+ NC() type(o) NC() context("unset") NC() NR()
+ else
+ local done = false
+ for k, v in table.sortedhash(l) do
+ NC() if not done then type(o) done = true end NC() type(v) NC() NR()
+ end
+ end
+ end
+ context.stoptabulate()
+ end
+ end
+end
+
+-- this will move
+
+tasks.new {
+ name = "processors",
+ arguments = 4,
+ processor = nodeprocessor,
+ sequence = {
+ "before", -- for users
+ "normalizers",
+ "characters",
+ "words",
+ "fonts",
+ "lists",
+ "after", -- for users
+ }
+}
+
+tasks.new {
+ name = "finalizers",
+ arguments = 1,
+ processor = nodeprocessor,
+ sequence = {
+ "before", -- for users
+ "normalizers",
+-- "characters",
+-- "finishers",
+ "fonts",
+ "lists",
+ "after", -- for users
+ }
+}
+
+tasks.new {
+ name = "shipouts",
+ arguments = 0,
+ processor = nodeprocessor,
+ sequence = {
+ "before", -- for users
+ "normalizers",
+ "finishers",
+ "after", -- for users
+ }
+}
+
+tasks.new {
+ name = "mvlbuilders",
+ arguments = 1,
+ processor = nodeprocessor,
+ sequence = {
+ "before", -- for users
+ "normalizers",
+ "after", -- for users
+ }
+}
+
+tasks.new {
+ name = "vboxbuilders",
+ arguments = 5,
+ processor = nodeprocessor,
+ sequence = {
+ "before", -- for users
+ "normalizers",
+ "after", -- for users
+ }
+}
+
+-- tasks.new {
+-- name = "parbuilders",
+-- arguments = 1,
+-- processor = nodeprocessor,
+-- sequence = {
+-- "before", -- for users
+-- "lists",
+-- "after", -- for users
+-- }
+-- }
+
+-- tasks.new {
+-- name = "pagebuilders",
+-- arguments = 5,
+-- processor = nodeprocessor,
+-- sequence = {
+-- "before", -- for users
+-- "lists",
+-- "after", -- for users
+-- }
+-- }
diff --git a/tex/context/base/node-tst.lua b/tex/context/base/node-tst.lua
index 98743ca0d..bfe0051bd 100644
--- a/tex/context/base/node-tst.lua
+++ b/tex/context/base/node-tst.lua
@@ -1,120 +1,120 @@
-if not modules then modules = { } end modules ['node-tst'] = {
- 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 nodes, node = nodes, node
-
-local chardata = characters.data
-local nodecodes = nodes.nodecodes
-local skipcodes = nodes.skipcodes
-
-local glue_code = nodecodes.glue
-local penalty_code = nodecodes.penalty
-local kern_code = nodecodes.kern
-local glyph_code = nodecodes.glyph
-local whatsit_code = nodecodes.whatsit
-local hlist_code = nodecodes.hlist
-
-local leftskip_code = skipcodes.leftskip
-local rightskip_code = skipcodes.rightskip
-local abovedisplayshortskip_code = skipcodes.abovedisplayshortskip
-local belowdisplayshortskip_code = skipcodes.belowdisplayshortskip
-
-local find_node_tail = node.tail or node.slide
-
-function nodes.leftmarginwidth(n) -- todo: three values
- while n do
- local id = n.id
- if id == glue_code then
- return n.subtype == leftskip_code and n.spec.width or 0
- elseif id == whatsit_code then
- n = n.next
- elseif id == hlist_code then
- return n.width
- else
- break
- end
- end
- return 0
-end
-
-function nodes.rightmarginwidth(n)
- if n then
- n = find_node_tail(n)
- while n do
- local id = n.id
- if id == glue_code then
- return n.subtype == rightskip_code and n.spec.width or 0
- elseif id == whatsit_code then
- n = n.prev
- else
- break
- end
- end
- end
- return false
-end
-
-function nodes.somespace(n,all)
- if n then
- local id = n.id
- if id == glue_code then
- return (all or (n.spec.width ~= 0)) and glue_code
- elseif id == kern_code then
- return (all or (n.kern ~= 0)) and kern
- elseif id == glyph_code then
- local category = chardata[n.char].category
- -- maybe more category checks are needed
- return (category == "zs") and glyph_code
- end
- end
- return false
-end
-
-function nodes.somepenalty(n,value)
- if n then
- local id = n.id
- if id == penalty_code then
- if value then
- return n.penalty == value
- else
- return true
- end
- end
- end
- return false
-end
-
-function nodes.is_display_math(head)
- local n = head.prev
- while n do
- local id = n.id
- if id == penalty_code then
- elseif id == glue_code then
- if n.subtype == abovedisplayshortskip_code then
- return true
- end
- else
- break
- end
- n = n.prev
- end
- n = head.next
- while n do
- local id = n.id
- if id == penalty_code then
- elseif id == glue_code then
- if n.subtype == belowdisplayshortskip_code then
- return true
- end
- else
- break
- end
- n = n.next
- end
- return false
-end
+if not modules then modules = { } end modules ['node-tst'] = {
+ 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 nodes, node = nodes, node
+
+local chardata = characters.data
+local nodecodes = nodes.nodecodes
+local skipcodes = nodes.skipcodes
+
+local glue_code = nodecodes.glue
+local penalty_code = nodecodes.penalty
+local kern_code = nodecodes.kern
+local glyph_code = nodecodes.glyph
+local whatsit_code = nodecodes.whatsit
+local hlist_code = nodecodes.hlist
+
+local leftskip_code = skipcodes.leftskip
+local rightskip_code = skipcodes.rightskip
+local abovedisplayshortskip_code = skipcodes.abovedisplayshortskip
+local belowdisplayshortskip_code = skipcodes.belowdisplayshortskip
+
+local find_node_tail = node.tail or node.slide
+
+function nodes.leftmarginwidth(n) -- todo: three values
+ while n do
+ local id = n.id
+ if id == glue_code then
+ return n.subtype == leftskip_code and n.spec.width or 0
+ elseif id == whatsit_code then
+ n = n.next
+ elseif id == hlist_code then
+ return n.width
+ else
+ break
+ end
+ end
+ return 0
+end
+
+function nodes.rightmarginwidth(n)
+ if n then
+ n = find_node_tail(n)
+ while n do
+ local id = n.id
+ if id == glue_code then
+ return n.subtype == rightskip_code and n.spec.width or 0
+ elseif id == whatsit_code then
+ n = n.prev
+ else
+ break
+ end
+ end
+ end
+ return false
+end
+
+function nodes.somespace(n,all)
+ if n then
+ local id = n.id
+ if id == glue_code then
+ return (all or (n.spec.width ~= 0)) and glue_code
+ elseif id == kern_code then
+ return (all or (n.kern ~= 0)) and kern
+ elseif id == glyph_code then
+ local category = chardata[n.char].category
+ -- maybe more category checks are needed
+ return (category == "zs") and glyph_code
+ end
+ end
+ return false
+end
+
+function nodes.somepenalty(n,value)
+ if n then
+ local id = n.id
+ if id == penalty_code then
+ if value then
+ return n.penalty == value
+ else
+ return true
+ end
+ end
+ end
+ return false
+end
+
+function nodes.is_display_math(head)
+ local n = head.prev
+ while n do
+ local id = n.id
+ if id == penalty_code then
+ elseif id == glue_code then
+ if n.subtype == abovedisplayshortskip_code then
+ return true
+ end
+ else
+ break
+ end
+ n = n.prev
+ end
+ n = head.next
+ while n do
+ local id = n.id
+ if id == penalty_code then
+ elseif id == glue_code then
+ if n.subtype == belowdisplayshortskip_code then
+ return true
+ end
+ else
+ break
+ end
+ n = n.next
+ end
+ return false
+end
diff --git a/tex/context/base/node-typ.lua b/tex/context/base/node-typ.lua
index 25ad31f83..6e1a31643 100644
--- a/tex/context/base/node-typ.lua
+++ b/tex/context/base/node-typ.lua
@@ -1,79 +1,79 @@
-if not modules then modules = { } end modules ['node-typ'] = {
- 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 utfvalues = utf.values
-
-local currentfont = font.current
-local fontparameters = fonts.hashes.parameters
-
-local hpack = node.hpack
-local vpack = node.vpack
-local fast_hpack = nodes.fasthpack
-
-local nodepool = nodes.pool
-
-local newglyph = nodepool.glyph
-local newglue = nodepool.glue
-
-typesetters = typesetters or { }
-
-local function tonodes(str,fontid,spacing) -- quick and dirty
- local head, prev = nil, nil
- if not fontid then
- fontid = currentfont()
- end
- local fp = fontparameters[fontid]
- local s, p, m
- if spacing then
- s, p, m = spacing, 0, 0
- else
- s, p, m = fp.space, fp.space_stretch, fp,space_shrink
- end
- local spacedone = false
- for c in utfvalues(str) do
- local next
- if c == 32 then
- if not spacedone then
- next = newglue(s,p,m)
- spacedone = true
- end
- else
- next = newglyph(fontid or 1,c)
- spacedone = false
- end
- if not next then
- -- nothing
- elseif not head then
- head = next
- else
- prev.next = next
- next.prev = prev
- end
- prev = next
- end
- return head
-end
-
-typesetters.tonodes = tonodes
-
-function typesetters.hpack(str,fontid,spacing)
- return hpack(tonodes(str,fontid,spacing),"exactly")
-end
-
-function typesetters.fast_hpack(str,fontid,spacing)
- return fast_hpack(tonodes(str,fontid,spacing),"exactly")
-end
-
-function typesetters.vpack(str,fontid,spacing)
- -- vpack is just a hack, and a proper implentation is on the agenda
- -- as it needs more info etc than currently available
- return vpack(tonodes(str,fontid,spacing))
-end
-
---~ node.write(typesetters.hpack("Hello World!"))
---~ node.write(typesetters.hpack("Hello World!",1,100*1024*10))
+if not modules then modules = { } end modules ['node-typ'] = {
+ 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 utfvalues = utf.values
+
+local currentfont = font.current
+local fontparameters = fonts.hashes.parameters
+
+local hpack = node.hpack
+local vpack = node.vpack
+local fast_hpack = nodes.fasthpack
+
+local nodepool = nodes.pool
+
+local newglyph = nodepool.glyph
+local newglue = nodepool.glue
+
+typesetters = typesetters or { }
+
+local function tonodes(str,fontid,spacing) -- quick and dirty
+ local head, prev = nil, nil
+ if not fontid then
+ fontid = currentfont()
+ end
+ local fp = fontparameters[fontid]
+ local s, p, m
+ if spacing then
+ s, p, m = spacing, 0, 0
+ else
+ s, p, m = fp.space, fp.space_stretch, fp,space_shrink
+ end
+ local spacedone = false
+ for c in utfvalues(str) do
+ local next
+ if c == 32 then
+ if not spacedone then
+ next = newglue(s,p,m)
+ spacedone = true
+ end
+ else
+ next = newglyph(fontid or 1,c)
+ spacedone = false
+ end
+ if not next then
+ -- nothing
+ elseif not head then
+ head = next
+ else
+ prev.next = next
+ next.prev = prev
+ end
+ prev = next
+ end
+ return head
+end
+
+typesetters.tonodes = tonodes
+
+function typesetters.hpack(str,fontid,spacing)
+ return hpack(tonodes(str,fontid,spacing),"exactly")
+end
+
+function typesetters.fast_hpack(str,fontid,spacing)
+ return fast_hpack(tonodes(str,fontid,spacing),"exactly")
+end
+
+function typesetters.vpack(str,fontid,spacing)
+ -- vpack is just a hack, and a proper implentation is on the agenda
+ -- as it needs more info etc than currently available
+ return vpack(tonodes(str,fontid,spacing))
+end
+
+--~ node.write(typesetters.hpack("Hello World!"))
+--~ node.write(typesetters.hpack("Hello World!",1,100*1024*10))
diff --git a/tex/context/base/pack-obj.lua b/tex/context/base/pack-obj.lua
index b218a0a5c..1e4e0f59e 100644
--- a/tex/context/base/pack-obj.lua
+++ b/tex/context/base/pack-obj.lua
@@ -1,77 +1,77 @@
-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 commands, context = commands, context
-
-local texcount = tex.count
-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)
-
-function jobobjects.save(tag,number,page)
- local t = { number, page }
- tobesaved[tag], collected[tag] = t, t
-end
-
-function jobobjects.set(tag,number,page)
- collected[tag] = { number, page }
-end
-
-function jobobjects.get(tag)
- return collected[tag] or tobesaved[tag]
-end
-
-function jobobjects.number(tag,default)
- local o = collected[tag] or tobesaved[tag]
- return o and o[1] or default
-end
-
-function jobobjects.page(tag,default)
- local o = collected[tag] or tobesaved[tag]
- return o and o[2] or default
-end
-
--- interface
-
-commands.saveobject = jobobjects.save
-commands.setobject = jobobjects.set
-
-function commands.objectnumber(tag,default)
- local o = collected[tag] or tobesaved[tag]
- context(o and o[1] or default)
-end
-
-function commands.objectpage(tag,default)
- local o = collected[tag] or tobesaved[tag]
- context(o and o[2] or default)
-end
-
-function commands.doifobjectreferencefoundelse(tag)
- commands.doifelse(collected[tag] or tobesaved[tag])
-end
-
+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 commands, context = commands, context
+
+local texcount = tex.count
+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)
+
+function jobobjects.save(tag,number,page)
+ local t = { number, page }
+ tobesaved[tag], collected[tag] = t, t
+end
+
+function jobobjects.set(tag,number,page)
+ collected[tag] = { number, page }
+end
+
+function jobobjects.get(tag)
+ return collected[tag] or tobesaved[tag]
+end
+
+function jobobjects.number(tag,default)
+ local o = collected[tag] or tobesaved[tag]
+ return o and o[1] or default
+end
+
+function jobobjects.page(tag,default)
+ local o = collected[tag] or tobesaved[tag]
+ return o and o[2] or default
+end
+
+-- interface
+
+commands.saveobject = jobobjects.save
+commands.setobject = jobobjects.set
+
+function commands.objectnumber(tag,default)
+ local o = collected[tag] or tobesaved[tag]
+ context(o and o[1] or default)
+end
+
+function commands.objectpage(tag,default)
+ local o = collected[tag] or tobesaved[tag]
+ context(o and o[2] or default)
+end
+
+function commands.doifobjectreferencefoundelse(tag)
+ commands.doifelse(collected[tag] or tobesaved[tag])
+end
+
diff --git a/tex/context/base/pack-rul.lua b/tex/context/base/pack-rul.lua
index 3dcabc3da..a990936e7 100644
--- a/tex/context/base/pack-rul.lua
+++ b/tex/context/base/pack-rul.lua
@@ -1,109 +1,109 @@
-if not modules then modules = { } end modules ['pack-rul'] = {
- version = 1.001,
- 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]]--
-
-local texsetdimen, texsetcount, texbox = tex.setdimen, tex.setcount, tex.box
-local hpack, free, copy, traverse_id = node.hpack, node.free, node.copy_list, node.traverse_id
-local texdimen, texcount = tex.dimen, tex.count
-
-local hlist_code = nodes.nodecodes.hlist
-local box_code = nodes.listcodes.box
-local node_dimensions = node.dimensions
-
-function commands.doreshapeframedbox(n)
- local box = texbox[n]
- local noflines = 0
- local firstheight = nil
- local lastdepth = nil
- local lastlinelength = 0
- local minwidth = 0
- local maxwidth = 0
- local totalwidth = 0
- if box.width ~= 0 then
- local list = box.list
- if list then
- for h in traverse_id(hlist_code,list) do -- no dir etc needed
- if not firstheight then
- firstheight = h.height
- end
- lastdepth = h.depth
- noflines = noflines + 1
- local l = h.list
- if l then
- if h.subtype == box_code then -- maybe more
- lastlinelength = h.width
- else
- lastlinelength = node_dimensions(l) -- used to be: hpack(copy(l)).width
- end
- if lastlinelength > maxwidth then
- maxwidth = lastlinelength
- end
- if lastlinelength < minwidth or minwidth == 0 then
- minwidth = lastlinelength
- end
- totalwidth = totalwidth + lastlinelength
- end
- end
- if firstheight then
- if maxwidth ~= 0 then
- for h in traverse_id(hlist_code,list) do
- local l = h.list
- if l then
- if h.subtype == box_code then
- -- explicit box, no 'line'
- else
- -- if h.width ~= maxwidth then -- else no display math handling (uses shift)
- -- challenge: adapt glue_set
- -- h.glue_set = h.glue_set * h.width/maxwidth -- interesting ... doesn't matter much
- -- h.width = maxwidth
- h.list = hpack(l,maxwidth,'exactly',h.dir)
- h.shift = 0 -- needed for display math
- h.width = maxwidth
- -- end
- end
- end
- end
- end
- box.width = maxwidth
- end
- end
- end
- -- print("reshape", noflines, firstheight or 0, lastdepth or 0)
- texsetcount("global","framednoflines", noflines)
- texsetdimen("global","framedfirstheight", firstheight or 0)
- texsetdimen("global","framedlastdepth", lastdepth or 0)
- texsetdimen("global","framedminwidth", minwidth)
- texsetdimen("global","framedmaxwidth", maxwidth)
- texsetdimen("global","framedaveragewidth", noflines > 0 and totalwidth/noflines or 0)
-end
-
-function commands.doanalyzeframedbox(n)
- local box = texbox[n]
- local noflines = 0
- local firstheight = nil
- local lastdepth = nil
- if box.width ~= 0 then
- local list = box.list
- if list then
- for h in traverse_id(hlist_code,list) do
- if not firstheight then
- firstheight = h.height
- end
- lastdepth = h.depth
- noflines = noflines + 1
- end
- end
- end
- -- print("analyze", noflines, firstheight or 0, lastdepth or 0)
- texsetcount("global","framednoflines", noflines)
- texsetdimen("global","framedfirstheight", firstheight or 0)
- texsetdimen("global","framedlastdepth", lastdepth or 0)
-end
+if not modules then modules = { } end modules ['pack-rul'] = {
+ version = 1.001,
+ 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]]--
+
+local texsetdimen, texsetcount, texbox = tex.setdimen, tex.setcount, tex.box
+local hpack, free, copy, traverse_id = node.hpack, node.free, node.copy_list, node.traverse_id
+local texdimen, texcount = tex.dimen, tex.count
+
+local hlist_code = nodes.nodecodes.hlist
+local box_code = nodes.listcodes.box
+local node_dimensions = node.dimensions
+
+function commands.doreshapeframedbox(n)
+ local box = texbox[n]
+ local noflines = 0
+ local firstheight = nil
+ local lastdepth = nil
+ local lastlinelength = 0
+ local minwidth = 0
+ local maxwidth = 0
+ local totalwidth = 0
+ if box.width ~= 0 then
+ local list = box.list
+ if list then
+ for h in traverse_id(hlist_code,list) do -- no dir etc needed
+ if not firstheight then
+ firstheight = h.height
+ end
+ lastdepth = h.depth
+ noflines = noflines + 1
+ local l = h.list
+ if l then
+ if h.subtype == box_code then -- maybe more
+ lastlinelength = h.width
+ else
+ lastlinelength = node_dimensions(l) -- used to be: hpack(copy(l)).width
+ end
+ if lastlinelength > maxwidth then
+ maxwidth = lastlinelength
+ end
+ if lastlinelength < minwidth or minwidth == 0 then
+ minwidth = lastlinelength
+ end
+ totalwidth = totalwidth + lastlinelength
+ end
+ end
+ if firstheight then
+ if maxwidth ~= 0 then
+ for h in traverse_id(hlist_code,list) do
+ local l = h.list
+ if l then
+ if h.subtype == box_code then
+ -- explicit box, no 'line'
+ else
+ -- if h.width ~= maxwidth then -- else no display math handling (uses shift)
+ -- challenge: adapt glue_set
+ -- h.glue_set = h.glue_set * h.width/maxwidth -- interesting ... doesn't matter much
+ -- h.width = maxwidth
+ h.list = hpack(l,maxwidth,'exactly',h.dir)
+ h.shift = 0 -- needed for display math
+ h.width = maxwidth
+ -- end
+ end
+ end
+ end
+ end
+ box.width = maxwidth
+ end
+ end
+ end
+ -- print("reshape", noflines, firstheight or 0, lastdepth or 0)
+ texsetcount("global","framednoflines", noflines)
+ texsetdimen("global","framedfirstheight", firstheight or 0)
+ texsetdimen("global","framedlastdepth", lastdepth or 0)
+ texsetdimen("global","framedminwidth", minwidth)
+ texsetdimen("global","framedmaxwidth", maxwidth)
+ texsetdimen("global","framedaveragewidth", noflines > 0 and totalwidth/noflines or 0)
+end
+
+function commands.doanalyzeframedbox(n)
+ local box = texbox[n]
+ local noflines = 0
+ local firstheight = nil
+ local lastdepth = nil
+ if box.width ~= 0 then
+ local list = box.list
+ if list then
+ for h in traverse_id(hlist_code,list) do
+ if not firstheight then
+ firstheight = h.height
+ end
+ lastdepth = h.depth
+ noflines = noflines + 1
+ end
+ end
+ end
+ -- print("analyze", noflines, firstheight or 0, lastdepth or 0)
+ texsetcount("global","framednoflines", noflines)
+ texsetdimen("global","framedfirstheight", firstheight or 0)
+ texsetdimen("global","framedlastdepth", lastdepth or 0)
+end
diff --git a/tex/context/base/page-flt.lua b/tex/context/base/page-flt.lua
index cd78b9356..ab7a534eb 100644
--- a/tex/context/base/page-flt.lua
+++ b/tex/context/base/page-flt.lua
@@ -1,289 +1,289 @@
-if not modules then modules = { } end modules ['page-flt'] = {
- version = 1.001,
- comment = "companion to page-flt.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- floats -> managers.floats
--- some functions are a tex/lua mix so we need a separation
-
-local insert, remove = table.insert, table.remove
-local find = string.find
-local setdimen, setcount, texbox = tex.setdimen, tex.setcount, tex.box
-
-local copy_node_list = node.copy_list
-
-local trace_floats = false trackers.register("graphics.floats", function(v) trace_floats = v end) -- name might change
-
-local report_floats = logs.reporter("structure","floats")
-
-local C, S, P, lpegmatch = lpeg.C, lpeg.S, lpeg.P, lpeg.match
-
--- we use floatbox, floatwidth, floatheight
--- text page leftpage rightpage (todo: top, bottom, margin, order)
-
-floats = floats or { }
-local floats = floats
-
-local noffloats, last, default, pushed = 0, nil, "text", { }
-
-local function initialize()
- return {
- text = { },
- page = { },
- leftpage = { },
- rightpage = { },
- somewhere = { },
- }
-end
-
-local stacks = initialize()
-
--- list location
-
-function floats.stacked(which) -- floats.thenofstacked
- return #stacks[which or default]
-end
-
-function floats.push()
- insert(pushed,stacks)
- stacks = initialize()
- setcount("global","savednoffloats",0)
-end
-
-function floats.pop()
- local popped = remove(pushed)
- if popped then
- for which, stack in next, stacks do
- for i=1,#stack do
- insert(popped[which],stack[i])
- end
- end
- stacks = popped
- setcount("global","savednoffloats",#stacks[default])
- end
-end
-
-local function setdimensions(b)
- local w, h, d = 0, 0, 0
- if b then
- w, h, d = b.width, b.height, b.depth
- end
- setdimen("global","floatwidth", w)
- setdimen("global","floatheight", h+d)
- return w, h, d
-end
-
-local function get(stack,n,bylabel)
- if bylabel then
- for i=1,#stack do
- local s = stack[i]
- local n = string.topattern(tostring(n)) -- to be sure
- if find(s.data.label,n) then
- return s, s.box, i
- end
- end
- else
- n = n or #stack
- if n > 0 then
- local t = stack[n]
- if t then
- return t, t.box, n
- end
- end
- end
-end
-
-function floats.save(which,data)
- which = which or default
- local b = texbox.floatbox
- if b then
- local stack = stacks[which]
- noffloats = noffloats + 1
- local w, h, d = b.width, b.height, b.depth
- local t = {
- n = noffloats,
- data = data or { },
- box = copy_node_list(b),
- }
- texbox.floatbox = nil
- insert(stack,t)
- setcount("global","savednoffloats",#stacks[default])
- if trace_floats then
- report_floats("%s, category %a, number %a, slot %a, width %p, height %p, depth %p","saving",which,noffloats,#stack,w,h,d)
- else
- interfaces.showmessage("floatblocks",2,noffloats)
- end
- else
- report_floats("ignoring empty, category %a, number %a",which,noffloats)
- end
-end
-
-function floats.resave(which)
- if last then
- which = which or default
- local stack = stacks[which]
- local b = texbox.floatbox
- local w, h, d = b.width, b.height, b.depth
- last.box = copy_node_list(b)
- texbox.floatbox = nil
- insert(stack,1,last)
- setcount("global","savednoffloats",#stacks[default])
- if trace_floats then
- report_floats("%s, category %a, number %a, slot %a width %p, height %p, depth %p","resaving",which,noffloats,#stack,w,h,d)
- else
- interfaces.showmessage("floatblocks",2,noffloats)
- end
- else
- report_floats("unable to resave float")
- end
-end
-
-function floats.flush(which,n,bylabel)
- which = which or default
- local stack = stacks[which]
- local t, b, n = get(stack,n or 1,bylabel)
- if t then
- local w, h, d = setdimensions(b)
- if trace_floats then
- report_floats("%s, category %a, number %a, slot %a width %p, height %p, depth %p","flushing",which,t.n,n,w,h,d)
- else
- interfaces.showmessage("floatblocks",3,t.n)
- end
- texbox.floatbox = b
- last = remove(stack,n)
- last.box = nil
- setcount("global","savednoffloats",#stacks[default]) -- default?
- else
- setdimensions()
- end
-end
-
-function floats.consult(which,n)
- which = which or default
- local stack = stacks[which]
- local t, b, n = get(stack,n)
- if t then
- local w, h, d = setdimensions(b)
- if trace_floats then
- report_floats("%s, category %a, number %a, slot %a width %p, height %p, depth %p","consulting",which,t.n,n,w,h,d)
- end
- return t, b, n
- else
- if trace_floats then
- report_floats("nothing to consult")
- end
- setdimensions()
- end
-end
-
-function floats.collect(which,maxwidth,distance)
- which = which or default
- local stack = stacks[which]
- local n, m = #stack, 0
- for i=1,n do
- local t, b, n = get(stack,i)
- if t then
- local w, h, d = setdimensions(b)
- if w + distance < maxwidth then
- m = m + 1
- maxwidth = maxwidth - w - distance
- else
- break
- end
- else
- break
- end
- end
- if m == 0 then
- m = 1
- end
- setcount("global","nofcollectedfloats",m)
-end
-
-function floats.getvariable(name,default)
- local value = last and last.data[name] or default
- return value ~= "" and value
-end
-
-function floats.checkedpagefloat(packed)
- if structures.pages.is_odd() then
- if #stacks.rightpage > 0 then
- return "rightpage"
- elseif #stacks.page > 0 then
- return "page"
- elseif #stacks.leftpage > 0 then
- if packed then
- return "leftpage"
- end
- end
- else
- if #stacks.leftpage > 0 then
- return "leftpage"
- elseif #stacks.page > 0 then
- return "page"
- elseif #stacks.rightpage > 0 then
- if packed then
- return "rightpage"
- end
- end
- end
-end
-
-function floats.nofstacked()
- return #stacks[which or default] or 0
-end
-
--- todo: check for digits !
-
-local method = C((1-S(", :"))^1)
-local position = P(":") * C((1-S("*,"))^1) * (P("*") * C((1-S(","))^1))^0
-local label = P(":") * C((1-S(",*: "))^0)
-
-local pattern = method * (
- label * position * C("")
- + C("") * position * C("")
- + label * C("") * C("")
- + C("") * C("") * C("")
-) + C("") * C("") * C("") * C("")
-
--- inspect { lpegmatch(pattern,"somewhere:blabla,crap") }
--- inspect { lpegmatch(pattern,"somewhere:1*2") }
--- inspect { lpegmatch(pattern,"somewhere:blabla:1*2") }
--- inspect { lpegmatch(pattern,"somewhere::1*2") }
--- inspect { lpegmatch(pattern,"somewhere,") }
--- inspect { lpegmatch(pattern,"somewhere") }
--- inspect { lpegmatch(pattern,"") }
-
-function floats.analysemethod(str) -- will become a more extensive parser
- return lpegmatch(pattern,str or "")
-end
-
--- interface
-
-local context = context
-local setvalue = context.setvalue
-
-commands.flushfloat = floats.flush
-commands.savefloat = floats.save
-commands.resavefloat = floats.resave
-commands.pushfloat = floats.push
-commands.popfloat = floats.pop
-commands.consultfloat = floats.consult
-commands.collectfloat = floats.collect
-
-function commands.getfloatvariable (...) local v = floats.getvariable(...) if v then context(v) end end
-function commands.checkedpagefloat (...) local v = floats.checkedpagefloat(...) if v then context(v) end end
-
-function commands.nofstackedfloats (...) context(floats.nofstacked(...)) end
-function commands.doifelsesavedfloat(...) commands.doifelse(floats.nofstacked(...)>0) end
-
-function commands.analysefloatmethod(str) -- currently only one method
- local method, label, row, column = floats.analysemethod(str)
- setvalue("floatmethod",method or "")
- setvalue("floatlabel", label or "")
- setvalue("floatrow", row or "")
- setvalue("floatcolumn",column or "")
-end
+if not modules then modules = { } end modules ['page-flt'] = {
+ version = 1.001,
+ comment = "companion to page-flt.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- floats -> managers.floats
+-- some functions are a tex/lua mix so we need a separation
+
+local insert, remove = table.insert, table.remove
+local find = string.find
+local setdimen, setcount, texbox = tex.setdimen, tex.setcount, tex.box
+
+local copy_node_list = node.copy_list
+
+local trace_floats = false trackers.register("graphics.floats", function(v) trace_floats = v end) -- name might change
+
+local report_floats = logs.reporter("structure","floats")
+
+local C, S, P, lpegmatch = lpeg.C, lpeg.S, lpeg.P, lpeg.match
+
+-- we use floatbox, floatwidth, floatheight
+-- text page leftpage rightpage (todo: top, bottom, margin, order)
+
+floats = floats or { }
+local floats = floats
+
+local noffloats, last, default, pushed = 0, nil, "text", { }
+
+local function initialize()
+ return {
+ text = { },
+ page = { },
+ leftpage = { },
+ rightpage = { },
+ somewhere = { },
+ }
+end
+
+local stacks = initialize()
+
+-- list location
+
+function floats.stacked(which) -- floats.thenofstacked
+ return #stacks[which or default]
+end
+
+function floats.push()
+ insert(pushed,stacks)
+ stacks = initialize()
+ setcount("global","savednoffloats",0)
+end
+
+function floats.pop()
+ local popped = remove(pushed)
+ if popped then
+ for which, stack in next, stacks do
+ for i=1,#stack do
+ insert(popped[which],stack[i])
+ end
+ end
+ stacks = popped
+ setcount("global","savednoffloats",#stacks[default])
+ end
+end
+
+local function setdimensions(b)
+ local w, h, d = 0, 0, 0
+ if b then
+ w, h, d = b.width, b.height, b.depth
+ end
+ setdimen("global","floatwidth", w)
+ setdimen("global","floatheight", h+d)
+ return w, h, d
+end
+
+local function get(stack,n,bylabel)
+ if bylabel then
+ for i=1,#stack do
+ local s = stack[i]
+ local n = string.topattern(tostring(n)) -- to be sure
+ if find(s.data.label,n) then
+ return s, s.box, i
+ end
+ end
+ else
+ n = n or #stack
+ if n > 0 then
+ local t = stack[n]
+ if t then
+ return t, t.box, n
+ end
+ end
+ end
+end
+
+function floats.save(which,data)
+ which = which or default
+ local b = texbox.floatbox
+ if b then
+ local stack = stacks[which]
+ noffloats = noffloats + 1
+ local w, h, d = b.width, b.height, b.depth
+ local t = {
+ n = noffloats,
+ data = data or { },
+ box = copy_node_list(b),
+ }
+ texbox.floatbox = nil
+ insert(stack,t)
+ setcount("global","savednoffloats",#stacks[default])
+ if trace_floats then
+ report_floats("%s, category %a, number %a, slot %a, width %p, height %p, depth %p","saving",which,noffloats,#stack,w,h,d)
+ else
+ interfaces.showmessage("floatblocks",2,noffloats)
+ end
+ else
+ report_floats("ignoring empty, category %a, number %a",which,noffloats)
+ end
+end
+
+function floats.resave(which)
+ if last then
+ which = which or default
+ local stack = stacks[which]
+ local b = texbox.floatbox
+ local w, h, d = b.width, b.height, b.depth
+ last.box = copy_node_list(b)
+ texbox.floatbox = nil
+ insert(stack,1,last)
+ setcount("global","savednoffloats",#stacks[default])
+ if trace_floats then
+ report_floats("%s, category %a, number %a, slot %a width %p, height %p, depth %p","resaving",which,noffloats,#stack,w,h,d)
+ else
+ interfaces.showmessage("floatblocks",2,noffloats)
+ end
+ else
+ report_floats("unable to resave float")
+ end
+end
+
+function floats.flush(which,n,bylabel)
+ which = which or default
+ local stack = stacks[which]
+ local t, b, n = get(stack,n or 1,bylabel)
+ if t then
+ local w, h, d = setdimensions(b)
+ if trace_floats then
+ report_floats("%s, category %a, number %a, slot %a width %p, height %p, depth %p","flushing",which,t.n,n,w,h,d)
+ else
+ interfaces.showmessage("floatblocks",3,t.n)
+ end
+ texbox.floatbox = b
+ last = remove(stack,n)
+ last.box = nil
+ setcount("global","savednoffloats",#stacks[default]) -- default?
+ else
+ setdimensions()
+ end
+end
+
+function floats.consult(which,n)
+ which = which or default
+ local stack = stacks[which]
+ local t, b, n = get(stack,n)
+ if t then
+ local w, h, d = setdimensions(b)
+ if trace_floats then
+ report_floats("%s, category %a, number %a, slot %a width %p, height %p, depth %p","consulting",which,t.n,n,w,h,d)
+ end
+ return t, b, n
+ else
+ if trace_floats then
+ report_floats("nothing to consult")
+ end
+ setdimensions()
+ end
+end
+
+function floats.collect(which,maxwidth,distance)
+ which = which or default
+ local stack = stacks[which]
+ local n, m = #stack, 0
+ for i=1,n do
+ local t, b, n = get(stack,i)
+ if t then
+ local w, h, d = setdimensions(b)
+ if w + distance < maxwidth then
+ m = m + 1
+ maxwidth = maxwidth - w - distance
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+ if m == 0 then
+ m = 1
+ end
+ setcount("global","nofcollectedfloats",m)
+end
+
+function floats.getvariable(name,default)
+ local value = last and last.data[name] or default
+ return value ~= "" and value
+end
+
+function floats.checkedpagefloat(packed)
+ if structures.pages.is_odd() then
+ if #stacks.rightpage > 0 then
+ return "rightpage"
+ elseif #stacks.page > 0 then
+ return "page"
+ elseif #stacks.leftpage > 0 then
+ if packed then
+ return "leftpage"
+ end
+ end
+ else
+ if #stacks.leftpage > 0 then
+ return "leftpage"
+ elseif #stacks.page > 0 then
+ return "page"
+ elseif #stacks.rightpage > 0 then
+ if packed then
+ return "rightpage"
+ end
+ end
+ end
+end
+
+function floats.nofstacked()
+ return #stacks[which or default] or 0
+end
+
+-- todo: check for digits !
+
+local method = C((1-S(", :"))^1)
+local position = P(":") * C((1-S("*,"))^1) * (P("*") * C((1-S(","))^1))^0
+local label = P(":") * C((1-S(",*: "))^0)
+
+local pattern = method * (
+ label * position * C("")
+ + C("") * position * C("")
+ + label * C("") * C("")
+ + C("") * C("") * C("")
+) + C("") * C("") * C("") * C("")
+
+-- inspect { lpegmatch(pattern,"somewhere:blabla,crap") }
+-- inspect { lpegmatch(pattern,"somewhere:1*2") }
+-- inspect { lpegmatch(pattern,"somewhere:blabla:1*2") }
+-- inspect { lpegmatch(pattern,"somewhere::1*2") }
+-- inspect { lpegmatch(pattern,"somewhere,") }
+-- inspect { lpegmatch(pattern,"somewhere") }
+-- inspect { lpegmatch(pattern,"") }
+
+function floats.analysemethod(str) -- will become a more extensive parser
+ return lpegmatch(pattern,str or "")
+end
+
+-- interface
+
+local context = context
+local setvalue = context.setvalue
+
+commands.flushfloat = floats.flush
+commands.savefloat = floats.save
+commands.resavefloat = floats.resave
+commands.pushfloat = floats.push
+commands.popfloat = floats.pop
+commands.consultfloat = floats.consult
+commands.collectfloat = floats.collect
+
+function commands.getfloatvariable (...) local v = floats.getvariable(...) if v then context(v) end end
+function commands.checkedpagefloat (...) local v = floats.checkedpagefloat(...) if v then context(v) end end
+
+function commands.nofstackedfloats (...) context(floats.nofstacked(...)) end
+function commands.doifelsesavedfloat(...) commands.doifelse(floats.nofstacked(...)>0) end
+
+function commands.analysefloatmethod(str) -- currently only one method
+ local method, label, row, column = floats.analysemethod(str)
+ setvalue("floatmethod",method or "")
+ setvalue("floatlabel", label or "")
+ setvalue("floatrow", row or "")
+ setvalue("floatcolumn",column or "")
+end
diff --git a/tex/context/base/page-inj.lua b/tex/context/base/page-inj.lua
index 205f8d397..5b450d60e 100644
--- a/tex/context/base/page-inj.lua
+++ b/tex/context/base/page-inj.lua
@@ -1,101 +1,101 @@
-if not modules then modules = { } end modules ["page-inj"] = {
- version = 1.000,
- comment = "Page injections",
- author = "Wolfgang Schuster & Hans Hagen",
- copyright = "Wolfgang Schuster & Hans Hagen",
- license = "see context related readme files",
-}
-
--- Adapted a bit by HH: numbered states, tracking, delayed, order, etc.
-
-local injections = pagebuilders.injections or { }
-pagebuilders.injections = injections
-
-local report = logs.reporter("pagebuilder","injections")
-local trace = false trackers.register("pagebuilder.injections",function(v) trace = v end)
-
-local variables = interfaces.variables
-
-local v_yes = variables.yes
-local v_previous = variables.previous
-local v_next = variables.next
-
-local order = 0
-local cache = { }
-
-function injections.save(specification) -- maybe not public, just commands.*
- order = order + 1
- cache[#cache+1] = {
- order = order,
- name = specification.name,
- state = tonumber(specification.state) or specification.state,
- parameters = specification.userdata,
- }
- tex.setcount("global","c_page_boxes_flush_n",#cache)
-end
-
-function injections.flushbefore() -- maybe not public, just commands.*
- if #cache > 0 then
- local delayed = { }
- context.unprotect()
- for i=1,#cache do
- local c = cache[i]
- local oldstate = c.state
- if oldstate == v_previous then
- if trace then
- report("entry %a, order %a, flushing due to state %a",i,c.order,oldstate)
- end
- context.page_injections_flush_saved(c.name,c.parameters)
- elseif type(oldstate) == "number" and oldstate < 0 then
- local newstate = oldstate + 1
- if newstate >= 0 then
- newstate = v_previous
- end
- if trace then
- report("entry %a, order %a, changing state from %a to %a",i,c.order,oldstate,newstate)
- end
- c.state = newstate
- delayed[#delayed+1] = c
- else
- delayed[#delayed+1] = c
- end
- end
- context.unprotect()
- cache = delayed
- tex.setcount("global","c_page_boxes_flush_n",#cache)
- end
-end
-
-function injections.flushafter() -- maybe not public, just commands.*
- if #cache > 0 then
- local delayed = { }
- context.unprotect()
- for i=1,#cache do
- local c = cache[i]
- local oldstate = c.state
- if oldstate == v_next then
- if trace then
- report("entry %a, order %a, flushing due to state %a",i,c.order,oldstate)
- end
- context.page_injections_flush_saved(c.name,c.parameters)
- elseif type(oldstate) == "number" and oldstate> 0 then
- local newstate = oldstate- 1
- if newstate <= 0 then
- newstate = v_next
- end
- if trace then
- report("entry %a, order %a, changing state from %a to %a",i,c.order,oldstate,newstate)
- end
- c.state = newstate
- delayed[#delayed+1] = c
- end
- end
- context.protect()
- cache = delayed
- tex.setcount("global","c_page_boxes_flush_n",#cache)
- end
-end
-
-commands.page_injections_save = injections.save
-commands.page_injections_flush_after = injections.flushafter
-commands.page_injections_flush_before = injections.flushbefore
+if not modules then modules = { } end modules ["page-inj"] = {
+ version = 1.000,
+ comment = "Page injections",
+ author = "Wolfgang Schuster & Hans Hagen",
+ copyright = "Wolfgang Schuster & Hans Hagen",
+ license = "see context related readme files",
+}
+
+-- Adapted a bit by HH: numbered states, tracking, delayed, order, etc.
+
+local injections = pagebuilders.injections or { }
+pagebuilders.injections = injections
+
+local report = logs.reporter("pagebuilder","injections")
+local trace = false trackers.register("pagebuilder.injections",function(v) trace = v end)
+
+local variables = interfaces.variables
+
+local v_yes = variables.yes
+local v_previous = variables.previous
+local v_next = variables.next
+
+local order = 0
+local cache = { }
+
+function injections.save(specification) -- maybe not public, just commands.*
+ order = order + 1
+ cache[#cache+1] = {
+ order = order,
+ name = specification.name,
+ state = tonumber(specification.state) or specification.state,
+ parameters = specification.userdata,
+ }
+ tex.setcount("global","c_page_boxes_flush_n",#cache)
+end
+
+function injections.flushbefore() -- maybe not public, just commands.*
+ if #cache > 0 then
+ local delayed = { }
+ context.unprotect()
+ for i=1,#cache do
+ local c = cache[i]
+ local oldstate = c.state
+ if oldstate == v_previous then
+ if trace then
+ report("entry %a, order %a, flushing due to state %a",i,c.order,oldstate)
+ end
+ context.page_injections_flush_saved(c.name,c.parameters)
+ elseif type(oldstate) == "number" and oldstate < 0 then
+ local newstate = oldstate + 1
+ if newstate >= 0 then
+ newstate = v_previous
+ end
+ if trace then
+ report("entry %a, order %a, changing state from %a to %a",i,c.order,oldstate,newstate)
+ end
+ c.state = newstate
+ delayed[#delayed+1] = c
+ else
+ delayed[#delayed+1] = c
+ end
+ end
+ context.unprotect()
+ cache = delayed
+ tex.setcount("global","c_page_boxes_flush_n",#cache)
+ end
+end
+
+function injections.flushafter() -- maybe not public, just commands.*
+ if #cache > 0 then
+ local delayed = { }
+ context.unprotect()
+ for i=1,#cache do
+ local c = cache[i]
+ local oldstate = c.state
+ if oldstate == v_next then
+ if trace then
+ report("entry %a, order %a, flushing due to state %a",i,c.order,oldstate)
+ end
+ context.page_injections_flush_saved(c.name,c.parameters)
+ elseif type(oldstate) == "number" and oldstate> 0 then
+ local newstate = oldstate- 1
+ if newstate <= 0 then
+ newstate = v_next
+ end
+ if trace then
+ report("entry %a, order %a, changing state from %a to %a",i,c.order,oldstate,newstate)
+ end
+ c.state = newstate
+ delayed[#delayed+1] = c
+ end
+ end
+ context.protect()
+ cache = delayed
+ tex.setcount("global","c_page_boxes_flush_n",#cache)
+ end
+end
+
+commands.page_injections_save = injections.save
+commands.page_injections_flush_after = injections.flushafter
+commands.page_injections_flush_before = injections.flushbefore
diff --git a/tex/context/base/page-ins.lua b/tex/context/base/page-ins.lua
index 15656a231..7f870735d 100644
--- a/tex/context/base/page-ins.lua
+++ b/tex/context/base/page-ins.lua
@@ -1,97 +1,97 @@
-if not modules then modules = { } end modules ['page-ins'] = {
- version = 1.001,
- comment = "companion to page-mix.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files",
- -- public = {
- -- functions = {
- -- "inserts.define",
- -- "inserts.getdata",
- -- },
- -- commands = {
- -- "defineinsertion",
- -- "inserttionnumber",
- -- }
- -- }
-}
-
--- Maybe we should only register in lua and forget about the tex end.
-
-structures = structures or { }
-structures.inserts = structures.inserts or { }
-local inserts = structures.inserts
-
-local report_inserts = logs.reporter("inserts")
-
-local allocate = utilities.storage.allocate
-
-inserts.stored = inserts.stored or allocate { } -- combining them in one is inefficient in the
-inserts.data = inserts.data or allocate { } -- bytecode storage pool
-
-local variables = interfaces.variables
-local v_page = variables.page
-local v_columns = variables.columns
-local v_firstcolumn = variables.firstcolumn
-local v_lastcolumn = variables.lastcolumn
-local v_text = variables.text
-
-storage.register("structures/inserts/stored", inserts.stored, "structures.inserts.stored")
-
-local data = inserts.data
-local stored = inserts.stored
-
-for name, specification in next, stored do
- data[specification.number] = specification
- data[name] = specification
-end
-
-function inserts.define(name,specification)
- specification.name= name
- local number = specification.number or 0
- data[name] = specification
- data[number] = specification
- -- only needed at runtime as this get stored in a bytecode register
- stored[name] = specification
- if not specification.location then
- specification.location = v_page
- end
- return specification
-end
-
-function inserts.setup(name,settings)
- local specification = data[name]
- for k, v in next, settings do
- -- maybe trace change
- specification[k] = v
- end
- return specification
-end
-
-function inserts.setlocation(name,location) -- a practical fast one
- data[name].location = location
-end
-
-function inserts.getlocation(name,location)
- return data[name].location or v_page
-end
-
-function inserts.getdata(name) -- or number
- return data[name]
-end
-
-function inserts.getname(number)
- return data[name].name
-end
-
-function inserts.getnumber(name)
- return data[name].number
-end
-
--- interface
-
-commands.defineinsertion = inserts.define
-commands.setupinsertion = inserts.setup
-commands.setinsertionlocation = inserts.setlocation
-commands.insertionnumber = function(name) context(data[name].number or 0) end
-
+if not modules then modules = { } end modules ['page-ins'] = {
+ version = 1.001,
+ comment = "companion to page-mix.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+ -- public = {
+ -- functions = {
+ -- "inserts.define",
+ -- "inserts.getdata",
+ -- },
+ -- commands = {
+ -- "defineinsertion",
+ -- "inserttionnumber",
+ -- }
+ -- }
+}
+
+-- Maybe we should only register in lua and forget about the tex end.
+
+structures = structures or { }
+structures.inserts = structures.inserts or { }
+local inserts = structures.inserts
+
+local report_inserts = logs.reporter("inserts")
+
+local allocate = utilities.storage.allocate
+
+inserts.stored = inserts.stored or allocate { } -- combining them in one is inefficient in the
+inserts.data = inserts.data or allocate { } -- bytecode storage pool
+
+local variables = interfaces.variables
+local v_page = variables.page
+local v_columns = variables.columns
+local v_firstcolumn = variables.firstcolumn
+local v_lastcolumn = variables.lastcolumn
+local v_text = variables.text
+
+storage.register("structures/inserts/stored", inserts.stored, "structures.inserts.stored")
+
+local data = inserts.data
+local stored = inserts.stored
+
+for name, specification in next, stored do
+ data[specification.number] = specification
+ data[name] = specification
+end
+
+function inserts.define(name,specification)
+ specification.name= name
+ local number = specification.number or 0
+ data[name] = specification
+ data[number] = specification
+ -- only needed at runtime as this get stored in a bytecode register
+ stored[name] = specification
+ if not specification.location then
+ specification.location = v_page
+ end
+ return specification
+end
+
+function inserts.setup(name,settings)
+ local specification = data[name]
+ for k, v in next, settings do
+ -- maybe trace change
+ specification[k] = v
+ end
+ return specification
+end
+
+function inserts.setlocation(name,location) -- a practical fast one
+ data[name].location = location
+end
+
+function inserts.getlocation(name,location)
+ return data[name].location or v_page
+end
+
+function inserts.getdata(name) -- or number
+ return data[name]
+end
+
+function inserts.getname(number)
+ return data[name].name
+end
+
+function inserts.getnumber(name)
+ return data[name].number
+end
+
+-- interface
+
+commands.defineinsertion = inserts.define
+commands.setupinsertion = inserts.setup
+commands.setinsertionlocation = inserts.setlocation
+commands.insertionnumber = function(name) context(data[name].number or 0) end
+
diff --git a/tex/context/base/page-lin.lua b/tex/context/base/page-lin.lua
index 5f7ea7eed..e6b500e8b 100644
--- a/tex/context/base/page-lin.lua
+++ b/tex/context/base/page-lin.lua
@@ -1,290 +1,290 @@
-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"
-}
-
--- experimental -> will become builders
-
-local trace_numbers = false trackers.register("lines.numbers", function(v) trace_numbers = v end)
-
-local report_lines = logs.reporter("lines")
-
-local texbox = tex.box
-
-local attributes, nodes, node, context = attributes, nodes, node, context
-
-nodes.lines = nodes.lines or { }
-local lines = nodes.lines
-
-lines.data = lines.data or { } -- start step tag
-local data = lines.data
-local last = #data
-
-lines.scratchbox = lines.scratchbox or 0
-
-local leftmarginwidth = nodes.leftmarginwidth
-
-storage.register("lines/data", lines.data, "nodes.lines.data")
-
--- if there is demand for it, we can support multiple numbering streams
--- and use more than one attibute
-
-local variables = interfaces.variables
-
-local nodecodes = nodes.nodecodes
-
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-local whatsit_code = nodecodes.whatsit
-
-local a_displaymath = attributes.private('displaymath')
-local a_linenumber = attributes.private('linenumber')
-local a_linereference = attributes.private('linereference')
-local a_verbatimline = attributes.private('verbatimline')
-
-local current_list = { }
-local cross_references = { }
-local chunksize = 250 -- not used in boxed
-
-local traverse_id = node.traverse_id
-local traverse = node.traverse
-local copy_node = node.copy
-local hpack_node = node.hpack
-local insert_node_after = node.insert_after
-local insert_node_before = node.insert_before
-
--- cross referencing
-
-function lines.number(n)
- n = tonumber(n)
- local cr = cross_references[n] or 0
- cross_references[n] = nil
- return cr
-end
-
-local function resolve(n,m) -- we can now check the 'line' flag (todo)
- while n do
- local id = n.id
- if id == whatsit_code then -- why whatsit
- local a = n[a_linereference]
- if a then
- cross_references[a] = m
- end
- elseif id == hlist_code or id == vlist_code then
- resolve(n.list,m)
- end
- n = n.next
- end
-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)
--- helpers.title(data.entries.linenumber or "?",data.metadata)
- context.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
-
-lines.boxed = { }
-local boxed = lines.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
-
-function commands.registerlinenumbering(configuration)
- context(boxed.register(configuration))
-end
-
-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
-
-commands.setuplinenumbering = boxed.setup
-
-local function check_number(n,a,skip,sameline)
- local d = data[a]
- if d then
- local tag, skipflag, s = d.tag or "", 0, d.start or 1
- current_list[#current_list+1] = { n, s }
- if sameline then
- skipflag = 0
- if trace_numbers then
- report_lines("skipping broken line number %s for setup %a: %s (%s)",#current_list,a,s,d.continue or "no")
- end
- elseif not skip and s % d.step == 0 then
- skipflag, d.start = 1, s + 1 -- (d.step or 1)
- if trace_numbers then
- report_lines("making number %s for setup %a: %s (%s)",#current_list,a,s,d.continue or "no")
- end
- else
- skipflag, d.start = 0, s + 1 -- (d.step or 1)
- if trace_numbers then
- report_lines("skipping line number %s for setup %a: %s (%s)",#current_list,a,s,d.continue or "no")
- end
- end
- context.makelinenumber(tag,skipflag,s,n.shift,n.width,leftmarginwidth(n.list),n.dir)
- end
-end
-
--- xlist
--- xlist
--- hlist
-
-local function identify(list)
- if list then
- for n in traverse_id(hlist_code,list) do
- if n[a_linenumber] then
- return list
- end
- end
- local n = list
- while n do
- local id = n.id
- if id == hlist_code or id == vlist_code then
- local ok = identify(n.list)
- if ok then
- return ok
- end
- end
- n = n.next
- end
- end
-end
-
-function boxed.stage_zero(n)
- return identify(texbox[n].list)
-end
-
--- reset ranges per page
--- store first and last per page
--- maybe just set marks directly
-
-function boxed.stage_one(n,nested)
- current_list = { }
- local head = texbox[n]
- if head then
- local list = head.list
- if nested then
- list = identify(list)
- end
- local last_a, last_v, skip = nil, -1, false
- for n in traverse_id(hlist_code,list) do -- attr test here and quit as soon as zero found
- if n.height == 0 and n.depth == 0 then
- -- skip funny hlists -- todo: check line subtype
- else
- local list = n.list
- local a = list[a_linenumber]
- if a and a > 0 then
- if last_a ~= a then
- local da = data[a]
- local ma = da.method
- if ma == variables.next then
- skip = true
- elseif ma == variables.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",a,da.start,da.continue or "no")
- end
- end
- if n[a_displaymath] then
- if nodes.is_display_math(n) then
- check_number(n,a,skip)
- end
- else
- local v = list[a_verbatimline]
- if not v or v ~= last_v then
- last_v = v
- check_number(n,a,skip)
- else
- check_number(n,a,skip,true)
- end
- end
- skip = false
- end
- end
- end
- end
-end
-
-function boxed.stage_two(n,m)
- if #current_list > 0 then
- m = m or lines.scratchbox
- local t, tn = { }, 0
- for l in traverse_id(hlist_code,texbox[m].list) do
- tn = tn + 1
- t[tn] = copy_node(l)
- end
- for i=1,#current_list do
- local li = current_list[i]
- local n, m, ti = li[1], li[2], t[i]
- if ti then
- ti.next, n.list = n.list, ti
- resolve(n,m)
- else
- report_lines("error in linenumbering (1)")
- return
- end
- end
- end
-end
-
-commands.linenumbersstageone = boxed.stage_one
-commands.linenumbersstagetwo = boxed.stage_two
+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"
+}
+
+-- experimental -> will become builders
+
+local trace_numbers = false trackers.register("lines.numbers", function(v) trace_numbers = v end)
+
+local report_lines = logs.reporter("lines")
+
+local texbox = tex.box
+
+local attributes, nodes, node, context = attributes, nodes, node, context
+
+nodes.lines = nodes.lines or { }
+local lines = nodes.lines
+
+lines.data = lines.data or { } -- start step tag
+local data = lines.data
+local last = #data
+
+lines.scratchbox = lines.scratchbox or 0
+
+local leftmarginwidth = nodes.leftmarginwidth
+
+storage.register("lines/data", lines.data, "nodes.lines.data")
+
+-- if there is demand for it, we can support multiple numbering streams
+-- and use more than one attibute
+
+local variables = interfaces.variables
+
+local nodecodes = nodes.nodecodes
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local whatsit_code = nodecodes.whatsit
+
+local a_displaymath = attributes.private('displaymath')
+local a_linenumber = attributes.private('linenumber')
+local a_linereference = attributes.private('linereference')
+local a_verbatimline = attributes.private('verbatimline')
+
+local current_list = { }
+local cross_references = { }
+local chunksize = 250 -- not used in boxed
+
+local traverse_id = node.traverse_id
+local traverse = node.traverse
+local copy_node = node.copy
+local hpack_node = node.hpack
+local insert_node_after = node.insert_after
+local insert_node_before = node.insert_before
+
+-- cross referencing
+
+function lines.number(n)
+ n = tonumber(n)
+ local cr = cross_references[n] or 0
+ cross_references[n] = nil
+ return cr
+end
+
+local function resolve(n,m) -- we can now check the 'line' flag (todo)
+ while n do
+ local id = n.id
+ if id == whatsit_code then -- why whatsit
+ local a = n[a_linereference]
+ if a then
+ cross_references[a] = m
+ end
+ elseif id == hlist_code or id == vlist_code then
+ resolve(n.list,m)
+ end
+ n = n.next
+ end
+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)
+-- helpers.title(data.entries.linenumber or "?",data.metadata)
+ context.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
+
+lines.boxed = { }
+local boxed = lines.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
+
+function commands.registerlinenumbering(configuration)
+ context(boxed.register(configuration))
+end
+
+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
+
+commands.setuplinenumbering = boxed.setup
+
+local function check_number(n,a,skip,sameline)
+ local d = data[a]
+ if d then
+ local tag, skipflag, s = d.tag or "", 0, d.start or 1
+ current_list[#current_list+1] = { n, s }
+ if sameline then
+ skipflag = 0
+ if trace_numbers then
+ report_lines("skipping broken line number %s for setup %a: %s (%s)",#current_list,a,s,d.continue or "no")
+ end
+ elseif not skip and s % d.step == 0 then
+ skipflag, d.start = 1, s + 1 -- (d.step or 1)
+ if trace_numbers then
+ report_lines("making number %s for setup %a: %s (%s)",#current_list,a,s,d.continue or "no")
+ end
+ else
+ skipflag, d.start = 0, s + 1 -- (d.step or 1)
+ if trace_numbers then
+ report_lines("skipping line number %s for setup %a: %s (%s)",#current_list,a,s,d.continue or "no")
+ end
+ end
+ context.makelinenumber(tag,skipflag,s,n.shift,n.width,leftmarginwidth(n.list),n.dir)
+ end
+end
+
+-- xlist
+-- xlist
+-- hlist
+
+local function identify(list)
+ if list then
+ for n in traverse_id(hlist_code,list) do
+ if n[a_linenumber] then
+ return list
+ end
+ end
+ local n = list
+ while n do
+ local id = n.id
+ if id == hlist_code or id == vlist_code then
+ local ok = identify(n.list)
+ if ok then
+ return ok
+ end
+ end
+ n = n.next
+ end
+ end
+end
+
+function boxed.stage_zero(n)
+ return identify(texbox[n].list)
+end
+
+-- reset ranges per page
+-- store first and last per page
+-- maybe just set marks directly
+
+function boxed.stage_one(n,nested)
+ current_list = { }
+ local head = texbox[n]
+ if head then
+ local list = head.list
+ if nested then
+ list = identify(list)
+ end
+ local last_a, last_v, skip = nil, -1, false
+ for n in traverse_id(hlist_code,list) do -- attr test here and quit as soon as zero found
+ if n.height == 0 and n.depth == 0 then
+ -- skip funny hlists -- todo: check line subtype
+ else
+ local list = n.list
+ local a = list[a_linenumber]
+ if a and a > 0 then
+ if last_a ~= a then
+ local da = data[a]
+ local ma = da.method
+ if ma == variables.next then
+ skip = true
+ elseif ma == variables.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",a,da.start,da.continue or "no")
+ end
+ end
+ if n[a_displaymath] then
+ if nodes.is_display_math(n) then
+ check_number(n,a,skip)
+ end
+ else
+ local v = list[a_verbatimline]
+ if not v or v ~= last_v then
+ last_v = v
+ check_number(n,a,skip)
+ else
+ check_number(n,a,skip,true)
+ end
+ end
+ skip = false
+ end
+ end
+ end
+ end
+end
+
+function boxed.stage_two(n,m)
+ if #current_list > 0 then
+ m = m or lines.scratchbox
+ local t, tn = { }, 0
+ for l in traverse_id(hlist_code,texbox[m].list) do
+ tn = tn + 1
+ t[tn] = copy_node(l)
+ end
+ for i=1,#current_list do
+ local li = current_list[i]
+ local n, m, ti = li[1], li[2], t[i]
+ if ti then
+ ti.next, n.list = n.list, ti
+ resolve(n,m)
+ else
+ report_lines("error in linenumbering (1)")
+ return
+ end
+ end
+ end
+end
+
+commands.linenumbersstageone = boxed.stage_one
+commands.linenumbersstagetwo = boxed.stage_two
diff --git a/tex/context/base/page-mix.lua b/tex/context/base/page-mix.lua
index 999427b8f..cf0094787 100644
--- a/tex/context/base/page-mix.lua
+++ b/tex/context/base/page-mix.lua
@@ -1,695 +1,695 @@
-if not modules then modules = { } end modules ["page-mix"] = {
- version = 1.001,
- comment = "companion to page-mix.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- inserts.getname(name)
-
--- local node, tex = node, tex
--- local nodes, interfaces, utilities = nodes, interfaces, utilities
--- local trackers, logs, storage = trackers, logs, storage
--- local number, table = number, table
-
-local concat = table.concat
-
-local nodecodes = nodes.nodecodes
-local gluecodes = nodes.gluecodes
-local nodepool = nodes.pool
-
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-local kern_code = nodecodes.kern
-local glue_code = nodecodes.glue
-local penalty_code = nodecodes.penalty
-local insert_code = nodecodes.ins
-local mark_code = nodecodes.mark
-
-local new_hlist = nodepool.hlist
-local new_vlist = nodepool.vlist
-local new_glue = nodepool.glue
-
-local hpack = node.hpack
-local vpack = node.vpack
-local freenode = node.free
-
-local texbox = tex.box
-local texskip = tex.skip
-local texdimen = tex.dimen
-local points = number.points
-local settings_to_hash = utilities.parsers.settings_to_hash
-
-local variables = interfaces.variables
-local v_yes = variables.yes
-local v_global = variables["global"]
-local v_local = variables["local"]
-local v_columns = variables.columns
-
-local trace_state = false trackers.register("mixedcolumns.trace", function(v) trace_state = v end)
-local trace_detail = false trackers.register("mixedcolumns.detail", function(v) trace_detail = v end)
-
-local report_state = logs.reporter("mixed columns")
-
-pagebuilders = pagebuilders or { }
-pagebuilders.mixedcolumns = pagebuilders.mixedcolumns or { }
-local mixedcolumns = pagebuilders.mixedcolumns
-
-local forcedbreak = -123
-
--- initializesplitter(specification)
--- cleanupsplitter()
-
--- Inserts complicate matters a lot. In order to deal with them well, we need to
--- distinguish several cases.
---
--- (1) full page columns: firstcolumn, columns, lastcolumn, page
--- (2) mid page columns : firstcolumn, columns, lastcolumn, page
---
--- We need to collect them accordingly.
-
-local function collectinserts(result,nxt,nxtid)
- local inserts, currentskips, nextskips, inserttotal = { }, 0, 0, 0
- while nxt do
- if nxtid == insert_code then
- inserttotal = inserttotal + nxt.height + nxt.depth
- local s = nxt.subtype
- local c = inserts[s]
- if not c then
- c = { }
- inserts[s] = c
- local width = texskip[s].width
- if not result.inserts[s] then
- currentskips = currentskips + width
- end
- nextskips = nextskips + width
- end
- c[#c+1] = nxt
- if trace_detail then
- report_state("insert of class %s found",s)
- end
- elseif nxtid == mark_code then
- if trace_detail then
- report_state("mark found")
- end
- else
- break
- end
- nxt = nxt.next
- if nxt then
- nxtid = nxt.id
- else
- break
- end
- end
- return nxt, inserts, currentskips, nextskips, inserttotal
-end
-
-local function appendinserts(ri,inserts)
- for class, collected in next, inserts do
- local ric = ri[class]
- if not ric then
- -- assign to collected
- ri[class] = collected
- else
- -- append to collected
- for j=1,#collected do
- ric[#ric+1] = collected[j]
- end
- end
- end
-end
-
-local function discardtopglue(current,discarded)
- local size = 0
- while current do
- local id = current.id
- if id == glue_code then
- size = size + current.spec.width
- discarded[#discarded+1] = current
- current = current.next
- elseif id == penalty_code then
- if current.penalty == forcedbreak then
- discarded[#discarded+1] = current
- current = current.next
- while current do
- local id = current.id
- if id == glue_code then
- size = size + current.spec.width
- discarded[#discarded+1] = current
- current = current.next
- else
- break
- end
- end
- else
- discarded[#discarded+1] = current
- current = current.next
- end
- else
- break
- end
- end
- return current, size
-end
-
-local function stripbottomglue(results,discarded)
- local height = 0
- for i=1,#results do
- local r = results[i]
- local t = r.tail
- while t and t ~= r.head do
- local prev = t.prev
- if not prev then
- break
- end
- local id = t.id
- if id == penalty_code then
- if t.penalty == forcedbreak then
- break
- else
- discarded[#discarded+1] = t
- r.tail = prev
- t = prev
- end
- elseif id == glue_code then
- discarded[#discarded+1] = t
- local width = t.spec.width
- if trace_state then
- report_state("columns %s, discarded bottom glue %p",i,width)
- end
- r.height = r.height - width
- r.tail = prev
- t = prev
- else
- break
- end
- end
- if r.height > height then
- height = r.height
- end
- end
- return height
-end
-
-local function setsplit(specification) -- a rather large function
- local box = specification.box
- if not box then
- report_state("fatal error, no box")
- return
- end
- local list = texbox[box]
- if not list then
- report_state("fatal error, no list")
- return
- end
- local head = list.head or specification.originalhead
- if not head then
- report_state("fatal error, no head")
- return
- end
- local discarded = { }
- local originalhead = head
- local originalwidth = specification.originalwidth or list.width
- local originalheight = specification.originalheight or list.height
- local current = head
- local skipped = 0
- local height = 0
- local depth = 0
- local skip = 0
- local options = settings_to_hash(specification.option or "")
- local stripbottom = specification.alternative == v_local
- local cycle = specification.cycle or 1
- local nofcolumns = specification.nofcolumns or 1
- if nofcolumns == 0 then
- nofcolumns = 1
- end
- local preheight = specification.preheight or 0
- local extra = specification.extra or 0
- local maxheight = specification.maxheight
- local optimal = originalheight/nofcolumns
- if specification.balance ~= v_yes then
- optimal = maxheight
- end
- local target = optimal + extra
- local overflow = target > maxheight - preheight
- local threshold = specification.threshold or 0
- if overflow then
- target = maxheight - preheight
- end
- if trace_state then
- report_state("cycle %s, maxheight %p, preheight %p, target %p, overflow %a, extra %p",
- cycle, maxheight, preheight , target, overflow, extra)
- end
- local results = { }
- for i=1,nofcolumns do
- results[i] = {
- head = false,
- tail = false,
- height = 0,
- depth = 0,
- inserts = { },
- delta = 0,
- }
- end
- local column = 1
- local line = 0
- local result = results[column]
- local lasthead = nil
- local rest = nil
- local function gotonext()
- if head == lasthead then
- if trace_state then
- report_state("empty column %s, needs more work",column)
- end
- rest = current
- return false, 0
- else
- lasthead = head
- result.head = head
- if current == head then
- result.tail = head
- else
- result.tail = current.prev
- end
- result.height = height
- result.depth = depth
- end
- head = current
- height = 0
- depth = 0
- if column == nofcolumns then
- column = 0 -- nicer in trace
- rest = head
- -- lasthead = head
- return false, 0
- else
- local skipped
- column = column + 1
- result = results[column]
- current, skipped = discardtopglue(current,discarded)
- head = current
- -- lasthead = head
- return true, skipped
- end
- end
- local function checked(advance,where)
- local total = skip + height + depth + advance
- local delta = total - target
- local state = "same"
- local okay = false
- local skipped = 0
- local curcol = column
- if delta > threshold then
- result.delta = delta
- okay, skipped = gotonext()
- if okay then
- state = "next"
- else
- state = "quit"
- end
- end
- if trace_detail then
- report_state("%-7s > column %s, delta %p, threshold %p, advance %p, total %p, target %p, discarded %p => %a (height %p, depth %p, skip %p)",
- where,curcol,delta,threshold,advance,total,target,state,skipped,height,depth,skip)
- end
- return state, skipped
- end
- current, skipped = discardtopglue(current,discarded)
- if trace_detail and skipped ~= 0 then
- report_state("check > column 1, discarded %p",skipped)
- end
- head = current
- while current do
- local id = current.id
- local nxt = current.next
-local lastcolumn = column
- if id == hlist_code or id == vlist_code then
- line = line + 1
- local nxtid = nxt and nxt.id
- local inserts, currentskips, nextskips, inserttotal = nil, 0, 0, 0
- local advance = current.height -- + current.depth
- if nxt and (nxtid == insert_code or nxtid == mark_code) then
- nxt, inserts, localskips, insertskips, inserttotal = collectinserts(result,nxt,nxtid)
- end
- local state, skipped = checked(advance+inserttotal+currentskips,"line")
- if trace_state then
- report_state("%-7s > column %s, state %a, line %s, advance %p, insert %p, height %p","line",column,state,line,advance,inserttotal,height)
- if skipped ~= 0 then
- report_state("%-7s > column %s, discarded %p","line",column,skipped)
- end
- end
- if state == "quit" then
- break
- else
- height = height + depth + skip + advance + inserttotal
- if state == "next" then
- height = height + nextskips
- else
- height = height + currentskips
- end
- end
- depth = current.depth
- skip = 0
- if inserts then
- appendinserts(result.inserts,inserts)
- end
- elseif id == glue_code then
- local advance = current.spec.width
- if advance ~= 0 then
- local state, skipped = checked(advance,"glue")
- if trace_state then
- report_state("%-7s > column %s, state %a, advance %p, height %p","glue",column,state,advance,height)
- if skipped ~= 0 then
- report_state("%-7s > column %s, discarded %p","glue",column,skipped)
- end
- end
- if state == "quit" then
- break
- end
- height = height + depth + skip
- depth = 0
- skip = height > 0 and advance or 0
- end
- elseif id == kern_code then
- local advance = current.kern
- if advance ~= 0 then
- local state, skipped = checked(advance,"kern")
- if trace_state then
- report_state("%-7s > column %s, state %a, advance %p, height %p, state %a","kern",column,state,advance,height)
- if skipped ~= 0 then
- report_state("%-7s > column %s, discarded %p","kern",column,skipped)
- end
- end
- if state == "quit" then
- break
- end
- height = height + depth + skip + advance
- depth = 0
- skip = 0
- end
- elseif id == penalty_code then
- local penalty = current.penalty
- if penalty == 0 then
- -- don't bother
- elseif penalty == forcedbreak then
- local okay, skipped = gotonext()
- if okay then
- if trace_state then
- report_state("cycle: %s, forced column break (same page)",cycle)
- if skipped ~= 0 then
- report_state("%-7s > column %s, discarded %p","penalty",column,skipped)
- end
- end
- else
- if trace_state then
- report_state("cycle: %s, forced column break (next page)",cycle)
- if skipped ~= 0 then
- report_state("%-7s > column %s, discarded %p","penalty",column,skipped)
- end
- end
- break
- end
- else
- -- todo: nobreak etc ... we might need to backtrack so we need to remember
- -- the last acceptable break
- -- club and widow and such i.e. resulting penalties (if we care)
- end
- end
-if lastcolumn == column then
- nxt = current.next -- can have changed
-end
- if nxt then
- current = nxt
- elseif head == lasthead then
- -- to be checked but break needed as otherwise we have a loop
- if trace_state then
- report_state("quit as head is lasthead")
- end
- break
- else
- local r = results[column]
- r.head = head
- r.tail = current
- r.height = height
- r.depth = depth
- break
- end
- end
- if not current then
- if trace_state then
- report_state("nilling rest")
- end
- rest = nil
- elseif rest == lasthead then
- if trace_state then
- report_state("nilling rest as rest is lasthead")
- end
- rest = nil
- end
-
- if stripbottom then
- local height = stripbottomglue(results,discarded)
- if height > 0 then
- target = height
- end
- end
-
- specification.results = results
- specification.height = target
- specification.originalheight = originalheight
- specification.originalwidth = originalwidth
- specification.originalhead = originalhead
- specification.targetheight = target or 0
- specification.rest = rest
- specification.overflow = overflow
- specification.discarded = discarded
-
- texbox[specification.box].head = nil
-
- return specification
-end
-
-function mixedcolumns.finalize(result)
- if result then
- local results = result.results
- for i=1,result.nofcolumns do
- local r = results[i]
- local h = r.head
- if h then
- h.prev = nil
- local t = r.tail
- if t then
- t.next = nil
- else
- h.next = nil
- r.tail = h
- end
- for c, list in next, r.inserts do
- local t = { }
- for i=1,#list do
- local l = list[i]
- local h = new_hlist()
- t[i] = h
- h.head = l.head
- h.height = l.height
- h.depth = l.depth
- l.head = nil
- end
- t[1].prev = nil -- needs checking
- t[#t].next = nil -- needs checking
- r.inserts[c] = t
- end
- end
- end
- end
-end
-
-local splitruns = 0
-
-local function report_deltas(result,str)
- local t = { }
- for i=1,result.nofcolumns do
- t[#t+1] = points(result.results[i].delta or 0)
- end
- report_state("%s, cycles %s, deltas % | t",str,result.cycle or 1,t)
-end
-
-function mixedcolumns.setsplit(specification)
- splitruns = splitruns + 1
- if trace_state then
- report_state("split run %s",splitruns)
- end
- local result = setsplit(specification)
- if result then
- if result.overflow then
- if trace_state then
- report_deltas(result,"overflow")
- end
- -- we might have some rest
- elseif result.rest and specification.balance == v_yes then
- local step = specification.step or 65536*2
- local cycle = 1
- local cycles = specification.cycles or 100
- while result.rest and cycle <= cycles do
- specification.extra = cycle * step
- result = setsplit(specification) or result
- if trace_state then
- report_state("cycle: %s.%s, original height %p, total height %p",
- splitruns,cycle,result.originalheight,result.nofcolumns*result.targetheight)
- end
- cycle = cycle + 1
- specification.cycle = cycle
- end
- if cycle > cycles then
- report_deltas(result,"too many balancing cycles")
- elseif trace_state then
- report_deltas(result,"balanced")
- end
- elseif trace_state then
- report_deltas(result,"done")
- end
- return result
- elseif trace_state then
- report_state("no result")
- end
-end
-
-local topskip_code = gluecodes.topskip
-local baselineskip_code = gluecodes.baselineskip
-
-function mixedcolumns.getsplit(result,n)
- if not result then
- report_state("flush, column %s, no result",n)
- return
- end
- local r = result.results[n]
- if not r then
- report_state("flush, column %s, empty",n)
- end
- local h = r.head
- if not h then
- return new_glue(result.originalwidth)
- end
-
- h.prev = nil -- move up
- local strutht = result.strutht
- local strutdp = result.strutdp
- local lineheight = strutht + strutdp
-
- local v = new_vlist()
- v.head = h
-
- -- local v = vpack(h,"exactly",height)
-
- if result.alternative == v_global then -- option
- result.height = result.maxheight
- end
-
- local ht = 0
- local dp = 0
- local wd = result.originalwidth
-
- local grid = result.grid
-
- if grid then
- ht = lineheight * math.ceil(result.height/lineheight) - strutdp
- dp = strutdp
- else
- ht = result.height
- dp = result.depth
- end
-
- v.width = wd
- v.height = ht
- v.depth = dp
-
- if trace_state then
- local id = h.id
- if id == hlist_code then
- report_state("flush, column %s, grid %a, width %p, height %p, depth %p, %s: %s",n,grid,wd,ht,dp,"top line",nodes.toutf(h.list))
- else
- report_state("flush, column %s, grid %a, width %p, height %p, depth %p, %s: %s",n,grid,wd,ht,dp,"head node",nodecodes[id])
- end
- end
-
- for c, list in next, r.inserts do
- -- tex.setbox("global",c,vpack(nodes.concat(list)))
- -- tex.setbox(c,vpack(nodes.concat(list)))
- texbox[c] = vpack(nodes.concat(list))
- r.inserts[c] = nil
- end
-
- return v
-end
-
-function mixedcolumns.getrest(result)
- local rest = result and result.rest
- result.rest = nil -- to be sure
- return rest
-end
-
-function mixedcolumns.getlist(result)
- local originalhead = result and result.originalhead
- result.originalhead = nil -- to be sure
- return originalhead
-end
-
-function mixedcolumns.cleanup(result)
- local discarded = result.discarded
- for i=1,#discarded do
- freenode(discarded[i])
- end
- result.discarded = { }
-end
-
--- interface --
-
-local result
-
-function commands.mixsetsplit(specification)
- if result then
- for k, v in next, specification do
- result[k] = v
- end
- result = mixedcolumns.setsplit(result)
- else
- result = mixedcolumns.setsplit(specification)
- end
-end
-
-function commands.mixgetsplit(n)
- if result then
- context(mixedcolumns.getsplit(result,n))
- end
-end
-
-function commands.mixfinalize()
- if result then
- mixedcolumns.finalize(result)
- end
-end
-
-function commands.mixflushrest()
- if result then
- context(mixedcolumns.getrest(result))
- end
-end
-
-function commands.mixflushlist()
- if result then
- context(mixedcolumns.getlist(result))
- end
-end
-
-function commands.mixstate()
- context(result and result.rest and 1 or 0)
-end
-
-function commands.mixcleanup()
- if result then
- mixedcolumns.cleanup(result)
- result = nil
- end
-end
+if not modules then modules = { } end modules ["page-mix"] = {
+ version = 1.001,
+ comment = "companion to page-mix.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- inserts.getname(name)
+
+-- local node, tex = node, tex
+-- local nodes, interfaces, utilities = nodes, interfaces, utilities
+-- local trackers, logs, storage = trackers, logs, storage
+-- local number, table = number, table
+
+local concat = table.concat
+
+local nodecodes = nodes.nodecodes
+local gluecodes = nodes.gluecodes
+local nodepool = nodes.pool
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local kern_code = nodecodes.kern
+local glue_code = nodecodes.glue
+local penalty_code = nodecodes.penalty
+local insert_code = nodecodes.ins
+local mark_code = nodecodes.mark
+
+local new_hlist = nodepool.hlist
+local new_vlist = nodepool.vlist
+local new_glue = nodepool.glue
+
+local hpack = node.hpack
+local vpack = node.vpack
+local freenode = node.free
+
+local texbox = tex.box
+local texskip = tex.skip
+local texdimen = tex.dimen
+local points = number.points
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+local variables = interfaces.variables
+local v_yes = variables.yes
+local v_global = variables["global"]
+local v_local = variables["local"]
+local v_columns = variables.columns
+
+local trace_state = false trackers.register("mixedcolumns.trace", function(v) trace_state = v end)
+local trace_detail = false trackers.register("mixedcolumns.detail", function(v) trace_detail = v end)
+
+local report_state = logs.reporter("mixed columns")
+
+pagebuilders = pagebuilders or { }
+pagebuilders.mixedcolumns = pagebuilders.mixedcolumns or { }
+local mixedcolumns = pagebuilders.mixedcolumns
+
+local forcedbreak = -123
+
+-- initializesplitter(specification)
+-- cleanupsplitter()
+
+-- Inserts complicate matters a lot. In order to deal with them well, we need to
+-- distinguish several cases.
+--
+-- (1) full page columns: firstcolumn, columns, lastcolumn, page
+-- (2) mid page columns : firstcolumn, columns, lastcolumn, page
+--
+-- We need to collect them accordingly.
+
+local function collectinserts(result,nxt,nxtid)
+ local inserts, currentskips, nextskips, inserttotal = { }, 0, 0, 0
+ while nxt do
+ if nxtid == insert_code then
+ inserttotal = inserttotal + nxt.height + nxt.depth
+ local s = nxt.subtype
+ local c = inserts[s]
+ if not c then
+ c = { }
+ inserts[s] = c
+ local width = texskip[s].width
+ if not result.inserts[s] then
+ currentskips = currentskips + width
+ end
+ nextskips = nextskips + width
+ end
+ c[#c+1] = nxt
+ if trace_detail then
+ report_state("insert of class %s found",s)
+ end
+ elseif nxtid == mark_code then
+ if trace_detail then
+ report_state("mark found")
+ end
+ else
+ break
+ end
+ nxt = nxt.next
+ if nxt then
+ nxtid = nxt.id
+ else
+ break
+ end
+ end
+ return nxt, inserts, currentskips, nextskips, inserttotal
+end
+
+local function appendinserts(ri,inserts)
+ for class, collected in next, inserts do
+ local ric = ri[class]
+ if not ric then
+ -- assign to collected
+ ri[class] = collected
+ else
+ -- append to collected
+ for j=1,#collected do
+ ric[#ric+1] = collected[j]
+ end
+ end
+ end
+end
+
+local function discardtopglue(current,discarded)
+ local size = 0
+ while current do
+ local id = current.id
+ if id == glue_code then
+ size = size + current.spec.width
+ discarded[#discarded+1] = current
+ current = current.next
+ elseif id == penalty_code then
+ if current.penalty == forcedbreak then
+ discarded[#discarded+1] = current
+ current = current.next
+ while current do
+ local id = current.id
+ if id == glue_code then
+ size = size + current.spec.width
+ discarded[#discarded+1] = current
+ current = current.next
+ else
+ break
+ end
+ end
+ else
+ discarded[#discarded+1] = current
+ current = current.next
+ end
+ else
+ break
+ end
+ end
+ return current, size
+end
+
+local function stripbottomglue(results,discarded)
+ local height = 0
+ for i=1,#results do
+ local r = results[i]
+ local t = r.tail
+ while t and t ~= r.head do
+ local prev = t.prev
+ if not prev then
+ break
+ end
+ local id = t.id
+ if id == penalty_code then
+ if t.penalty == forcedbreak then
+ break
+ else
+ discarded[#discarded+1] = t
+ r.tail = prev
+ t = prev
+ end
+ elseif id == glue_code then
+ discarded[#discarded+1] = t
+ local width = t.spec.width
+ if trace_state then
+ report_state("columns %s, discarded bottom glue %p",i,width)
+ end
+ r.height = r.height - width
+ r.tail = prev
+ t = prev
+ else
+ break
+ end
+ end
+ if r.height > height then
+ height = r.height
+ end
+ end
+ return height
+end
+
+local function setsplit(specification) -- a rather large function
+ local box = specification.box
+ if not box then
+ report_state("fatal error, no box")
+ return
+ end
+ local list = texbox[box]
+ if not list then
+ report_state("fatal error, no list")
+ return
+ end
+ local head = list.head or specification.originalhead
+ if not head then
+ report_state("fatal error, no head")
+ return
+ end
+ local discarded = { }
+ local originalhead = head
+ local originalwidth = specification.originalwidth or list.width
+ local originalheight = specification.originalheight or list.height
+ local current = head
+ local skipped = 0
+ local height = 0
+ local depth = 0
+ local skip = 0
+ local options = settings_to_hash(specification.option or "")
+ local stripbottom = specification.alternative == v_local
+ local cycle = specification.cycle or 1
+ local nofcolumns = specification.nofcolumns or 1
+ if nofcolumns == 0 then
+ nofcolumns = 1
+ end
+ local preheight = specification.preheight or 0
+ local extra = specification.extra or 0
+ local maxheight = specification.maxheight
+ local optimal = originalheight/nofcolumns
+ if specification.balance ~= v_yes then
+ optimal = maxheight
+ end
+ local target = optimal + extra
+ local overflow = target > maxheight - preheight
+ local threshold = specification.threshold or 0
+ if overflow then
+ target = maxheight - preheight
+ end
+ if trace_state then
+ report_state("cycle %s, maxheight %p, preheight %p, target %p, overflow %a, extra %p",
+ cycle, maxheight, preheight , target, overflow, extra)
+ end
+ local results = { }
+ for i=1,nofcolumns do
+ results[i] = {
+ head = false,
+ tail = false,
+ height = 0,
+ depth = 0,
+ inserts = { },
+ delta = 0,
+ }
+ end
+ local column = 1
+ local line = 0
+ local result = results[column]
+ local lasthead = nil
+ local rest = nil
+ local function gotonext()
+ if head == lasthead then
+ if trace_state then
+ report_state("empty column %s, needs more work",column)
+ end
+ rest = current
+ return false, 0
+ else
+ lasthead = head
+ result.head = head
+ if current == head then
+ result.tail = head
+ else
+ result.tail = current.prev
+ end
+ result.height = height
+ result.depth = depth
+ end
+ head = current
+ height = 0
+ depth = 0
+ if column == nofcolumns then
+ column = 0 -- nicer in trace
+ rest = head
+ -- lasthead = head
+ return false, 0
+ else
+ local skipped
+ column = column + 1
+ result = results[column]
+ current, skipped = discardtopglue(current,discarded)
+ head = current
+ -- lasthead = head
+ return true, skipped
+ end
+ end
+ local function checked(advance,where)
+ local total = skip + height + depth + advance
+ local delta = total - target
+ local state = "same"
+ local okay = false
+ local skipped = 0
+ local curcol = column
+ if delta > threshold then
+ result.delta = delta
+ okay, skipped = gotonext()
+ if okay then
+ state = "next"
+ else
+ state = "quit"
+ end
+ end
+ if trace_detail then
+ report_state("%-7s > column %s, delta %p, threshold %p, advance %p, total %p, target %p, discarded %p => %a (height %p, depth %p, skip %p)",
+ where,curcol,delta,threshold,advance,total,target,state,skipped,height,depth,skip)
+ end
+ return state, skipped
+ end
+ current, skipped = discardtopglue(current,discarded)
+ if trace_detail and skipped ~= 0 then
+ report_state("check > column 1, discarded %p",skipped)
+ end
+ head = current
+ while current do
+ local id = current.id
+ local nxt = current.next
+local lastcolumn = column
+ if id == hlist_code or id == vlist_code then
+ line = line + 1
+ local nxtid = nxt and nxt.id
+ local inserts, currentskips, nextskips, inserttotal = nil, 0, 0, 0
+ local advance = current.height -- + current.depth
+ if nxt and (nxtid == insert_code or nxtid == mark_code) then
+ nxt, inserts, localskips, insertskips, inserttotal = collectinserts(result,nxt,nxtid)
+ end
+ local state, skipped = checked(advance+inserttotal+currentskips,"line")
+ if trace_state then
+ report_state("%-7s > column %s, state %a, line %s, advance %p, insert %p, height %p","line",column,state,line,advance,inserttotal,height)
+ if skipped ~= 0 then
+ report_state("%-7s > column %s, discarded %p","line",column,skipped)
+ end
+ end
+ if state == "quit" then
+ break
+ else
+ height = height + depth + skip + advance + inserttotal
+ if state == "next" then
+ height = height + nextskips
+ else
+ height = height + currentskips
+ end
+ end
+ depth = current.depth
+ skip = 0
+ if inserts then
+ appendinserts(result.inserts,inserts)
+ end
+ elseif id == glue_code then
+ local advance = current.spec.width
+ if advance ~= 0 then
+ local state, skipped = checked(advance,"glue")
+ if trace_state then
+ report_state("%-7s > column %s, state %a, advance %p, height %p","glue",column,state,advance,height)
+ if skipped ~= 0 then
+ report_state("%-7s > column %s, discarded %p","glue",column,skipped)
+ end
+ end
+ if state == "quit" then
+ break
+ end
+ height = height + depth + skip
+ depth = 0
+ skip = height > 0 and advance or 0
+ end
+ elseif id == kern_code then
+ local advance = current.kern
+ if advance ~= 0 then
+ local state, skipped = checked(advance,"kern")
+ if trace_state then
+ report_state("%-7s > column %s, state %a, advance %p, height %p, state %a","kern",column,state,advance,height)
+ if skipped ~= 0 then
+ report_state("%-7s > column %s, discarded %p","kern",column,skipped)
+ end
+ end
+ if state == "quit" then
+ break
+ end
+ height = height + depth + skip + advance
+ depth = 0
+ skip = 0
+ end
+ elseif id == penalty_code then
+ local penalty = current.penalty
+ if penalty == 0 then
+ -- don't bother
+ elseif penalty == forcedbreak then
+ local okay, skipped = gotonext()
+ if okay then
+ if trace_state then
+ report_state("cycle: %s, forced column break (same page)",cycle)
+ if skipped ~= 0 then
+ report_state("%-7s > column %s, discarded %p","penalty",column,skipped)
+ end
+ end
+ else
+ if trace_state then
+ report_state("cycle: %s, forced column break (next page)",cycle)
+ if skipped ~= 0 then
+ report_state("%-7s > column %s, discarded %p","penalty",column,skipped)
+ end
+ end
+ break
+ end
+ else
+ -- todo: nobreak etc ... we might need to backtrack so we need to remember
+ -- the last acceptable break
+ -- club and widow and such i.e. resulting penalties (if we care)
+ end
+ end
+if lastcolumn == column then
+ nxt = current.next -- can have changed
+end
+ if nxt then
+ current = nxt
+ elseif head == lasthead then
+ -- to be checked but break needed as otherwise we have a loop
+ if trace_state then
+ report_state("quit as head is lasthead")
+ end
+ break
+ else
+ local r = results[column]
+ r.head = head
+ r.tail = current
+ r.height = height
+ r.depth = depth
+ break
+ end
+ end
+ if not current then
+ if trace_state then
+ report_state("nilling rest")
+ end
+ rest = nil
+ elseif rest == lasthead then
+ if trace_state then
+ report_state("nilling rest as rest is lasthead")
+ end
+ rest = nil
+ end
+
+ if stripbottom then
+ local height = stripbottomglue(results,discarded)
+ if height > 0 then
+ target = height
+ end
+ end
+
+ specification.results = results
+ specification.height = target
+ specification.originalheight = originalheight
+ specification.originalwidth = originalwidth
+ specification.originalhead = originalhead
+ specification.targetheight = target or 0
+ specification.rest = rest
+ specification.overflow = overflow
+ specification.discarded = discarded
+
+ texbox[specification.box].head = nil
+
+ return specification
+end
+
+function mixedcolumns.finalize(result)
+ if result then
+ local results = result.results
+ for i=1,result.nofcolumns do
+ local r = results[i]
+ local h = r.head
+ if h then
+ h.prev = nil
+ local t = r.tail
+ if t then
+ t.next = nil
+ else
+ h.next = nil
+ r.tail = h
+ end
+ for c, list in next, r.inserts do
+ local t = { }
+ for i=1,#list do
+ local l = list[i]
+ local h = new_hlist()
+ t[i] = h
+ h.head = l.head
+ h.height = l.height
+ h.depth = l.depth
+ l.head = nil
+ end
+ t[1].prev = nil -- needs checking
+ t[#t].next = nil -- needs checking
+ r.inserts[c] = t
+ end
+ end
+ end
+ end
+end
+
+local splitruns = 0
+
+local function report_deltas(result,str)
+ local t = { }
+ for i=1,result.nofcolumns do
+ t[#t+1] = points(result.results[i].delta or 0)
+ end
+ report_state("%s, cycles %s, deltas % | t",str,result.cycle or 1,t)
+end
+
+function mixedcolumns.setsplit(specification)
+ splitruns = splitruns + 1
+ if trace_state then
+ report_state("split run %s",splitruns)
+ end
+ local result = setsplit(specification)
+ if result then
+ if result.overflow then
+ if trace_state then
+ report_deltas(result,"overflow")
+ end
+ -- we might have some rest
+ elseif result.rest and specification.balance == v_yes then
+ local step = specification.step or 65536*2
+ local cycle = 1
+ local cycles = specification.cycles or 100
+ while result.rest and cycle <= cycles do
+ specification.extra = cycle * step
+ result = setsplit(specification) or result
+ if trace_state then
+ report_state("cycle: %s.%s, original height %p, total height %p",
+ splitruns,cycle,result.originalheight,result.nofcolumns*result.targetheight)
+ end
+ cycle = cycle + 1
+ specification.cycle = cycle
+ end
+ if cycle > cycles then
+ report_deltas(result,"too many balancing cycles")
+ elseif trace_state then
+ report_deltas(result,"balanced")
+ end
+ elseif trace_state then
+ report_deltas(result,"done")
+ end
+ return result
+ elseif trace_state then
+ report_state("no result")
+ end
+end
+
+local topskip_code = gluecodes.topskip
+local baselineskip_code = gluecodes.baselineskip
+
+function mixedcolumns.getsplit(result,n)
+ if not result then
+ report_state("flush, column %s, no result",n)
+ return
+ end
+ local r = result.results[n]
+ if not r then
+ report_state("flush, column %s, empty",n)
+ end
+ local h = r.head
+ if not h then
+ return new_glue(result.originalwidth)
+ end
+
+ h.prev = nil -- move up
+ local strutht = result.strutht
+ local strutdp = result.strutdp
+ local lineheight = strutht + strutdp
+
+ local v = new_vlist()
+ v.head = h
+
+ -- local v = vpack(h,"exactly",height)
+
+ if result.alternative == v_global then -- option
+ result.height = result.maxheight
+ end
+
+ local ht = 0
+ local dp = 0
+ local wd = result.originalwidth
+
+ local grid = result.grid
+
+ if grid then
+ ht = lineheight * math.ceil(result.height/lineheight) - strutdp
+ dp = strutdp
+ else
+ ht = result.height
+ dp = result.depth
+ end
+
+ v.width = wd
+ v.height = ht
+ v.depth = dp
+
+ if trace_state then
+ local id = h.id
+ if id == hlist_code then
+ report_state("flush, column %s, grid %a, width %p, height %p, depth %p, %s: %s",n,grid,wd,ht,dp,"top line",nodes.toutf(h.list))
+ else
+ report_state("flush, column %s, grid %a, width %p, height %p, depth %p, %s: %s",n,grid,wd,ht,dp,"head node",nodecodes[id])
+ end
+ end
+
+ for c, list in next, r.inserts do
+ -- tex.setbox("global",c,vpack(nodes.concat(list)))
+ -- tex.setbox(c,vpack(nodes.concat(list)))
+ texbox[c] = vpack(nodes.concat(list))
+ r.inserts[c] = nil
+ end
+
+ return v
+end
+
+function mixedcolumns.getrest(result)
+ local rest = result and result.rest
+ result.rest = nil -- to be sure
+ return rest
+end
+
+function mixedcolumns.getlist(result)
+ local originalhead = result and result.originalhead
+ result.originalhead = nil -- to be sure
+ return originalhead
+end
+
+function mixedcolumns.cleanup(result)
+ local discarded = result.discarded
+ for i=1,#discarded do
+ freenode(discarded[i])
+ end
+ result.discarded = { }
+end
+
+-- interface --
+
+local result
+
+function commands.mixsetsplit(specification)
+ if result then
+ for k, v in next, specification do
+ result[k] = v
+ end
+ result = mixedcolumns.setsplit(result)
+ else
+ result = mixedcolumns.setsplit(specification)
+ end
+end
+
+function commands.mixgetsplit(n)
+ if result then
+ context(mixedcolumns.getsplit(result,n))
+ end
+end
+
+function commands.mixfinalize()
+ if result then
+ mixedcolumns.finalize(result)
+ end
+end
+
+function commands.mixflushrest()
+ if result then
+ context(mixedcolumns.getrest(result))
+ end
+end
+
+function commands.mixflushlist()
+ if result then
+ context(mixedcolumns.getlist(result))
+ end
+end
+
+function commands.mixstate()
+ context(result and result.rest and 1 or 0)
+end
+
+function commands.mixcleanup()
+ if result then
+ mixedcolumns.cleanup(result)
+ result = nil
+ end
+end
diff --git a/tex/context/base/page-pst.lua b/tex/context/base/page-pst.lua
index 1256d4067..8586830cf 100644
--- a/tex/context/base/page-pst.lua
+++ b/tex/context/base/page-pst.lua
@@ -1,78 +1,78 @@
-if not modules then modules = { } end modules ['page-pst'] = {
- version = 1.001,
- comment = "companion to page-pst.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- todo: adapt message
-
-local format, validstring = string.format, string.valid
-local sortedkeys = table.sortedkeys
-
-local cache = { }
-
-local function flush(page)
- local c = cache[page]
- if c then
- for i=1,#c do
- context.viafile(c[i],format("page.%s",validstring(page,"nopage")))
- end
- cache[page] = nil
- end
-end
-
-local function setnextpage()
- local n = next(cache) and sortedkeys(cache)[1]
- if not n then
- n = 0 -- nothing in the cache
- elseif n == 0 then
- n = -1 -- generic buffer (0)
- elseif n > 0 then
- -- upcoming page (realpageno)
- end
- tex.setcount("global","c_page_postponed_blocks_next_page",n)
-end
-
-function commands.flushpostponedblocks(page)
- -- we need to flush previously pending pages as well and the zero
- -- slot is the generic one so that one is always flushed
- local t = sortedkeys(cache)
- local p = tonumber(page) or tex.count.realpageno or 0
- for i=1,#t do
- local ti = t[i]
- if ti <= p then
- flush(ti)
- else
- break
- end
- end
- setnextpage()
-end
-
-function commands.registerpostponedblock(page)
- if type(page) == "string" then
- if string.find(page,"^+") then
- page = tex.count.realpageno + (tonumber(page) or 1) -- future delta page
- else
- page = tonumber(page) or 0 -- preferred page or otherwise first possible occasion
- end
- end
- if not page then
- page = 0
- end
- local c = cache[page]
- if not c then
- c = { }
- cache[page] = c
- end
- c[#c+1] = buffers.raw("postponedblock")
- buffers.erase("postponedblock")
- if page == 0 then
- interfaces.showmessage("layouts",3,#c)
- else
- interfaces.showmessage("layouts",3,string.format("%s (realpage: %s)",#c,page))
- end
- setnextpage()
-end
+if not modules then modules = { } end modules ['page-pst'] = {
+ version = 1.001,
+ comment = "companion to page-pst.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo: adapt message
+
+local format, validstring = string.format, string.valid
+local sortedkeys = table.sortedkeys
+
+local cache = { }
+
+local function flush(page)
+ local c = cache[page]
+ if c then
+ for i=1,#c do
+ context.viafile(c[i],format("page.%s",validstring(page,"nopage")))
+ end
+ cache[page] = nil
+ end
+end
+
+local function setnextpage()
+ local n = next(cache) and sortedkeys(cache)[1]
+ if not n then
+ n = 0 -- nothing in the cache
+ elseif n == 0 then
+ n = -1 -- generic buffer (0)
+ elseif n > 0 then
+ -- upcoming page (realpageno)
+ end
+ tex.setcount("global","c_page_postponed_blocks_next_page",n)
+end
+
+function commands.flushpostponedblocks(page)
+ -- we need to flush previously pending pages as well and the zero
+ -- slot is the generic one so that one is always flushed
+ local t = sortedkeys(cache)
+ local p = tonumber(page) or tex.count.realpageno or 0
+ for i=1,#t do
+ local ti = t[i]
+ if ti <= p then
+ flush(ti)
+ else
+ break
+ end
+ end
+ setnextpage()
+end
+
+function commands.registerpostponedblock(page)
+ if type(page) == "string" then
+ if string.find(page,"^+") then
+ page = tex.count.realpageno + (tonumber(page) or 1) -- future delta page
+ else
+ page = tonumber(page) or 0 -- preferred page or otherwise first possible occasion
+ end
+ end
+ if not page then
+ page = 0
+ end
+ local c = cache[page]
+ if not c then
+ c = { }
+ cache[page] = c
+ end
+ c[#c+1] = buffers.raw("postponedblock")
+ buffers.erase("postponedblock")
+ if page == 0 then
+ interfaces.showmessage("layouts",3,#c)
+ else
+ interfaces.showmessage("layouts",3,string.format("%s (realpage: %s)",#c,page))
+ end
+ setnextpage()
+end
diff --git a/tex/context/base/page-str.lua b/tex/context/base/page-str.lua
index b9b5086cf..f6314657f 100644
--- a/tex/context/base/page-str.lua
+++ b/tex/context/base/page-str.lua
@@ -1,232 +1,232 @@
-if not modules then modules = { } end modules ['page-str'] = {
- version = 1.001,
- comment = "companion to page-str.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- streams -> managers.streams
-
--- work in progresss .. unfinished
-
-local concat, insert, remove = table.concat, table.insert, table.remove
-
-local find_tail, write_node, free_node, copy_nodelist = node.slide, node.write, node.free, node.copy_list
-local vpack_nodelist, hpack_nodelist = node.vpack, node.hpack
-local texdimen, texbox = tex.dimen, tex.box
-local settings_to_array = utilities.parsers.settings_to_array
-
-local nodes, node = nodes, node
-
-local nodepool = nodes.pool
-local tasks = nodes.tasks
-
-local new_kern = nodepool.kern
-local new_glyph = nodepool.glyph
-
-local trace_collecting = false trackers.register("streams.collecting", function(v) trace_collecting = v end)
-local trace_flushing = false trackers.register("streams.flushing", function(v) trace_flushing = v end)
-
-local report_streams = logs.reporter("streams")
-
-streams = streams or { } -- might move to the builders namespace
-local streams = streams
-
-local data, name, stack = { }, nil, { }
-
-function streams.enable(newname)
- if newname == "default" then
- name = nil
- else
- name = newname
- end
-end
-
-function streams.disable()
- name = stack[#stack]
-end
-
-function streams.start(newname)
- insert(stack,name)
- name = newname
-end
-
-function streams.stop(newname)
- name = remove(stack)
-end
-
-function streams.collect(head,where)
- if name and head and name ~= "default" then
- local tail = node.slide(head)
- local dana = data[name]
- if not dana then
- dana = { }
- data[name] = dana
- end
- local last = dana[#dana]
- if last then
- local tail = find_tail(last)
- tail.next, head.prev = head, tail
- elseif last == false then
- dana[#dana] = head
- else
- dana[1] = head
- end
- if trace_collecting then
- report_streams("appending snippet %a to slot %s",name,#dana)
- end
- return nil, true
- else
- return head, false
- end
-end
-
-function streams.push(thename)
- if not thename or thename == "" then
- thename = name
- end
- if thename and thename ~= "" then
- local dana = data[thename]
- if dana then
- dana[#dana+1] = false
- if trace_collecting then
- report_streams("pushing snippet %a",thename)
- end
- end
- end
-end
-
-function streams.flush(name,copy) -- problem: we need to migrate afterwards
- local dana = data[name]
- if dana then
- local dn = #dana
- if dn == 0 then
- -- nothing to flush
- elseif copy then
- if trace_flushing then
- report_streams("flushing copies of %s slots of %a",dn,name)
- end
- for i=1,dn do
- local di = dana[i]
- if di then
- write_node(copy_nodelist(di.list)) -- list, will be option
- end
- end
- if copy then
- data[name] = nil
- end
- else
- if trace_flushing then
- report_streams("flushing %s slots of %a",dn,name)
- end
- for i=1,dn do
- local di = dana[i]
- if di then
- write_node(di.list) -- list, will be option
- di.list = nil
- free_node(di)
- end
- end
- end
- end
-end
-
-function streams.synchronize(list) -- this is an experiment !
- -- we don't optimize this as we want to trace in detail
- list = settings_to_array(list)
- local max = 0
- if trace_flushing then
- report_streams("synchronizing list: % t",list)
- end
- for i=1,#list do
- local dana = data[list[i]]
- if dana then
- local n = #dana
- if n > max then
- max = n
- end
- end
- end
- if trace_flushing then
- report_streams("maximum number of slots: %s",max)
- end
- for m=1,max do
- local height, depth = 0, 0
- for i=1,#list do
- local name = list[i]
- local dana = data[name]
- local slot = dana[m]
- if slot then
- local vbox = vpack_nodelist(slot)
- local ht, dp = vbox.height, vbox.depth
- if ht > height then
- height = ht
- end
- if dp > depth then
- depth = dp
- end
- dana[m] = vbox
- if trace_flushing then
- report_streams("slot %s of %a is packed to height %p and depth %p",m,name,ht,dp)
- end
- end
- end
- if trace_flushing then
- report_streams("slot %s has max height %p and max depth %p",m,height,depth)
- end
- local strutht, strutdp = texdimen.globalbodyfontstrutheight, texdimen.globalbodyfontstrutdepth
- local struthtdp = strutht + strutdp
- for i=1,#list do
- local name = list[i]
- local dana = data[name]
- local vbox = dana[m]
- if vbox then
- local delta_height = height - vbox.height
- local delta_depth = depth - vbox.depth
- if delta_height > 0 or delta_depth > 0 then
- if false then
- -- actually we need to add glue and repack
- vbox.height, vbox.depth = height, depth
- if trace_flushing then
- report_streams("slot %s of %a with delta (%p,%p) is compensated",m,i,delta_height,delta_depth)
- end
- else
- -- this is not yet ok as we also need to keep an eye on vertical spacing
- -- so we might need to do some splitting or whatever
- local tail = vbox.list and find_tail(vbox.list)
- local n, delta = 0, delta_height -- for tracing
- while delta > 0 do
- -- we need to add some interline penalties
- local line = copy_nodelist(tex.box.strutbox)
- line.height, line.depth = strutht, strutdp
- if tail then
- tail.next, line.prev = line, tail
- end
- tail = line
- n, delta = n +1, delta - struthtdp
- end
- dana[m] = vpack_nodelist(vbox.list)
- vbox.list = nil
- free_node(vbox)
- if trace_flushing then
- report_streams("slot %s:%s with delta (%p,%p) is compensated by %s lines",m,i,delta_height,delta_depth,n)
- end
- end
- end
- else
- -- make dummy
- end
- end
- end
-end
-
-tasks.appendaction("mvlbuilders", "normalizers", "streams.collect")
-
-tasks.disableaction("mvlbuilders", "streams.collect")
-
-function streams.initialize()
- tasks.enableaction ("mvlbuilders", "streams.collect")
-end
-
--- todo: remove empty last { }'s
+if not modules then modules = { } end modules ['page-str'] = {
+ version = 1.001,
+ comment = "companion to page-str.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- streams -> managers.streams
+
+-- work in progresss .. unfinished
+
+local concat, insert, remove = table.concat, table.insert, table.remove
+
+local find_tail, write_node, free_node, copy_nodelist = node.slide, node.write, node.free, node.copy_list
+local vpack_nodelist, hpack_nodelist = node.vpack, node.hpack
+local texdimen, texbox = tex.dimen, tex.box
+local settings_to_array = utilities.parsers.settings_to_array
+
+local nodes, node = nodes, node
+
+local nodepool = nodes.pool
+local tasks = nodes.tasks
+
+local new_kern = nodepool.kern
+local new_glyph = nodepool.glyph
+
+local trace_collecting = false trackers.register("streams.collecting", function(v) trace_collecting = v end)
+local trace_flushing = false trackers.register("streams.flushing", function(v) trace_flushing = v end)
+
+local report_streams = logs.reporter("streams")
+
+streams = streams or { } -- might move to the builders namespace
+local streams = streams
+
+local data, name, stack = { }, nil, { }
+
+function streams.enable(newname)
+ if newname == "default" then
+ name = nil
+ else
+ name = newname
+ end
+end
+
+function streams.disable()
+ name = stack[#stack]
+end
+
+function streams.start(newname)
+ insert(stack,name)
+ name = newname
+end
+
+function streams.stop(newname)
+ name = remove(stack)
+end
+
+function streams.collect(head,where)
+ if name and head and name ~= "default" then
+ local tail = node.slide(head)
+ local dana = data[name]
+ if not dana then
+ dana = { }
+ data[name] = dana
+ end
+ local last = dana[#dana]
+ if last then
+ local tail = find_tail(last)
+ tail.next, head.prev = head, tail
+ elseif last == false then
+ dana[#dana] = head
+ else
+ dana[1] = head
+ end
+ if trace_collecting then
+ report_streams("appending snippet %a to slot %s",name,#dana)
+ end
+ return nil, true
+ else
+ return head, false
+ end
+end
+
+function streams.push(thename)
+ if not thename or thename == "" then
+ thename = name
+ end
+ if thename and thename ~= "" then
+ local dana = data[thename]
+ if dana then
+ dana[#dana+1] = false
+ if trace_collecting then
+ report_streams("pushing snippet %a",thename)
+ end
+ end
+ end
+end
+
+function streams.flush(name,copy) -- problem: we need to migrate afterwards
+ local dana = data[name]
+ if dana then
+ local dn = #dana
+ if dn == 0 then
+ -- nothing to flush
+ elseif copy then
+ if trace_flushing then
+ report_streams("flushing copies of %s slots of %a",dn,name)
+ end
+ for i=1,dn do
+ local di = dana[i]
+ if di then
+ write_node(copy_nodelist(di.list)) -- list, will be option
+ end
+ end
+ if copy then
+ data[name] = nil
+ end
+ else
+ if trace_flushing then
+ report_streams("flushing %s slots of %a",dn,name)
+ end
+ for i=1,dn do
+ local di = dana[i]
+ if di then
+ write_node(di.list) -- list, will be option
+ di.list = nil
+ free_node(di)
+ end
+ end
+ end
+ end
+end
+
+function streams.synchronize(list) -- this is an experiment !
+ -- we don't optimize this as we want to trace in detail
+ list = settings_to_array(list)
+ local max = 0
+ if trace_flushing then
+ report_streams("synchronizing list: % t",list)
+ end
+ for i=1,#list do
+ local dana = data[list[i]]
+ if dana then
+ local n = #dana
+ if n > max then
+ max = n
+ end
+ end
+ end
+ if trace_flushing then
+ report_streams("maximum number of slots: %s",max)
+ end
+ for m=1,max do
+ local height, depth = 0, 0
+ for i=1,#list do
+ local name = list[i]
+ local dana = data[name]
+ local slot = dana[m]
+ if slot then
+ local vbox = vpack_nodelist(slot)
+ local ht, dp = vbox.height, vbox.depth
+ if ht > height then
+ height = ht
+ end
+ if dp > depth then
+ depth = dp
+ end
+ dana[m] = vbox
+ if trace_flushing then
+ report_streams("slot %s of %a is packed to height %p and depth %p",m,name,ht,dp)
+ end
+ end
+ end
+ if trace_flushing then
+ report_streams("slot %s has max height %p and max depth %p",m,height,depth)
+ end
+ local strutht, strutdp = texdimen.globalbodyfontstrutheight, texdimen.globalbodyfontstrutdepth
+ local struthtdp = strutht + strutdp
+ for i=1,#list do
+ local name = list[i]
+ local dana = data[name]
+ local vbox = dana[m]
+ if vbox then
+ local delta_height = height - vbox.height
+ local delta_depth = depth - vbox.depth
+ if delta_height > 0 or delta_depth > 0 then
+ if false then
+ -- actually we need to add glue and repack
+ vbox.height, vbox.depth = height, depth
+ if trace_flushing then
+ report_streams("slot %s of %a with delta (%p,%p) is compensated",m,i,delta_height,delta_depth)
+ end
+ else
+ -- this is not yet ok as we also need to keep an eye on vertical spacing
+ -- so we might need to do some splitting or whatever
+ local tail = vbox.list and find_tail(vbox.list)
+ local n, delta = 0, delta_height -- for tracing
+ while delta > 0 do
+ -- we need to add some interline penalties
+ local line = copy_nodelist(tex.box.strutbox)
+ line.height, line.depth = strutht, strutdp
+ if tail then
+ tail.next, line.prev = line, tail
+ end
+ tail = line
+ n, delta = n +1, delta - struthtdp
+ end
+ dana[m] = vpack_nodelist(vbox.list)
+ vbox.list = nil
+ free_node(vbox)
+ if trace_flushing then
+ report_streams("slot %s:%s with delta (%p,%p) is compensated by %s lines",m,i,delta_height,delta_depth,n)
+ end
+ end
+ end
+ else
+ -- make dummy
+ end
+ end
+ end
+end
+
+tasks.appendaction("mvlbuilders", "normalizers", "streams.collect")
+
+tasks.disableaction("mvlbuilders", "streams.collect")
+
+function streams.initialize()
+ tasks.enableaction ("mvlbuilders", "streams.collect")
+end
+
+-- todo: remove empty last { }'s
diff --git a/tex/context/base/regi-8859-1.lua b/tex/context/base/regi-8859-1.lua
index 2a3caea54..ff2182afa 100644
--- a/tex/context/base/regi-8859-1.lua
+++ b/tex/context/base/regi-8859-1.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-8859-1'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
- 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
- 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
- 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
- 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
- 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
- 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
- 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
-}
+if not modules then modules = { } end modules ['regi-8859-1'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
+}
diff --git a/tex/context/base/regi-8859-10.lua b/tex/context/base/regi-8859-10.lua
index 1d3888c9e..f23744b4a 100644
--- a/tex/context/base/regi-8859-10.lua
+++ b/tex/context/base/regi-8859-10.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-8859-10'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
- 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
- 0x00A0, 0x0104, 0x0112, 0x0122, 0x012A, 0x0128, 0x0136, 0x00A7, 0x013B, 0x0110, 0x0160, 0x0166, 0x017D, 0x00AD, 0x016A, 0x014A,
- 0x00B0, 0x0105, 0x0113, 0x0123, 0x012B, 0x0129, 0x0137, 0x00B7, 0x013C, 0x0111, 0x0161, 0x0167, 0x017E, 0x2015, 0x016B, 0x014B,
- 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x00CF,
- 0x00D0, 0x0145, 0x014C, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0168, 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
- 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x00EF,
- 0x00F0, 0x0146, 0x014D, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0169, 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x0138
-}
+if not modules then modules = { } end modules ['regi-8859-10'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ 0x00A0, 0x0104, 0x0112, 0x0122, 0x012A, 0x0128, 0x0136, 0x00A7, 0x013B, 0x0110, 0x0160, 0x0166, 0x017D, 0x00AD, 0x016A, 0x014A,
+ 0x00B0, 0x0105, 0x0113, 0x0123, 0x012B, 0x0129, 0x0137, 0x00B7, 0x013C, 0x0111, 0x0161, 0x0167, 0x017E, 0x2015, 0x016B, 0x014B,
+ 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x00CF,
+ 0x00D0, 0x0145, 0x014C, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0168, 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
+ 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x00EF,
+ 0x00F0, 0x0146, 0x014D, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0169, 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x0138
+}
diff --git a/tex/context/base/regi-8859-11.lua b/tex/context/base/regi-8859-11.lua
index f7a87efe9..54e5626c2 100644
--- a/tex/context/base/regi-8859-11.lua
+++ b/tex/context/base/regi-8859-11.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-8859-11'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
- 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
- 0x00A0, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07, 0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F,
- 0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17, 0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F,
- 0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27, 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F,
- 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37, 0x0E38, 0x0E39, 0x0E3A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0E3F,
- 0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47, 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F,
- 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0x0000, 0x0000, 0x0000, 0x0000
-}
+if not modules then modules = { } end modules ['regi-8859-11'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ 0x00A0, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07, 0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F,
+ 0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17, 0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F,
+ 0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27, 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F,
+ 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37, 0x0E38, 0x0E39, 0x0E3A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0E3F,
+ 0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47, 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F,
+ 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0x0000, 0x0000, 0x0000, 0x0000
+}
diff --git a/tex/context/base/regi-8859-13.lua b/tex/context/base/regi-8859-13.lua
index 163b441c7..1646133b5 100644
--- a/tex/context/base/regi-8859-13.lua
+++ b/tex/context/base/regi-8859-13.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-8859-13'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
- 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
- 0x00A0, 0x201D, 0x00A2, 0x00A3, 0x00A4, 0x201E, 0x00A6, 0x00A7, 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00C6,
- 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x201C, 0x00B5, 0x00B6, 0x00B7, 0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6,
- 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B,
- 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF,
- 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C,
- 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, 0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x2019
-}
+if not modules then modules = { } end modules ['regi-8859-13'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ 0x00A0, 0x201D, 0x00A2, 0x00A3, 0x00A4, 0x201E, 0x00A6, 0x00A7, 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00C6,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x201C, 0x00B5, 0x00B6, 0x00B7, 0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6,
+ 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B,
+ 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF,
+ 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C,
+ 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, 0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x2019
+}
diff --git a/tex/context/base/regi-8859-14.lua b/tex/context/base/regi-8859-14.lua
index b69eaecea..2b0c68814 100644
--- a/tex/context/base/regi-8859-14.lua
+++ b/tex/context/base/regi-8859-14.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-8859-14'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
- 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
- 0x00A0, 0x1E02, 0x1E03, 0x00A3, 0x010A, 0x010B, 0x1E0A, 0x00A7, 0x1E80, 0x00A9, 0x1E82, 0x1E0B, 0x1EF2, 0x00AD, 0x00AE, 0x0178,
- 0x1E1E, 0x1E1F, 0x0120, 0x0121, 0x1E40, 0x1E41, 0x00B6, 0x1E56, 0x1E81, 0x1E57, 0x1E83, 0x1E60, 0x1EF3, 0x1E84, 0x1E85, 0x1E61,
- 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
- 0x0174, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x1E6A, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x0176, 0x00DF,
- 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
- 0x0175, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x1E6B, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x0177, 0x00FF
-}
+if not modules then modules = { } end modules ['regi-8859-14'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ 0x00A0, 0x1E02, 0x1E03, 0x00A3, 0x010A, 0x010B, 0x1E0A, 0x00A7, 0x1E80, 0x00A9, 0x1E82, 0x1E0B, 0x1EF2, 0x00AD, 0x00AE, 0x0178,
+ 0x1E1E, 0x1E1F, 0x0120, 0x0121, 0x1E40, 0x1E41, 0x00B6, 0x1E56, 0x1E81, 0x1E57, 0x1E83, 0x1E60, 0x1EF3, 0x1E84, 0x1E85, 0x1E61,
+ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ 0x0174, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x1E6A, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x0176, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ 0x0175, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x1E6B, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x0177, 0x00FF
+}
diff --git a/tex/context/base/regi-8859-15.lua b/tex/context/base/regi-8859-15.lua
index 3bc1d527a..48861f396 100644
--- a/tex/context/base/regi-8859-15.lua
+++ b/tex/context/base/regi-8859-15.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-8859-15'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
- 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
- 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AC, 0x00A5, 0x0160, 0x00A7, 0x0161, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
- 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x017D, 0x00B5, 0x00B6, 0x00B7, 0x017E, 0x00B9, 0x00BA, 0x00BB, 0x0152, 0x0153, 0x0178, 0x00BF,
- 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
- 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
- 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
- 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
-}
+if not modules then modules = { } end modules ['regi-8859-15'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AC, 0x00A5, 0x0160, 0x00A7, 0x0161, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x017D, 0x00B5, 0x00B6, 0x00B7, 0x017E, 0x00B9, 0x00BA, 0x00BB, 0x0152, 0x0153, 0x0178, 0x00BF,
+ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
+}
diff --git a/tex/context/base/regi-8859-16.lua b/tex/context/base/regi-8859-16.lua
index c2a235363..e122a2042 100644
--- a/tex/context/base/regi-8859-16.lua
+++ b/tex/context/base/regi-8859-16.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-8859-16'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
- 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
- 0x00A0, 0x0104, 0x0105, 0x0141, 0x20AC, 0x201E, 0x0160, 0x00A7, 0x0161, 0x00A9, 0x0218, 0x00AB, 0x0179, 0x00AD, 0x017A, 0x017B,
- 0x00B0, 0x00B1, 0x010C, 0x0142, 0x017D, 0x201D, 0x00B6, 0x00B7, 0x017E, 0x010D, 0x0219, 0x00BB, 0x0152, 0x0153, 0x0178, 0x017C,
- 0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0106, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
- 0x0110, 0x0143, 0x00D2, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x015A, 0x0170, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0118, 0x021A, 0x00DF,
- 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x0107, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
- 0x0111, 0x0144, 0x00F2, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x015B, 0x0171, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0119, 0x021B, 0x00FF
-}
+if not modules then modules = { } end modules ['regi-8859-16'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ 0x00A0, 0x0104, 0x0105, 0x0141, 0x20AC, 0x201E, 0x0160, 0x00A7, 0x0161, 0x00A9, 0x0218, 0x00AB, 0x0179, 0x00AD, 0x017A, 0x017B,
+ 0x00B0, 0x00B1, 0x010C, 0x0142, 0x017D, 0x201D, 0x00B6, 0x00B7, 0x017E, 0x010D, 0x0219, 0x00BB, 0x0152, 0x0153, 0x0178, 0x017C,
+ 0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0106, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ 0x0110, 0x0143, 0x00D2, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x015A, 0x0170, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0118, 0x021A, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x0107, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ 0x0111, 0x0144, 0x00F2, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x015B, 0x0171, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0119, 0x021B, 0x00FF
+}
diff --git a/tex/context/base/regi-8859-2.lua b/tex/context/base/regi-8859-2.lua
index f0fe5f404..affd6c3ca 100644
--- a/tex/context/base/regi-8859-2.lua
+++ b/tex/context/base/regi-8859-2.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-8859-2'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
- 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
- 0x00A0, 0x0104, 0x02D8, 0x0141, 0x00A4, 0x013D, 0x015A, 0x00A7, 0x00A8, 0x0160, 0x015E, 0x0164, 0x0179, 0x00AD, 0x017D, 0x017B,
- 0x00B0, 0x0105, 0x02DB, 0x0142, 0x00B4, 0x013E, 0x015B, 0x02C7, 0x00B8, 0x0161, 0x015F, 0x0165, 0x017A, 0x02DD, 0x017E, 0x017C,
- 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,
- 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF,
- 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,
- 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9
-}
+if not modules then modules = { } end modules ['regi-8859-2'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ 0x00A0, 0x0104, 0x02D8, 0x0141, 0x00A4, 0x013D, 0x015A, 0x00A7, 0x00A8, 0x0160, 0x015E, 0x0164, 0x0179, 0x00AD, 0x017D, 0x017B,
+ 0x00B0, 0x0105, 0x02DB, 0x0142, 0x00B4, 0x013E, 0x015B, 0x02C7, 0x00B8, 0x0161, 0x015F, 0x0165, 0x017A, 0x02DD, 0x017E, 0x017C,
+ 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,
+ 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF,
+ 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,
+ 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9
+}
diff --git a/tex/context/base/regi-8859-3.lua b/tex/context/base/regi-8859-3.lua
index e84220bde..4b5c54b4f 100644
--- a/tex/context/base/regi-8859-3.lua
+++ b/tex/context/base/regi-8859-3.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-8859-3'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
- 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
- 0x00A0, 0x0126, 0x02D8, 0x00A3, 0x00A4, 0x0000, 0x0124, 0x00A7, 0x00A8, 0x0130, 0x015E, 0x011E, 0x0134, 0x00AD, 0x0000, 0x017B,
- 0x00B0, 0x0127, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x0125, 0x00B7, 0x00B8, 0x0131, 0x015F, 0x011F, 0x0135, 0x00BD, 0x0000, 0x017C,
- 0x00C0, 0x00C1, 0x00C2, 0x0000, 0x00C4, 0x010A, 0x0108, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
- 0x0000, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x0120, 0x00D6, 0x00D7, 0x011C, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x016C, 0x015C, 0x00DF,
- 0x00E0, 0x00E1, 0x00E2, 0x0000, 0x00E4, 0x010B, 0x0109, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
- 0x0000, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x0121, 0x00F6, 0x00F7, 0x011D, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x016D, 0x015D, 0x02D9
-}
+if not modules then modules = { } end modules ['regi-8859-3'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ 0x00A0, 0x0126, 0x02D8, 0x00A3, 0x00A4, 0x0000, 0x0124, 0x00A7, 0x00A8, 0x0130, 0x015E, 0x011E, 0x0134, 0x00AD, 0x0000, 0x017B,
+ 0x00B0, 0x0127, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x0125, 0x00B7, 0x00B8, 0x0131, 0x015F, 0x011F, 0x0135, 0x00BD, 0x0000, 0x017C,
+ 0x00C0, 0x00C1, 0x00C2, 0x0000, 0x00C4, 0x010A, 0x0108, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ 0x0000, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x0120, 0x00D6, 0x00D7, 0x011C, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x016C, 0x015C, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x0000, 0x00E4, 0x010B, 0x0109, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ 0x0000, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x0121, 0x00F6, 0x00F7, 0x011D, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x016D, 0x015D, 0x02D9
+}
diff --git a/tex/context/base/regi-8859-4.lua b/tex/context/base/regi-8859-4.lua
index 9fdc39a40..774ec2e10 100644
--- a/tex/context/base/regi-8859-4.lua
+++ b/tex/context/base/regi-8859-4.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-8859-4'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
- 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
- 0x00A0, 0x0104, 0x0138, 0x0156, 0x00A4, 0x0128, 0x013B, 0x00A7, 0x00A8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00AD, 0x017D, 0x00AF,
- 0x00B0, 0x0105, 0x02DB, 0x0157, 0x00B4, 0x0129, 0x013C, 0x02C7, 0x00B8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014A, 0x017E, 0x014B,
- 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x012A,
- 0x0110, 0x0145, 0x014C, 0x0136, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x0168, 0x016A, 0x00DF,
- 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x012B,
- 0x0111, 0x0146, 0x014D, 0x0137, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x0169, 0x016B, 0x02D9
-}
+if not modules then modules = { } end modules ['regi-8859-4'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ 0x00A0, 0x0104, 0x0138, 0x0156, 0x00A4, 0x0128, 0x013B, 0x00A7, 0x00A8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00AD, 0x017D, 0x00AF,
+ 0x00B0, 0x0105, 0x02DB, 0x0157, 0x00B4, 0x0129, 0x013C, 0x02C7, 0x00B8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014A, 0x017E, 0x014B,
+ 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x012A,
+ 0x0110, 0x0145, 0x014C, 0x0136, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x0168, 0x016A, 0x00DF,
+ 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x012B,
+ 0x0111, 0x0146, 0x014D, 0x0137, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x0169, 0x016B, 0x02D9
+}
diff --git a/tex/context/base/regi-8859-5.lua b/tex/context/base/regi-8859-5.lua
index af35a71b8..1137f37bb 100644
--- a/tex/context/base/regi-8859-5.lua
+++ b/tex/context/base/regi-8859-5.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-8859-5'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
- 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
- 0x00A0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x00AD, 0x040E, 0x040F,
- 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
- 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
- 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
- 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
- 0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x00A7, 0x045E, 0x045F
-}
+if not modules then modules = { } end modules ['regi-8859-5'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ 0x00A0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x00AD, 0x040E, 0x040F,
+ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
+ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
+ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+ 0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x00A7, 0x045E, 0x045F
+}
diff --git a/tex/context/base/regi-8859-6.lua b/tex/context/base/regi-8859-6.lua
index 89ca3ce7f..651ae79ff 100644
--- a/tex/context/base/regi-8859-6.lua
+++ b/tex/context/base/regi-8859-6.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-8859-6'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
- 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
- 0x00A0, 0x0000, 0x0000, 0x0000, 0x00A4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x060C, 0x00AD, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x061B, 0x0000, 0x0000, 0x0000, 0x061F,
- 0x0000, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F,
- 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F,
- 0x0650, 0x0651, 0x0652, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
-}
+if not modules then modules = { } end modules ['regi-8859-6'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ 0x00A0, 0x0000, 0x0000, 0x0000, 0x00A4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x060C, 0x00AD, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x061B, 0x0000, 0x0000, 0x0000, 0x061F,
+ 0x0000, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F,
+ 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F,
+ 0x0650, 0x0651, 0x0652, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+}
diff --git a/tex/context/base/regi-8859-7.lua b/tex/context/base/regi-8859-7.lua
index 8769b0483..08cbbab6e 100644
--- a/tex/context/base/regi-8859-7.lua
+++ b/tex/context/base/regi-8859-7.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-8859-7'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
- 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
- 0x00A0, 0x2018, 0x2019, 0x00A3, 0x20AC, 0x20AF, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x037A, 0x00AB, 0x00AC, 0x00AD, 0x0000, 0x2015,
- 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x0385, 0x0386, 0x00B7, 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F,
- 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
- 0x03A0, 0x03A1, 0x0000, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
- 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
- 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x0000
-}
+if not modules then modules = { } end modules ['regi-8859-7'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ 0x00A0, 0x2018, 0x2019, 0x00A3, 0x20AC, 0x20AF, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x037A, 0x00AB, 0x00AC, 0x00AD, 0x0000, 0x2015,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x0385, 0x0386, 0x00B7, 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F,
+ 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
+ 0x03A0, 0x03A1, 0x0000, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
+ 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
+ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x0000
+}
diff --git a/tex/context/base/regi-8859-8.lua b/tex/context/base/regi-8859-8.lua
index e72d7c7fb..b69609991 100644
--- a/tex/context/base/regi-8859-8.lua
+++ b/tex/context/base/regi-8859-8.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-8859-8'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
- 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
- 0x00A0, 0x0000, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
- 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2017,
- 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
- 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x0000, 0x0000, 0x200E, 0x200F, 0x0000
-}
+if not modules then modules = { } end modules ['regi-8859-8'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ 0x00A0, 0x0000, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2017,
+ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
+ 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x0000, 0x0000, 0x200E, 0x200F, 0x0000
+}
diff --git a/tex/context/base/regi-8859-9.lua b/tex/context/base/regi-8859-9.lua
index eb9515af9..773307fff 100644
--- a/tex/context/base/regi-8859-9.lua
+++ b/tex/context/base/regi-8859-9.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-8859-9'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
- 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
- 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
- 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
- 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
- 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF,
- 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
- 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF
-}
+if not modules then modules = { } end modules ['regi-8859-9'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF
+}
diff --git a/tex/context/base/regi-cp1250.lua b/tex/context/base/regi-cp1250.lua
index 80a4b8639..00d55d1b8 100644
--- a/tex/context/base/regi-cp1250.lua
+++ b/tex/context/base/regi-cp1250.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-cp1250'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179,
- 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A,
- 0x00A0, 0x02C7, 0x02D8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x017B,
- 0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E, 0x017C,
- 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,
- 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF,
- 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,
- 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9
-}
+if not modules then modules = { } end modules ['regi-cp1250'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179,
+ 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A,
+ 0x00A0, 0x02C7, 0x02D8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x017B,
+ 0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E, 0x017C,
+ 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,
+ 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF,
+ 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,
+ 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9
+}
diff --git a/tex/context/base/regi-cp1251.lua b/tex/context/base/regi-cp1251.lua
index 07f1d81ad..7bb72e0cc 100644
--- a/tex/context/base/regi-cp1251.lua
+++ b/tex/context/base/regi-cp1251.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-cp1251'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, 0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F,
- 0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F,
- 0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7, 0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407,
- 0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7, 0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457,
- 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
- 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
- 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
- 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F
-}
+if not modules then modules = { } end modules ['regi-cp1251'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, 0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F,
+ 0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F,
+ 0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7, 0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407,
+ 0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7, 0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457,
+ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
+ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
+ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F
+}
diff --git a/tex/context/base/regi-cp1252.lua b/tex/context/base/regi-cp1252.lua
index 08bd22bf6..86954c9af 100644
--- a/tex/context/base/regi-cp1252.lua
+++ b/tex/context/base/regi-cp1252.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-cp1252'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017D, 0x0000,
- 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x017E, 0x0178,
- 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
- 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
- 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
- 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
- 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
- 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
-}
+if not modules then modules = { } end modules ['regi-cp1252'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017D, 0x0000,
+ 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x017E, 0x0178,
+ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
+}
diff --git a/tex/context/base/regi-cp1253.lua b/tex/context/base/regi-cp1253.lua
index d272692cf..31a411efe 100644
--- a/tex/context/base/regi-cp1253.lua
+++ b/tex/context/base/regi-cp1253.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-cp1253'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000, 0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x00A0, 0x0385, 0x0386, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x0000, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x2015,
- 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5, 0x00B6, 0x00B7, 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F,
- 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
- 0x03A0, 0x03A1, 0x0000, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
- 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
- 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x0000
-}
+if not modules then modules = { } end modules ['regi-cp1253'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000, 0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x00A0, 0x0385, 0x0386, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x0000, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x2015,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5, 0x00B6, 0x00B7, 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F,
+ 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
+ 0x03A0, 0x03A1, 0x0000, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
+ 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
+ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x0000
+}
diff --git a/tex/context/base/regi-cp1254.lua b/tex/context/base/regi-cp1254.lua
index c8ef03da9..73b9927c6 100644
--- a/tex/context/base/regi-cp1254.lua
+++ b/tex/context/base/regi-cp1254.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-cp1254'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178,
- 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
- 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
- 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
- 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF,
- 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
- 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF
-}
+if not modules then modules = { } end modules ['regi-cp1254'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178,
+ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF
+}
diff --git a/tex/context/base/regi-cp1255.lua b/tex/context/base/regi-cp1255.lua
index 7f33b67a8..2abb16b54 100644
--- a/tex/context/base/regi-cp1255.lua
+++ b/tex/context/base/regi-cp1255.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-cp1255'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AA, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
- 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
- 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, 0x05B8, 0x05B9, 0x0000, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF,
- 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
- 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x0000, 0x0000, 0x200E, 0x200F, 0x0000
-}
+if not modules then modules = { } end modules ['regi-cp1255'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AA, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+ 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, 0x05B8, 0x05B9, 0x0000, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF,
+ 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
+ 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x0000, 0x0000, 0x200E, 0x200F, 0x0000
+}
diff --git a/tex/context/base/regi-cp1256.lua b/tex/context/base/regi-cp1256.lua
index e9a4363c7..a0697c321 100644
--- a/tex/context/base/regi-cp1256.lua
+++ b/tex/context/base/regi-cp1256.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-cp1256'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688,
- 0x06AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x06A9, 0x2122, 0x0691, 0x203A, 0x0153, 0x200C, 0x200D, 0x06BA,
- 0x00A0, 0x060C, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x06BE, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
- 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x061B, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x061F,
- 0x06C1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F,
- 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7, 0x0637, 0x0638, 0x0639, 0x063A, 0x0640, 0x0641, 0x0642, 0x0643,
- 0x00E0, 0x0644, 0x00E2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0649, 0x064A, 0x00EE, 0x00EF,
- 0x064B, 0x064C, 0x064D, 0x064E, 0x00F4, 0x064F, 0x0650, 0x00F7, 0x0651, 0x00F9, 0x0652, 0x00FB, 0x00FC, 0x200E, 0x200F, 0x06D2
-}
+if not modules then modules = { } end modules ['regi-cp1256'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688,
+ 0x06AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x06A9, 0x2122, 0x0691, 0x203A, 0x0153, 0x200C, 0x200D, 0x06BA,
+ 0x00A0, 0x060C, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x06BE, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x061B, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x061F,
+ 0x06C1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F,
+ 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7, 0x0637, 0x0638, 0x0639, 0x063A, 0x0640, 0x0641, 0x0642, 0x0643,
+ 0x00E0, 0x0644, 0x00E2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0649, 0x064A, 0x00EE, 0x00EF,
+ 0x064B, 0x064C, 0x064D, 0x064E, 0x00F4, 0x064F, 0x0650, 0x00F7, 0x0651, 0x00F9, 0x0652, 0x00FB, 0x00FC, 0x200E, 0x200F, 0x06D2
+}
diff --git a/tex/context/base/regi-cp1257.lua b/tex/context/base/regi-cp1257.lua
index a4a492a13..6e39c10d4 100644
--- a/tex/context/base/regi-cp1257.lua
+++ b/tex/context/base/regi-cp1257.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-cp1257'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000, 0x2030, 0x0000, 0x2039, 0x0000, 0x00A8, 0x02C7, 0x00B8,
- 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x00AF, 0x02DB, 0x0000,
- 0x00A0, 0x0000, 0x00A2, 0x00A3, 0x00A4, 0x0000, 0x00A6, 0x00A7, 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00C6,
- 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6,
- 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B,
- 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF,
- 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C,
- 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, 0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x02D9
-}
+if not modules then modules = { } end modules ['regi-cp1257'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000, 0x2030, 0x0000, 0x2039, 0x0000, 0x00A8, 0x02C7, 0x00B8,
+ 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x00AF, 0x02DB, 0x0000,
+ 0x00A0, 0x0000, 0x00A2, 0x00A3, 0x00A4, 0x0000, 0x00A6, 0x00A7, 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00C6,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6,
+ 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B,
+ 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF,
+ 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C,
+ 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, 0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x02D9
+}
diff --git a/tex/context/base/regi-cp1258.lua b/tex/context/base/regi-cp1258.lua
index a4630e7e9..cf64d2ab6 100644
--- a/tex/context/base/regi-cp1258.lua
+++ b/tex/context/base/regi-cp1258.lua
@@ -1,26 +1,26 @@
-if not modules then modules = { } end modules ['regi-cp1258'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-return { [0] =
- 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
- 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
- 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
- 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
- 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
- 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
- 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
- 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
- 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0000, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0000, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178,
- 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
- 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
- 0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x0300, 0x00CD, 0x00CE, 0x00CF,
- 0x0110, 0x00D1, 0x0309, 0x00D3, 0x00D4, 0x01A0, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x01AF, 0x0303, 0x00DF,
- 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0301, 0x00ED, 0x00EE, 0x00EF,
- 0x0111, 0x00F1, 0x0323, 0x00F3, 0x00F4, 0x01A1, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x01B0, 0x20AB, 0x00FF
-}
+if not modules then modules = { } end modules ['regi-cp1258'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+return { [0] =
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0000, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0000, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178,
+ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+ 0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x0300, 0x00CD, 0x00CE, 0x00CF,
+ 0x0110, 0x00D1, 0x0309, 0x00D3, 0x00D4, 0x01A0, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x01AF, 0x0303, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0301, 0x00ED, 0x00EE, 0x00EF,
+ 0x0111, 0x00F1, 0x0323, 0x00F3, 0x00F4, 0x01A1, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x01B0, 0x20AB, 0x00FF
+}
diff --git a/tex/context/base/regi-demo.lua b/tex/context/base/regi-demo.lua
index f709a11aa..689f44e32 100644
--- a/tex/context/base/regi-demo.lua
+++ b/tex/context/base/regi-demo.lua
@@ -1,22 +1,22 @@
-if not modules then modules = { } end modules ['regi-demo'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- digits -> *
-
-return {
- [0x0030] = 0x002A,
- [0x0031] = 0x002A,
- [0x0032] = 0x002A,
- [0x0033] = 0x002A,
- [0x0034] = 0x002A,
- [0x0035] = 0x002A,
- [0x0036] = 0x002A,
- [0x0037] = 0x002A,
- [0x0038] = 0x002A,
- [0x0039] = 0x002A,
-}
+if not modules then modules = { } end modules ['regi-demo'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- digits -> *
+
+return {
+ [0x0030] = 0x002A,
+ [0x0031] = 0x002A,
+ [0x0032] = 0x002A,
+ [0x0033] = 0x002A,
+ [0x0034] = 0x002A,
+ [0x0035] = 0x002A,
+ [0x0036] = 0x002A,
+ [0x0037] = 0x002A,
+ [0x0038] = 0x002A,
+ [0x0039] = 0x002A,
+}
diff --git a/tex/context/base/regi-ini.lua b/tex/context/base/regi-ini.lua
index 784a1ed46..d5d278b16 100644
--- a/tex/context/base/regi-ini.lua
+++ b/tex/context/base/regi-ini.lua
@@ -1,388 +1,388 @@
-if not modules then modules = { } end modules ['regi-ini'] = {
- version = 1.001,
- comment = "companion to regi-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
---[[ldx--
-<p>Regimes take care of converting the input characters into
-<l n='utf'/> sequences. The conversion tables are loaded at
-runtime.</p>
---ldx]]--
-
-local commands, context = commands, context
-
-local utfchar = utf.char
-local P, Cs, lpegmatch = lpeg.P, lpeg.Cs, lpeg.match
-local char, gsub, format, gmatch, byte, match = string.char, string.gsub, string.format, string.gmatch, string.byte, string.match
-local next = next
-local insert, remove, fastcopy = table.insert, table.remove, table.fastcopy
-local concat = table.concat
-local totable = string.totable
-
-local allocate = utilities.storage.allocate
-local sequencers = utilities.sequencers
-local textlineactions = resolvers.openers.helpers.textlineactions
-local setmetatableindex = table.setmetatableindex
-
---[[ldx--
-<p>We will hook regime handling code into the input methods.</p>
---ldx]]--
-
-local trace_translating = false trackers.register("regimes.translating", function(v) trace_translating = v end)
-
-local report_loading = logs.reporter("regimes","loading")
-local report_translating = logs.reporter("regimes","translating")
-
-regimes = regimes or { }
-local regimes = regimes
-
-local mapping = allocate {
- utf = false
-}
-
-local backmapping = allocate {
-}
-
--- regimes.mapping = mapping
-
-local synonyms = { -- backward compatibility list
-
- ["windows-1250"] = "cp1250",
- ["windows-1251"] = "cp1251",
- ["windows-1252"] = "cp1252",
- ["windows-1253"] = "cp1253",
- ["windows-1254"] = "cp1254",
- ["windows-1255"] = "cp1255",
- ["windows-1256"] = "cp1256",
- ["windows-1257"] = "cp1257",
- ["windows-1258"] = "cp1258",
-
- ["il1"] = "8859-1",
- ["il2"] = "8859-2",
- ["il3"] = "8859-3",
- ["il4"] = "8859-4",
- ["il5"] = "8859-9",
- ["il6"] = "8859-10",
- ["il7"] = "8859-13",
- ["il8"] = "8859-14",
- ["il9"] = "8859-15",
- ["il10"] = "8859-16",
-
- ["iso-8859-1"] = "8859-1",
- ["iso-8859-2"] = "8859-2",
- ["iso-8859-3"] = "8859-3",
- ["iso-8859-4"] = "8859-4",
- ["iso-8859-9"] = "8859-9",
- ["iso-8859-10"] = "8859-10",
- ["iso-8859-13"] = "8859-13",
- ["iso-8859-14"] = "8859-14",
- ["iso-8859-15"] = "8859-15",
- ["iso-8859-16"] = "8859-16",
-
- ["latin1"] = "8859-1",
- ["latin2"] = "8859-2",
- ["latin3"] = "8859-3",
- ["latin4"] = "8859-4",
- ["latin5"] = "8859-9",
- ["latin6"] = "8859-10",
- ["latin7"] = "8859-13",
- ["latin8"] = "8859-14",
- ["latin9"] = "8859-15",
- ["latin10"] = "8859-16",
-
- ["utf-8"] = "utf",
- ["utf8"] = "utf",
- [""] = "utf",
-
- ["windows"] = "cp1252",
-
-}
-
-local currentregime = "utf"
-
-local function loadregime(mapping,regime)
- local name = resolvers.findfile(format("regi-%s.lua",regime)) or ""
- local data = name ~= "" and dofile(name)
- if data then
- vector = { }
- for eightbit, unicode in next, data do
- vector[char(eightbit)] = utfchar(unicode)
- end
- report_loading("vector %a is loaded",regime)
- else
- vector = false
- report_loading("vector %a is unknown",regime)
- end
- mapping[regime] = vector
- return vector
-end
-
-local function loadreverse(t,k)
- local t = { }
- for k, v in next, mapping[k] do
- t[v] = k
- end
- backmapping[k] = t
- return t
-end
-
-setmetatableindex(mapping, loadregime)
-setmetatableindex(backmapping,loadreverse)
-
-local function translate(line,regime)
- if line and #line > 0 then
- local map = mapping[regime and synonyms[regime] or regime or currentregime]
- if map then
- line = gsub(line,".",map)
- end
- end
- return line
-end
-
--- local remappers = { }
---
--- local function toregime(vector,str,default) -- toregime('8859-1',"abcde Ä","?")
--- local t = backmapping[vector]
--- local remapper = remappers[vector]
--- if not remapper then
--- remapper = utf.remapper(t)
--- remappers[t] = remapper
--- end
--- local m = getmetatable(t)
--- setmetatableindex(t, function(t,k)
--- local v = default or "?"
--- t[k] = v
--- return v
--- end)
--- str = remapper(str)
--- setmetatable(t,m)
--- return str
--- end
---
--- -- much faster (but only matters when we have > 10K calls
-
-local cache = { } -- if really needed we can copy vectors and hash defaults
-
-setmetatableindex(cache, function(t,k)
- local v = { remappers = { } }
- t[k] = v
- return v
-end)
-
-local function toregime(vector,str,default) -- toregime('8859-1',"abcde Ä","?")
- local d = default or "?"
- local c = cache[vector].remappers
- local r = c[d]
- if not r then
- local t = fastcopy(backmapping[vector])
- setmetatableindex(t, function(t,k)
- local v = d
- t[k] = v
- return v
- end)
- r = utf.remapper(t)
- c[d] = r
- end
- return r(str)
-end
-
-local function disable()
- currentregime = "utf"
- sequencers.disableaction(textlineactions,"regimes.process")
-end
-
-local function enable(regime)
- regime = synonyms[regime] or regime
- if mapping[regime] == false then
- disable()
- else
- currentregime = regime
- sequencers.enableaction(textlineactions,"regimes.process")
- end
-end
-
-regimes.toregime = toregime
-regimes.translate = translate
-regimes.enable = enable
-regimes.disable = disable
-
--- The following function can be used when we want to make sure that
--- utf gets passed unharmed. This is needed for modules.
-
-local level = 0
-
-function regimes.process(str,filename,currentline,noflines,coding)
- if level == 0 and coding ~= "utf-8" then
- str = translate(str,currentregime)
- if trace_translating then
- report_translating("utf: %s",str)
- end
- end
- return str
-end
-
-local function push()
- level = level + 1
- if trace_translating then
- report_translating("pushing level %s",level)
- end
-end
-
-local function pop()
- if level > 0 then
- if trace_translating then
- report_translating("popping level %s",level)
- end
- level = level - 1
- end
-end
-
-regimes.push = push
-regimes.pop = pop
-
-sequencers.prependaction(textlineactions,"system","regimes.process")
-sequencers.disableaction(textlineactions,"regimes.process")
-
--- interface:
-
-commands.enableregime = enable
-commands.disableregime = disable
-
-commands.pushregime = push
-commands.popregime = pop
-
-function commands.currentregime()
- context(currentregime)
-end
-
-local stack = { }
-
-function commands.startregime(regime)
- insert(stack,currentregime)
- if trace_translating then
- report_translating("start using %a",regime)
- end
- enable(regime)
-end
-
-function commands.stopregime()
- if #stack > 0 then
- local regime = remove(stack)
- if trace_translating then
- report_translating("stop using %a",regime)
- end
- enable(regime)
- end
-end
-
--- Next we provide some hacks. Unfortunately we run into crappy encoded
--- (read : mixed) encoded xml files that have these ë ä ö ü sequences
--- instead of ë ä ö ü
-
-local patterns = { }
-
--- function regimes.cleanup(regime,str)
--- local p = patterns[regime]
--- if p == nil then
--- regime = regime and synonyms[regime] or regime or currentregime
--- local vector = regime ~= "utf" and mapping[regime]
--- if vector then
--- local list = { }
--- for k, uchar in next, vector do
--- local stream = totable(uchar)
--- for i=1,#stream do
--- stream[i] = vector[stream[i]]
--- end
--- list[concat(stream)] = uchar
--- end
--- p = lpeg.append(list,nil,true)
--- p = Cs((p+1)^0)
--- -- lpeg.print(p) -- size 1604
--- else
--- p = false
--- end
--- patterns[vector] = p
--- end
--- return p and lpegmatch(p,str) or str
--- end
---
--- twice as fast and much less lpeg bytecode
-
-function regimes.cleanup(regime,str)
- local p = patterns[regime]
- if p == nil then
- regime = regime and synonyms[regime] or regime or currentregime
- local vector = regime ~= "utf" and mapping[regime]
- if vector then
- local utfchars = { }
- local firsts = { }
- for k, uchar in next, vector do
- local stream = { }
- local split = totable(uchar)
- local nofsplits = #split
- if nofsplits > 1 then
- local first
- for i=1,nofsplits do
- local u = vector[split[i]]
- if not first then
- first = firsts[u]
- if not first then
- first = { }
- firsts[u] = first
- end
- end
- stream[i] = u
- end
- local nofstream = #stream
- if nofstream > 1 then
- first[#first+1] = concat(stream,2,nofstream)
- utfchars[concat(stream)] = uchar
- end
- end
- end
- p = P(false)
- for k, v in next, firsts do
- local q = P(false)
- for i=1,#v do
- q = q + P(v[i])
- end
- p = p + P(k) * q
- end
- p = Cs(((p+1)/utfchars)^1)
- -- lpeg.print(p) -- size: 1042
- else
- p = false
- end
- patterns[regime] = p
- end
- return p and lpegmatch(p,str) or str
-end
-
--- local map = require("regi-cp1252")
--- local old = [[test ë ä ö ü crap]]
--- local new = correctencoding(map,old)
---
--- print(old,new)
-
--- obsolete:
---
--- function regimes.setsynonym(synonym,target)
--- synonyms[synonym] = target
--- end
---
--- function regimes.truename(regime)
--- return regime and synonyms[regime] or regime or currentregime
--- end
---
--- commands.setregimesynonym = regimes.setsynonym
---
--- function commands.trueregimename(regime)
--- context(regimes.truename(regime))
--- end
---
--- function regimes.load(regime)
--- return mapping[synonyms[regime] or regime]
--- end
+if not modules then modules = { } end modules ['regi-ini'] = {
+ version = 1.001,
+ comment = "companion to regi-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[ldx--
+<p>Regimes take care of converting the input characters into
+<l n='utf'/> sequences. The conversion tables are loaded at
+runtime.</p>
+--ldx]]--
+
+local commands, context = commands, context
+
+local utfchar = utf.char
+local P, Cs, lpegmatch = lpeg.P, lpeg.Cs, lpeg.match
+local char, gsub, format, gmatch, byte, match = string.char, string.gsub, string.format, string.gmatch, string.byte, string.match
+local next = next
+local insert, remove, fastcopy = table.insert, table.remove, table.fastcopy
+local concat = table.concat
+local totable = string.totable
+
+local allocate = utilities.storage.allocate
+local sequencers = utilities.sequencers
+local textlineactions = resolvers.openers.helpers.textlineactions
+local setmetatableindex = table.setmetatableindex
+
+--[[ldx--
+<p>We will hook regime handling code into the input methods.</p>
+--ldx]]--
+
+local trace_translating = false trackers.register("regimes.translating", function(v) trace_translating = v end)
+
+local report_loading = logs.reporter("regimes","loading")
+local report_translating = logs.reporter("regimes","translating")
+
+regimes = regimes or { }
+local regimes = regimes
+
+local mapping = allocate {
+ utf = false
+}
+
+local backmapping = allocate {
+}
+
+-- regimes.mapping = mapping
+
+local synonyms = { -- backward compatibility list
+
+ ["windows-1250"] = "cp1250",
+ ["windows-1251"] = "cp1251",
+ ["windows-1252"] = "cp1252",
+ ["windows-1253"] = "cp1253",
+ ["windows-1254"] = "cp1254",
+ ["windows-1255"] = "cp1255",
+ ["windows-1256"] = "cp1256",
+ ["windows-1257"] = "cp1257",
+ ["windows-1258"] = "cp1258",
+
+ ["il1"] = "8859-1",
+ ["il2"] = "8859-2",
+ ["il3"] = "8859-3",
+ ["il4"] = "8859-4",
+ ["il5"] = "8859-9",
+ ["il6"] = "8859-10",
+ ["il7"] = "8859-13",
+ ["il8"] = "8859-14",
+ ["il9"] = "8859-15",
+ ["il10"] = "8859-16",
+
+ ["iso-8859-1"] = "8859-1",
+ ["iso-8859-2"] = "8859-2",
+ ["iso-8859-3"] = "8859-3",
+ ["iso-8859-4"] = "8859-4",
+ ["iso-8859-9"] = "8859-9",
+ ["iso-8859-10"] = "8859-10",
+ ["iso-8859-13"] = "8859-13",
+ ["iso-8859-14"] = "8859-14",
+ ["iso-8859-15"] = "8859-15",
+ ["iso-8859-16"] = "8859-16",
+
+ ["latin1"] = "8859-1",
+ ["latin2"] = "8859-2",
+ ["latin3"] = "8859-3",
+ ["latin4"] = "8859-4",
+ ["latin5"] = "8859-9",
+ ["latin6"] = "8859-10",
+ ["latin7"] = "8859-13",
+ ["latin8"] = "8859-14",
+ ["latin9"] = "8859-15",
+ ["latin10"] = "8859-16",
+
+ ["utf-8"] = "utf",
+ ["utf8"] = "utf",
+ [""] = "utf",
+
+ ["windows"] = "cp1252",
+
+}
+
+local currentregime = "utf"
+
+local function loadregime(mapping,regime)
+ local name = resolvers.findfile(format("regi-%s.lua",regime)) or ""
+ local data = name ~= "" and dofile(name)
+ if data then
+ vector = { }
+ for eightbit, unicode in next, data do
+ vector[char(eightbit)] = utfchar(unicode)
+ end
+ report_loading("vector %a is loaded",regime)
+ else
+ vector = false
+ report_loading("vector %a is unknown",regime)
+ end
+ mapping[regime] = vector
+ return vector
+end
+
+local function loadreverse(t,k)
+ local t = { }
+ for k, v in next, mapping[k] do
+ t[v] = k
+ end
+ backmapping[k] = t
+ return t
+end
+
+setmetatableindex(mapping, loadregime)
+setmetatableindex(backmapping,loadreverse)
+
+local function translate(line,regime)
+ if line and #line > 0 then
+ local map = mapping[regime and synonyms[regime] or regime or currentregime]
+ if map then
+ line = gsub(line,".",map)
+ end
+ end
+ return line
+end
+
+-- local remappers = { }
+--
+-- local function toregime(vector,str,default) -- toregime('8859-1',"abcde Ä","?")
+-- local t = backmapping[vector]
+-- local remapper = remappers[vector]
+-- if not remapper then
+-- remapper = utf.remapper(t)
+-- remappers[t] = remapper
+-- end
+-- local m = getmetatable(t)
+-- setmetatableindex(t, function(t,k)
+-- local v = default or "?"
+-- t[k] = v
+-- return v
+-- end)
+-- str = remapper(str)
+-- setmetatable(t,m)
+-- return str
+-- end
+--
+-- -- much faster (but only matters when we have > 10K calls
+
+local cache = { } -- if really needed we can copy vectors and hash defaults
+
+setmetatableindex(cache, function(t,k)
+ local v = { remappers = { } }
+ t[k] = v
+ return v
+end)
+
+local function toregime(vector,str,default) -- toregime('8859-1',"abcde Ä","?")
+ local d = default or "?"
+ local c = cache[vector].remappers
+ local r = c[d]
+ if not r then
+ local t = fastcopy(backmapping[vector])
+ setmetatableindex(t, function(t,k)
+ local v = d
+ t[k] = v
+ return v
+ end)
+ r = utf.remapper(t)
+ c[d] = r
+ end
+ return r(str)
+end
+
+local function disable()
+ currentregime = "utf"
+ sequencers.disableaction(textlineactions,"regimes.process")
+end
+
+local function enable(regime)
+ regime = synonyms[regime] or regime
+ if mapping[regime] == false then
+ disable()
+ else
+ currentregime = regime
+ sequencers.enableaction(textlineactions,"regimes.process")
+ end
+end
+
+regimes.toregime = toregime
+regimes.translate = translate
+regimes.enable = enable
+regimes.disable = disable
+
+-- The following function can be used when we want to make sure that
+-- utf gets passed unharmed. This is needed for modules.
+
+local level = 0
+
+function regimes.process(str,filename,currentline,noflines,coding)
+ if level == 0 and coding ~= "utf-8" then
+ str = translate(str,currentregime)
+ if trace_translating then
+ report_translating("utf: %s",str)
+ end
+ end
+ return str
+end
+
+local function push()
+ level = level + 1
+ if trace_translating then
+ report_translating("pushing level %s",level)
+ end
+end
+
+local function pop()
+ if level > 0 then
+ if trace_translating then
+ report_translating("popping level %s",level)
+ end
+ level = level - 1
+ end
+end
+
+regimes.push = push
+regimes.pop = pop
+
+sequencers.prependaction(textlineactions,"system","regimes.process")
+sequencers.disableaction(textlineactions,"regimes.process")
+
+-- interface:
+
+commands.enableregime = enable
+commands.disableregime = disable
+
+commands.pushregime = push
+commands.popregime = pop
+
+function commands.currentregime()
+ context(currentregime)
+end
+
+local stack = { }
+
+function commands.startregime(regime)
+ insert(stack,currentregime)
+ if trace_translating then
+ report_translating("start using %a",regime)
+ end
+ enable(regime)
+end
+
+function commands.stopregime()
+ if #stack > 0 then
+ local regime = remove(stack)
+ if trace_translating then
+ report_translating("stop using %a",regime)
+ end
+ enable(regime)
+ end
+end
+
+-- Next we provide some hacks. Unfortunately we run into crappy encoded
+-- (read : mixed) encoded xml files that have these ë ä ö ü sequences
+-- instead of ë ä ö ü
+
+local patterns = { }
+
+-- function regimes.cleanup(regime,str)
+-- local p = patterns[regime]
+-- if p == nil then
+-- regime = regime and synonyms[regime] or regime or currentregime
+-- local vector = regime ~= "utf" and mapping[regime]
+-- if vector then
+-- local list = { }
+-- for k, uchar in next, vector do
+-- local stream = totable(uchar)
+-- for i=1,#stream do
+-- stream[i] = vector[stream[i]]
+-- end
+-- list[concat(stream)] = uchar
+-- end
+-- p = lpeg.append(list,nil,true)
+-- p = Cs((p+1)^0)
+-- -- lpeg.print(p) -- size 1604
+-- else
+-- p = false
+-- end
+-- patterns[vector] = p
+-- end
+-- return p and lpegmatch(p,str) or str
+-- end
+--
+-- twice as fast and much less lpeg bytecode
+
+function regimes.cleanup(regime,str)
+ local p = patterns[regime]
+ if p == nil then
+ regime = regime and synonyms[regime] or regime or currentregime
+ local vector = regime ~= "utf" and mapping[regime]
+ if vector then
+ local utfchars = { }
+ local firsts = { }
+ for k, uchar in next, vector do
+ local stream = { }
+ local split = totable(uchar)
+ local nofsplits = #split
+ if nofsplits > 1 then
+ local first
+ for i=1,nofsplits do
+ local u = vector[split[i]]
+ if not first then
+ first = firsts[u]
+ if not first then
+ first = { }
+ firsts[u] = first
+ end
+ end
+ stream[i] = u
+ end
+ local nofstream = #stream
+ if nofstream > 1 then
+ first[#first+1] = concat(stream,2,nofstream)
+ utfchars[concat(stream)] = uchar
+ end
+ end
+ end
+ p = P(false)
+ for k, v in next, firsts do
+ local q = P(false)
+ for i=1,#v do
+ q = q + P(v[i])
+ end
+ p = p + P(k) * q
+ end
+ p = Cs(((p+1)/utfchars)^1)
+ -- lpeg.print(p) -- size: 1042
+ else
+ p = false
+ end
+ patterns[regime] = p
+ end
+ return p and lpegmatch(p,str) or str
+end
+
+-- local map = require("regi-cp1252")
+-- local old = [[test ë ä ö ü crap]]
+-- local new = correctencoding(map,old)
+--
+-- print(old,new)
+
+-- obsolete:
+--
+-- function regimes.setsynonym(synonym,target)
+-- synonyms[synonym] = target
+-- end
+--
+-- function regimes.truename(regime)
+-- return regime and synonyms[regime] or regime or currentregime
+-- end
+--
+-- commands.setregimesynonym = regimes.setsynonym
+--
+-- function commands.trueregimename(regime)
+-- context(regimes.truename(regime))
+-- end
+--
+-- function regimes.load(regime)
+-- return mapping[synonyms[regime] or regime]
+-- end
diff --git a/tex/context/base/s-fonts-coverage.lua b/tex/context/base/s-fonts-coverage.lua
index 668c430a9..db47e57c4 100644
--- a/tex/context/base/s-fonts-coverage.lua
+++ b/tex/context/base/s-fonts-coverage.lua
@@ -1,113 +1,113 @@
-if not modules then modules = { } end modules ['s-fonts-coverage'] = {
- version = 1.001,
- comment = "companion to s-fonts-coverage.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-moduledata.fonts = moduledata.fonts or { }
-moduledata.fonts.coverage = moduledata.fonts.coverage or { }
-
-local upper, format = string.upper, string.format
-local lpegmatch = lpeg.match
-local concat = table.concat
-
-local context = context
-local NC, NR, HL = context.NC, context.NR, context.HL
-local char, bold, getvalue = context.char, context.bold, context.getvalue
-
-local chardata = characters.data
-
-function moduledata.fonts.coverage.showcomparison(specification)
-
- specification = interfaces.checkedspecification(specification)
-
- local fontfiles = utilities.parsers.settings_to_array(specification.list or "")
- local pattern = upper(specification.pattern or "")
-
- local present = { }
- local names = { }
- local files = { }
-
- if not pattern then
- -- skip
- elseif pattern == "" then
- pattern = nil
- elseif tonumber(pattern) then
- pattern = tonumber(pattern)
- else
- pattern = lpeg.oneof(utilities.parsers.settings_to_array(pattern))
- pattern = (1-pattern)^0 * pattern
- end
-
- for i=1,#fontfiles do
- local fontname = format("testfont-%s",i)
- local fontfile = fontfiles[i]
- local fontsize = tex.dimen.bodyfontsize
- local id, fontdata = fonts.definers.define {
- name = fontfile,
- size = fontsize,
- cs = fontname,
- }
- if id and fontdata then
- for k, v in next, fontdata.characters do
- present[k] = true
- end
- names[#names+1] = fontname
- files[#files+1] = fontfile
- end
- end
-
- local t = { }
-
- context.starttabulate { "|Tr" .. string.rep("|l",#names) .. "|" }
- for i=1,#files do
- local file = files[i]
- t[#t+1] = i .. "=" .. file
- NC()
- context(i)
- NC()
- context(file)
- NC()
- NR()
- end
- context.stoptabulate()
-
- context.setupfootertexts {
- table.concat(t," ")
- }
-
- context.starttabulate { "|Tl" .. string.rep("|c",#names) .. "|Tl|" }
- NC()
- bold("unicode")
- NC()
- for i=1,#names do
- bold(i)
- NC()
- end
- bold("description")
- NC()
- NR()
- HL()
- for k, v in table.sortedpairs(present) do
- if k > 0 then
- local description = chardata[k].description
- if not pattern or (pattern == k) or (description and lpegmatch(pattern,description)) then
- NC()
- context("%05X",k)
- NC()
- for i=1,#names do
- getvalue(names[i])
- char(k)
- NC()
- end
- context(description)
- NC()
- NR()
- end
- end
- end
- context.stoptabulate()
-
-end
+if not modules then modules = { } end modules ['s-fonts-coverage'] = {
+ version = 1.001,
+ comment = "companion to s-fonts-coverage.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.fonts = moduledata.fonts or { }
+moduledata.fonts.coverage = moduledata.fonts.coverage or { }
+
+local upper, format = string.upper, string.format
+local lpegmatch = lpeg.match
+local concat = table.concat
+
+local context = context
+local NC, NR, HL = context.NC, context.NR, context.HL
+local char, bold, getvalue = context.char, context.bold, context.getvalue
+
+local chardata = characters.data
+
+function moduledata.fonts.coverage.showcomparison(specification)
+
+ specification = interfaces.checkedspecification(specification)
+
+ local fontfiles = utilities.parsers.settings_to_array(specification.list or "")
+ local pattern = upper(specification.pattern or "")
+
+ local present = { }
+ local names = { }
+ local files = { }
+
+ if not pattern then
+ -- skip
+ elseif pattern == "" then
+ pattern = nil
+ elseif tonumber(pattern) then
+ pattern = tonumber(pattern)
+ else
+ pattern = lpeg.oneof(utilities.parsers.settings_to_array(pattern))
+ pattern = (1-pattern)^0 * pattern
+ end
+
+ for i=1,#fontfiles do
+ local fontname = format("testfont-%s",i)
+ local fontfile = fontfiles[i]
+ local fontsize = tex.dimen.bodyfontsize
+ local id, fontdata = fonts.definers.define {
+ name = fontfile,
+ size = fontsize,
+ cs = fontname,
+ }
+ if id and fontdata then
+ for k, v in next, fontdata.characters do
+ present[k] = true
+ end
+ names[#names+1] = fontname
+ files[#files+1] = fontfile
+ end
+ end
+
+ local t = { }
+
+ context.starttabulate { "|Tr" .. string.rep("|l",#names) .. "|" }
+ for i=1,#files do
+ local file = files[i]
+ t[#t+1] = i .. "=" .. file
+ NC()
+ context(i)
+ NC()
+ context(file)
+ NC()
+ NR()
+ end
+ context.stoptabulate()
+
+ context.setupfootertexts {
+ table.concat(t," ")
+ }
+
+ context.starttabulate { "|Tl" .. string.rep("|c",#names) .. "|Tl|" }
+ NC()
+ bold("unicode")
+ NC()
+ for i=1,#names do
+ bold(i)
+ NC()
+ end
+ bold("description")
+ NC()
+ NR()
+ HL()
+ for k, v in table.sortedpairs(present) do
+ if k > 0 then
+ local description = chardata[k].description
+ if not pattern or (pattern == k) or (description and lpegmatch(pattern,description)) then
+ NC()
+ context("%05X",k)
+ NC()
+ for i=1,#names do
+ getvalue(names[i])
+ char(k)
+ NC()
+ end
+ context(description)
+ NC()
+ NR()
+ end
+ end
+ end
+ context.stoptabulate()
+
+end
diff --git a/tex/context/base/s-fonts-features.lua b/tex/context/base/s-fonts-features.lua
index a45195df7..0a7cf8b13 100644
--- a/tex/context/base/s-fonts-features.lua
+++ b/tex/context/base/s-fonts-features.lua
@@ -1,161 +1,161 @@
-if not modules then modules = { } end modules ['s-fonts-features'] = {
- version = 1.001,
- comment = "companion to s-fonts-features.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-moduledata.fonts = moduledata.fonts or { }
-moduledata.fonts.features = moduledata.fonts.features or { }
-
--- for the moment only otf
-
-local sortedhash = table.sortedhash
-
-local NC, NR, bold = context.NC, context.NR, context.bold
-
-function moduledata.fonts.features.showused(specification)
-
- specification = interfaces.checkedspecification(specification)
-
- -- local list = utilities.parsers.settings_to_set(specification.list or "all")
-
- context.starttabulate { "|T|T|T|T|T|" }
-
- context.HL()
-
- NC() bold("feature")
- NC()
- NC() bold("description")
- NC() bold("value")
- NC() bold("internal")
- NC() NR()
-
- context.HL()
-
- local usedfeatures = fonts.handlers.otf.statistics.usedfeatures
- local features = fonts.handlers.otf.tables.features
- local descriptions = fonts.handlers.otf.features.descriptions
-
- for feature, keys in sortedhash(usedfeatures) do
- -- if list.all or (list.otf and rawget(features,feature)) or (list.extra and rawget(descriptions,feature)) then
- local done = false
- for k, v in sortedhash(keys) do
- if done then
- NC()
- NC()
- NC()
- elseif rawget(descriptions,feature) then
- NC() context(feature)
- NC() context("+") -- extra
- NC() context(descriptions[feature])
- done = true
- elseif rawget(features,feature) then
- NC() context(feature)
- NC() -- otf
- NC() context(features[feature])
- done = true
- else
- NC() context(feature)
- NC() context("-") -- unknown
- NC()
- done = true
- end
- NC() context(k)
- NC() context(tostring(v))
- NC() NR()
- end
- -- end
- end
-
- context.HL()
-
- context.stoptabulate()
-
-end
-
-local function collectkerns(tfmdata,feature)
- local combinations = { }
- local resources = tfmdata.resources
- local characters = tfmdata.characters
- local sequences = resources.sequences
- local lookuphash = resources.lookuphash
- local feature = feature or "kern"
- if sequences then
- for i=1,#sequences do
- local sequence = sequences[i]
- if sequence.features and sequence.features[feature] then
- local lookuplist = sequence.subtables
- if lookuplist then
- for l=1,#lookuplist do
- local lookupname = lookuplist[l]
- local lookupdata = lookuphash[lookupname]
- for unicode, data in next, lookupdata do
- local kerns = combinations[unicode]
- if not kerns then
- kerns = { }
- combinations[unicode] = kerns
- end
- for otherunicode, kern in next, data do
- if not kerns[otherunicode] and kern ~= 0 then
- kerns[otherunicode] = kern
- end
- end
- end
- end
- end
- end
- end
- end
- return combinations
-end
-
-local showkernpair = context.showkernpair
-
-function moduledata.fonts.features.showbasekerns(specification)
- -- assumes that the font is loaded in base mode
- specification = interfaces.checkedspecification(specification)
- local id, cs = fonts.definers.internal(specification,"<module:fonts:features:font>")
- local tfmdata = fonts.hashes.identifiers[id]
- local done = false
- for unicode, character in sortedhash(tfmdata.characters) do
- local kerns = character.kerns
- if kerns then
- context.par()
- for othercode, kern in sortedhash(kerns) do
- showkernpair(unicode,kern,othercode)
- end
- context.par()
- done = true
- end
- end
- if not done then
- context("no kern pairs found")
- context.par()
- end
-end
-
-function moduledata.fonts.features.showallkerns(specification)
- specification = interfaces.checkedspecification(specification)
- local id, cs = fonts.definers.internal(specification,"<module:fonts:features:font>")
- local tfmdata = fonts.hashes.identifiers[id]
- local allkerns = collectkerns(tfmdata)
- local characters = tfmdata.characters
- if next(allkerns) then
- for first, pairs in sortedhash(allkerns) do
- context.par()
- for second, kern in sortedhash(pairs) do
- -- local kerns = characters[first].kerns
- -- if not kerns and pairs[second] then
- -- -- weird
- -- end
- showkernpair(first,kern,second,0)
- end
- context.par()
- end
- else
- context("no kern pairs found")
- context.par()
- end
-end
+if not modules then modules = { } end modules ['s-fonts-features'] = {
+ version = 1.001,
+ comment = "companion to s-fonts-features.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.fonts = moduledata.fonts or { }
+moduledata.fonts.features = moduledata.fonts.features or { }
+
+-- for the moment only otf
+
+local sortedhash = table.sortedhash
+
+local NC, NR, bold = context.NC, context.NR, context.bold
+
+function moduledata.fonts.features.showused(specification)
+
+ specification = interfaces.checkedspecification(specification)
+
+ -- local list = utilities.parsers.settings_to_set(specification.list or "all")
+
+ context.starttabulate { "|T|T|T|T|T|" }
+
+ context.HL()
+
+ NC() bold("feature")
+ NC()
+ NC() bold("description")
+ NC() bold("value")
+ NC() bold("internal")
+ NC() NR()
+
+ context.HL()
+
+ local usedfeatures = fonts.handlers.otf.statistics.usedfeatures
+ local features = fonts.handlers.otf.tables.features
+ local descriptions = fonts.handlers.otf.features.descriptions
+
+ for feature, keys in sortedhash(usedfeatures) do
+ -- if list.all or (list.otf and rawget(features,feature)) or (list.extra and rawget(descriptions,feature)) then
+ local done = false
+ for k, v in sortedhash(keys) do
+ if done then
+ NC()
+ NC()
+ NC()
+ elseif rawget(descriptions,feature) then
+ NC() context(feature)
+ NC() context("+") -- extra
+ NC() context(descriptions[feature])
+ done = true
+ elseif rawget(features,feature) then
+ NC() context(feature)
+ NC() -- otf
+ NC() context(features[feature])
+ done = true
+ else
+ NC() context(feature)
+ NC() context("-") -- unknown
+ NC()
+ done = true
+ end
+ NC() context(k)
+ NC() context(tostring(v))
+ NC() NR()
+ end
+ -- end
+ end
+
+ context.HL()
+
+ context.stoptabulate()
+
+end
+
+local function collectkerns(tfmdata,feature)
+ local combinations = { }
+ local resources = tfmdata.resources
+ local characters = tfmdata.characters
+ local sequences = resources.sequences
+ local lookuphash = resources.lookuphash
+ local feature = feature or "kern"
+ if sequences then
+ for i=1,#sequences do
+ local sequence = sequences[i]
+ if sequence.features and sequence.features[feature] then
+ local lookuplist = sequence.subtables
+ if lookuplist then
+ for l=1,#lookuplist do
+ local lookupname = lookuplist[l]
+ local lookupdata = lookuphash[lookupname]
+ for unicode, data in next, lookupdata do
+ local kerns = combinations[unicode]
+ if not kerns then
+ kerns = { }
+ combinations[unicode] = kerns
+ end
+ for otherunicode, kern in next, data do
+ if not kerns[otherunicode] and kern ~= 0 then
+ kerns[otherunicode] = kern
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ return combinations
+end
+
+local showkernpair = context.showkernpair
+
+function moduledata.fonts.features.showbasekerns(specification)
+ -- assumes that the font is loaded in base mode
+ specification = interfaces.checkedspecification(specification)
+ local id, cs = fonts.definers.internal(specification,"<module:fonts:features:font>")
+ local tfmdata = fonts.hashes.identifiers[id]
+ local done = false
+ for unicode, character in sortedhash(tfmdata.characters) do
+ local kerns = character.kerns
+ if kerns then
+ context.par()
+ for othercode, kern in sortedhash(kerns) do
+ showkernpair(unicode,kern,othercode)
+ end
+ context.par()
+ done = true
+ end
+ end
+ if not done then
+ context("no kern pairs found")
+ context.par()
+ end
+end
+
+function moduledata.fonts.features.showallkerns(specification)
+ specification = interfaces.checkedspecification(specification)
+ local id, cs = fonts.definers.internal(specification,"<module:fonts:features:font>")
+ local tfmdata = fonts.hashes.identifiers[id]
+ local allkerns = collectkerns(tfmdata)
+ local characters = tfmdata.characters
+ if next(allkerns) then
+ for first, pairs in sortedhash(allkerns) do
+ context.par()
+ for second, kern in sortedhash(pairs) do
+ -- local kerns = characters[first].kerns
+ -- if not kerns and pairs[second] then
+ -- -- weird
+ -- end
+ showkernpair(first,kern,second,0)
+ end
+ context.par()
+ end
+ else
+ context("no kern pairs found")
+ context.par()
+ end
+end
diff --git a/tex/context/base/s-fonts-goodies.lua b/tex/context/base/s-fonts-goodies.lua
index 5b83760d7..381fc45ea 100644
--- a/tex/context/base/s-fonts-goodies.lua
+++ b/tex/context/base/s-fonts-goodies.lua
@@ -1,117 +1,117 @@
-if not modules then modules = { } end modules['s-fonts-goodies'] = {
- version = 1.001,
- comment = "companion to s-fonts-goodies.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-moduledata.fonts = moduledata.fonts or { }
-moduledata.fonts.goodies = moduledata.fonts.goodies or { }
-
-local NC, NR, HL = context.NC, context.NR, context.HL
-
-local function initialized(specification)
- specification = interfaces.checkedspecification(specification)
- local name = specification.name
- if name then
- local goodies = fonts.goodies.load(name)
- if goodies then
- return specification, goodies
- end
- end
-end
-
-function moduledata.fonts.goodies.showstylistics(specification)
- local specification, goodies = initialized(specification)
- if goodies then
- local stylistics = goodies.stylistics
- if stylistics then
- context.starttabulate { "|Tl|Tpl|" }
- HL()
- NC() context.bold("feature")
- NC() context.bold("meaning")
- NC() NR()
- HL()
- for feature, meaning in table.sortedpairs(stylistics) do
- NC() context(feature)
- NC() context(string.lower(meaning))
- NC() NR()
- end
- HL()
- context.stoptabulate()
- end
- end
-end
-
-function moduledata.fonts.goodies.showfeaturesets(specification)
- local specification, goodies = initialized(specification)
- if goodies then
- local featuresets = goodies.featuresets
- if featuresets then
- context.starttabulate { "|Tl|Tpl|" }
- HL()
- NC() context.bold("featureset")
- NC() context.bold("definitions")
- NC() NR()
- HL()
- for featureset, definitions in table.sortedpairs(featuresets) do
- NC() context.type(featureset) NC()
- for k, v in table.sortedpairs(definitions) do
- context("%s=%S",k,v)
- context.quad()
- end
- NC() NR()
- end
- HL()
- context.stoptabulate()
- end
- end
-end
-
-function moduledata.fonts.goodies.showcolorschemes(specification)
- local specification, goodies = initialized(specification)
- if goodies then
- local colorschemes = goodies.colorschemes
- if colorschemes then
- context.starttabulate { "|Tl|Tpl|" }
- HL()
- NC() context.bold("colorscheme")
- NC() context.bold("numbers")
- NC() NR()
- HL()
- for colorscheme, numbers in table.sortedpairs(colorschemes) do
- NC() context.type(colorscheme) NC()
- for i=1,#numbers do
- context(i)
- context.quad()
- end
- NC() NR()
- end
- HL()
- context.stoptabulate()
- end
- end
-end
-
-function moduledata.fonts.goodies.showfiles(specification)
- local specification, goodies = initialized(specification)
- if goodies then
- local files = goodies.files
- if files and files.list then
- for filename, specification in table.sortedpairs(files.list) do
- context.start()
- context.dontleavehmode()
- context.definedfont{ filename .. "*default" }
- context("%s-%s-%s-%s-%s",
- specification.name or files.name,
- specification.weight or "normal",
- specification.style or "normal",
- specification.width or "normal",
- specification.variant or "normal")
- context.par()
- context.stop()
- end
- end
- end
-end
+if not modules then modules = { } end modules['s-fonts-goodies'] = {
+ version = 1.001,
+ comment = "companion to s-fonts-goodies.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.fonts = moduledata.fonts or { }
+moduledata.fonts.goodies = moduledata.fonts.goodies or { }
+
+local NC, NR, HL = context.NC, context.NR, context.HL
+
+local function initialized(specification)
+ specification = interfaces.checkedspecification(specification)
+ local name = specification.name
+ if name then
+ local goodies = fonts.goodies.load(name)
+ if goodies then
+ return specification, goodies
+ end
+ end
+end
+
+function moduledata.fonts.goodies.showstylistics(specification)
+ local specification, goodies = initialized(specification)
+ if goodies then
+ local stylistics = goodies.stylistics
+ if stylistics then
+ context.starttabulate { "|Tl|Tpl|" }
+ HL()
+ NC() context.bold("feature")
+ NC() context.bold("meaning")
+ NC() NR()
+ HL()
+ for feature, meaning in table.sortedpairs(stylistics) do
+ NC() context(feature)
+ NC() context(string.lower(meaning))
+ NC() NR()
+ end
+ HL()
+ context.stoptabulate()
+ end
+ end
+end
+
+function moduledata.fonts.goodies.showfeaturesets(specification)
+ local specification, goodies = initialized(specification)
+ if goodies then
+ local featuresets = goodies.featuresets
+ if featuresets then
+ context.starttabulate { "|Tl|Tpl|" }
+ HL()
+ NC() context.bold("featureset")
+ NC() context.bold("definitions")
+ NC() NR()
+ HL()
+ for featureset, definitions in table.sortedpairs(featuresets) do
+ NC() context.type(featureset) NC()
+ for k, v in table.sortedpairs(definitions) do
+ context("%s=%S",k,v)
+ context.quad()
+ end
+ NC() NR()
+ end
+ HL()
+ context.stoptabulate()
+ end
+ end
+end
+
+function moduledata.fonts.goodies.showcolorschemes(specification)
+ local specification, goodies = initialized(specification)
+ if goodies then
+ local colorschemes = goodies.colorschemes
+ if colorschemes then
+ context.starttabulate { "|Tl|Tpl|" }
+ HL()
+ NC() context.bold("colorscheme")
+ NC() context.bold("numbers")
+ NC() NR()
+ HL()
+ for colorscheme, numbers in table.sortedpairs(colorschemes) do
+ NC() context.type(colorscheme) NC()
+ for i=1,#numbers do
+ context(i)
+ context.quad()
+ end
+ NC() NR()
+ end
+ HL()
+ context.stoptabulate()
+ end
+ end
+end
+
+function moduledata.fonts.goodies.showfiles(specification)
+ local specification, goodies = initialized(specification)
+ if goodies then
+ local files = goodies.files
+ if files and files.list then
+ for filename, specification in table.sortedpairs(files.list) do
+ context.start()
+ context.dontleavehmode()
+ context.definedfont{ filename .. "*default" }
+ context("%s-%s-%s-%s-%s",
+ specification.name or files.name,
+ specification.weight or "normal",
+ specification.style or "normal",
+ specification.width or "normal",
+ specification.variant or "normal")
+ context.par()
+ context.stop()
+ end
+ end
+ end
+end
diff --git a/tex/context/base/s-fonts-missing.lua b/tex/context/base/s-fonts-missing.lua
index 331e73715..829fed45f 100644
--- a/tex/context/base/s-fonts-missing.lua
+++ b/tex/context/base/s-fonts-missing.lua
@@ -1,101 +1,101 @@
-if not modules then modules = { } end modules ['s-fonts-missing'] = {
- version = 1.001,
- comment = "companion to s-fonts-missing.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-moduledata.fonts = moduledata.fonts or { }
-moduledata.fonts.missing = moduledata.fonts.missing or { }
-
-local function legend(id)
- local c = fonts.hashes.identifiers[id]
- local privates = c.properties.privates
- if privates then
- local categories = table.swapped(fonts.loggers.category_to_placeholder)
- -- context.starttabulate { "|l|c|c|l|" }
- context.starttabulate { "|l|c|l|" }
- context.HL()
- context.NC()
- context.bold("name")
- context.NC()
- context.bold("symbol")
- context.NC()
- -- context.bold("node")
- -- context.NC()
- context.bold("category")
- context.NC()
- context.NR()
- context.HL()
- for k, v in table.sortedhash(privates) do
- local tag = characters.categorytags[categories[k]]
- if tag and tag ~= "" then
- context.NC()
- context(k)
- context.NC()
- context.dontleavehmode()
- context.char(v)
- context.NC()
- -- context.dontleavehmode()
- -- commands.getprivatechar(k)
- -- context.NC()
- context(string.lower(tag))
- context.NC()
- context.NR()
- end
- end
- context.HL()
- context.stoptabulate()
- end
-end
-
-function moduledata.fonts.missing.showlegend(specification)
- specification = interfaces.checkedspecification(specification)
- context.begingroup()
- context.definedfont { "Mono*missing" } -- otherwise no privates added
- context(function() legend(specification.id or font.current()) end)
- context.endgroup()
-end
-
-local function missings()
- local collected = fonts.checkers.getmissing()
- for filename, list in table.sortedhash(collected) do
- if #list > 0 then
- context.starttabulate { "|l|l|" }
- context.NC()
- context.bold("filename")
- context.NC()
- context(file.basename(filename))
- context.NC()
- context.NR()
- context.NC()
- context.bold("missing")
- context.NC()
- context(#list)
- context.NC()
- context.NR()
- context.stoptabulate()
- context.starttabulate { "|l|c|l|" }
- for i=1,#list do
- local u = list[i]
- context.NC()
- context("%U",u)
- context.NC()
- context.char(u)
- context.NC()
- context(characters.data[u].description)
- context.NC()
- context.NR()
- end
- context.stoptabulate()
- end
- end
-end
-
-function moduledata.fonts.missing.showcharacters(specification)
- context.begingroup()
- context.definedfont { "Mono*missing" } -- otherwise no privates added
- context(function() missings() end)
- context.endgroup()
-end
+if not modules then modules = { } end modules ['s-fonts-missing'] = {
+ version = 1.001,
+ comment = "companion to s-fonts-missing.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.fonts = moduledata.fonts or { }
+moduledata.fonts.missing = moduledata.fonts.missing or { }
+
+local function legend(id)
+ local c = fonts.hashes.identifiers[id]
+ local privates = c.properties.privates
+ if privates then
+ local categories = table.swapped(fonts.loggers.category_to_placeholder)
+ -- context.starttabulate { "|l|c|c|l|" }
+ context.starttabulate { "|l|c|l|" }
+ context.HL()
+ context.NC()
+ context.bold("name")
+ context.NC()
+ context.bold("symbol")
+ context.NC()
+ -- context.bold("node")
+ -- context.NC()
+ context.bold("category")
+ context.NC()
+ context.NR()
+ context.HL()
+ for k, v in table.sortedhash(privates) do
+ local tag = characters.categorytags[categories[k]]
+ if tag and tag ~= "" then
+ context.NC()
+ context(k)
+ context.NC()
+ context.dontleavehmode()
+ context.char(v)
+ context.NC()
+ -- context.dontleavehmode()
+ -- commands.getprivatechar(k)
+ -- context.NC()
+ context(string.lower(tag))
+ context.NC()
+ context.NR()
+ end
+ end
+ context.HL()
+ context.stoptabulate()
+ end
+end
+
+function moduledata.fonts.missing.showlegend(specification)
+ specification = interfaces.checkedspecification(specification)
+ context.begingroup()
+ context.definedfont { "Mono*missing" } -- otherwise no privates added
+ context(function() legend(specification.id or font.current()) end)
+ context.endgroup()
+end
+
+local function missings()
+ local collected = fonts.checkers.getmissing()
+ for filename, list in table.sortedhash(collected) do
+ if #list > 0 then
+ context.starttabulate { "|l|l|" }
+ context.NC()
+ context.bold("filename")
+ context.NC()
+ context(file.basename(filename))
+ context.NC()
+ context.NR()
+ context.NC()
+ context.bold("missing")
+ context.NC()
+ context(#list)
+ context.NC()
+ context.NR()
+ context.stoptabulate()
+ context.starttabulate { "|l|c|l|" }
+ for i=1,#list do
+ local u = list[i]
+ context.NC()
+ context("%U",u)
+ context.NC()
+ context.char(u)
+ context.NC()
+ context(characters.data[u].description)
+ context.NC()
+ context.NR()
+ end
+ context.stoptabulate()
+ end
+ end
+end
+
+function moduledata.fonts.missing.showcharacters(specification)
+ context.begingroup()
+ context.definedfont { "Mono*missing" } -- otherwise no privates added
+ context(function() missings() end)
+ context.endgroup()
+end
diff --git a/tex/context/base/s-fonts-shapes.lua b/tex/context/base/s-fonts-shapes.lua
index 5898b5469..b387c11dd 100644
--- a/tex/context/base/s-fonts-shapes.lua
+++ b/tex/context/base/s-fonts-shapes.lua
@@ -1,328 +1,328 @@
-if not modules then modules = { } end modules['s-fonts-shapes'] = {
- version = 1.001,
- comment = "companion to s-fonts-shapes.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-moduledata.fonts = moduledata.fonts or { }
-moduledata.fonts.shapes = moduledata.fonts.shapes or { }
-
-local fontdata = fonts.hashes.identifiers
-
-local context = context
-local NC, NR = context.NC, context.NR
-local space, dontleavehmode, glyph, getvalue = context.space, context.dontleavehmode, context.glyph, context.getvalue
-local formatters = string.formatters
-
-function moduledata.fonts.shapes.showlist(specification) -- todo: ranges
- specification = interfaces.checkedspecification(specification)
- local id, cs = fonts.definers.internal(specification,"<module:fonts:shapes:font>")
- local chrs = fontdata[id].characters
- function char(k)
- dontleavehmode()
- glyph(id,k)
- end
- local function special(v)
- local specials = v.specials
- if specials and #specials > 1 then
- context("%s:",specials[1])
- for i=2,#specials do
- space()
- char(specials[i])
- end
- end
- end
- context.begingroup()
- context.tt()
- context.starttabulate { "|l|c|c|c|c|l|l|" }
- context.FL()
- NC() context.bold("unicode")
- NC() context.bold("glyph")
- NC() context.bold("shape")
- NC() context.bold("lower")
- NC() context.bold("upper")
- NC() context.bold("specials")
- NC() context.bold("description")
- NC() NR()
- context.TL()
- for k, v in next, characters.data do
- if chrs[k] then
- NC() context("0x%05X",k)
- NC() char(k) -- getvalue(cs) context.char(k)
- NC() char(v.shcode)
- NC() char(v.lccode or k)
- NC() char(v.uccode or k)
- NC() special(v)
- NC() context.tx(v.description)
- NC() NR()
- end
- end
- context.stoptabulate()
- context.endgroup()
-end
-
-function moduledata.fonts.shapes.showlist(specification) -- todo: ranges
- specification = interfaces.checkedspecification(specification)
- local id, cs = fonts.definers.internal(specification,"<module:fonts:shapes:font>")
- local chrs = fontdata[id].characters
- function char(k)
- dontleavehmode()
- glyph(id,k)
- end
- local function special(v)
- local specials = v.specials
- if specials and #specials > 1 then
- context("%s:",specials[1])
- for i=2,#specials do
- space()
- char(specials[i])
- end
- end
- end
- context.begingroup()
- context.tt()
- context.starttabulate { "|l|c|c|c|c|l|l|" }
- context.FL()
- NC() context.bold("unicode")
- NC() context.bold("glyph")
- NC() context.bold("shape")
- NC() context.bold("lower")
- NC() context.bold("upper")
- NC() context.bold("specials")
- NC() context.bold("description")
- NC() NR()
- context.TL()
- for k, v in next, characters.data do
- if chrs[k] then
- NC() context("0x%05X",k)
- NC() char(k)
- NC() char(v.shcode)
- NC() char(v.lccode or k)
- NC() char(v.uccode or k)
- NC() special(v)
- NC() context.tx(v.description)
- NC() NR()
- end
- end
- context.stoptabulate()
- context.endgroup()
-end
-
-local descriptions = nil
-local characters = nil
-
-local function showglyphshape(specification)
- specification = interfaces.checkedspecification(specification)
- local id, cs = fonts.definers.internal(specification,"<module:fonts:shapes:font>")
- local tfmdata = fontdata[id]
- local charnum = tonumber(specification.character)
- if not charnum then
- charnum = fonts.helpers.nametoslot(n)
- end
- context.start()
- context.dontleavehmode()
- context.obeyMPboxdepth()
- local characters = tfmdata.characters
- local descriptions = tfmdata.descriptions
- local parameters = tfmdata.parameters
- local c = characters[charnum]
- local d = descriptions[charnum]
- if d then
- local factor = (parameters.size/parameters.units)*((7200/7227)/65536)
- local llx, lly, urx, ury = unpack(d.boundingbox)
- llx, lly, urx, ury = llx*factor, lly*factor, urx*factor, ury*factor
- local width, italic = (d.width or 0)*factor, (d.italic or 0)*factor
- local top_accent, bot_accent = (d.top_accent or 0)*factor, (d.bot_accent or 0)*factor
- local anchors, math = d.anchors, d.math
- context.startMPcode()
- context("pickup pencircle scaled .25bp ;")
- context('picture p ; p := image(draw textext.drt("\\getuvalue{%s}\\gray\\char%s");); draw p ;',cs,charnum)
- context('draw (%s,%s)--(%s,%s)--(%s,%s)--(%s,%s)--cycle withcolor green ;',llx,lly,urx,lly,urx,ury,llx,ury)
- context('draw (%s,%s)--(%s,%s) withcolor green ;',llx,0,urx,0)
- context('draw boundingbox p withcolor .2white withpen pencircle scaled .065bp ;')
- context("defaultscale := 0.05 ; ")
- -- inefficient but non critical
- local function slant_1(v,dx,dy,txt,xsign,ysign,loc,labloc)
- if #v > 0 then
- local l = { }
- for kk, vv in ipairs(v) do
- local h, k = vv.height, vv.kern
- if h and k then
- l[#l+1] = formatters["((%s,%s) shifted (%s,%s))"](xsign*k*factor,ysign*h*factor,dx,dy)
- end
- end
- context("draw ((%s,%s) shifted (%s,%s))--%s dashed (evenly scaled .25) withcolor .5white;", xsign*v[1].kern*factor,lly,dx,dy,l[1])
- context("draw laddered (%s) withcolor .5white ;",table.concat(l,".."))
- context("draw ((%s,%s) shifted (%s,%s))--%s dashed (evenly scaled .25) withcolor .5white;", xsign*v[#v].kern*factor,ury,dx,dy,l[#l])
- for k, v in ipairs(l) do
- context("draw %s withcolor blue withpen pencircle scaled 1bp;",v)
- end
- end
- end
- local function slant_2(v,dx,dy,txt,xsign,ysign,loc,labloc)
- if #v > 0 then
- local l = { }
- for kk, vv in ipairs(v) do
- local h, k = vv.height, vv.kern
- if h and k then
- l[#l+1] = formatters["((%s,%s) shifted (%s,%s))"](xsign*k*factor,ysign*h*factor,dx,dy)
- end
- end
- if loc == "top" then
- context('label.%s("\\type{%s}",%s shifted (0,-1bp)) ;',loc,txt,l[#l])
- else
- context('label.%s("\\type{%s}",%s shifted (0,2bp)) ;',loc,txt,l[1])
- end
- for kk, vv in ipairs(v) do
- local h, k = vv.height, vv.kern
- if h and k then
- context('label.top("(%s,%s)",%s shifted (0,-2bp));',k,h,l[kk])
- end
- end
- end
- end
- if math then
- local kerns = math.kerns
- if kerns then
- for _, slant in ipairs { slant_1, slant_2 } do
- for k,v in pairs(kerns) do
- if k == "top_right" then
- slant(v,width+italic,0,k,1,1,"top","ulft")
- elseif k == "bottom_right" then
- slant(v,width,0,k,1,1,"bot","lrt")
- elseif k == "top_left" then
- slant(v,0,0,k,-1,1,"top","ulft")
- elseif k == "bottom_left" then
- slant(v,0,0,k,-1,1,"bot","lrt")
- end
- end
- end
- end
- end
- local function show(x,y,txt)
- local xx, yy = x*factor, y*factor
- context("draw (%s,%s) withcolor blue withpen pencircle scaled 1bp;",xx,yy)
- context('label.top("\\type{%s}",(%s,%s-2bp)) ;',txt,xx,yy)
- context('label.bot("(%s,%s)",(%s,%s+2bp)) ;',x,y,xx,yy)
- end
- if anchors then
- local a = anchors.baselig
- if a then
- for k, v in pairs(a) do
- for kk, vv in ipairs(v) do
- show(vv[1],vv[2],k .. ":" .. kk)
- end
- end
- end
- local a = anchors.mark
- if a then
- for k, v in pairs(a) do
- show(v[1],v[2],k)
- end
- end
- local a = anchors.basechar
- if a then
- for k, v in pairs(a) do
- show(v[1],v[2],k)
- end
- end
- local ba = anchors.centry
- if a then
- for k, v in pairs(a) do
- show(v[1],v[2],k)
- end
- end
- local a = anchors.cexit
- if a then
- for k, v in pairs(a) do
- show(v[1],v[2],k)
- end
- end
- end
- if italic ~= 0 then
- context('draw (%s,%s-1bp)--(%s,%s-0.5bp) withcolor blue;',width,ury,width,ury)
- context('draw (%s,%s-1bp)--(%s,%s-0.5bp) withcolor blue;',width+italic,ury,width+italic,ury)
- context('draw (%s,%s-1bp)--(%s,%s-1bp) withcolor blue;',width,ury,width+italic,ury)
- context('label.lft("\\type{%s}",(%s+2bp,%s-1bp));',"italic",width,ury)
- context('label.rt("%s",(%s-2bp,%s-1bp));',d.italic,width+italic,ury)
- end
- if top_accent ~= 0 then
- context('draw (%s,%s+1bp)--(%s,%s-1bp) withcolor blue;',top_accent,ury,top_accent,ury)
- context('label.bot("\\type{%s}",(%s,%s+1bp));',"top_accent",top_accent,ury)
- context('label.top("%s",(%s,%s-1bp));',d.top_accent,top_accent,ury)
- end
- if bot_accent ~= 0 then
- context('draw (%s,%s+1bp)--(%s,%s-1bp) withcolor blue;',bot_accent,lly,bot_accent,lly)
- context('label.top("\\type{%s}",(%s,%s-1bp));',"bot_accent",top_accent,ury)
- context('label.bot("%s",(%s,%s+1bp));',d.bot_accent,bot_accent,lly)
- end
- context('draw origin withcolor red withpen pencircle scaled 1bp;')
- context("setbounds currentpicture to boundingbox currentpicture enlarged 1bp ;")
- context("currentpicture := currentpicture scaled 8 ;")
- context.stopMPcode()
- -- elseif c then
- -- lastdata, lastunicode = nil, nil
- -- local factor = (7200/7227)/65536
- -- context.startMPcode()
- -- context("pickup pencircle scaled .25bp ; ")
- -- context('picture p ; p := image(draw textext.drt("\\gray\\char%s");); draw p ;',charnum)
- -- context('draw boundingbox p withcolor .2white withpen pencircle scaled .065bp ;')
- -- context("defaultscale := 0.05 ; ")
- -- local italic, top_accent, bot_accent = (c.italic or 0)*factor, (c.top_accent or 0)*factor, (c.bot_accent or 0)*factor
- -- local width, height, depth = (c.width or 0)*factor, (c.height or 0)*factor, (c.depth or 0)*factor
- -- local ury = height
- -- if italic ~= 0 then
- -- context('draw (%s,%s-1bp)--(%s,%s-0.5bp) withcolor blue;',width,ury,width,ury)
- -- context('draw (%s,%s-1bp)--(%s,%s-0.5bp) withcolor blue;',width+italic,ury,width+italic,ury)
- -- context('draw (%s,%s-1bp)--(%s,%s-1bp) withcolor blue;',width,ury,width+italic,height)
- -- context('label.lft("\\type{%s}",(%s+2bp,%s-1bp));',"italic",width,height)
- -- context('label.rt("%6.3f bp",(%s-2bp,%s-1bp));',italic,width+italic,height)
- -- end
- -- if top_accent ~= 0 then
- -- context('draw (%s,%s+1bp)--(%s,%s-1bp) withcolor blue;',top_accent,ury,top_accent,height)
- -- context('label.bot("\\type{%s}",(%s,%s+1bp));',"top_accent",top_accent,height)
- -- context('label.top("%6.3f bp",(%s,%s-1bp));',top_accent,top_accent,height)
- -- end
- -- if bot_accent ~= 0 then
- -- context('draw (%s,%s+1bp)--(%s,%s-1bp) withcolor blue;',bot_accent,lly,bot_accent,height)
- -- context('label.top("\\type{%s}",(%s,%s-1bp));',"bot_accent",top_accent,height)
- -- context('label.bot("%6.3f bp",(%s,%s+1bp));',bot_accent,bot_accent,height)
- -- end
- -- context('draw origin withcolor red withpen pencircle scaled 1bp;')
- -- context("setbounds currentpicture to boundingbox currentpicture enlarged 1bp ;")
- -- context("currentpicture := currentpicture scaled 8 ;")
- -- context.stopMPcode()
- else
- lastdata, lastunicode = nil, nil
- context("no such shape: 0x%05X",charnum)
- end
- context.stop()
-end
-
-moduledata.fonts.shapes.showglyphshape = showglyphshape
-
-function moduledata.fonts.shapes.showallglypshapes(specification)
- specification = interfaces.checkedspecification(specification)
- local id, cs = fonts.definers.internal(specification,"<module:fonts:shapes:font>")
- local descriptions = fontdata[id].descriptions
- for unicode, description in fonts.iterators.descriptions(tfmdata) do
- context.modulefontsstartshowglyphshape(unicode,description.name)
- showglyphshape { number = id, character = unicode }
- context.modulefontsstopshowglyphshape()
- end
-end
-
-function moduledata.fonts.shapes.showlastglyphshapefield(unicode,name)
- if not descriptions then
- -- bad news
- elseif name == "unicode" then
- context("U+%05X",descriptions.unicode)
- else
- local d = descriptions[name]
- if d then
- context(d)
- end
- end
-end
+if not modules then modules = { } end modules['s-fonts-shapes'] = {
+ version = 1.001,
+ comment = "companion to s-fonts-shapes.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.fonts = moduledata.fonts or { }
+moduledata.fonts.shapes = moduledata.fonts.shapes or { }
+
+local fontdata = fonts.hashes.identifiers
+
+local context = context
+local NC, NR = context.NC, context.NR
+local space, dontleavehmode, glyph, getvalue = context.space, context.dontleavehmode, context.glyph, context.getvalue
+local formatters = string.formatters
+
+function moduledata.fonts.shapes.showlist(specification) -- todo: ranges
+ specification = interfaces.checkedspecification(specification)
+ local id, cs = fonts.definers.internal(specification,"<module:fonts:shapes:font>")
+ local chrs = fontdata[id].characters
+ function char(k)
+ dontleavehmode()
+ glyph(id,k)
+ end
+ local function special(v)
+ local specials = v.specials
+ if specials and #specials > 1 then
+ context("%s:",specials[1])
+ for i=2,#specials do
+ space()
+ char(specials[i])
+ end
+ end
+ end
+ context.begingroup()
+ context.tt()
+ context.starttabulate { "|l|c|c|c|c|l|l|" }
+ context.FL()
+ NC() context.bold("unicode")
+ NC() context.bold("glyph")
+ NC() context.bold("shape")
+ NC() context.bold("lower")
+ NC() context.bold("upper")
+ NC() context.bold("specials")
+ NC() context.bold("description")
+ NC() NR()
+ context.TL()
+ for k, v in next, characters.data do
+ if chrs[k] then
+ NC() context("0x%05X",k)
+ NC() char(k) -- getvalue(cs) context.char(k)
+ NC() char(v.shcode)
+ NC() char(v.lccode or k)
+ NC() char(v.uccode or k)
+ NC() special(v)
+ NC() context.tx(v.description)
+ NC() NR()
+ end
+ end
+ context.stoptabulate()
+ context.endgroup()
+end
+
+function moduledata.fonts.shapes.showlist(specification) -- todo: ranges
+ specification = interfaces.checkedspecification(specification)
+ local id, cs = fonts.definers.internal(specification,"<module:fonts:shapes:font>")
+ local chrs = fontdata[id].characters
+ function char(k)
+ dontleavehmode()
+ glyph(id,k)
+ end
+ local function special(v)
+ local specials = v.specials
+ if specials and #specials > 1 then
+ context("%s:",specials[1])
+ for i=2,#specials do
+ space()
+ char(specials[i])
+ end
+ end
+ end
+ context.begingroup()
+ context.tt()
+ context.starttabulate { "|l|c|c|c|c|l|l|" }
+ context.FL()
+ NC() context.bold("unicode")
+ NC() context.bold("glyph")
+ NC() context.bold("shape")
+ NC() context.bold("lower")
+ NC() context.bold("upper")
+ NC() context.bold("specials")
+ NC() context.bold("description")
+ NC() NR()
+ context.TL()
+ for k, v in next, characters.data do
+ if chrs[k] then
+ NC() context("0x%05X",k)
+ NC() char(k)
+ NC() char(v.shcode)
+ NC() char(v.lccode or k)
+ NC() char(v.uccode or k)
+ NC() special(v)
+ NC() context.tx(v.description)
+ NC() NR()
+ end
+ end
+ context.stoptabulate()
+ context.endgroup()
+end
+
+local descriptions = nil
+local characters = nil
+
+local function showglyphshape(specification)
+ specification = interfaces.checkedspecification(specification)
+ local id, cs = fonts.definers.internal(specification,"<module:fonts:shapes:font>")
+ local tfmdata = fontdata[id]
+ local charnum = tonumber(specification.character)
+ if not charnum then
+ charnum = fonts.helpers.nametoslot(n)
+ end
+ context.start()
+ context.dontleavehmode()
+ context.obeyMPboxdepth()
+ local characters = tfmdata.characters
+ local descriptions = tfmdata.descriptions
+ local parameters = tfmdata.parameters
+ local c = characters[charnum]
+ local d = descriptions[charnum]
+ if d then
+ local factor = (parameters.size/parameters.units)*((7200/7227)/65536)
+ local llx, lly, urx, ury = unpack(d.boundingbox)
+ llx, lly, urx, ury = llx*factor, lly*factor, urx*factor, ury*factor
+ local width, italic = (d.width or 0)*factor, (d.italic or 0)*factor
+ local top_accent, bot_accent = (d.top_accent or 0)*factor, (d.bot_accent or 0)*factor
+ local anchors, math = d.anchors, d.math
+ context.startMPcode()
+ context("pickup pencircle scaled .25bp ;")
+ context('picture p ; p := image(draw textext.drt("\\getuvalue{%s}\\gray\\char%s");); draw p ;',cs,charnum)
+ context('draw (%s,%s)--(%s,%s)--(%s,%s)--(%s,%s)--cycle withcolor green ;',llx,lly,urx,lly,urx,ury,llx,ury)
+ context('draw (%s,%s)--(%s,%s) withcolor green ;',llx,0,urx,0)
+ context('draw boundingbox p withcolor .2white withpen pencircle scaled .065bp ;')
+ context("defaultscale := 0.05 ; ")
+ -- inefficient but non critical
+ local function slant_1(v,dx,dy,txt,xsign,ysign,loc,labloc)
+ if #v > 0 then
+ local l = { }
+ for kk, vv in ipairs(v) do
+ local h, k = vv.height, vv.kern
+ if h and k then
+ l[#l+1] = formatters["((%s,%s) shifted (%s,%s))"](xsign*k*factor,ysign*h*factor,dx,dy)
+ end
+ end
+ context("draw ((%s,%s) shifted (%s,%s))--%s dashed (evenly scaled .25) withcolor .5white;", xsign*v[1].kern*factor,lly,dx,dy,l[1])
+ context("draw laddered (%s) withcolor .5white ;",table.concat(l,".."))
+ context("draw ((%s,%s) shifted (%s,%s))--%s dashed (evenly scaled .25) withcolor .5white;", xsign*v[#v].kern*factor,ury,dx,dy,l[#l])
+ for k, v in ipairs(l) do
+ context("draw %s withcolor blue withpen pencircle scaled 1bp;",v)
+ end
+ end
+ end
+ local function slant_2(v,dx,dy,txt,xsign,ysign,loc,labloc)
+ if #v > 0 then
+ local l = { }
+ for kk, vv in ipairs(v) do
+ local h, k = vv.height, vv.kern
+ if h and k then
+ l[#l+1] = formatters["((%s,%s) shifted (%s,%s))"](xsign*k*factor,ysign*h*factor,dx,dy)
+ end
+ end
+ if loc == "top" then
+ context('label.%s("\\type{%s}",%s shifted (0,-1bp)) ;',loc,txt,l[#l])
+ else
+ context('label.%s("\\type{%s}",%s shifted (0,2bp)) ;',loc,txt,l[1])
+ end
+ for kk, vv in ipairs(v) do
+ local h, k = vv.height, vv.kern
+ if h and k then
+ context('label.top("(%s,%s)",%s shifted (0,-2bp));',k,h,l[kk])
+ end
+ end
+ end
+ end
+ if math then
+ local kerns = math.kerns
+ if kerns then
+ for _, slant in ipairs { slant_1, slant_2 } do
+ for k,v in pairs(kerns) do
+ if k == "top_right" then
+ slant(v,width+italic,0,k,1,1,"top","ulft")
+ elseif k == "bottom_right" then
+ slant(v,width,0,k,1,1,"bot","lrt")
+ elseif k == "top_left" then
+ slant(v,0,0,k,-1,1,"top","ulft")
+ elseif k == "bottom_left" then
+ slant(v,0,0,k,-1,1,"bot","lrt")
+ end
+ end
+ end
+ end
+ end
+ local function show(x,y,txt)
+ local xx, yy = x*factor, y*factor
+ context("draw (%s,%s) withcolor blue withpen pencircle scaled 1bp;",xx,yy)
+ context('label.top("\\type{%s}",(%s,%s-2bp)) ;',txt,xx,yy)
+ context('label.bot("(%s,%s)",(%s,%s+2bp)) ;',x,y,xx,yy)
+ end
+ if anchors then
+ local a = anchors.baselig
+ if a then
+ for k, v in pairs(a) do
+ for kk, vv in ipairs(v) do
+ show(vv[1],vv[2],k .. ":" .. kk)
+ end
+ end
+ end
+ local a = anchors.mark
+ if a then
+ for k, v in pairs(a) do
+ show(v[1],v[2],k)
+ end
+ end
+ local a = anchors.basechar
+ if a then
+ for k, v in pairs(a) do
+ show(v[1],v[2],k)
+ end
+ end
+ local ba = anchors.centry
+ if a then
+ for k, v in pairs(a) do
+ show(v[1],v[2],k)
+ end
+ end
+ local a = anchors.cexit
+ if a then
+ for k, v in pairs(a) do
+ show(v[1],v[2],k)
+ end
+ end
+ end
+ if italic ~= 0 then
+ context('draw (%s,%s-1bp)--(%s,%s-0.5bp) withcolor blue;',width,ury,width,ury)
+ context('draw (%s,%s-1bp)--(%s,%s-0.5bp) withcolor blue;',width+italic,ury,width+italic,ury)
+ context('draw (%s,%s-1bp)--(%s,%s-1bp) withcolor blue;',width,ury,width+italic,ury)
+ context('label.lft("\\type{%s}",(%s+2bp,%s-1bp));',"italic",width,ury)
+ context('label.rt("%s",(%s-2bp,%s-1bp));',d.italic,width+italic,ury)
+ end
+ if top_accent ~= 0 then
+ context('draw (%s,%s+1bp)--(%s,%s-1bp) withcolor blue;',top_accent,ury,top_accent,ury)
+ context('label.bot("\\type{%s}",(%s,%s+1bp));',"top_accent",top_accent,ury)
+ context('label.top("%s",(%s,%s-1bp));',d.top_accent,top_accent,ury)
+ end
+ if bot_accent ~= 0 then
+ context('draw (%s,%s+1bp)--(%s,%s-1bp) withcolor blue;',bot_accent,lly,bot_accent,lly)
+ context('label.top("\\type{%s}",(%s,%s-1bp));',"bot_accent",top_accent,ury)
+ context('label.bot("%s",(%s,%s+1bp));',d.bot_accent,bot_accent,lly)
+ end
+ context('draw origin withcolor red withpen pencircle scaled 1bp;')
+ context("setbounds currentpicture to boundingbox currentpicture enlarged 1bp ;")
+ context("currentpicture := currentpicture scaled 8 ;")
+ context.stopMPcode()
+ -- elseif c then
+ -- lastdata, lastunicode = nil, nil
+ -- local factor = (7200/7227)/65536
+ -- context.startMPcode()
+ -- context("pickup pencircle scaled .25bp ; ")
+ -- context('picture p ; p := image(draw textext.drt("\\gray\\char%s");); draw p ;',charnum)
+ -- context('draw boundingbox p withcolor .2white withpen pencircle scaled .065bp ;')
+ -- context("defaultscale := 0.05 ; ")
+ -- local italic, top_accent, bot_accent = (c.italic or 0)*factor, (c.top_accent or 0)*factor, (c.bot_accent or 0)*factor
+ -- local width, height, depth = (c.width or 0)*factor, (c.height or 0)*factor, (c.depth or 0)*factor
+ -- local ury = height
+ -- if italic ~= 0 then
+ -- context('draw (%s,%s-1bp)--(%s,%s-0.5bp) withcolor blue;',width,ury,width,ury)
+ -- context('draw (%s,%s-1bp)--(%s,%s-0.5bp) withcolor blue;',width+italic,ury,width+italic,ury)
+ -- context('draw (%s,%s-1bp)--(%s,%s-1bp) withcolor blue;',width,ury,width+italic,height)
+ -- context('label.lft("\\type{%s}",(%s+2bp,%s-1bp));',"italic",width,height)
+ -- context('label.rt("%6.3f bp",(%s-2bp,%s-1bp));',italic,width+italic,height)
+ -- end
+ -- if top_accent ~= 0 then
+ -- context('draw (%s,%s+1bp)--(%s,%s-1bp) withcolor blue;',top_accent,ury,top_accent,height)
+ -- context('label.bot("\\type{%s}",(%s,%s+1bp));',"top_accent",top_accent,height)
+ -- context('label.top("%6.3f bp",(%s,%s-1bp));',top_accent,top_accent,height)
+ -- end
+ -- if bot_accent ~= 0 then
+ -- context('draw (%s,%s+1bp)--(%s,%s-1bp) withcolor blue;',bot_accent,lly,bot_accent,height)
+ -- context('label.top("\\type{%s}",(%s,%s-1bp));',"bot_accent",top_accent,height)
+ -- context('label.bot("%6.3f bp",(%s,%s+1bp));',bot_accent,bot_accent,height)
+ -- end
+ -- context('draw origin withcolor red withpen pencircle scaled 1bp;')
+ -- context("setbounds currentpicture to boundingbox currentpicture enlarged 1bp ;")
+ -- context("currentpicture := currentpicture scaled 8 ;")
+ -- context.stopMPcode()
+ else
+ lastdata, lastunicode = nil, nil
+ context("no such shape: 0x%05X",charnum)
+ end
+ context.stop()
+end
+
+moduledata.fonts.shapes.showglyphshape = showglyphshape
+
+function moduledata.fonts.shapes.showallglypshapes(specification)
+ specification = interfaces.checkedspecification(specification)
+ local id, cs = fonts.definers.internal(specification,"<module:fonts:shapes:font>")
+ local descriptions = fontdata[id].descriptions
+ for unicode, description in fonts.iterators.descriptions(tfmdata) do
+ context.modulefontsstartshowglyphshape(unicode,description.name)
+ showglyphshape { number = id, character = unicode }
+ context.modulefontsstopshowglyphshape()
+ end
+end
+
+function moduledata.fonts.shapes.showlastglyphshapefield(unicode,name)
+ if not descriptions then
+ -- bad news
+ elseif name == "unicode" then
+ context("U+%05X",descriptions.unicode)
+ else
+ local d = descriptions[name]
+ if d then
+ context(d)
+ end
+ end
+end
diff --git a/tex/context/base/s-fonts-system.lua b/tex/context/base/s-fonts-system.lua
index a8b6ddaa9..0c0ad4d86 100644
--- a/tex/context/base/s-fonts-system.lua
+++ b/tex/context/base/s-fonts-system.lua
@@ -1,68 +1,68 @@
-if not modules then modules = { } end modules ['s-fonts-system'] = {
- version = 1.001,
- comment = "companion to s-fonts-system.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- ["zapfinoforteltpro"]={
--- ["designsize"]=0,
--- ["familyname"]="zapfinoforteltpro",
--- ["filename"]="zapfinoforteltpro.otf",
--- ["fontname"]="zapfinoforteltpro",
--- ["fontweight"]="regular",
--- ["format"]="otf",
--- ["fullname"]="zapfinoforteltpro",
--- ["maxsize"]=0,
--- ["minsize"]=0,
--- ["modification"]=1105543074,
--- ["modifiers"]="regular",
--- ["rawname"]="ZapfinoForteLTPro",
--- ["style"]="normal",
--- ["subfamily"]="regular",
--- ["variant"]="normal",
--- ["weight"]="normal",
--- ["width"]="normal",
--- }
-
-moduledata.fonts = moduledata.fonts or { }
-moduledata.fonts.system = moduledata.fonts.system or { }
-
-local lower = string.lower
-
-local context = context
-local NC, NR, HL = context.NC, context.NR, context.HL
-local bold = context.bold
-
-function moduledata.fonts.system.showinstalled(specification)
- specification = interfaces.checkedspecification(specification)
- local pattern = lower(specification.pattern or "")
- local list = fonts.names.list(pattern,false,true)
- if list then
- local files = { }
- for k, v in next, list do
- files[file.basename(string.lower(v.filename))] = v
- end
- context.starttabulate { "|Tl|Tl|Tl|Tl|Tl|Tl|" }
- HL()
- NC() bold("filename")
- NC() bold("fontname")
- NC() bold("subfamily")
- NC() bold("variant")
- NC() bold("weight")
- NC() bold("width")
- NC() NR()
- HL()
- for filename, data in table.sortedpairs(files) do
- NC() context(filename)
- NC() context(data.fontname)
- NC() context(data.subfamily)
- NC() context(data.variant)
- NC() context(data.weight)
- NC() context(data.width)
- NC() NR()
- end
- context.stoptabulate()
- end
-end
+if not modules then modules = { } end modules ['s-fonts-system'] = {
+ version = 1.001,
+ comment = "companion to s-fonts-system.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- ["zapfinoforteltpro"]={
+-- ["designsize"]=0,
+-- ["familyname"]="zapfinoforteltpro",
+-- ["filename"]="zapfinoforteltpro.otf",
+-- ["fontname"]="zapfinoforteltpro",
+-- ["fontweight"]="regular",
+-- ["format"]="otf",
+-- ["fullname"]="zapfinoforteltpro",
+-- ["maxsize"]=0,
+-- ["minsize"]=0,
+-- ["modification"]=1105543074,
+-- ["modifiers"]="regular",
+-- ["rawname"]="ZapfinoForteLTPro",
+-- ["style"]="normal",
+-- ["subfamily"]="regular",
+-- ["variant"]="normal",
+-- ["weight"]="normal",
+-- ["width"]="normal",
+-- }
+
+moduledata.fonts = moduledata.fonts or { }
+moduledata.fonts.system = moduledata.fonts.system or { }
+
+local lower = string.lower
+
+local context = context
+local NC, NR, HL = context.NC, context.NR, context.HL
+local bold = context.bold
+
+function moduledata.fonts.system.showinstalled(specification)
+ specification = interfaces.checkedspecification(specification)
+ local pattern = lower(specification.pattern or "")
+ local list = fonts.names.list(pattern,false,true)
+ if list then
+ local files = { }
+ for k, v in next, list do
+ files[file.basename(string.lower(v.filename))] = v
+ end
+ context.starttabulate { "|Tl|Tl|Tl|Tl|Tl|Tl|" }
+ HL()
+ NC() bold("filename")
+ NC() bold("fontname")
+ NC() bold("subfamily")
+ NC() bold("variant")
+ NC() bold("weight")
+ NC() bold("width")
+ NC() NR()
+ HL()
+ for filename, data in table.sortedpairs(files) do
+ NC() context(filename)
+ NC() context(data.fontname)
+ NC() context(data.subfamily)
+ NC() context(data.variant)
+ NC() context(data.weight)
+ NC() context(data.width)
+ NC() NR()
+ end
+ context.stoptabulate()
+ end
+end
diff --git a/tex/context/base/s-fonts-tables.lua b/tex/context/base/s-fonts-tables.lua
index 4f147f3e0..5c91d5ee7 100644
--- a/tex/context/base/s-fonts-tables.lua
+++ b/tex/context/base/s-fonts-tables.lua
@@ -1,312 +1,312 @@
-if not modules then modules = { } end modules ['s-fonts-tables'] = {
- version = 1.001,
- comment = "companion to s-fonts-tables.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-moduledata.fonts = moduledata.fonts or { }
-moduledata.fonts.tables = moduledata.fonts.tables or { }
-
-local setmetatableindex = table.setmetatableindex
-local sortedhash = table.sortedhash
-local sortedkeys = table.sortedkeys
-local format = string.format
-local concat = table.concat
-
-local tabletracers = moduledata.fonts.tables
-
-local digits = {
- dflt = {
- dflt = "1234567890 1/2",
- },
-}
-
-local punctuation = {
- dflt = {
- dflt = ". , : ; ? ! ‹ › « »",
- },
-}
-
-local symbols = {
- dflt = {
- dflt = "@ # $ % & * () [] {} <> + - = / |",
- },
-}
-
-local LATN = "abcdefghijklmnopqrstuvwxyz"
-
-local uppercase = {
- latn = {
- dflt = LATN,
- fra = LATN .. " ÀÁÂÈÉÊÒÓÔÙÚÛÆÇ",
- },
- grek = {
- dftl = "ΑΒΓΔΕΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ",
- },
- cyrl= {
- dflt = "АБВГДЕЖЗИІЙКЛМНОПРСТУФХЦЧШЩЪЫЬѢЭЮЯѲ"
- },
-}
-
-local latn = "abcdefghijklmnopqrstuvwxyz"
-
-local lowercase = {
- latn = {
- dftl = latn,
- nld = latn .. " ïèéë",
- deu = latn .. " äöüß",
- fra = latn .. " àáâèéêòóôùúûæç",
- },
- grek = {
- dftl = "αβγδεηθικλμνξοπρστυφχψω",
- },
- cyrl= {
- dflt = "абвгдежзиійклмнопрстуфхцчшщъыьѣэюяѳ"
- },
-}
-
-local samples = {
- digits = digits,
- punctuation = punctuation,
- symbols = symbols,
- uppercase = uppercase,
- lowercase = lowercase,
-}
-
-tabletracers.samples = samples
-
-setmetatableindex(uppercase, function(t,k) return rawget(t,"latn") end)
-setmetatableindex(lowercase, function(t,k) return rawget(t,"latn") end)
-setmetatableindex(digits, function(t,k) return rawget(t,"dflt") end)
-setmetatableindex(symbols, function(t,k) return rawget(t,"dflt") end)
-setmetatableindex(punctuation, function(t,k) return rawget(t,"dflt") end)
-
-setmetatableindex(uppercase.latn, function(t,k) return rawget(t,"dflt") end)
-setmetatableindex(uppercase.grek, function(t,k) return rawget(t,"dflt") end)
-setmetatableindex(uppercase.cyrl, function(t,k) return rawget(t,"dflt") end)
-
-setmetatableindex(lowercase.latn, function(t,k) return rawget(t,"dflt") end)
-setmetatableindex(lowercase.grek, function(t,k) return rawget(t,"dflt") end)
-setmetatableindex(lowercase.cyrl, function(t,k) return rawget(t,"dflt") end)
-
-setmetatableindex(digits.dflt, function(t,k) return rawget(t,"dflt") end)
-setmetatableindex(symbols.dflt, function(t,k) return rawget(t,"dflt") end)
-setmetatableindex(punctuation.dflt, function(t,k) return rawget(t,"dflt") end)
-
-local function typesettable(t,keys,synonyms,nesting,prefix)
- if t then
- if not prefix then
- context.starttabulate { "|Tl|Tl|Tl|" }
- end
- for k, v in sortedhash(keys) do
- if k == "synonyms" then
- elseif type(v) ~= "table" then
- context.NC()
- if prefix then
- context("%s.%s",prefix,k)
- else
- context(k)
- end
- context.NC()
- local tk = t[k]
- if v == "boolean" then
- context(tostring(tk or false))
- elseif not tk then
- context("<unset>")
- elseif v == "filename" then
- context(file.basename(tk))
- elseif v == "basepoints" then
- context("%sbp",tk)
- elseif v == "scaledpoints" then
- context("%p",tk)
- elseif v == "table" then
- context("<table>")
- else -- if v == "integerscale" then
- context(tostring(tk))
- end
- context.NC()
- local synonym = (not prefix and synonyms[k]) or (prefix and synonyms[format("%s.%s",prefix,k)])
- if synonym then
- context(format("(%s)",concat(synonym," ")))
- end
- context.NC()
- context.NR()
- elseif nesting == false then
- context("<table>")
- else -- true or nil
- typesettable(t[k],v,synonyms,nesting,k)
- end
- end
- if not prefix then
- context.stoptabulate()
- end
- end
-end
-
-local function typeset(t,keys,nesting,prefix)
- local synonyms = keys.synonyms or { }
- local collected = { }
- for k, v in next, synonyms do
- local c = collected[v]
- if not c then
- c = { }
- collected[v] = c
- end
- c[#c+1] = k
- end
- for k, v in next, collected do
- table.sort(v)
- end
- typesettable(t,keys,collected,nesting,prefix)
-end
-
-tabletracers.typeset = typeset
-
-function tabletracers.showproperties(nesting)
- local tfmdata = fonts.hashes.identifiers[font.current()]
- typeset(tfmdata.properties,fonts.constructors.keys.properties,nesting)
-end
-
-function tabletracers.showparameters(nesting)
- local tfmdata = fonts.hashes.identifiers[font.current()]
- typeset(tfmdata.parameters,fonts.constructors.keys.parameters,nesting)
-end
-
-function tabletracers.showpositionings()
- local tfmdata = fonts.hashes.identifiers[font.current()]
- local resources = tfmdata.resources
- if resources then
- local features = resources.features
- if features then
- local gpos = features.gpos
- if gpos and next(gpos) then
- context.starttabulate { "|Tl|Tl|Tlp|" }
- for feature, scripts in sortedhash(gpos) do
- for script, languages in sortedhash(scripts) do
- context.NC()
- context(feature)
- context.NC()
- context(script)
- context.NC()
- context(concat(sortedkeys(languages)," "))
- context.NC()
- context.NR()
- end
- end
- context.stoptabulate()
- else
- context("no entries")
- context.par()
- end
- end
- end
-end
-
-local dynamics = true
-
-function tabletracers.showsubstitutions()
- local tfmdata = fonts.hashes.identifiers[font.current()]
- local resources = tfmdata.resources
- if resources then
- local features = resources.features
- if features then
- local gsub = features.gsub
- if gsub then
- local makes_sense = { }
- for feature, scripts in sortedhash(gsub) do
- for script, languages in sortedhash(scripts) do
- for language in sortedhash(languages) do
- local tag = format("dummy-%s-%s-%s",feature,script,language)
- local fnt = format("file:%s*%s",file.basename(tfmdata.properties.filename),tag)
- context.definefontfeature (
- { tag },
- {
- mode = "node",
- script = script,
- language = language,
- [feature] = "yes"
- }
- )
- if not dynamics then
- context.definefont( { fnt }, { fnt } )
- end
- makes_sense[#makes_sense+1] = {
- feature = feature,
- tag = tag,
- script = script,
- language = language,
- fontname = fnt,
- }
- end
- end
- end
- if #makes_sense > 0 then
- context.starttabulate { "|Tl|Tl|Tl|p|" }
- for i=1,#makes_sense do
- local data = makes_sense[i]
- local script = data.script
- local language = data.language
- context.NC()
- context(data.feature)
- context.NC()
- context(script)
- context.NC()
- context(language)
- context.NC()
- if not dynamics then
- context.startfont { data.fontname }
- else
- context.addff(data.tag)
- end
- context.verbatim(samples.lowercase [script][language]) context.par()
- context.verbatim(samples.uppercase [script][language]) context.par()
- context.verbatim(samples.digits [script][language]) context.par()
- context.verbatim(samples.punctuation[script][language]) context.quad()
- context.verbatim(samples.symbols [script][language])
- if not dynamics then
- context.stopfont()
- end
- context.NC()
- context.NR()
- end
- context.stoptabulate()
- else
- context("no entries")
- context.par()
- end
- end
- end
- end
-end
-
-function tabletracers.showall(specification) -- not interfaced
-
- specification = interfaces.checkedspecification(specification)
-
- if specification.title then
- context.starttitle { title = specification.title }
- end
-
- context.startsubject { title = "Properties" }
- tabletracers.showproperties()
- context.stopsubject()
-
- context.startsubject { title = "Parameters" }
- tabletracers.showparameters()
- context.stopsubject()
-
- context.startsubject { title = "Positioning features" }
- tabletracers.showpositionings()
- context.stopsubject()
-
- context.startsubject { title = "Substitution features" }
- tabletracers.showsubstitutions()
- context.stopsubject()
-
- if title then
- context.stoptitle()
- end
-
-end
+if not modules then modules = { } end modules ['s-fonts-tables'] = {
+ version = 1.001,
+ comment = "companion to s-fonts-tables.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.fonts = moduledata.fonts or { }
+moduledata.fonts.tables = moduledata.fonts.tables or { }
+
+local setmetatableindex = table.setmetatableindex
+local sortedhash = table.sortedhash
+local sortedkeys = table.sortedkeys
+local format = string.format
+local concat = table.concat
+
+local tabletracers = moduledata.fonts.tables
+
+local digits = {
+ dflt = {
+ dflt = "1234567890 1/2",
+ },
+}
+
+local punctuation = {
+ dflt = {
+ dflt = ". , : ; ? ! ‹ › « »",
+ },
+}
+
+local symbols = {
+ dflt = {
+ dflt = "@ # $ % & * () [] {} <> + - = / |",
+ },
+}
+
+local LATN = "abcdefghijklmnopqrstuvwxyz"
+
+local uppercase = {
+ latn = {
+ dflt = LATN,
+ fra = LATN .. " ÀÁÂÈÉÊÒÓÔÙÚÛÆÇ",
+ },
+ grek = {
+ dftl = "ΑΒΓΔΕΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ",
+ },
+ cyrl= {
+ dflt = "АБВГДЕЖЗИІЙКЛМНОПРСТУФХЦЧШЩЪЫЬѢЭЮЯѲ"
+ },
+}
+
+local latn = "abcdefghijklmnopqrstuvwxyz"
+
+local lowercase = {
+ latn = {
+ dftl = latn,
+ nld = latn .. " ïèéë",
+ deu = latn .. " äöüß",
+ fra = latn .. " àáâèéêòóôùúûæç",
+ },
+ grek = {
+ dftl = "αβγδεηθικλμνξοπρστυφχψω",
+ },
+ cyrl= {
+ dflt = "абвгдежзиійклмнопрстуфхцчшщъыьѣэюяѳ"
+ },
+}
+
+local samples = {
+ digits = digits,
+ punctuation = punctuation,
+ symbols = symbols,
+ uppercase = uppercase,
+ lowercase = lowercase,
+}
+
+tabletracers.samples = samples
+
+setmetatableindex(uppercase, function(t,k) return rawget(t,"latn") end)
+setmetatableindex(lowercase, function(t,k) return rawget(t,"latn") end)
+setmetatableindex(digits, function(t,k) return rawget(t,"dflt") end)
+setmetatableindex(symbols, function(t,k) return rawget(t,"dflt") end)
+setmetatableindex(punctuation, function(t,k) return rawget(t,"dflt") end)
+
+setmetatableindex(uppercase.latn, function(t,k) return rawget(t,"dflt") end)
+setmetatableindex(uppercase.grek, function(t,k) return rawget(t,"dflt") end)
+setmetatableindex(uppercase.cyrl, function(t,k) return rawget(t,"dflt") end)
+
+setmetatableindex(lowercase.latn, function(t,k) return rawget(t,"dflt") end)
+setmetatableindex(lowercase.grek, function(t,k) return rawget(t,"dflt") end)
+setmetatableindex(lowercase.cyrl, function(t,k) return rawget(t,"dflt") end)
+
+setmetatableindex(digits.dflt, function(t,k) return rawget(t,"dflt") end)
+setmetatableindex(symbols.dflt, function(t,k) return rawget(t,"dflt") end)
+setmetatableindex(punctuation.dflt, function(t,k) return rawget(t,"dflt") end)
+
+local function typesettable(t,keys,synonyms,nesting,prefix)
+ if t then
+ if not prefix then
+ context.starttabulate { "|Tl|Tl|Tl|" }
+ end
+ for k, v in sortedhash(keys) do
+ if k == "synonyms" then
+ elseif type(v) ~= "table" then
+ context.NC()
+ if prefix then
+ context("%s.%s",prefix,k)
+ else
+ context(k)
+ end
+ context.NC()
+ local tk = t[k]
+ if v == "boolean" then
+ context(tostring(tk or false))
+ elseif not tk then
+ context("<unset>")
+ elseif v == "filename" then
+ context(file.basename(tk))
+ elseif v == "basepoints" then
+ context("%sbp",tk)
+ elseif v == "scaledpoints" then
+ context("%p",tk)
+ elseif v == "table" then
+ context("<table>")
+ else -- if v == "integerscale" then
+ context(tostring(tk))
+ end
+ context.NC()
+ local synonym = (not prefix and synonyms[k]) or (prefix and synonyms[format("%s.%s",prefix,k)])
+ if synonym then
+ context(format("(%s)",concat(synonym," ")))
+ end
+ context.NC()
+ context.NR()
+ elseif nesting == false then
+ context("<table>")
+ else -- true or nil
+ typesettable(t[k],v,synonyms,nesting,k)
+ end
+ end
+ if not prefix then
+ context.stoptabulate()
+ end
+ end
+end
+
+local function typeset(t,keys,nesting,prefix)
+ local synonyms = keys.synonyms or { }
+ local collected = { }
+ for k, v in next, synonyms do
+ local c = collected[v]
+ if not c then
+ c = { }
+ collected[v] = c
+ end
+ c[#c+1] = k
+ end
+ for k, v in next, collected do
+ table.sort(v)
+ end
+ typesettable(t,keys,collected,nesting,prefix)
+end
+
+tabletracers.typeset = typeset
+
+function tabletracers.showproperties(nesting)
+ local tfmdata = fonts.hashes.identifiers[font.current()]
+ typeset(tfmdata.properties,fonts.constructors.keys.properties,nesting)
+end
+
+function tabletracers.showparameters(nesting)
+ local tfmdata = fonts.hashes.identifiers[font.current()]
+ typeset(tfmdata.parameters,fonts.constructors.keys.parameters,nesting)
+end
+
+function tabletracers.showpositionings()
+ local tfmdata = fonts.hashes.identifiers[font.current()]
+ local resources = tfmdata.resources
+ if resources then
+ local features = resources.features
+ if features then
+ local gpos = features.gpos
+ if gpos and next(gpos) then
+ context.starttabulate { "|Tl|Tl|Tlp|" }
+ for feature, scripts in sortedhash(gpos) do
+ for script, languages in sortedhash(scripts) do
+ context.NC()
+ context(feature)
+ context.NC()
+ context(script)
+ context.NC()
+ context(concat(sortedkeys(languages)," "))
+ context.NC()
+ context.NR()
+ end
+ end
+ context.stoptabulate()
+ else
+ context("no entries")
+ context.par()
+ end
+ end
+ end
+end
+
+local dynamics = true
+
+function tabletracers.showsubstitutions()
+ local tfmdata = fonts.hashes.identifiers[font.current()]
+ local resources = tfmdata.resources
+ if resources then
+ local features = resources.features
+ if features then
+ local gsub = features.gsub
+ if gsub then
+ local makes_sense = { }
+ for feature, scripts in sortedhash(gsub) do
+ for script, languages in sortedhash(scripts) do
+ for language in sortedhash(languages) do
+ local tag = format("dummy-%s-%s-%s",feature,script,language)
+ local fnt = format("file:%s*%s",file.basename(tfmdata.properties.filename),tag)
+ context.definefontfeature (
+ { tag },
+ {
+ mode = "node",
+ script = script,
+ language = language,
+ [feature] = "yes"
+ }
+ )
+ if not dynamics then
+ context.definefont( { fnt }, { fnt } )
+ end
+ makes_sense[#makes_sense+1] = {
+ feature = feature,
+ tag = tag,
+ script = script,
+ language = language,
+ fontname = fnt,
+ }
+ end
+ end
+ end
+ if #makes_sense > 0 then
+ context.starttabulate { "|Tl|Tl|Tl|p|" }
+ for i=1,#makes_sense do
+ local data = makes_sense[i]
+ local script = data.script
+ local language = data.language
+ context.NC()
+ context(data.feature)
+ context.NC()
+ context(script)
+ context.NC()
+ context(language)
+ context.NC()
+ if not dynamics then
+ context.startfont { data.fontname }
+ else
+ context.addff(data.tag)
+ end
+ context.verbatim(samples.lowercase [script][language]) context.par()
+ context.verbatim(samples.uppercase [script][language]) context.par()
+ context.verbatim(samples.digits [script][language]) context.par()
+ context.verbatim(samples.punctuation[script][language]) context.quad()
+ context.verbatim(samples.symbols [script][language])
+ if not dynamics then
+ context.stopfont()
+ end
+ context.NC()
+ context.NR()
+ end
+ context.stoptabulate()
+ else
+ context("no entries")
+ context.par()
+ end
+ end
+ end
+ end
+end
+
+function tabletracers.showall(specification) -- not interfaced
+
+ specification = interfaces.checkedspecification(specification)
+
+ if specification.title then
+ context.starttitle { title = specification.title }
+ end
+
+ context.startsubject { title = "Properties" }
+ tabletracers.showproperties()
+ context.stopsubject()
+
+ context.startsubject { title = "Parameters" }
+ tabletracers.showparameters()
+ context.stopsubject()
+
+ context.startsubject { title = "Positioning features" }
+ tabletracers.showpositionings()
+ context.stopsubject()
+
+ context.startsubject { title = "Substitution features" }
+ tabletracers.showsubstitutions()
+ context.stopsubject()
+
+ if title then
+ context.stoptitle()
+ end
+
+end
diff --git a/tex/context/base/s-fonts-vectors.lua b/tex/context/base/s-fonts-vectors.lua
index 436f3e63d..1bac0ae8b 100644
--- a/tex/context/base/s-fonts-vectors.lua
+++ b/tex/context/base/s-fonts-vectors.lua
@@ -1,104 +1,104 @@
-if not modules then modules = { } end modules ['s-fonts-vectors'] = {
- version = 1.001,
- comment = "companion to s-fonts-vectors.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-moduledata.fonts = moduledata.fonts or { }
-moduledata.fonts.protrusions = moduledata.fonts.protrusions or { }
-moduledata.fonts.expansions = moduledata.fonts.expansions or { }
-
-local NC, NR = context.NC, context.NR
-
-local classes = fonts.protrusions.classes
-local vectors = fonts.protrusions.vectors
-
-function moduledata.fonts.protrusions.showvector(specification)
- specification = interfaces.checkedspecification(specification)
- local vector = vectors[specification.name or "?"]
- if vector then
- context.blank()
- context.startcolumns { n = specification.columns or 3 }
- context.starttabulate { "|T||cw(.5em)||" }
- for unicode, values in table.sortedhash(vector) do
- NC() context("%U",unicode)
- NC() context("%.02f",values[1])
- NC() context("%c",unicode)
- NC() context("%.02f",values[2])
- NC() NR()
- end
- context.stoptabulate()
- context.stopcolumns()
- context.blank()
- end
-end
-
-function moduledata.fonts.protrusions.showclass(specification)
- specification = interfaces.checkedspecification(specification)
- local class = specification.name and classes[specification.name]
- local classes = class and { class} or classes
- context.starttabulate { "|l|l|r|r|r|" }
- NC() context.bold("name")
- NC() context.bold("vector")
- NC() context.bold("factor")
- NC() context.bold("left")
- NC() context.bold("right")
- NC() NR()
- for name, class in table.sortedhash(classes) do
- NC() context(name)
- NC() context(class.vector)
- NC() context("%.02f",class.factor)
- NC() context("%.02f",class.left)
- NC() context("%.02f",class.right)
- NC() NR()
- end
- context.stoptabulate()
-end
-
-local classes = fonts.expansions.classes
-local vectors = fonts.expansions.vectors
-
-function moduledata.fonts.expansions.showvector(specification)
- specification = interfaces.checkedspecification(specification)
- local vector = vectors[specification.name or "?"]
- if vector then
- context.blank()
- context.startcolumns { n = specification.columns or 3 }
- context.starttabulate { "|T|cw(.5em)||" }
- for unicode, value in table.sortedhash(vector) do
- NC() context("%U",unicode)
- NC() context("%c",unicode)
- NC() context("%.02f",value)
- NC() NR()
- end
- context.stoptabulate()
- context.stopcolumns()
- context.blank()
- end
-end
-
-function moduledata.fonts.expansions.showclass(specification)
- specification = interfaces.checkedspecification(specification)
- local class = specification.name and classes[specification.name]
- local classes = class and { class} or classes
- context.starttabulate { "|l|l|r|r|r|" }
- NC() context.bold("name")
- NC() context.bold("vector")
- NC() context.bold("step")
- NC() context.bold("factor")
- NC() context.bold("stretch")
- NC() context.bold("shrink")
- NC() NR()
- for name, class in table.sortedhash(classes) do
- NC() context(name)
- NC() context(class.vector)
- NC() context("%.02f",class.step)
- NC() context("%.02f",class.factor)
- NC() context("% 2i",class.stretch)
- NC() context("% 2i",class.shrink)
- NC() NR()
- end
- context.stoptabulate()
-end
+if not modules then modules = { } end modules ['s-fonts-vectors'] = {
+ version = 1.001,
+ comment = "companion to s-fonts-vectors.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.fonts = moduledata.fonts or { }
+moduledata.fonts.protrusions = moduledata.fonts.protrusions or { }
+moduledata.fonts.expansions = moduledata.fonts.expansions or { }
+
+local NC, NR = context.NC, context.NR
+
+local classes = fonts.protrusions.classes
+local vectors = fonts.protrusions.vectors
+
+function moduledata.fonts.protrusions.showvector(specification)
+ specification = interfaces.checkedspecification(specification)
+ local vector = vectors[specification.name or "?"]
+ if vector then
+ context.blank()
+ context.startcolumns { n = specification.columns or 3 }
+ context.starttabulate { "|T||cw(.5em)||" }
+ for unicode, values in table.sortedhash(vector) do
+ NC() context("%U",unicode)
+ NC() context("%.02f",values[1])
+ NC() context("%c",unicode)
+ NC() context("%.02f",values[2])
+ NC() NR()
+ end
+ context.stoptabulate()
+ context.stopcolumns()
+ context.blank()
+ end
+end
+
+function moduledata.fonts.protrusions.showclass(specification)
+ specification = interfaces.checkedspecification(specification)
+ local class = specification.name and classes[specification.name]
+ local classes = class and { class} or classes
+ context.starttabulate { "|l|l|r|r|r|" }
+ NC() context.bold("name")
+ NC() context.bold("vector")
+ NC() context.bold("factor")
+ NC() context.bold("left")
+ NC() context.bold("right")
+ NC() NR()
+ for name, class in table.sortedhash(classes) do
+ NC() context(name)
+ NC() context(class.vector)
+ NC() context("%.02f",class.factor)
+ NC() context("%.02f",class.left)
+ NC() context("%.02f",class.right)
+ NC() NR()
+ end
+ context.stoptabulate()
+end
+
+local classes = fonts.expansions.classes
+local vectors = fonts.expansions.vectors
+
+function moduledata.fonts.expansions.showvector(specification)
+ specification = interfaces.checkedspecification(specification)
+ local vector = vectors[specification.name or "?"]
+ if vector then
+ context.blank()
+ context.startcolumns { n = specification.columns or 3 }
+ context.starttabulate { "|T|cw(.5em)||" }
+ for unicode, value in table.sortedhash(vector) do
+ NC() context("%U",unicode)
+ NC() context("%c",unicode)
+ NC() context("%.02f",value)
+ NC() NR()
+ end
+ context.stoptabulate()
+ context.stopcolumns()
+ context.blank()
+ end
+end
+
+function moduledata.fonts.expansions.showclass(specification)
+ specification = interfaces.checkedspecification(specification)
+ local class = specification.name and classes[specification.name]
+ local classes = class and { class} or classes
+ context.starttabulate { "|l|l|r|r|r|" }
+ NC() context.bold("name")
+ NC() context.bold("vector")
+ NC() context.bold("step")
+ NC() context.bold("factor")
+ NC() context.bold("stretch")
+ NC() context.bold("shrink")
+ NC() NR()
+ for name, class in table.sortedhash(classes) do
+ NC() context(name)
+ NC() context(class.vector)
+ NC() context("%.02f",class.step)
+ NC() context("%.02f",class.factor)
+ NC() context("% 2i",class.stretch)
+ NC() context("% 2i",class.shrink)
+ NC() NR()
+ end
+ context.stoptabulate()
+end
diff --git a/tex/context/base/s-languages-sorting.lua b/tex/context/base/s-languages-sorting.lua
index b7d75f8b8..82a0827bb 100644
--- a/tex/context/base/s-languages-sorting.lua
+++ b/tex/context/base/s-languages-sorting.lua
@@ -1,118 +1,118 @@
-if not modules then modules = { } end modules ['s-languages-system'] = {
- version = 1.001,
- comment = "companion to s-languages-system.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-moduledata.languages = moduledata.languages or { }
-moduledata.languages.sorting = moduledata.languages.sorting or { }
-
-local formatters = string.formatters
-local utfbyte, utfcharacters = utf.byte, utf.characters
-local sortedpairs = table.sortedpairs
-
-local definitions = sorters.definitions
-local constants = sorters.constants
-local replacementoffset = constants.replacementoffset
-
-local currentfont = font.current
-local fontchars = fonts.hashes.characters
-
-local c_darkblue = { "darkblue" }
-local c_darkred = { "darkred" }
-local f_chr = formatters["\\tttf%H"]
-
-local function chr(str,done)
- if done then
- context.space()
- end
- local c = fontchars[currentfont()]
- for s in utfcharacters(str) do
- local u = utfbyte(s)
- if c[u] then
- context(s)
- elseif u > replacementoffset then
- context.color(c_darkblue, f_chr(u))
- else
- context.color(c_darkred, f_chr(u))
- end
- end
- return true
-end
-
-local function map(a,b,done)
- if done then
- context.space()
- end
- -- context.tttf()
- chr(a)
- context("=")
- chr(b)
- return true
-end
-
-local function nop()
- -- context.tttf()
- context("none")
-end
-
-local function key(data,field)
- context.NC()
- context(field)
- context.NC()
- context(data[field])
- context.NC()
- context.NR()
-end
-
-function moduledata.languages.sorting.showinstalled(tag)
- if not tag or tag == "" or tag == interfaces.variables.all then
- for tag, data in sortedpairs(definitions) do
- moduledata.languages.sorting.showinstalled (tag)
- end
- else
- sorters.update() -- syncs data
- local data = definitions[tag]
- if data then
- context.starttabulate { "|lB|pl|" }
- key(data,"language")
- key(data,"parent")
- key(data,"method")
- context.NC()
- context("replacements")
- context.NC()
- local replacements = data.replacements
- if #replacements == 0 then
- nop()
- else
- for i=1,#replacements do
- local r = replacements[i]
- map(r[1],r[2],i > 1)
- end
- end
- context.NC()
- context.NR()
- context.NC()
- context("order")
- context.NC()
- local orders = data.orders
- for i=1,#orders do
- chr(orders[i],i > 1)
- end
- context.NC()
- context.NR()
- context.NC()
- context("entries")
- context.NC()
- local done = false
- for k, e in sortedpairs(data.entries) do
- done = map(k,e,done)
- end
- context.NC()
- context.NR()
- context.stoptabulate()
- end
- end
-end
+if not modules then modules = { } end modules ['s-languages-system'] = {
+ version = 1.001,
+ comment = "companion to s-languages-system.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.languages = moduledata.languages or { }
+moduledata.languages.sorting = moduledata.languages.sorting or { }
+
+local formatters = string.formatters
+local utfbyte, utfcharacters = utf.byte, utf.characters
+local sortedpairs = table.sortedpairs
+
+local definitions = sorters.definitions
+local constants = sorters.constants
+local replacementoffset = constants.replacementoffset
+
+local currentfont = font.current
+local fontchars = fonts.hashes.characters
+
+local c_darkblue = { "darkblue" }
+local c_darkred = { "darkred" }
+local f_chr = formatters["\\tttf%H"]
+
+local function chr(str,done)
+ if done then
+ context.space()
+ end
+ local c = fontchars[currentfont()]
+ for s in utfcharacters(str) do
+ local u = utfbyte(s)
+ if c[u] then
+ context(s)
+ elseif u > replacementoffset then
+ context.color(c_darkblue, f_chr(u))
+ else
+ context.color(c_darkred, f_chr(u))
+ end
+ end
+ return true
+end
+
+local function map(a,b,done)
+ if done then
+ context.space()
+ end
+ -- context.tttf()
+ chr(a)
+ context("=")
+ chr(b)
+ return true
+end
+
+local function nop()
+ -- context.tttf()
+ context("none")
+end
+
+local function key(data,field)
+ context.NC()
+ context(field)
+ context.NC()
+ context(data[field])
+ context.NC()
+ context.NR()
+end
+
+function moduledata.languages.sorting.showinstalled(tag)
+ if not tag or tag == "" or tag == interfaces.variables.all then
+ for tag, data in sortedpairs(definitions) do
+ moduledata.languages.sorting.showinstalled (tag)
+ end
+ else
+ sorters.update() -- syncs data
+ local data = definitions[tag]
+ if data then
+ context.starttabulate { "|lB|pl|" }
+ key(data,"language")
+ key(data,"parent")
+ key(data,"method")
+ context.NC()
+ context("replacements")
+ context.NC()
+ local replacements = data.replacements
+ if #replacements == 0 then
+ nop()
+ else
+ for i=1,#replacements do
+ local r = replacements[i]
+ map(r[1],r[2],i > 1)
+ end
+ end
+ context.NC()
+ context.NR()
+ context.NC()
+ context("order")
+ context.NC()
+ local orders = data.orders
+ for i=1,#orders do
+ chr(orders[i],i > 1)
+ end
+ context.NC()
+ context.NR()
+ context.NC()
+ context("entries")
+ context.NC()
+ local done = false
+ for k, e in sortedpairs(data.entries) do
+ done = map(k,e,done)
+ end
+ context.NC()
+ context.NR()
+ context.stoptabulate()
+ end
+ end
+end
diff --git a/tex/context/base/s-languages-system.lua b/tex/context/base/s-languages-system.lua
index 4c27b5b2a..5afc4d403 100644
--- a/tex/context/base/s-languages-system.lua
+++ b/tex/context/base/s-languages-system.lua
@@ -1,35 +1,35 @@
-if not modules then modules = { } end modules ['s-languages-system'] = {
- version = 1.001,
- comment = "companion to s-languages-system.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-moduledata.languages = moduledata.languages or { }
-moduledata.languages.system = moduledata.languages.system or { }
-
-local NC, NR, HL = context.NC, context.NR, context.HL
-
-function moduledata.languages.system.showinstalled()
- local numbers = languages.numbers
- local registered = languages.registered
- context.starttabulate { "|r|l|l|l|l|" }
- NC() context("id")
- NC() context("tag")
- NC() context("synonyms")
- NC() context("parent")
- NC() context("loaded")
- NC() NR() HL()
- for i=1,#numbers do
- local tag = numbers[i]
- local data = registered[tag]
- NC() context(data.number)
- NC() context(tag)
- NC() context("% t",table.sortedkeys(data.synonyms))
- NC() context(data.parent)
- NC() context("%+t",table.sortedkeys(data.used))
- NC() NR()
- end
- context.stoptabulate()
-end
+if not modules then modules = { } end modules ['s-languages-system'] = {
+ version = 1.001,
+ comment = "companion to s-languages-system.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.languages = moduledata.languages or { }
+moduledata.languages.system = moduledata.languages.system or { }
+
+local NC, NR, HL = context.NC, context.NR, context.HL
+
+function moduledata.languages.system.showinstalled()
+ local numbers = languages.numbers
+ local registered = languages.registered
+ context.starttabulate { "|r|l|l|l|l|" }
+ NC() context("id")
+ NC() context("tag")
+ NC() context("synonyms")
+ NC() context("parent")
+ NC() context("loaded")
+ NC() NR() HL()
+ for i=1,#numbers do
+ local tag = numbers[i]
+ local data = registered[tag]
+ NC() context(data.number)
+ NC() context(tag)
+ NC() context("% t",table.sortedkeys(data.synonyms))
+ NC() context(data.parent)
+ NC() context("%+t",table.sortedkeys(data.used))
+ NC() NR()
+ end
+ context.stoptabulate()
+end
diff --git a/tex/context/base/s-math-coverage.lua b/tex/context/base/s-math-coverage.lua
index 52e9b777c..258019c9d 100644
--- a/tex/context/base/s-math-coverage.lua
+++ b/tex/context/base/s-math-coverage.lua
@@ -1,180 +1,180 @@
-if not modules then modules = { } end modules ['s-math-coverage'] = {
- version = 1.001,
- comment = "companion to s-math-coverage.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-moduledata.math = moduledata.math or { }
-moduledata.math.coverage = moduledata.math.coverage or { }
-
-local utfchar, utfbyte = utf.char, utf.byte
-local formatters, lower = string.formatters, string.lower
-local concat = table.concat
-
-local context = context
-local NC, NR, HL = context.NC, context.NR, context.HL
-local char, getglyph, bold = context.char, context.getglyph, context.bold
-
-local ucgreek = {
- 0x0391, 0x0392, 0x0393, 0x0394, 0x0395,
- 0x0396, 0x0397, 0x0398, 0x0399, 0x039A,
- 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
- 0x03A0, 0x03A1, 0x03A3, 0x03A4, 0x03A5,
- 0x03A6, 0x03A7, 0x03A8, 0x03A9
-}
-
-local lcgreek = {
- 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5,
- 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA,
- 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
- 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4,
- 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9,
- 0x03D1, 0x03D5, 0x03D6, 0x03F0, 0x03F1,
- 0x03F4, 0x03F5
-}
-
-local ucletters = {
- 0x00041, 0x00042, 0x00043, 0x00044, 0x00045,
- 0x00046, 0x00047, 0x00048, 0x00049, 0x0004A,
- 0x0004B, 0x0004C, 0x0004D, 0x0004E, 0x0004F,
- 0x00050, 0x00051, 0x00052, 0x00053, 0x00054,
- 0x00055, 0x00056, 0x00057, 0x00058, 0x00059,
- 0x0005A,
-}
-
-local lcletters = {
- 0x00061, 0x00062, 0x00063, 0x00064, 0x00065,
- 0x00066, 0x00067, 0x00068, 0x00069, 0x0006A,
- 0x0006B, 0x0006C, 0x0006D, 0x0006E, 0x0006F,
- 0x00070, 0x00071, 0x00072, 0x00073, 0x00074,
- 0x00075, 0x00076, 0x00077, 0x00078, 0x00079,
- 0x0007A,
-}
-
-local digits = {
- 0x00030, 0x00031, 0x00032, 0x00033, 0x00034,
- 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
-}
-
-local styles = {
- "regular", "sansserif", "monospaced", "fraktur", "script", "blackboard"
-}
-
-local alternatives = {
- "normal", "bold", "italic", "bolditalic"
-}
-
-local alphabets = {
- ucletters, lcletters, ucgreek, lcgreek, digits,
-}
-
-local getboth = mathematics.getboth
-local remapalphabets = mathematics.remapalphabets
-
-local chardata = characters.data
-local superscripts = characters.superscripts
-local subscripts = characters.subscripts
-
-function moduledata.math.coverage.showalphabets()
- context.starttabulate { "|lT|l|Tl|" }
- for i=1,#styles do
- local style = styles[i]
- for i=1,#alternatives do
- local alternative = alternatives[i]
- for i=1,#alphabets do
- local alphabet = alphabets[i]
- NC()
- if i == 1 then
- context("%s %s",style,alternative)
- end
- NC()
- context.startimath()
- context.setmathattribute(style,alternative)
- for i=1,#alphabet do
- local letter = alphabet[i]
- local id = getboth(style,alternative)
- local unicode = remapalphabets(letter,id)
- if not unicode then
- context.underbar(utfchar(letter))
- elseif unicode == letter then
- context(utfchar(unicode))
- else
- context(utfchar(unicode))
- end
- end
- context.stopimath()
- NC()
- local first = alphabet[1]
- local last = alphabet[#alphabet]
- local id = getboth(style,alternative)
- local f_unicode = remapalphabets(first,id) or utfbyte(first)
- local l_unicode = remapalphabets(last,id) or utfbyte(last)
- context("%05X - %05X",f_unicode,l_unicode)
- NC()
- NR()
- end
- end
- end
- context.stoptabulate()
-end
-
-function moduledata.math.coverage.showcharacters()
- context.startcolumns()
- context.setupalign { "nothyphenated" }
- context.starttabulate { "|T|i2|Tpl|" }
- for u, d in table.sortedpairs(chardata) do
- local mathclass = d.mathclass
- local mathspec = d.mathspec
- if mathclass or mathspec then
- NC()
- context("%05X",u)
- NC()
- getglyph("MathRoman",u)
- NC()
- if mathspec then
- local t = { }
- for i=1,#mathspec do
- t[mathspec[i].class] = true
- end
- t = table.sortedkeys(t)
- context("% t",t)
- else
- context(mathclass)
- end
- NC()
- NR()
- end
- end
- context.stoptabulate()
- context.stopcolumns()
-end
-
--- This is a somewhat tricky table as we need to bypass the math machinery.
-
-function moduledata.math.coverage.showscripts()
- context.starttabulate { "|cT|c|cT|c|c|c|l|" }
- for k, v in table.sortedpairs(table.merged(superscripts,subscripts)) do
- local ck = utfchar(k)
- local cv = utfchar(v)
- local ss = superscripts[k] and "^" or "_"
- NC()
- context("%05X",k)
- NC()
- context(ck)
- NC()
- context("%05X",v)
- NC()
- context(cv)
- NC()
- context.formatted.rawmathematics("x%s = x%s%s",ck,ss,cv)
- NC()
- context.formatted.mathematics("x%s = x%s%s",ck,ss,cv)
- NC()
- context(lower(chardata[k].description))
- NC()
- NR()
- end
- context.stoptabulate()
-end
+if not modules then modules = { } end modules ['s-math-coverage'] = {
+ version = 1.001,
+ comment = "companion to s-math-coverage.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.math = moduledata.math or { }
+moduledata.math.coverage = moduledata.math.coverage or { }
+
+local utfchar, utfbyte = utf.char, utf.byte
+local formatters, lower = string.formatters, string.lower
+local concat = table.concat
+
+local context = context
+local NC, NR, HL = context.NC, context.NR, context.HL
+local char, getglyph, bold = context.char, context.getglyph, context.bold
+
+local ucgreek = {
+ 0x0391, 0x0392, 0x0393, 0x0394, 0x0395,
+ 0x0396, 0x0397, 0x0398, 0x0399, 0x039A,
+ 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
+ 0x03A0, 0x03A1, 0x03A3, 0x03A4, 0x03A5,
+ 0x03A6, 0x03A7, 0x03A8, 0x03A9
+}
+
+local lcgreek = {
+ 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5,
+ 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA,
+ 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
+ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4,
+ 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9,
+ 0x03D1, 0x03D5, 0x03D6, 0x03F0, 0x03F1,
+ 0x03F4, 0x03F5
+}
+
+local ucletters = {
+ 0x00041, 0x00042, 0x00043, 0x00044, 0x00045,
+ 0x00046, 0x00047, 0x00048, 0x00049, 0x0004A,
+ 0x0004B, 0x0004C, 0x0004D, 0x0004E, 0x0004F,
+ 0x00050, 0x00051, 0x00052, 0x00053, 0x00054,
+ 0x00055, 0x00056, 0x00057, 0x00058, 0x00059,
+ 0x0005A,
+}
+
+local lcletters = {
+ 0x00061, 0x00062, 0x00063, 0x00064, 0x00065,
+ 0x00066, 0x00067, 0x00068, 0x00069, 0x0006A,
+ 0x0006B, 0x0006C, 0x0006D, 0x0006E, 0x0006F,
+ 0x00070, 0x00071, 0x00072, 0x00073, 0x00074,
+ 0x00075, 0x00076, 0x00077, 0x00078, 0x00079,
+ 0x0007A,
+}
+
+local digits = {
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034,
+ 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+}
+
+local styles = {
+ "regular", "sansserif", "monospaced", "fraktur", "script", "blackboard"
+}
+
+local alternatives = {
+ "normal", "bold", "italic", "bolditalic"
+}
+
+local alphabets = {
+ ucletters, lcletters, ucgreek, lcgreek, digits,
+}
+
+local getboth = mathematics.getboth
+local remapalphabets = mathematics.remapalphabets
+
+local chardata = characters.data
+local superscripts = characters.superscripts
+local subscripts = characters.subscripts
+
+function moduledata.math.coverage.showalphabets()
+ context.starttabulate { "|lT|l|Tl|" }
+ for i=1,#styles do
+ local style = styles[i]
+ for i=1,#alternatives do
+ local alternative = alternatives[i]
+ for i=1,#alphabets do
+ local alphabet = alphabets[i]
+ NC()
+ if i == 1 then
+ context("%s %s",style,alternative)
+ end
+ NC()
+ context.startimath()
+ context.setmathattribute(style,alternative)
+ for i=1,#alphabet do
+ local letter = alphabet[i]
+ local id = getboth(style,alternative)
+ local unicode = remapalphabets(letter,id)
+ if not unicode then
+ context.underbar(utfchar(letter))
+ elseif unicode == letter then
+ context(utfchar(unicode))
+ else
+ context(utfchar(unicode))
+ end
+ end
+ context.stopimath()
+ NC()
+ local first = alphabet[1]
+ local last = alphabet[#alphabet]
+ local id = getboth(style,alternative)
+ local f_unicode = remapalphabets(first,id) or utfbyte(first)
+ local l_unicode = remapalphabets(last,id) or utfbyte(last)
+ context("%05X - %05X",f_unicode,l_unicode)
+ NC()
+ NR()
+ end
+ end
+ end
+ context.stoptabulate()
+end
+
+function moduledata.math.coverage.showcharacters()
+ context.startcolumns()
+ context.setupalign { "nothyphenated" }
+ context.starttabulate { "|T|i2|Tpl|" }
+ for u, d in table.sortedpairs(chardata) do
+ local mathclass = d.mathclass
+ local mathspec = d.mathspec
+ if mathclass or mathspec then
+ NC()
+ context("%05X",u)
+ NC()
+ getglyph("MathRoman",u)
+ NC()
+ if mathspec then
+ local t = { }
+ for i=1,#mathspec do
+ t[mathspec[i].class] = true
+ end
+ t = table.sortedkeys(t)
+ context("% t",t)
+ else
+ context(mathclass)
+ end
+ NC()
+ NR()
+ end
+ end
+ context.stoptabulate()
+ context.stopcolumns()
+end
+
+-- This is a somewhat tricky table as we need to bypass the math machinery.
+
+function moduledata.math.coverage.showscripts()
+ context.starttabulate { "|cT|c|cT|c|c|c|l|" }
+ for k, v in table.sortedpairs(table.merged(superscripts,subscripts)) do
+ local ck = utfchar(k)
+ local cv = utfchar(v)
+ local ss = superscripts[k] and "^" or "_"
+ NC()
+ context("%05X",k)
+ NC()
+ context(ck)
+ NC()
+ context("%05X",v)
+ NC()
+ context(cv)
+ NC()
+ context.formatted.rawmathematics("x%s = x%s%s",ck,ss,cv)
+ NC()
+ context.formatted.mathematics("x%s = x%s%s",ck,ss,cv)
+ NC()
+ context(lower(chardata[k].description))
+ NC()
+ NR()
+ end
+ context.stoptabulate()
+end
diff --git a/tex/context/base/s-math-parameters.lua b/tex/context/base/s-math-parameters.lua
index 50500466a..8e8c15a2d 100644
--- a/tex/context/base/s-math-parameters.lua
+++ b/tex/context/base/s-math-parameters.lua
@@ -1,135 +1,135 @@
-if not modules then modules = { } end modules ['s-math-coverage'] = {
- version = 1.001,
- comment = "companion to s-math-coverage.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-moduledata.math = moduledata.math or { }
-moduledata.math.parameters = moduledata.math.parameters or { }
-
-local tables = utilities.tables.definedtable("math","tracing","spacing","tables")
-
-tables.styleaxis = {
- "ord", "op", "bin", "rel", "open", "close", "punct", "inner",
-}
-
-tables.parameters = {
- "quad", "axis", "operatorsize",
- "overbarkern", "overbarrule", "overbarvgap",
- "underbarkern", "underbarrule", "underbarvgap",
- "radicalkern", "radicalrule", "radicalvgap",
- "radicaldegreebefore", "radicaldegreeafter", "radicaldegreeraise",
- "stackvgap", "stacknumup", "stackdenomdown",
- "fractionrule", "fractionnumvgap", "fractionnumup",
- "fractiondenomvgap", "fractiondenomdown", "fractiondelsize",
- "limitabovevgap", "limitabovebgap", "limitabovekern",
- "limitbelowvgap", "limitbelowbgap", "limitbelowkern",
- "underdelimitervgap", "underdelimiterbgap",
- "overdelimitervgap", "overdelimiterbgap",
- "subshiftdrop", "supshiftdrop", "subshiftdown",
- "subsupshiftdown", "subtopmax", "supshiftup",
- "supbottommin", "supsubbottommax", "subsupvgap",
- "spaceafterscript", "connectoroverlapmin",
-}
-
-tables.styles = {
- "display",
- "text",
- "script",
- "scriptscript",
-}
-
-function tables.stripmu(str)
- str = string.gsub(str,"mu","")
- str = string.gsub(str," ","")
- str = string.gsub(str,"plus","+")
- str = string.gsub(str,"minus","-")
- return str
-end
-
-function tables.strippt(old)
- local new = string.gsub(old,"pt","")
- if new ~= old then
- new = string.format("%0.4f",tonumber(new))
- end
- return new
-end
-
-function moduledata.math.parameters.showspacing()
-
- local styles = tables.styles
- local styleaxis = tables.styleaxis
-
- context.starttabulate { "|Tl|Tl|" .. string.rep("Tc|",(#styles*2)) }
- context.HL()
- context.NC()
- context.NC()
- context.NC()
- for i=1,#styles do
- context.bold(styles[i])
- context.NC()
- context.bold("(cramped)")
- context.NC()
- end
- context.NR()
- context.HL()
- for i=1,#styleaxis do
- -- print(key,tex.getmath(key,"text"))
- local one = styleaxis[i]
- for j=1,#styleaxis do
- local two = styleaxis[j]
- context.NC()
- if j == 1 then
- context.bold(one)
- end
- context.NC()
- context.bold(two)
- context.NC()
- for i=1,#styles do
- context("\\ctxlua{context(math.tracing.spacing.tables.stripmu('\\the\\Umath%s%sspacing\\%sstyle'))}",one,two,styles[i])
- context.NC()
- context("\\ctxlua{context(math.tracing.spacing.tables.stripmu('\\the\\Umath%s%sspacing\\cramped%sstyle'))}",one,two,styles[i])
- context.NC()
- end
- context.NR()
- end
- end
- context.stoptabulate()
-end
-
-function moduledata.math.parameters.showparameters()
-
- local styles = tables.styles
- local parameters = tables.parameters
-
- context.starttabulate { "|l|" .. string.rep("Tc|",(#styles*2)) }
- context.HL()
- context.NC()
- context.NC()
- for i=1,#styles do
- context.bold(styles[i])
- context.NC()
- context.bold("(cramped)")
- context.NC()
- end
- context.NR()
- context.HL()
- for i=1,#parameters do
- local parameter = parameters[i]
- -- print(parameter,tex.getmath(parameter,"text"))
- context.NC()
- context.type(parameter)
- context.NC()
- for i=1,#styles do
- context("\\ctxlua{context(math.tracing.spacing.tables.strippt('\\the\\Umath%s\\%sstyle'))}",parameter,styles[i])
- context.NC()
- context("\\ctxlua{context(math.tracing.spacing.tables.strippt('\\the\\Umath%s\\cramped%sstyle'))}",parameter,styles[i])
- context.NC()
- end
- context.NR()
- end
- context.stoptabulate()
-
-end
+if not modules then modules = { } end modules ['s-math-coverage'] = {
+ version = 1.001,
+ comment = "companion to s-math-coverage.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.math = moduledata.math or { }
+moduledata.math.parameters = moduledata.math.parameters or { }
+
+local tables = utilities.tables.definedtable("math","tracing","spacing","tables")
+
+tables.styleaxis = {
+ "ord", "op", "bin", "rel", "open", "close", "punct", "inner",
+}
+
+tables.parameters = {
+ "quad", "axis", "operatorsize",
+ "overbarkern", "overbarrule", "overbarvgap",
+ "underbarkern", "underbarrule", "underbarvgap",
+ "radicalkern", "radicalrule", "radicalvgap",
+ "radicaldegreebefore", "radicaldegreeafter", "radicaldegreeraise",
+ "stackvgap", "stacknumup", "stackdenomdown",
+ "fractionrule", "fractionnumvgap", "fractionnumup",
+ "fractiondenomvgap", "fractiondenomdown", "fractiondelsize",
+ "limitabovevgap", "limitabovebgap", "limitabovekern",
+ "limitbelowvgap", "limitbelowbgap", "limitbelowkern",
+ "underdelimitervgap", "underdelimiterbgap",
+ "overdelimitervgap", "overdelimiterbgap",
+ "subshiftdrop", "supshiftdrop", "subshiftdown",
+ "subsupshiftdown", "subtopmax", "supshiftup",
+ "supbottommin", "supsubbottommax", "subsupvgap",
+ "spaceafterscript", "connectoroverlapmin",
+}
+
+tables.styles = {
+ "display",
+ "text",
+ "script",
+ "scriptscript",
+}
+
+function tables.stripmu(str)
+ str = string.gsub(str,"mu","")
+ str = string.gsub(str," ","")
+ str = string.gsub(str,"plus","+")
+ str = string.gsub(str,"minus","-")
+ return str
+end
+
+function tables.strippt(old)
+ local new = string.gsub(old,"pt","")
+ if new ~= old then
+ new = string.format("%0.4f",tonumber(new))
+ end
+ return new
+end
+
+function moduledata.math.parameters.showspacing()
+
+ local styles = tables.styles
+ local styleaxis = tables.styleaxis
+
+ context.starttabulate { "|Tl|Tl|" .. string.rep("Tc|",(#styles*2)) }
+ context.HL()
+ context.NC()
+ context.NC()
+ context.NC()
+ for i=1,#styles do
+ context.bold(styles[i])
+ context.NC()
+ context.bold("(cramped)")
+ context.NC()
+ end
+ context.NR()
+ context.HL()
+ for i=1,#styleaxis do
+ -- print(key,tex.getmath(key,"text"))
+ local one = styleaxis[i]
+ for j=1,#styleaxis do
+ local two = styleaxis[j]
+ context.NC()
+ if j == 1 then
+ context.bold(one)
+ end
+ context.NC()
+ context.bold(two)
+ context.NC()
+ for i=1,#styles do
+ context("\\ctxlua{context(math.tracing.spacing.tables.stripmu('\\the\\Umath%s%sspacing\\%sstyle'))}",one,two,styles[i])
+ context.NC()
+ context("\\ctxlua{context(math.tracing.spacing.tables.stripmu('\\the\\Umath%s%sspacing\\cramped%sstyle'))}",one,two,styles[i])
+ context.NC()
+ end
+ context.NR()
+ end
+ end
+ context.stoptabulate()
+end
+
+function moduledata.math.parameters.showparameters()
+
+ local styles = tables.styles
+ local parameters = tables.parameters
+
+ context.starttabulate { "|l|" .. string.rep("Tc|",(#styles*2)) }
+ context.HL()
+ context.NC()
+ context.NC()
+ for i=1,#styles do
+ context.bold(styles[i])
+ context.NC()
+ context.bold("(cramped)")
+ context.NC()
+ end
+ context.NR()
+ context.HL()
+ for i=1,#parameters do
+ local parameter = parameters[i]
+ -- print(parameter,tex.getmath(parameter,"text"))
+ context.NC()
+ context.type(parameter)
+ context.NC()
+ for i=1,#styles do
+ context("\\ctxlua{context(math.tracing.spacing.tables.strippt('\\the\\Umath%s\\%sstyle'))}",parameter,styles[i])
+ context.NC()
+ context("\\ctxlua{context(math.tracing.spacing.tables.strippt('\\the\\Umath%s\\cramped%sstyle'))}",parameter,styles[i])
+ context.NC()
+ end
+ context.NR()
+ end
+ context.stoptabulate()
+
+end
diff --git a/tex/context/base/s-pre-71.lua b/tex/context/base/s-pre-71.lua
index 7d5c011f1..bfa45a705 100644
--- a/tex/context/base/s-pre-71.lua
+++ b/tex/context/base/s-pre-71.lua
@@ -1,63 +1,63 @@
-if not modules then modules = { } end modules ['steps'] = {
- version = 1.001,
- comment = "companion to steps.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-moduledata.steps = moduledata.steps or { }
-local steps = moduledata.steps
-
-local locations = {
- 'lefttop',
- 'middletop',
- 'righttop',
- 'middleleft',
- 'middle',
- 'middleright',
- 'leftbottom',
- 'middlebottom',
- 'rightbottom',
-}
-
-local done, current, previous, n
-
-function steps.reset_locations()
- done, current, previous, n = table.tohash(locations,false), 0, 0, 0
-end
-
-function steps.next_location(loc)
- previous = current
- n = n + 1
- loc = loc and loc ~= "" and tonumber(loc)
- while true do
- current = loc or math.random(1,#locations)
- if not done[current] then
- done[current] = true
- break
- end
- end
-end
-
-function steps.current_location()
- context(locations[current] or "")
-end
-
-function steps.previous_location()
- context(locations[previous] or "")
-end
-
-function steps.current_n()
- context(current)
-end
-
-function steps.previous_n()
- context(previous)
-end
-
-function steps.step()
- context(n)
-end
-
-steps.reset_locations()
+if not modules then modules = { } end modules ['steps'] = {
+ version = 1.001,
+ comment = "companion to steps.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+moduledata.steps = moduledata.steps or { }
+local steps = moduledata.steps
+
+local locations = {
+ 'lefttop',
+ 'middletop',
+ 'righttop',
+ 'middleleft',
+ 'middle',
+ 'middleright',
+ 'leftbottom',
+ 'middlebottom',
+ 'rightbottom',
+}
+
+local done, current, previous, n
+
+function steps.reset_locations()
+ done, current, previous, n = table.tohash(locations,false), 0, 0, 0
+end
+
+function steps.next_location(loc)
+ previous = current
+ n = n + 1
+ loc = loc and loc ~= "" and tonumber(loc)
+ while true do
+ current = loc or math.random(1,#locations)
+ if not done[current] then
+ done[current] = true
+ break
+ end
+ end
+end
+
+function steps.current_location()
+ context(locations[current] or "")
+end
+
+function steps.previous_location()
+ context(locations[previous] or "")
+end
+
+function steps.current_n()
+ context(current)
+end
+
+function steps.previous_n()
+ context(previous)
+end
+
+function steps.step()
+ context(n)
+end
+
+steps.reset_locations()
diff --git a/tex/context/base/scrn-but.lua b/tex/context/base/scrn-but.lua
index 4766df9d7..e49372ce9 100644
--- a/tex/context/base/scrn-but.lua
+++ b/tex/context/base/scrn-but.lua
@@ -1,19 +1,19 @@
-if not modules then modules = { } end modules ['scrn-but'] = {
- version = 1.001,
- comment = "companion to scrn-but.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local f_two_colon = string.formatters["%s:%s"]
-
-function commands.registerbuttons(tag,register,language)
- local data = sorters.definitions[language]
- local orders = daya and data.orders or sorters.definitions.default.orders
- local tag = tag == "" and { "" } or { tag }
- for i=1,#orders do
- local order = orders[i]
- context.menubutton(tag,f_two_colon(register,order),order)
- end
-end
+if not modules then modules = { } end modules ['scrn-but'] = {
+ version = 1.001,
+ comment = "companion to scrn-but.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local f_two_colon = string.formatters["%s:%s"]
+
+function commands.registerbuttons(tag,register,language)
+ local data = sorters.definitions[language]
+ local orders = daya and data.orders or sorters.definitions.default.orders
+ local tag = tag == "" and { "" } or { tag }
+ for i=1,#orders do
+ local order = orders[i]
+ context.menubutton(tag,f_two_colon(register,order),order)
+ end
+end
diff --git a/tex/context/base/scrn-fld.lua b/tex/context/base/scrn-fld.lua
index 846385686..9836cbebe 100644
--- a/tex/context/base/scrn-fld.lua
+++ b/tex/context/base/scrn-fld.lua
@@ -1,85 +1,85 @@
-if not modules then modules = { } end modules ['scrn-fld'] = {
- version = 1.001,
- comment = "companion to scrn-fld.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- we should move some code from lpdf-fld to here
-
-local variables = interfaces.variables
-local v_yes = variables.yes
-
-local fields = { }
-interactions.fields = fields
-
-local codeinjections = backends.codeinjections
-local nodeinjections = backends.nodeinjections
-
-local function define(specification)
- codeinjections.definefield(specification)
-end
-
-local function defineset(name,set)
- codeinjections.definefield(name,set)
-end
-
-local function clone(specification)
- codeinjections.clonefield(specification)
-end
-
-local function insert(name,specification)
- return nodeinjections.typesetfield(name,specification)
-end
-
-fields.define = define
-fields.defineset = defineset
-fields.clone = clone
-fields.insert = insert
-
-commands.definefield = define
-commands.definefieldset = defineset
-commands.clonefield = clone
-
-function commands.insertfield(name,specification)
- tex.box["b_scrn_field_body"] = insert(name,specification)
-end
-
--- (for the monent) only tex interface
-
-function commands.getfieldcategory(name)
- local g = codeinjections.getfieldcategory(name)
- if g then
- context(g)
- end
-end
-
-function commands.getdefaultfieldvalue(name)
- local d = codeinjections.getdefaultfieldvalue(name)
- if d then
- context(d)
- end
-end
-
-function commands.exportformdata(export)
- if export == v_yes then
- codeinjections.exportformdata()
- end
-end
-
-function commands.setformsmethod(method)
- codeinjections.setformsmethod(method)
-end
-
-function commands.doiffieldcategoryelse(name)
- commands.doifelse(codeinjections.validfieldcategory(name))
-end
-
-function commands.doiffieldsetelse(tag)
- commands.doifelse(codeinjections.validfieldset(name))
-end
-
-function commands.doiffieldelse(name)
- commands.doifelse(codeinjections.validfield(name))
-end
+if not modules then modules = { } end modules ['scrn-fld'] = {
+ version = 1.001,
+ comment = "companion to scrn-fld.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- we should move some code from lpdf-fld to here
+
+local variables = interfaces.variables
+local v_yes = variables.yes
+
+local fields = { }
+interactions.fields = fields
+
+local codeinjections = backends.codeinjections
+local nodeinjections = backends.nodeinjections
+
+local function define(specification)
+ codeinjections.definefield(specification)
+end
+
+local function defineset(name,set)
+ codeinjections.definefield(name,set)
+end
+
+local function clone(specification)
+ codeinjections.clonefield(specification)
+end
+
+local function insert(name,specification)
+ return nodeinjections.typesetfield(name,specification)
+end
+
+fields.define = define
+fields.defineset = defineset
+fields.clone = clone
+fields.insert = insert
+
+commands.definefield = define
+commands.definefieldset = defineset
+commands.clonefield = clone
+
+function commands.insertfield(name,specification)
+ tex.box["b_scrn_field_body"] = insert(name,specification)
+end
+
+-- (for the monent) only tex interface
+
+function commands.getfieldcategory(name)
+ local g = codeinjections.getfieldcategory(name)
+ if g then
+ context(g)
+ end
+end
+
+function commands.getdefaultfieldvalue(name)
+ local d = codeinjections.getdefaultfieldvalue(name)
+ if d then
+ context(d)
+ end
+end
+
+function commands.exportformdata(export)
+ if export == v_yes then
+ codeinjections.exportformdata()
+ end
+end
+
+function commands.setformsmethod(method)
+ codeinjections.setformsmethod(method)
+end
+
+function commands.doiffieldcategoryelse(name)
+ commands.doifelse(codeinjections.validfieldcategory(name))
+end
+
+function commands.doiffieldsetelse(tag)
+ commands.doifelse(codeinjections.validfieldset(name))
+end
+
+function commands.doiffieldelse(name)
+ commands.doifelse(codeinjections.validfield(name))
+end
diff --git a/tex/context/base/scrn-hlp.lua b/tex/context/base/scrn-hlp.lua
index 06abb3237..5f8368c6d 100644
--- a/tex/context/base/scrn-hlp.lua
+++ b/tex/context/base/scrn-hlp.lua
@@ -1,119 +1,119 @@
-if not modules then modules = { } end modules ['scrn-hlp'] = {
- version = 1.001,
- comment = "companion to scrn-hlp.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 help = { }
-interactions.help = help
-
-local a_help = attributes.private("help")
-
-local copy_nodelist = node.copy_list
-local hpack_nodelist = node.hpack
-
-local register_list = nodes.pool.register
-
-local nodecodes = nodes.nodecodes
-
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-
-local data, references = { }, { }
-
-local helpscript = [[
- function Hide_All_Help(prefix) {
- var n = 0
- while (true) {
- n += 1 ;
- v = this.getField(prefix + n) ;
- if (v) {
- v.hidden = true ;
- this.dirty = false ;
- } else {
- return ;
- }
- }
- }
-]]
-
-local template = "javascript(Hide_All_Help{help:}),action(show{help:%s})"
-
-function help.register(number,name,box)
- if helpscript then
- interactions.javascripts.setpreamble("HelpTexts",helpscript)
- helpscript = false
- end
- local b = copy_nodelist(tex.box[box])
- register_list(b)
- data[number] = b
- if name and name ~= "" then
- references[name] = number
- structures.references.define("",name,format(template,number))
- end
-end
-
-local function collect(head,used)
- while head do
- local id = head.id
- if id == hlist_code then
- local a = head[a_help]
- if a then
- if not used then
- used = { a }
- else
- used[#used+1] = a
- end
- else
- used = collect(head.list,used)
- end
- elseif id == vlist_code then
- used = collect(head.list,used)
- end
- head = head.next
- end
- return used
-end
-
-function help.collect(box)
- if next(data) then
- return collect(tex.box[box].list)
- end
-end
-
-commands.registerhelp = help.register
-
-function commands.collecthelp(box)
- local used = help.collect(box)
- if used then
- local done = { }
- context.startoverlay()
- for i=1,#used do
- local d = data[used[i]]
- if d and not done[d] then
- local box = hpack_nodelist(copy_nodelist(d))
- context(false,box)
- done[d] = true
- else
- -- error
- end
- end
- context.stopoverlay()
- end
-end
-
-function help.reference(name)
- return references[name] or tonumber(name) or 0
-end
-
-function commands.helpreference(name)
- context(references[name] or tonumber(name) or 0)
-end
-
-function commands.helpaction(name)
- context(template,references[name] or tonumber(name) or 0)
-end
+if not modules then modules = { } end modules ['scrn-hlp'] = {
+ version = 1.001,
+ comment = "companion to scrn-hlp.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 help = { }
+interactions.help = help
+
+local a_help = attributes.private("help")
+
+local copy_nodelist = node.copy_list
+local hpack_nodelist = node.hpack
+
+local register_list = nodes.pool.register
+
+local nodecodes = nodes.nodecodes
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+
+local data, references = { }, { }
+
+local helpscript = [[
+ function Hide_All_Help(prefix) {
+ var n = 0
+ while (true) {
+ n += 1 ;
+ v = this.getField(prefix + n) ;
+ if (v) {
+ v.hidden = true ;
+ this.dirty = false ;
+ } else {
+ return ;
+ }
+ }
+ }
+]]
+
+local template = "javascript(Hide_All_Help{help:}),action(show{help:%s})"
+
+function help.register(number,name,box)
+ if helpscript then
+ interactions.javascripts.setpreamble("HelpTexts",helpscript)
+ helpscript = false
+ end
+ local b = copy_nodelist(tex.box[box])
+ register_list(b)
+ data[number] = b
+ if name and name ~= "" then
+ references[name] = number
+ structures.references.define("",name,format(template,number))
+ end
+end
+
+local function collect(head,used)
+ while head do
+ local id = head.id
+ if id == hlist_code then
+ local a = head[a_help]
+ if a then
+ if not used then
+ used = { a }
+ else
+ used[#used+1] = a
+ end
+ else
+ used = collect(head.list,used)
+ end
+ elseif id == vlist_code then
+ used = collect(head.list,used)
+ end
+ head = head.next
+ end
+ return used
+end
+
+function help.collect(box)
+ if next(data) then
+ return collect(tex.box[box].list)
+ end
+end
+
+commands.registerhelp = help.register
+
+function commands.collecthelp(box)
+ local used = help.collect(box)
+ if used then
+ local done = { }
+ context.startoverlay()
+ for i=1,#used do
+ local d = data[used[i]]
+ if d and not done[d] then
+ local box = hpack_nodelist(copy_nodelist(d))
+ context(false,box)
+ done[d] = true
+ else
+ -- error
+ end
+ end
+ context.stopoverlay()
+ end
+end
+
+function help.reference(name)
+ return references[name] or tonumber(name) or 0
+end
+
+function commands.helpreference(name)
+ context(references[name] or tonumber(name) or 0)
+end
+
+function commands.helpaction(name)
+ context(template,references[name] or tonumber(name) or 0)
+end
diff --git a/tex/context/base/scrn-ini.lua b/tex/context/base/scrn-ini.lua
index deca9cbbb..4831408f9 100644
--- a/tex/context/base/scrn-ini.lua
+++ b/tex/context/base/scrn-ini.lua
@@ -1,32 +1,32 @@
-if not modules then modules = { } end modules ['scrn-ini'] = {
- version = 1.001,
- comment = "companion to scrn-int.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local next = next
-
-interactions = { }
-interactions.general = interactions.general or { }
-local general = interactions.general
-
-local codeinjections = backends.codeinjections
-
-local identitydata = { }
-
-local function setupidentity(specification)
- for k, v in next, specification do
- identitydata[k] = v
- end
- codeinjections.setupidentity(specification)
-end
-
-function general.getidentity()
- return identitydata
-end
-
-general.setupidentity = setupidentity
-
-commands.setupidentity = setupidentity
+if not modules then modules = { } end modules ['scrn-ini'] = {
+ version = 1.001,
+ comment = "companion to scrn-int.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local next = next
+
+interactions = { }
+interactions.general = interactions.general or { }
+local general = interactions.general
+
+local codeinjections = backends.codeinjections
+
+local identitydata = { }
+
+local function setupidentity(specification)
+ for k, v in next, specification do
+ identitydata[k] = v
+ end
+ codeinjections.setupidentity(specification)
+end
+
+function general.getidentity()
+ return identitydata
+end
+
+general.setupidentity = setupidentity
+
+commands.setupidentity = setupidentity
diff --git a/tex/context/base/scrn-pag.lua b/tex/context/base/scrn-pag.lua
index 2a44ffbcd..7003d0285 100644
--- a/tex/context/base/scrn-pag.lua
+++ b/tex/context/base/scrn-pag.lua
@@ -1,27 +1,27 @@
-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 codeinjections = backends.codeinjections
-
-local function setupcanvas(specification)
- codeinjections.setupcanvas(specification)
-end
-
-local function setpagetransition(specification)
- codeinjections.setpagetransition(specification)
-end
-
-pages.setupcanvas = setupcanvas
-pages.setpagetransition = setpagetransition
-
-commands.setupcanvas = setupcanvas
-commands.setpagetransition = setpagetransition
+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 codeinjections = backends.codeinjections
+
+local function setupcanvas(specification)
+ codeinjections.setupcanvas(specification)
+end
+
+local function setpagetransition(specification)
+ codeinjections.setpagetransition(specification)
+end
+
+pages.setupcanvas = setupcanvas
+pages.setpagetransition = setpagetransition
+
+commands.setupcanvas = setupcanvas
+commands.setpagetransition = setpagetransition
diff --git a/tex/context/base/scrn-ref.lua b/tex/context/base/scrn-ref.lua
index fb79ff6d8..df71b6a97 100644
--- a/tex/context/base/scrn-ref.lua
+++ b/tex/context/base/scrn-ref.lua
@@ -1,65 +1,65 @@
-if not modules then modules = { } end modules ['scrn-ref'] = {
- version = 1.001,
- comment = "companion to scrn-int.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-interactions = interactions or { }
-interactions.references = interactions.references or { }
-local references = interactions.references
-
-local codeinjections = backends.codeinjections
-
-local expandcurrent = structures.references.expandcurrent
-local identify = structures.references.identify
-
-local function check(what)
- if what and what ~= "" then
- local set, bug = identify("",what)
- return not bug and #set > 0 and set
- end
-end
-
-local function setopendocumentaction(open)
- local opendocument = check(open)
- if opendocument then
- codeinjections.registerdocumentopenaction(opendocument)
- expandcurrent()
- end
-end
-
-local function setclosedocumentaction(close)
- local closedocument = check(close)
- if closedocument then
- codeinjections.registerdocumentcloseaction(closedocument)
- expandcurrent()
- end
-end
-
-local function setopenpageaction(open)
- local openpage = check(open)
- if openpage then
- codeinjections.registerpageopenaction(openpage)
- expandcurrent()
- end
-end
-
-local function setclosepageaction(close)
- local closepage = check(close)
- if closepage then
- codeinjections.registerpagecloseaction(closepage)
- expandcurrent()
- end
-end
-
-references.setopendocument = setopendocumentaction
-references.setclosedocument = setclosedocumentaction
-references.setopenpage = setopenpageaction
-references.setclosepage = setclosepageaction
-
-commands.setopendocumentaction = setopendocumentaction
-commands.setclosedocumentaction = setclosedocumentaction
-commands.setopenpageaction = setopenpageaction
-commands.setclosepageaction = setclosepageaction
+if not modules then modules = { } end modules ['scrn-ref'] = {
+ version = 1.001,
+ comment = "companion to scrn-int.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+interactions = interactions or { }
+interactions.references = interactions.references or { }
+local references = interactions.references
+
+local codeinjections = backends.codeinjections
+
+local expandcurrent = structures.references.expandcurrent
+local identify = structures.references.identify
+
+local function check(what)
+ if what and what ~= "" then
+ local set, bug = identify("",what)
+ return not bug and #set > 0 and set
+ end
+end
+
+local function setopendocumentaction(open)
+ local opendocument = check(open)
+ if opendocument then
+ codeinjections.registerdocumentopenaction(opendocument)
+ expandcurrent()
+ end
+end
+
+local function setclosedocumentaction(close)
+ local closedocument = check(close)
+ if closedocument then
+ codeinjections.registerdocumentcloseaction(closedocument)
+ expandcurrent()
+ end
+end
+
+local function setopenpageaction(open)
+ local openpage = check(open)
+ if openpage then
+ codeinjections.registerpageopenaction(openpage)
+ expandcurrent()
+ end
+end
+
+local function setclosepageaction(close)
+ local closepage = check(close)
+ if closepage then
+ codeinjections.registerpagecloseaction(closepage)
+ expandcurrent()
+ end
+end
+
+references.setopendocument = setopendocumentaction
+references.setclosedocument = setclosedocumentaction
+references.setopenpage = setopenpageaction
+references.setclosepage = setclosepageaction
+
+commands.setopendocumentaction = setopendocumentaction
+commands.setclosedocumentaction = setclosedocumentaction
+commands.setopenpageaction = setopenpageaction
+commands.setclosepageaction = setclosepageaction
diff --git a/tex/context/base/scrn-wid.lua b/tex/context/base/scrn-wid.lua
index e0c3d54b6..4ad46761e 100644
--- a/tex/context/base/scrn-wid.lua
+++ b/tex/context/base/scrn-wid.lua
@@ -1,214 +1,214 @@
-if not modules then modules = { } end modules ['scrn-wid'] = {
- version = 1.001,
- comment = "companion to scrn-wid.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-interactions = interactions or { }
-local interactions = interactions
-
-local attachments = { }
-local comments = { }
-local soundclips = { }
-local renderings = { }
-local linkedlists = { }
-
-interactions.attachments = attachments
-interactions.soundclips = soundclips
-interactions.renderings = renderings
-interactions.linkedlists = linkedlists
-
-local jobpasses = job.passes
-
-local codeinjections = backends.codeinjections
-local nodeinjections = backends.nodeinjections
-
-local variables = interfaces.variables
-local v_auto = variables.auto
-
-local trace_attachments = false trackers.register("widgets.attachments", function(v) trace_attachments = v end)
-
-local report_attachments = logs.reporter("widgets","attachments")
-
--- Symbols
-
-function commands.presetsymbollist(list)
- codeinjections.presetsymbollist(list)
-end
-
--- Attachments
---
--- registered : unique id
--- tag : used at the tex end
--- file : name that the file has on the filesystem
--- name : name that the file will get in the output
--- title : up to the backend
--- subtitle : up to the backend
--- author : up to the backend
--- method : up to the backend (hidden == no rendering)
-
-local nofautoattachments, lastregistered = 0, nil
-
-local function checkregistered(specification)
- local registered = specification.registered
- if not registered or registered == "" or registered == v_auto then
- nofautoattachments = nofautoattachments + 1
- lastregistered = "attachment-" .. nofautoattachments
- specification.registered = lastregistered
- return lastregistered
- else
- return registered
- end
-end
-
-local function checkbuffer(specification)
- local buffer = specification.buffer
- if buffer ~= "" then
- specification.data = buffers.getcontent(buffer) or "<no data>"
- end
-end
-
-function attachments.register(specification) -- beware of tag/registered mixup(tag is namespace)
- local registered = checkregistered(specification)
- checkbuffer(specification)
- attachments[registered] = specification
- if trace_attachments then
- report_attachments("registering %a",registered)
- end
- return specification
-end
-
-function attachments.insert(specification)
- local registered = checkregistered(specification)
- local r = attachments[registered]
- if r then
- if trace_attachments then
- report_attachments("including registered %a",registered)
- end
- for k, v in next, r do
- local s = specification[k]
- if s == "" then
- specification[k] = v
- end
- end
- elseif trace_attachments then
- report_attachments("including unregistered %a",registered)
- end
- checkbuffer(specification)
- return nodeinjections.attachfile(specification)
-end
-
-commands.registerattachment = attachments.register
-
-function commands.insertattachment(specification)
- tex.box["b_scrn_attachment_link"] = attachments.insert(specification)
-end
-
--- Comment
-
-function comments.insert(specification)
- local buffer = specification.buffer
- if buffer ~= "" then
- specification.data = buffers.getcontent(buffer) or ""
- end
- return nodeinjections.comment(specification)
-end
-
-function commands.insertcomment(specification)
- tex.box["b_scrn_comment_link"] = comments.insert(specification)
-end
-
--- Soundclips
-
-function soundclips.register(specification)
- local tag = specification.tag
- if tag and tag ~= "" then
- local filename = specification.file
- if not filename or filename == "" then
- filename = tag
- specification.file = filename
- end
- soundclips[tag] = specification
- return specification
- end
-end
-
-function soundclips.insert(tag)
- local sc = soundclips[tag]
- if not sc then
- -- todo: message
- return soundclips.register { tag = tag }
- else
- return sc
- end
-end
-
-commands.registersoundclip = soundclips.register
-commands.insertsoundclip = soundclips.insert
-
--- Renderings
-
-function renderings.register(specification)
- if specification.label then
- renderings[specification.label] = specification
- return specification
- end
-end
-
-function renderings.rendering(label)
- local rn = renderings[label]
- if not rn then
- -- todo: message
- return renderings.register { label = label }
- else
- return rn
- end
-end
-
-local function var(label,key)
- local rn = renderings[label]
- return rn and rn[key] or ""
-end
-
-renderings.var = var
-
-function commands.renderingvar(label,key)
- context(var(label,key))
-end
-
-commands.registerrendering = renderings.register
-
--- Rendering:
-
-function commands.insertrenderingwindow(specification)
- codeinjections.insertrenderingwindow(specification)
-end
-
--- Linkedlists (only a context interface)
-
-function commands.definelinkedlist(tag)
- -- no need
-end
-
-function commands.enhancelinkedlist(tag,n)
- local ll = jobpasses.gettobesaved(tag)
- if ll then
- ll[n] = texcount.realpageno
- end
-end
-
-function commands.addlinklistelement(tag)
- local tobesaved = jobpasses.gettobesaved(tag)
- local collected = jobpasses.getcollected(tag) or { }
- local currentlink = #tobesaved + 1
- local noflinks = #collected
- tobesaved[currentlink] = 0
- local f = collected[1] or 0
- local l = collected[noflinks] or 0
- local p = collected[currentlink-1] or f
- local n = collected[currentlink+1] or l
- context.setlinkedlistproperties(currentlink,noflinks,f,p,n,l)
- -- context.ctxlatelua(function() commands.enhancelinkedlist(tag,currentlink) end)
-end
+if not modules then modules = { } end modules ['scrn-wid'] = {
+ version = 1.001,
+ comment = "companion to scrn-wid.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+interactions = interactions or { }
+local interactions = interactions
+
+local attachments = { }
+local comments = { }
+local soundclips = { }
+local renderings = { }
+local linkedlists = { }
+
+interactions.attachments = attachments
+interactions.soundclips = soundclips
+interactions.renderings = renderings
+interactions.linkedlists = linkedlists
+
+local jobpasses = job.passes
+
+local codeinjections = backends.codeinjections
+local nodeinjections = backends.nodeinjections
+
+local variables = interfaces.variables
+local v_auto = variables.auto
+
+local trace_attachments = false trackers.register("widgets.attachments", function(v) trace_attachments = v end)
+
+local report_attachments = logs.reporter("widgets","attachments")
+
+-- Symbols
+
+function commands.presetsymbollist(list)
+ codeinjections.presetsymbollist(list)
+end
+
+-- Attachments
+--
+-- registered : unique id
+-- tag : used at the tex end
+-- file : name that the file has on the filesystem
+-- name : name that the file will get in the output
+-- title : up to the backend
+-- subtitle : up to the backend
+-- author : up to the backend
+-- method : up to the backend (hidden == no rendering)
+
+local nofautoattachments, lastregistered = 0, nil
+
+local function checkregistered(specification)
+ local registered = specification.registered
+ if not registered or registered == "" or registered == v_auto then
+ nofautoattachments = nofautoattachments + 1
+ lastregistered = "attachment-" .. nofautoattachments
+ specification.registered = lastregistered
+ return lastregistered
+ else
+ return registered
+ end
+end
+
+local function checkbuffer(specification)
+ local buffer = specification.buffer
+ if buffer ~= "" then
+ specification.data = buffers.getcontent(buffer) or "<no data>"
+ end
+end
+
+function attachments.register(specification) -- beware of tag/registered mixup(tag is namespace)
+ local registered = checkregistered(specification)
+ checkbuffer(specification)
+ attachments[registered] = specification
+ if trace_attachments then
+ report_attachments("registering %a",registered)
+ end
+ return specification
+end
+
+function attachments.insert(specification)
+ local registered = checkregistered(specification)
+ local r = attachments[registered]
+ if r then
+ if trace_attachments then
+ report_attachments("including registered %a",registered)
+ end
+ for k, v in next, r do
+ local s = specification[k]
+ if s == "" then
+ specification[k] = v
+ end
+ end
+ elseif trace_attachments then
+ report_attachments("including unregistered %a",registered)
+ end
+ checkbuffer(specification)
+ return nodeinjections.attachfile(specification)
+end
+
+commands.registerattachment = attachments.register
+
+function commands.insertattachment(specification)
+ tex.box["b_scrn_attachment_link"] = attachments.insert(specification)
+end
+
+-- Comment
+
+function comments.insert(specification)
+ local buffer = specification.buffer
+ if buffer ~= "" then
+ specification.data = buffers.getcontent(buffer) or ""
+ end
+ return nodeinjections.comment(specification)
+end
+
+function commands.insertcomment(specification)
+ tex.box["b_scrn_comment_link"] = comments.insert(specification)
+end
+
+-- Soundclips
+
+function soundclips.register(specification)
+ local tag = specification.tag
+ if tag and tag ~= "" then
+ local filename = specification.file
+ if not filename or filename == "" then
+ filename = tag
+ specification.file = filename
+ end
+ soundclips[tag] = specification
+ return specification
+ end
+end
+
+function soundclips.insert(tag)
+ local sc = soundclips[tag]
+ if not sc then
+ -- todo: message
+ return soundclips.register { tag = tag }
+ else
+ return sc
+ end
+end
+
+commands.registersoundclip = soundclips.register
+commands.insertsoundclip = soundclips.insert
+
+-- Renderings
+
+function renderings.register(specification)
+ if specification.label then
+ renderings[specification.label] = specification
+ return specification
+ end
+end
+
+function renderings.rendering(label)
+ local rn = renderings[label]
+ if not rn then
+ -- todo: message
+ return renderings.register { label = label }
+ else
+ return rn
+ end
+end
+
+local function var(label,key)
+ local rn = renderings[label]
+ return rn and rn[key] or ""
+end
+
+renderings.var = var
+
+function commands.renderingvar(label,key)
+ context(var(label,key))
+end
+
+commands.registerrendering = renderings.register
+
+-- Rendering:
+
+function commands.insertrenderingwindow(specification)
+ codeinjections.insertrenderingwindow(specification)
+end
+
+-- Linkedlists (only a context interface)
+
+function commands.definelinkedlist(tag)
+ -- no need
+end
+
+function commands.enhancelinkedlist(tag,n)
+ local ll = jobpasses.gettobesaved(tag)
+ if ll then
+ ll[n] = texcount.realpageno
+ end
+end
+
+function commands.addlinklistelement(tag)
+ local tobesaved = jobpasses.gettobesaved(tag)
+ local collected = jobpasses.getcollected(tag) or { }
+ local currentlink = #tobesaved + 1
+ local noflinks = #collected
+ tobesaved[currentlink] = 0
+ local f = collected[1] or 0
+ local l = collected[noflinks] or 0
+ local p = collected[currentlink-1] or f
+ local n = collected[currentlink+1] or l
+ context.setlinkedlistproperties(currentlink,noflinks,f,p,n,l)
+ -- context.ctxlatelua(function() commands.enhancelinkedlist(tag,currentlink) end)
+end
diff --git a/tex/context/base/scrp-cjk.lua b/tex/context/base/scrp-cjk.lua
index 083fc4e53..f7167b45c 100644
--- a/tex/context/base/scrp-cjk.lua
+++ b/tex/context/base/scrp-cjk.lua
@@ -1,951 +1,951 @@
-if not modules then modules = { } end modules ['scrp-cjk'] = {
- version = 1.001,
- comment = "companion to scrp-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- We can speed this up by preallocating nodes and copying them but the
--- gain is not that large.
-
--- The input line endings: there is no way to distinguish between
--- inline spaces and endofline turned into spaces (would not make
--- sense either because otherwise a wanted space at the end of a
--- line would have to be a hard coded ones.
-
-local utfchar = utf.char
-
-local insert_node_after = node.insert_after
-local insert_node_before = node.insert_before
-local remove_node = nodes.remove
-
-local nodepool = nodes.pool
-local new_glue = nodepool.glue
-local new_kern = nodepool.kern
-local new_penalty = nodepool.penalty
-
-local nodecodes = nodes.nodecodes
-local skipcodes = nodes.skipcodes
-local glyph_code = nodecodes.glyph
-local glue_code = nodecodes.glue
-local userskip_code = skipcodes.userskip
-
-local a_scriptstatus = attributes.private('scriptstatus')
-local a_scriptinjection = attributes.private('scriptinjection')
-
-local categorytonumber = scripts.categorytonumber
-local numbertocategory = scripts.numbertocategory
-local hash = scripts.hash
-local numbertodataset = scripts.numbertodataset
-
-local fonthashes = fonts.hashes
-local fontdata = fonthashes.identifiers
-local quaddata = fonthashes.quads
-local spacedata = fonthashes.spaces
-
-local trace_details = false trackers.register("scripts.details", function(v) trace_details = v end)
-
-local report_details = logs.reporter("scripts","detail")
-
--- raggedleft is controlled by leftskip and we might end up with a situation where
--- the intercharacter spacing interferes with this; the solution is to patch the
--- nodelist but better is to use veryraggedleft
-
-local inter_char_shrink = 0
-local inter_char_stretch = 0
-local inter_char_half_shrink = 0
-local inter_char_half_stretch = 0
-local inter_char_quarter_shrink = 0
-local inter_char_quarter_stretch = 0
-
-local full_char_width = 0
-local half_char_width = 0
-local quarter_char_width = 0
-
-local inter_char_hangul_penalty = 0
-
-local function set_parameters(font,data)
- -- beware: parameters can be nil in e.g. punk variants
- local quad = quaddata[font]
- full_char_width = quad
- half_char_width = quad/2
- quarter_char_width = quad/4
- inter_char_shrink = data.inter_char_shrink_factor * quad
- inter_char_stretch = data.inter_char_stretch_factor * quad
- inter_char_half_shrink = data.inter_char_half_shrink_factor * quad
- inter_char_half_stretch = data.inter_char_half_stretch_factor * quad
- inter_char_quarter_shrink = data.inter_char_quarter_shrink_factor * quad
- inter_char_quarter_stretch = data.inter_char_quarter_stretch_factor * quad
- inter_char_hangul_penalty = data.inter_char_hangul_penalty
-end
-
--- a test version did compensate for crappy halfwidth but we can best do that
--- at font definition time and/or just assume a correct font
-
-local function trace_detail(current,what)
- local prev = current.prev
- local c_id = current.id
- local p_id = prev and prev.id
- if c_id == glyph_code then
- local c_ch = current.char
- if p_id == glyph_code then
- local p_ch = p_id and prev.char
- report_details("[%C %a] [%s] [%C %a]",p_ch,hash[p_ch],what,c_ch,hash[c_ch])
- else
- report_details("[%s] [%C %a]",what,c_ch,hash[c_ch])
- end
- else
- if p_id == glyph_code then
- local p_ch = p_id and prev.char
- report_details("[%C %a] [%s]",p_ch,hash[p_ch],what)
- else
- report_details("[%s]",what)
- end
- end
-end
-
-local function trace_detail_between(p,n,what)
- local p_ch = p.char
- local n_ch = n.char
- report_details("[%C %a] [%s] [%C %a]",p_ch,hash[p_ch],what,n_ch,hash[n_ch])
-end
-
-local function nobreak(head,current)
- if trace_details then
- trace_detail(current,"break")
- end
- insert_node_before(head,current,new_penalty(10000))
-end
-
-local function stretch_break(head,current)
- if trace_details then
- trace_detail(current,"stretch break")
- end
- insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
-end
-
-local function shrink_break(head,current)
- if trace_details then
- trace_detail(current,"shrink break")
- end
- insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
-end
-
-local function nobreak_stretch(head,current)
- if trace_details then
- trace_detail(current,"no break stretch")
- end
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
-end
-
-local function korean_break(head,current)
- if trace_details then
- trace_detail(current,"korean break")
- end
- insert_node_before(head,current,new_penalty(inter_char_hangul_penalty))
-end
-
-local function nobreak_shrink(head,current)
- if trace_details then
- trace_detail(current,"nobreak shrink")
- end
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
-end
-
-local function nobreak_autoshrink(head,current)
- if trace_details then
- trace_detail(current,"nobreak autoshrink")
- end
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
-end
-
-local function nobreak_stretch_nobreak_shrink(head,current)
- if trace_details then
- trace_detail(current,"nobreak stretch nobreak shrink")
- end
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
-end
-
-local function nobreak_stretch_nobreak_autoshrink(head,current)
- if trace_details then
- trace_detail(current,"nobreak stretch nobreak autoshrink")
- end
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
-end
-
-local function nobreak_shrink_nobreak_stretch(head,current)
- if trace_details then
- trace_detail(current,"nobreak shrink nobreak stretch")
- end
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
-end
-
-local function nobreak_autoshrink_nobreak_stretch(head,current)
- if trace_details then
- trace_detail(current,"nobreak autoshrink nobreak stretch")
- end
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
-end
-
-local function nobreak_shrink_break_stretch(head,current)
- if trace_details then
- trace_detail(current,"nobreak shrink break stretch")
- end
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
- insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
-end
-
-local function nobreak_autoshrink_break_stretch(head,current)
- if trace_details then
- trace_detail(current,"nobreak autoshrink break stretch")
- end
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
- insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
-end
-
-local function nobreak_shrink_break_stretch_nobreak_shrink(head,current)
- if trace_details then
- trace_detail(current,"nobreak shrink break stretch nobreak shrink")
- end
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
- insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
-end
-
-local function japanese_between_full_close_open(head,current) -- todo: check width
- if trace_details then
- trace_detail(current,"japanese between full close open")
- end
- insert_node_before(head,current,new_kern(-half_char_width))
- insert_node_before(head,current,new_glue(half_char_width,0,inter_char_half_shrink))
- insert_node_before(head,current,new_kern(-half_char_width))
-end
-
-local function japanese_between_full_close_full_close(head,current) -- todo: check width
- if trace_details then
- trace_detail(current,"japanese between full close full close")
- end
- insert_node_before(head,current,new_kern(-half_char_width))
- -- insert_node_before(head,current,new_glue(half_char_width,0,inter_char_half_shrink))
-end
-
-local function japanese_before_full_width_punct(head,current) -- todo: check width
- if trace_details then
- trace_detail(current,"japanese before full width punct")
- end
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(quarter_char_width,0,inter_char_quarter_shrink))
- insert_node_before(head,current,new_kern(-quarter_char_width))
-end
-
-local function japanese_after_full_width_punct(head,current) -- todo: check width
- if trace_details then
- trace_detail(current,"japanese after full width punct")
- end
- insert_node_before(head,current,new_kern(-quarter_char_width))
- insert_node_before(head,current,new_glue(quarter_char_width,0,inter_char_quarter_shrink))
-end
-
-local function nobreak_autoshrink_break_stretch_nobreak_autoshrink(head,current)
- if trace_details then
- trace_detail(current,"nobreak autoshrink break stretch nobreak autoshrink")
- end
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
- insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
-end
-
-local function nobreak_autoshrink_break_stretch_nobreak_shrink(head,current)
- if trace_details then
- trace_detail(current,"nobreak autoshrink break stretch nobreak shrink")
- end
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
- insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
-end
-
-local function nobreak_shrink_break_stretch_nobreak_autoshrink(head,current)
- if trace_details then
- trace_detail(current,"nobreak shrink break stretch nobreak autoshrink")
- end
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
- insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
-end
-
-local function nobreak_stretch_break_shrink(head,current)
- if trace_details then
- trace_detail(current,"nobreak stretch break shrink")
- end
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
- insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
-end
-
-local function nobreak_stretch_break_autoshrink(head,current)
- if trace_details then
- trace_detail(current,"nobreak stretch break autoshrink")
- end
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
- insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
-end
-
--- Korean: hangul
-
-local korean_0 = {
-}
-
-local korean_1 = {
- jamo_initial = korean_break,
- korean = korean_break,
- chinese = korean_break,
- hiragana = korean_break,
- katakana = korean_break,
- half_width_open = stretch_break,
- half_width_close = nobreak,
- full_width_open = stretch_break,
- full_width_close = nobreak,
- full_width_punct = nobreak,
--- hyphen = nil,
- non_starter = korean_break,
- other = korean_break,
-}
-
-local korean_2 = {
- jamo_initial = stretch_break,
- korean = stretch_break,
- chinese = stretch_break,
- hiragana = stretch_break,
- katakana = stretch_break,
- half_width_open = stretch_break,
- half_width_close = nobreak,
- full_width_open = stretch_break,
- full_width_close = nobreak,
- full_width_punct = nobreak,
--- hyphen = nil,
- non_starter = stretch_break,
- other = stretch_break,
-}
-
-local korean_3 = {
- jamo_initial = stretch_break,
- korean = stretch_break,
- chinese = stretch_break,
- hiragana = stretch_break,
- katakana = stretch_break,
- half_width_open = stretch_break,
- half_width_close = nobreak,
- full_width_open = stretch_break,
- full_width_close = nobreak,
- full_width_punct = nobreak,
--- hyphen = nil,
- non_starter = nobreak,
- other = nobreak,
-}
-
-local korean_4 = {
- jamo_initial = nobreak,
- korean = nobreak,
- chinese = nobreak,
- hiragana = nobreak,
- katakana = nobreak,
- half_width_open = nobreak,
- half_width_close = nobreak,
- full_width_open = nobreak,
- full_width_close = nobreak,
- full_width_punct = nobreak,
- hyphen = nobreak,
- non_starter = nobreak,
- other = nobreak,
-}
-
-local korean_5 = {
- jamo_initial = stretch_break,
- korean = stretch_break,
- chinese = stretch_break,
- hiragana = stretch_break,
- katakana = stretch_break,
- half_width_open = stretch_break,
- half_width_close = nobreak_stretch,
- full_width_open = stretch_break,
- full_width_close = nobreak_stretch,
- full_width_punct = nobreak_stretch,
- hyphen = nobreak_stretch,
- non_starter = nobreak_stretch,
- other = stretch_break,
-}
-
-local injectors = { -- [previous] [current]
- jamo_final = korean_1,
- korean = korean_1,
- chinese = korean_1,
- hiragana = korean_1,
- katakana = korean_1,
- hyphen = korean_2,
- start = korean_0,
- other = korean_2,
- non_starter = korean_3,
- full_width_open = korean_4,
- half_width_open = korean_4,
- full_width_close = korean_5,
- full_width_punct = korean_5,
- half_width_close = korean_5,
-}
-
-local function process(head,first,last)
- if first ~= last then
- local lastfont, previous, last = nil, "start", nil
- while true do
- local upcoming, id = first.next, first.id
- if id == glyph_code then
- local a = first[a_scriptstatus]
- local current = numbertocategory[a]
- local action = injectors[previous]
- if action then
- action = action[current]
- if action then
- local font = first.font
- if font ~= lastfont then
- lastfont = font
- set_parameters(font,numbertodataset[first[a_scriptinjection]])
- end
- action(head,first)
- end
- end
- previous = current
- else -- glue
- local p, n = first.prev, upcoming
- if p and n then
- local pid, nid = p.id, n.id
- if pid == glyph_code and nid == glyph_code then
- local pa, na = p[a_scriptstatus], n[a_scriptstatus]
- local pcjk, ncjk = pa and numbertocategory[pa], na and numbertocategory[na]
- if not pcjk or not ncjk
- or pcjk == "korean" or ncjk == "korean"
- or pcjk == "other" or ncjk == "other"
- or pcjk == "jamo_final" or ncjk == "jamo_initial" then
- previous = "start"
- else -- if head ~= first then
- remove_node(head,first,true)
- previous = pcjk
- -- else
- -- previous = pcjk
- end
- else
- previous = "start"
- end
- else
- previous = "start"
- end
- end
- if upcoming == last then -- was stop
- break
- else
- first = upcoming
- end
- end
- end
-end
-
-scripts.installmethod {
- name = "hangul",
- injector = process,
- datasets = { -- todo: metatables
- default = {
- inter_char_shrink_factor = 0.50, -- of quad
- inter_char_stretch_factor = 0.50, -- of quad
- inter_char_half_shrink_factor = 0.50, -- of quad
- inter_char_half_stretch_factor = 0.50, -- of quad
- inter_char_quarter_shrink_factor = 0.50, -- of quad
- inter_char_quarter_stretch_factor = 0.50, -- of quad
- inter_char_hangul_penalty = 50,
- },
- },
-}
-
--- Chinese: hanzi
-
-local chinese_0 = {
-}
-
-local chinese_1 = {
- jamo_initial = korean_break,
- korean = korean_break,
- chinese = stretch_break,
- hiragana = stretch_break,
- katakana = stretch_break,
- half_width_open = nobreak_stretch_break_autoshrink,
- half_width_close = nobreak_stretch,
- full_width_open = nobreak_stretch_break_shrink,
- full_width_close = nobreak_stretch,
- full_width_punct = nobreak_stretch,
--- hyphen = nil,
- non_starter = nobreak_stretch,
- other = stretch_break,
-}
-
-local chinese_2 = {
- jamo_initial = korean_break,
- korean = stretch_break,
- chinese = stretch_break,
- hiragana = stretch_break,
- katakana = stretch_break,
- half_width_open = nobreak_stretch_break_autoshrink,
- half_width_close = nobreak_stretch,
- full_width_open = nobreak_stretch_break_shrink,
- full_width_close = nobreak_stretch,
- full_width_punct = nobreak_stretch,
- hyphen = nobreak_stretch,
- non_starter = nobreak_stretch,
- other = stretch_break,
-}
-
-local chinese_3 = {
- jamo_initial = korean_break,
- korean = stretch_break,
- chinese = stretch_break,
- hiragana = stretch_break,
- katakana = stretch_break,
- half_width_open = nobreak_stretch_break_autoshrink,
- half_width_close = nobreak_stretch,
- full_width_open = nobreak_stretch_break_shrink,
- full_width_close = nobreak_stretch,
- full_width_punct = nobreak_stretch,
--- hyphen = nil,
- non_starter = nobreak_stretch,
- other = stretch_break,
-}
-
-local chinese_4 = {
--- jamo_initial = nil,
--- korean = nil,
--- chinese = nil,
--- hiragana = nil,
--- katakana = nil,
- half_width_open = nobreak_autoshrink,
- half_width_close = nil,
- full_width_open = nobreak_shrink,
- full_width_close = nobreak,
- full_width_punct = nobreak,
--- hyphen = nil,
- non_starter = nobreak,
--- other = nil,
-}
-
-local chinese_5 = {
- jamo_initial = stretch_break,
- korean = stretch_break,
- chinese = stretch_break,
- hiragana = stretch_break,
- katakana = stretch_break,
- half_width_open = nobreak_stretch_break_autoshrink,
- half_width_close = nobreak_stretch,
- full_width_open = nobreak_stretch_break_shrink,
- full_width_close = nobreak_stretch,
- full_width_punct = nobreak_stretch,
--- hyphen = nil,
- non_starter = nobreak_stretch,
- other = stretch_break,
-}
-
-local chinese_6 = {
- jamo_initial = nobreak_stretch,
- korean = nobreak_stretch,
- chinese = nobreak_stretch,
- hiragana = nobreak_stretch,
- katakana = nobreak_stretch,
- half_width_open = nobreak_stretch_break_autoshrink,
- half_width_close = nobreak_stretch,
- full_width_open = nobreak_stretch_break_shrink,
- full_width_close = nobreak_stretch,
- full_width_punct = nobreak_stretch,
- hyphen = nobreak_stretch,
- non_starter = nobreak_stretch,
- other = nobreak_stretch,
-}
-
-local chinese_7 = {
- jami_initial = nobreak_shrink_break_stretch,
- korean = nobreak_shrink_break_stretch,
- chinese = stretch_break, -- nobreak_shrink_break_stretch,
- hiragana = stretch_break, -- nobreak_shrink_break_stretch,
- katakana = stretch_break, -- nobreak_shrink_break_stretch,
- half_width_open = nobreak_shrink_break_stretch_nobreak_autoshrink,
- half_width_close = nobreak_shrink_nobreak_stretch,
- full_width_open = nobreak_shrink_break_stretch_nobreak_shrink,
- full_width_close = nobreak_shrink_nobreak_stretch,
- full_width_punct = nobreak_shrink_nobreak_stretch,
- hyphen = nobreak_shrink_break_stretch,
- non_starter = nobreak_shrink_break_stretch,
- other = nobreak_shrink_break_stretch,
-}
-
-local chinese_8 = {
- jami_initial = nobreak_shrink_break_stretch,
- korean = nobreak_autoshrink_break_stretch,
- chinese = stretch_break, -- nobreak_autoshrink_break_stretch,
- hiragana = stretch_break, -- nobreak_autoshrink_break_stretch,
- katakana = stretch_break, -- nobreak_autoshrink_break_stretch,
- half_width_open = nobreak_autoshrink_break_stretch_nobreak_autoshrink,
- half_width_close = nobreak_autoshrink_nobreak_stretch,
- full_width_open = nobreak_autoshrink_break_stretch_nobreak_shrink,
- full_width_close = nobreak_autoshrink_nobreak_stretch,
- full_width_punct = nobreak_autoshrink_nobreak_stretch,
- hyphen = nobreak_autoshrink_break_stretch,
- non_starter = nobreak_autoshrink_break_stretch,
- other = nobreak_autoshrink_break_stretch,
-}
-
-local injectors = { -- [previous] [current]
- jamo_final = chinese_1,
- korean = chinese_1,
- chinese = chinese_2,
- hiragana = chinese_2,
- katakana = chinese_2,
- hyphen = chinese_3,
- start = chinese_4,
- other = chinese_5,
- non_starter = chinese_5,
- full_width_open = chinese_6,
- half_width_open = chinese_6,
- full_width_close = chinese_7,
- full_width_punct = chinese_7,
- half_width_close = chinese_8,
-}
-
-local function process(head,first,last)
- if first ~= last then
- local lastfont, previous, last = nil, "start", nil
- while true do
- local upcoming, id = first.next, first.id
- if id == glyph_code then
- local a = first[a_scriptstatus]
- local current = numbertocategory[a]
- local action = injectors[previous]
- if action then
- action = action[current]
- if action then
- local font = first.font
- if font ~= lastfont then
- lastfont = font
- set_parameters(font,numbertodataset[first[a_scriptinjection]])
- end
- action(head,first)
- end
- end
- previous = current
- else -- glue
- local p, n = first.prev, upcoming
- if p and n then
- local pid, nid = p.id, n.id
- if pid == glyph_code and nid == glyph_code then
- local pa, na = p[a_scriptstatus], n[a_scriptstatus]
- local pcjk, ncjk = pa and numbertocategory[pa], na and numbertocategory[na]
- if not pcjk or not ncjk
- or pcjk == "korean" or ncjk == "korean"
- or pcjk == "other" or ncjk == "other"
- or pcjk == "jamo_final" or ncjk == "jamo_initial"
- or pcjk == "half_width_close" or ncjk == "half_width_open" then -- extra compared to korean
- previous = "start"
- else -- if head ~= first then
- remove_node(head,first,true)
- previous = pcjk
- -- else
- -- previous = pcjk
- end
- else
- previous = "start"
- end
- else
- previous = "start"
- end
- end
- if upcoming == last then -- was stop
- break
- else
- first = upcoming
- end
- end
- end
-end
-
-scripts.installmethod {
- name = "hanzi",
- injector = process,
- datasets = {
- default = {
- inter_char_shrink_factor = 0.50, -- of quad
- inter_char_stretch_factor = 0.50, -- of quad
- inter_char_half_shrink_factor = 0.50, -- of quad
- inter_char_half_stretch_factor = 0.50, -- of quad
- inter_char_quarter_shrink_factor = 0.50, -- of quad
- inter_char_quarter_stretch_factor = 0.50, -- of quad
- inter_char_hangul_penalty = 50,
- },
- },
-}
-
--- Japanese: idiographic, hiragana, katakana, romanji / jis
-
-local japanese_0 = {
-}
-
-local japanese_1 = {
- jamo_initial = korean_break,
- korean = korean_break,
- chinese = stretch_break,
- hiragana = stretch_break,
- katakana = stretch_break,
- half_width_open = nobreak_stretch_break_autoshrink,
- half_width_close = nobreak_stretch,
- full_width_open = nobreak_stretch_break_shrink,
- full_width_close = nobreak_stretch,
- full_width_punct = nobreak_stretch,
--- hyphen = nil,
- non_starter = nobreak_stretch,
- other = stretch_break,
-}
-
-local japanese_2 = {
- jamo_initial = korean_break,
- korean = stretch_break,
- chinese = stretch_break,
- hiragana = stretch_break,
- katakana = stretch_break,
- half_width_open = nobreak_stretch_break_autoshrink,
- half_width_close = nobreak_stretch,
- full_width_open = nobreak_stretch_break_shrink,
- full_width_close = nobreak_stretch,
- full_width_punct = japanese_before_full_width_punct, -- nobreak_stretch,
- hyphen = nobreak_stretch,
- non_starter = nobreak_stretch,
- other = stretch_break,
-}
-
-local japanese_3 = {
- jamo_initial = korean_break,
- korean = stretch_break,
- chinese = stretch_break,
- hiragana = stretch_break,
- katakana = stretch_break,
- half_width_open = nobreak_stretch_break_autoshrink,
- half_width_close = nobreak_stretch,
- full_width_open = nobreak_stretch_break_shrink,
- full_width_close = nobreak_stretch,
- full_width_punct = nobreak_stretch,
--- hyphen = nil,
- non_starter = nobreak_stretch,
- other = stretch_break,
-}
-
-local japanese_4 = {
--- jamo_initial = nil,
--- korean = nil,
--- chinese = nil,
--- hiragana = nil,
--- katakana = nil,
- half_width_open = nobreak_autoshrink,
- half_width_close = nil,
- full_width_open = nobreak_shrink,
- full_width_close = nobreak,
- full_width_punct = nobreak,
--- hyphen = nil,
- non_starter = nobreak,
--- other = nil,
-}
-
-local japanese_5 = {
- jamo_initial = stretch_break,
- korean = stretch_break,
- chinese = stretch_break,
- hiragana = stretch_break,
- katakana = stretch_break,
- half_width_open = nobreak_stretch_break_autoshrink,
- half_width_close = nobreak_stretch,
- full_width_open = nobreak_stretch_break_shrink,
- full_width_close = nobreak_stretch,
- full_width_punct = nobreak_stretch,
--- hyphen = nil,
- non_starter = nobreak_stretch,
- other = stretch_break,
-}
-
-local japanese_6 = {
- jamo_initial = nobreak_stretch,
- korean = nobreak_stretch,
- chinese = nobreak_stretch,
- hiragana = nobreak_stretch,
- katakana = nobreak_stretch,
- half_width_open = nobreak_stretch_break_autoshrink,
- half_width_close = nobreak_stretch,
- full_width_open = nobreak_stretch_break_shrink,
- full_width_close = nobreak_stretch,
- full_width_punct = nobreak_stretch,
- hyphen = nobreak_stretch,
- non_starter = nobreak_stretch,
- other = nobreak_stretch,
-}
-
-local japanese_7 = {
- jami_initial = nobreak_shrink_break_stretch,
- korean = nobreak_shrink_break_stretch,
- chinese = japanese_after_full_width_punct, -- stretch_break
- hiragana = japanese_after_full_width_punct, -- stretch_break
- katakana = japanese_after_full_width_punct, -- stretch_break
- half_width_open = nobreak_shrink_break_stretch_nobreak_autoshrink,
- half_width_close = nobreak_shrink_nobreak_stretch,
- full_width_open = japanese_between_full_close_open, -- !!
- full_width_close = japanese_between_full_close_full_close, -- nobreak_shrink_nobreak_stretch,
- full_width_punct = nobreak_shrink_nobreak_stretch,
- hyphen = nobreak_shrink_break_stretch,
- non_starter = nobreak_shrink_break_stretch,
- other = nobreak_shrink_break_stretch,
-}
-
-local japanese_8 = {
- jami_initial = nobreak_shrink_break_stretch,
- korean = nobreak_autoshrink_break_stretch,
- chinese = stretch_break,
- hiragana = stretch_break,
- katakana = stretch_break,
- half_width_open = nobreak_autoshrink_break_stretch_nobreak_autoshrink,
- half_width_close = nobreak_autoshrink_nobreak_stretch,
- full_width_open = nobreak_autoshrink_break_stretch_nobreak_shrink,
- full_width_close = nobreak_autoshrink_nobreak_stretch,
- full_width_punct = nobreak_autoshrink_nobreak_stretch,
- hyphen = nobreak_autoshrink_break_stretch,
- non_starter = nobreak_autoshrink_break_stretch,
- other = nobreak_autoshrink_break_stretch,
-}
-
-local injectors = { -- [previous] [current]
- jamo_final = japanese_1,
- korean = japanese_1,
- chinese = japanese_2,
- hiragana = japanese_2,
- katakana = japanese_2,
- hyphen = japanese_3,
- start = japanese_4,
- other = japanese_5,
- non_starter = japanese_5,
- full_width_open = japanese_6,
- half_width_open = japanese_6,
- full_width_close = japanese_7,
- full_width_punct = japanese_7,
- half_width_close = japanese_8,
-}
-
-local function process(head,first,last)
- if first ~= last then
- local lastfont, previous, last = nil, "start", nil
- while true do
- local upcoming, id = first.next, first.id
- if id == glyph_code then
- local a = first[a_scriptstatus]
- local current = numbertocategory[a]
- local action = injectors[previous]
- if action then
- action = action[current]
- if action then
- local font = first.font
- if font ~= lastfont then
- lastfont = font
- set_parameters(font,numbertodataset[first[a_scriptinjection]])
- end
- action(head,first)
- end
- end
- previous = current
-
--- elseif id == math_code then
--- upcoming = end_of_math(current).next
--- previous = "start"
-
- else -- glue
- local p, n = first.prev, upcoming -- we should remember prev
- if p and n then
- local pid, nid = p.id, n.id
- if pid == glyph_code and nid == glyph_code then
- local pa, na = p[a_scriptstatus], n[a_scriptstatus]
- local pcjk, ncjk = pa and numbertocategory[pa], na and numbertocategory[na]
- if not pcjk or not ncjk
- or pcjk == "korean" or ncjk == "korean"
- or pcjk == "other" or ncjk == "other"
- or pcjk == "jamo_final" or ncjk == "jamo_initial"
- or pcjk == "half_width_close" or ncjk == "half_width_open" then -- extra compared to korean
- previous = "start"
- else -- if head ~= first then
-if id == glue_code and first.subtype == userskip_code then -- also scriptstatus check?
- -- for the moment no distinction possible between space and userskip
- local w = first.spec.width
- local s = spacedata[p.font]
- if w == s then -- could be option
- if trace_details then
- trace_detail_between(p,n,"space removed")
- end
- remove_node(head,first,true)
- end
-end
- previous = pcjk
- -- else
- -- previous = pcjk
- end
- else
- previous = "start"
- end
- else
- previous = "start"
- end
- end
- if upcoming == last then -- was stop
- break
- else
- first = upcoming
- end
- end
- end
-end
-
-scripts.installmethod {
- name = "nihongo", -- what name to use?
- injector = process,
- datasets = {
- default = {
- inter_char_shrink_factor = 0.50, -- of quad
- inter_char_stretch_factor = 0.50, -- of quad
- inter_char_half_shrink_factor = 0.50, -- of quad
- inter_char_half_stretch_factor = 0.50, -- of quad
- inter_char_quarter_shrink_factor = 0.25, -- of quad
- inter_char_quarter_stretch_factor = 0.25, -- of quad
- inter_char_hangul_penalty = 50,
- },
- },
-}
-
+if not modules then modules = { } end modules ['scrp-cjk'] = {
+ version = 1.001,
+ comment = "companion to scrp-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- We can speed this up by preallocating nodes and copying them but the
+-- gain is not that large.
+
+-- The input line endings: there is no way to distinguish between
+-- inline spaces and endofline turned into spaces (would not make
+-- sense either because otherwise a wanted space at the end of a
+-- line would have to be a hard coded ones.
+
+local utfchar = utf.char
+
+local insert_node_after = node.insert_after
+local insert_node_before = node.insert_before
+local remove_node = nodes.remove
+
+local nodepool = nodes.pool
+local new_glue = nodepool.glue
+local new_kern = nodepool.kern
+local new_penalty = nodepool.penalty
+
+local nodecodes = nodes.nodecodes
+local skipcodes = nodes.skipcodes
+local glyph_code = nodecodes.glyph
+local glue_code = nodecodes.glue
+local userskip_code = skipcodes.userskip
+
+local a_scriptstatus = attributes.private('scriptstatus')
+local a_scriptinjection = attributes.private('scriptinjection')
+
+local categorytonumber = scripts.categorytonumber
+local numbertocategory = scripts.numbertocategory
+local hash = scripts.hash
+local numbertodataset = scripts.numbertodataset
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+local quaddata = fonthashes.quads
+local spacedata = fonthashes.spaces
+
+local trace_details = false trackers.register("scripts.details", function(v) trace_details = v end)
+
+local report_details = logs.reporter("scripts","detail")
+
+-- raggedleft is controlled by leftskip and we might end up with a situation where
+-- the intercharacter spacing interferes with this; the solution is to patch the
+-- nodelist but better is to use veryraggedleft
+
+local inter_char_shrink = 0
+local inter_char_stretch = 0
+local inter_char_half_shrink = 0
+local inter_char_half_stretch = 0
+local inter_char_quarter_shrink = 0
+local inter_char_quarter_stretch = 0
+
+local full_char_width = 0
+local half_char_width = 0
+local quarter_char_width = 0
+
+local inter_char_hangul_penalty = 0
+
+local function set_parameters(font,data)
+ -- beware: parameters can be nil in e.g. punk variants
+ local quad = quaddata[font]
+ full_char_width = quad
+ half_char_width = quad/2
+ quarter_char_width = quad/4
+ inter_char_shrink = data.inter_char_shrink_factor * quad
+ inter_char_stretch = data.inter_char_stretch_factor * quad
+ inter_char_half_shrink = data.inter_char_half_shrink_factor * quad
+ inter_char_half_stretch = data.inter_char_half_stretch_factor * quad
+ inter_char_quarter_shrink = data.inter_char_quarter_shrink_factor * quad
+ inter_char_quarter_stretch = data.inter_char_quarter_stretch_factor * quad
+ inter_char_hangul_penalty = data.inter_char_hangul_penalty
+end
+
+-- a test version did compensate for crappy halfwidth but we can best do that
+-- at font definition time and/or just assume a correct font
+
+local function trace_detail(current,what)
+ local prev = current.prev
+ local c_id = current.id
+ local p_id = prev and prev.id
+ if c_id == glyph_code then
+ local c_ch = current.char
+ if p_id == glyph_code then
+ local p_ch = p_id and prev.char
+ report_details("[%C %a] [%s] [%C %a]",p_ch,hash[p_ch],what,c_ch,hash[c_ch])
+ else
+ report_details("[%s] [%C %a]",what,c_ch,hash[c_ch])
+ end
+ else
+ if p_id == glyph_code then
+ local p_ch = p_id and prev.char
+ report_details("[%C %a] [%s]",p_ch,hash[p_ch],what)
+ else
+ report_details("[%s]",what)
+ end
+ end
+end
+
+local function trace_detail_between(p,n,what)
+ local p_ch = p.char
+ local n_ch = n.char
+ report_details("[%C %a] [%s] [%C %a]",p_ch,hash[p_ch],what,n_ch,hash[n_ch])
+end
+
+local function nobreak(head,current)
+ if trace_details then
+ trace_detail(current,"break")
+ end
+ insert_node_before(head,current,new_penalty(10000))
+end
+
+local function stretch_break(head,current)
+ if trace_details then
+ trace_detail(current,"stretch break")
+ end
+ insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
+end
+
+local function shrink_break(head,current)
+ if trace_details then
+ trace_detail(current,"shrink break")
+ end
+ insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
+end
+
+local function nobreak_stretch(head,current)
+ if trace_details then
+ trace_detail(current,"no break stretch")
+ end
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
+end
+
+local function korean_break(head,current)
+ if trace_details then
+ trace_detail(current,"korean break")
+ end
+ insert_node_before(head,current,new_penalty(inter_char_hangul_penalty))
+end
+
+local function nobreak_shrink(head,current)
+ if trace_details then
+ trace_detail(current,"nobreak shrink")
+ end
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
+end
+
+local function nobreak_autoshrink(head,current)
+ if trace_details then
+ trace_detail(current,"nobreak autoshrink")
+ end
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
+end
+
+local function nobreak_stretch_nobreak_shrink(head,current)
+ if trace_details then
+ trace_detail(current,"nobreak stretch nobreak shrink")
+ end
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
+end
+
+local function nobreak_stretch_nobreak_autoshrink(head,current)
+ if trace_details then
+ trace_detail(current,"nobreak stretch nobreak autoshrink")
+ end
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
+end
+
+local function nobreak_shrink_nobreak_stretch(head,current)
+ if trace_details then
+ trace_detail(current,"nobreak shrink nobreak stretch")
+ end
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
+end
+
+local function nobreak_autoshrink_nobreak_stretch(head,current)
+ if trace_details then
+ trace_detail(current,"nobreak autoshrink nobreak stretch")
+ end
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
+end
+
+local function nobreak_shrink_break_stretch(head,current)
+ if trace_details then
+ trace_detail(current,"nobreak shrink break stretch")
+ end
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
+ insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
+end
+
+local function nobreak_autoshrink_break_stretch(head,current)
+ if trace_details then
+ trace_detail(current,"nobreak autoshrink break stretch")
+ end
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
+ insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
+end
+
+local function nobreak_shrink_break_stretch_nobreak_shrink(head,current)
+ if trace_details then
+ trace_detail(current,"nobreak shrink break stretch nobreak shrink")
+ end
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
+ insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
+end
+
+local function japanese_between_full_close_open(head,current) -- todo: check width
+ if trace_details then
+ trace_detail(current,"japanese between full close open")
+ end
+ insert_node_before(head,current,new_kern(-half_char_width))
+ insert_node_before(head,current,new_glue(half_char_width,0,inter_char_half_shrink))
+ insert_node_before(head,current,new_kern(-half_char_width))
+end
+
+local function japanese_between_full_close_full_close(head,current) -- todo: check width
+ if trace_details then
+ trace_detail(current,"japanese between full close full close")
+ end
+ insert_node_before(head,current,new_kern(-half_char_width))
+ -- insert_node_before(head,current,new_glue(half_char_width,0,inter_char_half_shrink))
+end
+
+local function japanese_before_full_width_punct(head,current) -- todo: check width
+ if trace_details then
+ trace_detail(current,"japanese before full width punct")
+ end
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(quarter_char_width,0,inter_char_quarter_shrink))
+ insert_node_before(head,current,new_kern(-quarter_char_width))
+end
+
+local function japanese_after_full_width_punct(head,current) -- todo: check width
+ if trace_details then
+ trace_detail(current,"japanese after full width punct")
+ end
+ insert_node_before(head,current,new_kern(-quarter_char_width))
+ insert_node_before(head,current,new_glue(quarter_char_width,0,inter_char_quarter_shrink))
+end
+
+local function nobreak_autoshrink_break_stretch_nobreak_autoshrink(head,current)
+ if trace_details then
+ trace_detail(current,"nobreak autoshrink break stretch nobreak autoshrink")
+ end
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
+ insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
+end
+
+local function nobreak_autoshrink_break_stretch_nobreak_shrink(head,current)
+ if trace_details then
+ trace_detail(current,"nobreak autoshrink break stretch nobreak shrink")
+ end
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
+ insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
+end
+
+local function nobreak_shrink_break_stretch_nobreak_autoshrink(head,current)
+ if trace_details then
+ trace_detail(current,"nobreak shrink break stretch nobreak autoshrink")
+ end
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
+ insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
+end
+
+local function nobreak_stretch_break_shrink(head,current)
+ if trace_details then
+ trace_detail(current,"nobreak stretch break shrink")
+ end
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
+ insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
+end
+
+local function nobreak_stretch_break_autoshrink(head,current)
+ if trace_details then
+ trace_detail(current,"nobreak stretch break autoshrink")
+ end
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,new_glue(0,inter_char_stretch,0))
+ insert_node_before(head,current,new_glue(0,0,inter_char_half_shrink))
+end
+
+-- Korean: hangul
+
+local korean_0 = {
+}
+
+local korean_1 = {
+ jamo_initial = korean_break,
+ korean = korean_break,
+ chinese = korean_break,
+ hiragana = korean_break,
+ katakana = korean_break,
+ half_width_open = stretch_break,
+ half_width_close = nobreak,
+ full_width_open = stretch_break,
+ full_width_close = nobreak,
+ full_width_punct = nobreak,
+-- hyphen = nil,
+ non_starter = korean_break,
+ other = korean_break,
+}
+
+local korean_2 = {
+ jamo_initial = stretch_break,
+ korean = stretch_break,
+ chinese = stretch_break,
+ hiragana = stretch_break,
+ katakana = stretch_break,
+ half_width_open = stretch_break,
+ half_width_close = nobreak,
+ full_width_open = stretch_break,
+ full_width_close = nobreak,
+ full_width_punct = nobreak,
+-- hyphen = nil,
+ non_starter = stretch_break,
+ other = stretch_break,
+}
+
+local korean_3 = {
+ jamo_initial = stretch_break,
+ korean = stretch_break,
+ chinese = stretch_break,
+ hiragana = stretch_break,
+ katakana = stretch_break,
+ half_width_open = stretch_break,
+ half_width_close = nobreak,
+ full_width_open = stretch_break,
+ full_width_close = nobreak,
+ full_width_punct = nobreak,
+-- hyphen = nil,
+ non_starter = nobreak,
+ other = nobreak,
+}
+
+local korean_4 = {
+ jamo_initial = nobreak,
+ korean = nobreak,
+ chinese = nobreak,
+ hiragana = nobreak,
+ katakana = nobreak,
+ half_width_open = nobreak,
+ half_width_close = nobreak,
+ full_width_open = nobreak,
+ full_width_close = nobreak,
+ full_width_punct = nobreak,
+ hyphen = nobreak,
+ non_starter = nobreak,
+ other = nobreak,
+}
+
+local korean_5 = {
+ jamo_initial = stretch_break,
+ korean = stretch_break,
+ chinese = stretch_break,
+ hiragana = stretch_break,
+ katakana = stretch_break,
+ half_width_open = stretch_break,
+ half_width_close = nobreak_stretch,
+ full_width_open = stretch_break,
+ full_width_close = nobreak_stretch,
+ full_width_punct = nobreak_stretch,
+ hyphen = nobreak_stretch,
+ non_starter = nobreak_stretch,
+ other = stretch_break,
+}
+
+local injectors = { -- [previous] [current]
+ jamo_final = korean_1,
+ korean = korean_1,
+ chinese = korean_1,
+ hiragana = korean_1,
+ katakana = korean_1,
+ hyphen = korean_2,
+ start = korean_0,
+ other = korean_2,
+ non_starter = korean_3,
+ full_width_open = korean_4,
+ half_width_open = korean_4,
+ full_width_close = korean_5,
+ full_width_punct = korean_5,
+ half_width_close = korean_5,
+}
+
+local function process(head,first,last)
+ if first ~= last then
+ local lastfont, previous, last = nil, "start", nil
+ while true do
+ local upcoming, id = first.next, first.id
+ if id == glyph_code then
+ local a = first[a_scriptstatus]
+ local current = numbertocategory[a]
+ local action = injectors[previous]
+ if action then
+ action = action[current]
+ if action then
+ local font = first.font
+ if font ~= lastfont then
+ lastfont = font
+ set_parameters(font,numbertodataset[first[a_scriptinjection]])
+ end
+ action(head,first)
+ end
+ end
+ previous = current
+ else -- glue
+ local p, n = first.prev, upcoming
+ if p and n then
+ local pid, nid = p.id, n.id
+ if pid == glyph_code and nid == glyph_code then
+ local pa, na = p[a_scriptstatus], n[a_scriptstatus]
+ local pcjk, ncjk = pa and numbertocategory[pa], na and numbertocategory[na]
+ if not pcjk or not ncjk
+ or pcjk == "korean" or ncjk == "korean"
+ or pcjk == "other" or ncjk == "other"
+ or pcjk == "jamo_final" or ncjk == "jamo_initial" then
+ previous = "start"
+ else -- if head ~= first then
+ remove_node(head,first,true)
+ previous = pcjk
+ -- else
+ -- previous = pcjk
+ end
+ else
+ previous = "start"
+ end
+ else
+ previous = "start"
+ end
+ end
+ if upcoming == last then -- was stop
+ break
+ else
+ first = upcoming
+ end
+ end
+ end
+end
+
+scripts.installmethod {
+ name = "hangul",
+ injector = process,
+ datasets = { -- todo: metatables
+ default = {
+ inter_char_shrink_factor = 0.50, -- of quad
+ inter_char_stretch_factor = 0.50, -- of quad
+ inter_char_half_shrink_factor = 0.50, -- of quad
+ inter_char_half_stretch_factor = 0.50, -- of quad
+ inter_char_quarter_shrink_factor = 0.50, -- of quad
+ inter_char_quarter_stretch_factor = 0.50, -- of quad
+ inter_char_hangul_penalty = 50,
+ },
+ },
+}
+
+-- Chinese: hanzi
+
+local chinese_0 = {
+}
+
+local chinese_1 = {
+ jamo_initial = korean_break,
+ korean = korean_break,
+ chinese = stretch_break,
+ hiragana = stretch_break,
+ katakana = stretch_break,
+ half_width_open = nobreak_stretch_break_autoshrink,
+ half_width_close = nobreak_stretch,
+ full_width_open = nobreak_stretch_break_shrink,
+ full_width_close = nobreak_stretch,
+ full_width_punct = nobreak_stretch,
+-- hyphen = nil,
+ non_starter = nobreak_stretch,
+ other = stretch_break,
+}
+
+local chinese_2 = {
+ jamo_initial = korean_break,
+ korean = stretch_break,
+ chinese = stretch_break,
+ hiragana = stretch_break,
+ katakana = stretch_break,
+ half_width_open = nobreak_stretch_break_autoshrink,
+ half_width_close = nobreak_stretch,
+ full_width_open = nobreak_stretch_break_shrink,
+ full_width_close = nobreak_stretch,
+ full_width_punct = nobreak_stretch,
+ hyphen = nobreak_stretch,
+ non_starter = nobreak_stretch,
+ other = stretch_break,
+}
+
+local chinese_3 = {
+ jamo_initial = korean_break,
+ korean = stretch_break,
+ chinese = stretch_break,
+ hiragana = stretch_break,
+ katakana = stretch_break,
+ half_width_open = nobreak_stretch_break_autoshrink,
+ half_width_close = nobreak_stretch,
+ full_width_open = nobreak_stretch_break_shrink,
+ full_width_close = nobreak_stretch,
+ full_width_punct = nobreak_stretch,
+-- hyphen = nil,
+ non_starter = nobreak_stretch,
+ other = stretch_break,
+}
+
+local chinese_4 = {
+-- jamo_initial = nil,
+-- korean = nil,
+-- chinese = nil,
+-- hiragana = nil,
+-- katakana = nil,
+ half_width_open = nobreak_autoshrink,
+ half_width_close = nil,
+ full_width_open = nobreak_shrink,
+ full_width_close = nobreak,
+ full_width_punct = nobreak,
+-- hyphen = nil,
+ non_starter = nobreak,
+-- other = nil,
+}
+
+local chinese_5 = {
+ jamo_initial = stretch_break,
+ korean = stretch_break,
+ chinese = stretch_break,
+ hiragana = stretch_break,
+ katakana = stretch_break,
+ half_width_open = nobreak_stretch_break_autoshrink,
+ half_width_close = nobreak_stretch,
+ full_width_open = nobreak_stretch_break_shrink,
+ full_width_close = nobreak_stretch,
+ full_width_punct = nobreak_stretch,
+-- hyphen = nil,
+ non_starter = nobreak_stretch,
+ other = stretch_break,
+}
+
+local chinese_6 = {
+ jamo_initial = nobreak_stretch,
+ korean = nobreak_stretch,
+ chinese = nobreak_stretch,
+ hiragana = nobreak_stretch,
+ katakana = nobreak_stretch,
+ half_width_open = nobreak_stretch_break_autoshrink,
+ half_width_close = nobreak_stretch,
+ full_width_open = nobreak_stretch_break_shrink,
+ full_width_close = nobreak_stretch,
+ full_width_punct = nobreak_stretch,
+ hyphen = nobreak_stretch,
+ non_starter = nobreak_stretch,
+ other = nobreak_stretch,
+}
+
+local chinese_7 = {
+ jami_initial = nobreak_shrink_break_stretch,
+ korean = nobreak_shrink_break_stretch,
+ chinese = stretch_break, -- nobreak_shrink_break_stretch,
+ hiragana = stretch_break, -- nobreak_shrink_break_stretch,
+ katakana = stretch_break, -- nobreak_shrink_break_stretch,
+ half_width_open = nobreak_shrink_break_stretch_nobreak_autoshrink,
+ half_width_close = nobreak_shrink_nobreak_stretch,
+ full_width_open = nobreak_shrink_break_stretch_nobreak_shrink,
+ full_width_close = nobreak_shrink_nobreak_stretch,
+ full_width_punct = nobreak_shrink_nobreak_stretch,
+ hyphen = nobreak_shrink_break_stretch,
+ non_starter = nobreak_shrink_break_stretch,
+ other = nobreak_shrink_break_stretch,
+}
+
+local chinese_8 = {
+ jami_initial = nobreak_shrink_break_stretch,
+ korean = nobreak_autoshrink_break_stretch,
+ chinese = stretch_break, -- nobreak_autoshrink_break_stretch,
+ hiragana = stretch_break, -- nobreak_autoshrink_break_stretch,
+ katakana = stretch_break, -- nobreak_autoshrink_break_stretch,
+ half_width_open = nobreak_autoshrink_break_stretch_nobreak_autoshrink,
+ half_width_close = nobreak_autoshrink_nobreak_stretch,
+ full_width_open = nobreak_autoshrink_break_stretch_nobreak_shrink,
+ full_width_close = nobreak_autoshrink_nobreak_stretch,
+ full_width_punct = nobreak_autoshrink_nobreak_stretch,
+ hyphen = nobreak_autoshrink_break_stretch,
+ non_starter = nobreak_autoshrink_break_stretch,
+ other = nobreak_autoshrink_break_stretch,
+}
+
+local injectors = { -- [previous] [current]
+ jamo_final = chinese_1,
+ korean = chinese_1,
+ chinese = chinese_2,
+ hiragana = chinese_2,
+ katakana = chinese_2,
+ hyphen = chinese_3,
+ start = chinese_4,
+ other = chinese_5,
+ non_starter = chinese_5,
+ full_width_open = chinese_6,
+ half_width_open = chinese_6,
+ full_width_close = chinese_7,
+ full_width_punct = chinese_7,
+ half_width_close = chinese_8,
+}
+
+local function process(head,first,last)
+ if first ~= last then
+ local lastfont, previous, last = nil, "start", nil
+ while true do
+ local upcoming, id = first.next, first.id
+ if id == glyph_code then
+ local a = first[a_scriptstatus]
+ local current = numbertocategory[a]
+ local action = injectors[previous]
+ if action then
+ action = action[current]
+ if action then
+ local font = first.font
+ if font ~= lastfont then
+ lastfont = font
+ set_parameters(font,numbertodataset[first[a_scriptinjection]])
+ end
+ action(head,first)
+ end
+ end
+ previous = current
+ else -- glue
+ local p, n = first.prev, upcoming
+ if p and n then
+ local pid, nid = p.id, n.id
+ if pid == glyph_code and nid == glyph_code then
+ local pa, na = p[a_scriptstatus], n[a_scriptstatus]
+ local pcjk, ncjk = pa and numbertocategory[pa], na and numbertocategory[na]
+ if not pcjk or not ncjk
+ or pcjk == "korean" or ncjk == "korean"
+ or pcjk == "other" or ncjk == "other"
+ or pcjk == "jamo_final" or ncjk == "jamo_initial"
+ or pcjk == "half_width_close" or ncjk == "half_width_open" then -- extra compared to korean
+ previous = "start"
+ else -- if head ~= first then
+ remove_node(head,first,true)
+ previous = pcjk
+ -- else
+ -- previous = pcjk
+ end
+ else
+ previous = "start"
+ end
+ else
+ previous = "start"
+ end
+ end
+ if upcoming == last then -- was stop
+ break
+ else
+ first = upcoming
+ end
+ end
+ end
+end
+
+scripts.installmethod {
+ name = "hanzi",
+ injector = process,
+ datasets = {
+ default = {
+ inter_char_shrink_factor = 0.50, -- of quad
+ inter_char_stretch_factor = 0.50, -- of quad
+ inter_char_half_shrink_factor = 0.50, -- of quad
+ inter_char_half_stretch_factor = 0.50, -- of quad
+ inter_char_quarter_shrink_factor = 0.50, -- of quad
+ inter_char_quarter_stretch_factor = 0.50, -- of quad
+ inter_char_hangul_penalty = 50,
+ },
+ },
+}
+
+-- Japanese: idiographic, hiragana, katakana, romanji / jis
+
+local japanese_0 = {
+}
+
+local japanese_1 = {
+ jamo_initial = korean_break,
+ korean = korean_break,
+ chinese = stretch_break,
+ hiragana = stretch_break,
+ katakana = stretch_break,
+ half_width_open = nobreak_stretch_break_autoshrink,
+ half_width_close = nobreak_stretch,
+ full_width_open = nobreak_stretch_break_shrink,
+ full_width_close = nobreak_stretch,
+ full_width_punct = nobreak_stretch,
+-- hyphen = nil,
+ non_starter = nobreak_stretch,
+ other = stretch_break,
+}
+
+local japanese_2 = {
+ jamo_initial = korean_break,
+ korean = stretch_break,
+ chinese = stretch_break,
+ hiragana = stretch_break,
+ katakana = stretch_break,
+ half_width_open = nobreak_stretch_break_autoshrink,
+ half_width_close = nobreak_stretch,
+ full_width_open = nobreak_stretch_break_shrink,
+ full_width_close = nobreak_stretch,
+ full_width_punct = japanese_before_full_width_punct, -- nobreak_stretch,
+ hyphen = nobreak_stretch,
+ non_starter = nobreak_stretch,
+ other = stretch_break,
+}
+
+local japanese_3 = {
+ jamo_initial = korean_break,
+ korean = stretch_break,
+ chinese = stretch_break,
+ hiragana = stretch_break,
+ katakana = stretch_break,
+ half_width_open = nobreak_stretch_break_autoshrink,
+ half_width_close = nobreak_stretch,
+ full_width_open = nobreak_stretch_break_shrink,
+ full_width_close = nobreak_stretch,
+ full_width_punct = nobreak_stretch,
+-- hyphen = nil,
+ non_starter = nobreak_stretch,
+ other = stretch_break,
+}
+
+local japanese_4 = {
+-- jamo_initial = nil,
+-- korean = nil,
+-- chinese = nil,
+-- hiragana = nil,
+-- katakana = nil,
+ half_width_open = nobreak_autoshrink,
+ half_width_close = nil,
+ full_width_open = nobreak_shrink,
+ full_width_close = nobreak,
+ full_width_punct = nobreak,
+-- hyphen = nil,
+ non_starter = nobreak,
+-- other = nil,
+}
+
+local japanese_5 = {
+ jamo_initial = stretch_break,
+ korean = stretch_break,
+ chinese = stretch_break,
+ hiragana = stretch_break,
+ katakana = stretch_break,
+ half_width_open = nobreak_stretch_break_autoshrink,
+ half_width_close = nobreak_stretch,
+ full_width_open = nobreak_stretch_break_shrink,
+ full_width_close = nobreak_stretch,
+ full_width_punct = nobreak_stretch,
+-- hyphen = nil,
+ non_starter = nobreak_stretch,
+ other = stretch_break,
+}
+
+local japanese_6 = {
+ jamo_initial = nobreak_stretch,
+ korean = nobreak_stretch,
+ chinese = nobreak_stretch,
+ hiragana = nobreak_stretch,
+ katakana = nobreak_stretch,
+ half_width_open = nobreak_stretch_break_autoshrink,
+ half_width_close = nobreak_stretch,
+ full_width_open = nobreak_stretch_break_shrink,
+ full_width_close = nobreak_stretch,
+ full_width_punct = nobreak_stretch,
+ hyphen = nobreak_stretch,
+ non_starter = nobreak_stretch,
+ other = nobreak_stretch,
+}
+
+local japanese_7 = {
+ jami_initial = nobreak_shrink_break_stretch,
+ korean = nobreak_shrink_break_stretch,
+ chinese = japanese_after_full_width_punct, -- stretch_break
+ hiragana = japanese_after_full_width_punct, -- stretch_break
+ katakana = japanese_after_full_width_punct, -- stretch_break
+ half_width_open = nobreak_shrink_break_stretch_nobreak_autoshrink,
+ half_width_close = nobreak_shrink_nobreak_stretch,
+ full_width_open = japanese_between_full_close_open, -- !!
+ full_width_close = japanese_between_full_close_full_close, -- nobreak_shrink_nobreak_stretch,
+ full_width_punct = nobreak_shrink_nobreak_stretch,
+ hyphen = nobreak_shrink_break_stretch,
+ non_starter = nobreak_shrink_break_stretch,
+ other = nobreak_shrink_break_stretch,
+}
+
+local japanese_8 = {
+ jami_initial = nobreak_shrink_break_stretch,
+ korean = nobreak_autoshrink_break_stretch,
+ chinese = stretch_break,
+ hiragana = stretch_break,
+ katakana = stretch_break,
+ half_width_open = nobreak_autoshrink_break_stretch_nobreak_autoshrink,
+ half_width_close = nobreak_autoshrink_nobreak_stretch,
+ full_width_open = nobreak_autoshrink_break_stretch_nobreak_shrink,
+ full_width_close = nobreak_autoshrink_nobreak_stretch,
+ full_width_punct = nobreak_autoshrink_nobreak_stretch,
+ hyphen = nobreak_autoshrink_break_stretch,
+ non_starter = nobreak_autoshrink_break_stretch,
+ other = nobreak_autoshrink_break_stretch,
+}
+
+local injectors = { -- [previous] [current]
+ jamo_final = japanese_1,
+ korean = japanese_1,
+ chinese = japanese_2,
+ hiragana = japanese_2,
+ katakana = japanese_2,
+ hyphen = japanese_3,
+ start = japanese_4,
+ other = japanese_5,
+ non_starter = japanese_5,
+ full_width_open = japanese_6,
+ half_width_open = japanese_6,
+ full_width_close = japanese_7,
+ full_width_punct = japanese_7,
+ half_width_close = japanese_8,
+}
+
+local function process(head,first,last)
+ if first ~= last then
+ local lastfont, previous, last = nil, "start", nil
+ while true do
+ local upcoming, id = first.next, first.id
+ if id == glyph_code then
+ local a = first[a_scriptstatus]
+ local current = numbertocategory[a]
+ local action = injectors[previous]
+ if action then
+ action = action[current]
+ if action then
+ local font = first.font
+ if font ~= lastfont then
+ lastfont = font
+ set_parameters(font,numbertodataset[first[a_scriptinjection]])
+ end
+ action(head,first)
+ end
+ end
+ previous = current
+
+-- elseif id == math_code then
+-- upcoming = end_of_math(current).next
+-- previous = "start"
+
+ else -- glue
+ local p, n = first.prev, upcoming -- we should remember prev
+ if p and n then
+ local pid, nid = p.id, n.id
+ if pid == glyph_code and nid == glyph_code then
+ local pa, na = p[a_scriptstatus], n[a_scriptstatus]
+ local pcjk, ncjk = pa and numbertocategory[pa], na and numbertocategory[na]
+ if not pcjk or not ncjk
+ or pcjk == "korean" or ncjk == "korean"
+ or pcjk == "other" or ncjk == "other"
+ or pcjk == "jamo_final" or ncjk == "jamo_initial"
+ or pcjk == "half_width_close" or ncjk == "half_width_open" then -- extra compared to korean
+ previous = "start"
+ else -- if head ~= first then
+if id == glue_code and first.subtype == userskip_code then -- also scriptstatus check?
+ -- for the moment no distinction possible between space and userskip
+ local w = first.spec.width
+ local s = spacedata[p.font]
+ if w == s then -- could be option
+ if trace_details then
+ trace_detail_between(p,n,"space removed")
+ end
+ remove_node(head,first,true)
+ end
+end
+ previous = pcjk
+ -- else
+ -- previous = pcjk
+ end
+ else
+ previous = "start"
+ end
+ else
+ previous = "start"
+ end
+ end
+ if upcoming == last then -- was stop
+ break
+ else
+ first = upcoming
+ end
+ end
+ end
+end
+
+scripts.installmethod {
+ name = "nihongo", -- what name to use?
+ injector = process,
+ datasets = {
+ default = {
+ inter_char_shrink_factor = 0.50, -- of quad
+ inter_char_stretch_factor = 0.50, -- of quad
+ inter_char_half_shrink_factor = 0.50, -- of quad
+ inter_char_half_stretch_factor = 0.50, -- of quad
+ inter_char_quarter_shrink_factor = 0.25, -- of quad
+ inter_char_quarter_stretch_factor = 0.25, -- of quad
+ inter_char_hangul_penalty = 50,
+ },
+ },
+}
+
diff --git a/tex/context/base/scrp-eth.lua b/tex/context/base/scrp-eth.lua
index 20b00a0ec..597afa1b5 100644
--- a/tex/context/base/scrp-eth.lua
+++ b/tex/context/base/scrp-eth.lua
@@ -1,150 +1,150 @@
-if not modules then modules = { } end modules ['scrp-eth'] = {
- version = 1.001,
- comment = "companion to scrp-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 review the script code but for the moment we
--- do it this way; so space settings like with cjk yet
-
-local insert_node_before = node.insert_before
-
-local nodepool = nodes.pool
-
-local new_glue = nodepool.glue
-local new_penalty = nodepool.penalty
-
-local nodecodes = nodes.nodecodes
-local glyph_code = nodecodes.glyph
-
-local a_scriptstatus = attributes.private('scriptstatus')
-local a_scriptinjection = attributes.private('scriptinjection')
-
-local categorytonumber = scripts.categorytonumber
-local numbertocategory = scripts.numbertocategory
-local hash = scripts.hash
-local numbertodataset = scripts.numbertodataset
-
-local fonthashes = fonts.hashes
-local parameters = fonthashes.parameters
-
-local space, stretch, shrink, lastfont
-
-local inter_character_space_factor = 1
-local inter_character_stretch_factor = 1
-local inter_character_shrink_factor = 1
-
-local function space_glue(current)
- local data = numbertodataset[current[a_scriptinjection]]
- if data then
- inter_character_space_factor = data.inter_character_space_factor or 1
- inter_character_stretch_factor = data.inter_character_stretch_factor or 1
- inter_character_shrink_factor = data.inter_character_shrink_factor or 1
- end
- local font = current.font
- if lastfont ~= font then
- local pf = parameters[font]
- space = pf.space
- stretch = pf.space_stretch
- shrink = pf.space_shrink
- lastfont = font
- end
- return new_glue(
- inter_character_space_factor * space,
- inter_character_stretch_factor * stretch,
- inter_character_shrink_factor * shrink
- )
-end
-
-local function insert_space(head,current)
- insert_node_before(head,current,space_glue(current))
-end
-
-local function insert_zerowidthspace(head,current)
- insert_node_before(head,current,new_glue(0))
-end
-
-local function insert_nobreakspace(head,current)
- insert_node_before(head,current,new_penalty(10000))
- insert_node_before(head,current,space_glue(current))
-end
-
--- syllable [zerowidthspace] syllable
--- syllable [zerowidthspace] word
--- syllable [zerowidthspace] sentence
--- word [nobreakspace] syllable
--- word [space] word
--- word [space] sentence
--- sentence [nobreakspace] syllable
--- sentence [space] word
--- sentence [space] sentence
-
-local injectors = { -- [previous] [current]
- ethiopic_syllable = {
- ethiopic_syllable = insert_zerowidthspace,
- ethiopic_word = insert_nobreakspace,
- ethiopic_sentence = insert_nobreakspace,
- },
- ethiopic_word = {
- ethiopic_syllable = insert_space,
- ethiopic_word = insert_space,
- ethiopic_sentence = insert_space,
- },
- ethiopic_sentence = {
- ethiopic_syllable = insert_space,
- ethiopic_word = insert_space,
- ethiopic_sentence = insert_space,
- },
-}
-
-local function process(head,first,last)
- if first ~= last then
- local injector = false
- local current = first
- while current do
- local id = current.id
- if id == glyph_code then
- local scriptstatus = current[a_scriptstatus]
- local category = numbertocategory[scriptstatus]
- if injector then
- local action = injector[category]
- if action then
- action(head,current)
- end
- end
- injector = injectors[category]
- else
- -- nothing yet
- end
- if current == last then
- break
- else
- current = current.next
- end
- end
- end
-end
-
-scripts.installmethod {
- name = "ethiopic",
- injector = process,
- datasets = {
- default = {
- inter_character_space_factor = 1,
- inter_character_stretch_factor = 1,
- inter_character_shrink_factor = 1,
- },
- half = {
- inter_character_space_factor = 0.5,
- inter_character_stretch_factor = 0.5,
- inter_character_shrink_factor = 0.5,
- },
- quarter = {
- inter_character_space_factor = 0.25,
- inter_character_stretch_factor = 0.25,
- inter_character_shrink_factor = 0.25,
- },
- },
-}
+if not modules then modules = { } end modules ['scrp-eth'] = {
+ version = 1.001,
+ comment = "companion to scrp-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 review the script code but for the moment we
+-- do it this way; so space settings like with cjk yet
+
+local insert_node_before = node.insert_before
+
+local nodepool = nodes.pool
+
+local new_glue = nodepool.glue
+local new_penalty = nodepool.penalty
+
+local nodecodes = nodes.nodecodes
+local glyph_code = nodecodes.glyph
+
+local a_scriptstatus = attributes.private('scriptstatus')
+local a_scriptinjection = attributes.private('scriptinjection')
+
+local categorytonumber = scripts.categorytonumber
+local numbertocategory = scripts.numbertocategory
+local hash = scripts.hash
+local numbertodataset = scripts.numbertodataset
+
+local fonthashes = fonts.hashes
+local parameters = fonthashes.parameters
+
+local space, stretch, shrink, lastfont
+
+local inter_character_space_factor = 1
+local inter_character_stretch_factor = 1
+local inter_character_shrink_factor = 1
+
+local function space_glue(current)
+ local data = numbertodataset[current[a_scriptinjection]]
+ if data then
+ inter_character_space_factor = data.inter_character_space_factor or 1
+ inter_character_stretch_factor = data.inter_character_stretch_factor or 1
+ inter_character_shrink_factor = data.inter_character_shrink_factor or 1
+ end
+ local font = current.font
+ if lastfont ~= font then
+ local pf = parameters[font]
+ space = pf.space
+ stretch = pf.space_stretch
+ shrink = pf.space_shrink
+ lastfont = font
+ end
+ return new_glue(
+ inter_character_space_factor * space,
+ inter_character_stretch_factor * stretch,
+ inter_character_shrink_factor * shrink
+ )
+end
+
+local function insert_space(head,current)
+ insert_node_before(head,current,space_glue(current))
+end
+
+local function insert_zerowidthspace(head,current)
+ insert_node_before(head,current,new_glue(0))
+end
+
+local function insert_nobreakspace(head,current)
+ insert_node_before(head,current,new_penalty(10000))
+ insert_node_before(head,current,space_glue(current))
+end
+
+-- syllable [zerowidthspace] syllable
+-- syllable [zerowidthspace] word
+-- syllable [zerowidthspace] sentence
+-- word [nobreakspace] syllable
+-- word [space] word
+-- word [space] sentence
+-- sentence [nobreakspace] syllable
+-- sentence [space] word
+-- sentence [space] sentence
+
+local injectors = { -- [previous] [current]
+ ethiopic_syllable = {
+ ethiopic_syllable = insert_zerowidthspace,
+ ethiopic_word = insert_nobreakspace,
+ ethiopic_sentence = insert_nobreakspace,
+ },
+ ethiopic_word = {
+ ethiopic_syllable = insert_space,
+ ethiopic_word = insert_space,
+ ethiopic_sentence = insert_space,
+ },
+ ethiopic_sentence = {
+ ethiopic_syllable = insert_space,
+ ethiopic_word = insert_space,
+ ethiopic_sentence = insert_space,
+ },
+}
+
+local function process(head,first,last)
+ if first ~= last then
+ local injector = false
+ local current = first
+ while current do
+ local id = current.id
+ if id == glyph_code then
+ local scriptstatus = current[a_scriptstatus]
+ local category = numbertocategory[scriptstatus]
+ if injector then
+ local action = injector[category]
+ if action then
+ action(head,current)
+ end
+ end
+ injector = injectors[category]
+ else
+ -- nothing yet
+ end
+ if current == last then
+ break
+ else
+ current = current.next
+ end
+ end
+ end
+end
+
+scripts.installmethod {
+ name = "ethiopic",
+ injector = process,
+ datasets = {
+ default = {
+ inter_character_space_factor = 1,
+ inter_character_stretch_factor = 1,
+ inter_character_shrink_factor = 1,
+ },
+ half = {
+ inter_character_space_factor = 0.5,
+ inter_character_stretch_factor = 0.5,
+ inter_character_shrink_factor = 0.5,
+ },
+ quarter = {
+ inter_character_space_factor = 0.25,
+ inter_character_stretch_factor = 0.25,
+ inter_character_shrink_factor = 0.25,
+ },
+ },
+}
diff --git a/tex/context/base/scrp-ini.lua b/tex/context/base/scrp-ini.lua
index fbe673db9..18f86475f 100644
--- a/tex/context/base/scrp-ini.lua
+++ b/tex/context/base/scrp-ini.lua
@@ -1,634 +1,634 @@
-if not modules then modules = { } end modules ['scrp-ini'] = {
- version = 1.001,
- comment = "companion to scrp-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- We need to rewrite this a bit ... rather old code ... will be done when japanese
--- is finished.
-
-local attributes, nodes, node = attributes, nodes, node
-
-local trace_analyzing = false trackers.register("scripts.analyzing", function(v) trace_analyzing = v end)
-local trace_injections = false trackers.register("scripts.injections", function(v) trace_injections = v end)
-
-local report_preprocessing = logs.reporter("scripts","preprocessing")
-
-local utfchar = utf.char
-
-local first_glyph = node.first_glyph or node.first_character
-local traverse_id = node.traverse_id
-
-local texsetattribute = tex.setattribute
-
-local nodecodes = nodes.nodecodes
-local unsetvalue = attributes.unsetvalue
-
-local glyph_code = nodecodes.glyph
-local glue_code = nodecodes.glue
-
-local a_scriptinjection = attributes.private('scriptinjection')
-local a_scriptsplitting = attributes.private('scriptsplitting')
-local a_scriptstatus = attributes.private('scriptstatus')
-
-local fontdata = fonts.hashes.identifiers
-local allocate = utilities.storage.allocate
-local setnodecolor = nodes.tracers.colors.set
-local setmetatableindex = table.setmetatableindex
-
-local enableaction = nodes.tasks.enableaction
-local disableaction = nodes.tasks.disableaction
-
-scripts = scripts or { }
-local scripts = scripts
-
-scripts.hash = scripts.hash or { }
-local hash = scripts.hash
-
-local handlers = allocate()
-scripts.handlers = handlers
-
-local injectors = allocate()
-scripts.injectors = handlers
-
-local splitters = allocate()
-scripts.splitters = splitters
-
-local hash = { -- we could put these presets in char-def.lua
- --
- -- half width opening parenthesis
- --
- [0x0028] = "half_width_open",
- [0x005B] = "half_width_open",
- [0x007B] = "half_width_open",
- [0x2018] = "half_width_open", -- ‘
- [0x201C] = "half_width_open", -- “
- --
- -- full width opening parenthesis
- --
- [0x3008] = "full_width_open", -- 〈 Left book quote
- [0x300A] = "full_width_open", -- 《 Left double book quote
- [0x300C] = "full_width_open", -- 「 left quote
- [0x300E] = "full_width_open", -- 『 left double quote
- [0x3010] = "full_width_open", -- 【 left double book quote
- [0x3014] = "full_width_open", -- 〔 left book quote
- [0x3016] = "full_width_open", --〖 left double book quote
- [0x3018] = "full_width_open", -- left tortoise bracket
- [0x301A] = "full_width_open", -- left square bracket
- [0x301D] = "full_width_open", -- reverse double prime qm
- [0xFF08] = "full_width_open", -- ( left parenthesis
- [0xFF3B] = "full_width_open", -- [ left square brackets
- [0xFF5B] = "full_width_open", -- { left curve bracket
- --
- -- half width closing parenthesis
- --
- [0x0029] = "half_width_close",
- [0x005D] = "half_width_close",
- [0x007D] = "half_width_close",
- [0x2019] = "half_width_close", -- ’ right quote, right
- [0x201D] = "half_width_close", -- ” right double quote
- --
- -- full width closing parenthesis
- --
- [0x3009] = "full_width_close", -- 〉 book quote
- [0x300B] = "full_width_close", -- 》 double book quote
- [0x300D] = "full_width_close", -- 」 right quote, right
- [0x300F] = "full_width_close", -- 』 right double quote
- [0x3011] = "full_width_close", -- 】 right double book quote
- [0x3015] = "full_width_close", -- 〕 right book quote
- [0x3017] = "full_width_close", -- 〗 right double book quote
- [0x3019] = "full_width_close", -- right tortoise bracket
- [0x301B] = "full_width_close", -- right square bracket
- [0x301E] = "full_width_close", -- double prime qm
- [0x301F] = "full_width_close", -- low double prime qm
- [0xFF09] = "full_width_close", -- ) right parenthesis
- [0xFF3D] = "full_width_close", -- ] right square brackets
- [0xFF5D] = "full_width_close", -- } right curve brackets
- --
- [0xFF62] = "half_width_open", -- left corner bracket
- [0xFF63] = "half_width_close", -- right corner bracket
- --
- -- vertical opening vertical
- --
- -- 0xFE35, 0xFE37, 0xFE39, 0xFE3B, 0xFE3D, 0xFE3F, 0xFE41, 0xFE43, 0xFE47,
- --
- -- vertical closing
- --
- -- 0xFE36, 0xFE38, 0xFE3A, 0xFE3C, 0xFE3E, 0xFE40, 0xFE42, 0xFE44, 0xFE48,
- --
- -- half width opening punctuation
- --
- -- <empty>
- --
- -- full width opening punctuation
- --
- -- 0x2236, -- ∶
- -- 0xFF0C, -- ,
- --
- -- half width closing punctuation_hw
- --
- [0x0021] = "half_width_close", -- !
- [0x002C] = "half_width_close", -- ,
- [0x002E] = "half_width_close", -- .
- [0x003A] = "half_width_close", -- :
- [0x003B] = "half_width_close", -- ;
- [0x003F] = "half_width_close", -- ?
- [0xFF61] = "half_width_close", -- hw full stop
- --
- -- full width closing punctuation
- --
- [0x3001] = "full_width_close", -- 、
- [0x3002] = "full_width_close", -- 。
- [0xFF0C] = "full_width_close", -- ,
- [0xFF0E] = "full_width_close", --
- --
- -- depends on font
- --
- [0xFF01] = "full_width_close", -- !
- [0xFF1F] = "full_width_close", -- ?
- --
- [0xFF1A] = "full_width_punct", -- :
- [0xFF1B] = "full_width_punct", -- ;
- --
- -- non starter
- --
- [0x3005] = "non_starter", [0x3041] = "non_starter", [0x3043] = "non_starter", [0x3045] = "non_starter", [0x3047] = "non_starter",
- [0x3049] = "non_starter", [0x3063] = "non_starter", [0x3083] = "non_starter", [0x3085] = "non_starter", [0x3087] = "non_starter",
- [0x308E] = "non_starter", [0x3095] = "non_starter", [0x3096] = "non_starter", [0x309B] = "non_starter", [0x309C] = "non_starter",
- [0x309D] = "non_starter", [0x309E] = "non_starter", [0x30A0] = "non_starter", [0x30A1] = "non_starter", [0x30A3] = "non_starter",
- [0x30A5] = "non_starter", [0x30A7] = "non_starter", [0x30A9] = "non_starter", [0x30C3] = "non_starter", [0x30E3] = "non_starter",
- [0x30E5] = "non_starter", [0x30E7] = "non_starter", [0x30EE] = "non_starter", [0x30F5] = "non_starter", [0x30F6] = "non_starter",
- [0x30FC] = "non_starter", [0x30FD] = "non_starter", [0x30FE] = "non_starter", [0x31F0] = "non_starter", [0x31F1] = "non_starter",
- [0x30F2] = "non_starter", [0x30F3] = "non_starter", [0x30F4] = "non_starter", [0x31F5] = "non_starter", [0x31F6] = "non_starter",
- [0x30F7] = "non_starter", [0x30F8] = "non_starter", [0x30F9] = "non_starter", [0x31FA] = "non_starter", [0x31FB] = "non_starter",
- [0x30FC] = "non_starter", [0x30FD] = "non_starter", [0x30FE] = "non_starter", [0x31FF] = "non_starter",
- --
- -- hyphenation
- --
- [0x2026] = "hyphen", -- … ellipsis
- [0x2014] = "hyphen", -- — hyphen
- --
- [0x1361] = "ethiopic_word",
- [0x1362] = "ethiopic_sentence",
- --
-}
-
-local function provide(t,k)
- local v
- if not tonumber(k) then v = false
- elseif (k >= 0x03040 and k <= 0x030FF)
- or (k >= 0x031F0 and k <= 0x031FF)
- or (k >= 0x032D0 and k <= 0x032FE)
- or (k >= 0x0FF00 and k <= 0x0FFEF) then v = "katakana"
- elseif (k >= 0x03400 and k <= 0x04DFF)
- or (k >= 0x04E00 and k <= 0x09FFF)
- or (k >= 0x0F900 and k <= 0x0FAFF)
- or (k >= 0x20000 and k <= 0x2A6DF)
- or (k >= 0x2F800 and k <= 0x2FA1F) then v = "chinese"
- elseif (k >= 0x0AC00 and k <= 0x0D7A3) then v = "korean"
- elseif (k >= 0x01100 and k <= 0x0115F) then v = "jamo_initial"
- elseif (k >= 0x01160 and k <= 0x011A7) then v = "jamo_medial"
- elseif (k >= 0x011A8 and k <= 0x011FF) then v = "jamo_final"
- elseif (k >= 0x01200 and k <= 0x0139F) then v = "ethiopic_syllable"
- else v = false
- end
- t[k] = v
- return v
-end
-
-setmetatableindex(hash,provide)
-
-scripts.hash = hash
-
-local numbertodataset = allocate()
-local numbertohandler = allocate()
-
---~ storage.register("scripts/hash", hash, "scripts.hash")
-
-scripts.numbertodataset = numbertodataset
-scripts.numbertohandler = numbertohandler
-
-local defaults = {
- inter_char_shrink_factor = 0,
- inter_char_shrink_factor = 0,
- inter_char_stretch_factor = 0,
- inter_char_half_shrink_factor = 0,
- inter_char_half_stretch_factor = 0,
- inter_char_quarter_shrink_factor = 0,
- inter_char_quarter_stretch_factor = 0,
- inter_char_hangul_penalty = 0,
-
- inter_word_stretch_factor = 0,
-}
-
-scripts.defaults = defaults -- so we can add more
-
-function scripts.installmethod(handler)
- local name = handler.name
- handlers[name] = handler
- local attributes = { }
- local datasets = handler.datasets
- if not datasets or not datasets.default then
- report_preprocessing("missing (default) dataset in script %a",name)
- datasets.default = { } -- slower but an error anyway
- end
- for k, v in next, datasets do
- setmetatableindex(v,defaults)
- end
- setmetatable(attributes, {
- __index = function(t,k)
- local v = datasets[k] or datasets.default
- local a = unsetvalue
- if v then
- v.name = name -- for tracing
- a = #numbertodataset + 1
- numbertodataset[a] = v
- numbertohandler[a] = handler
- end
- t[k] = a
- return a
- end
- } )
- handler.attributes = attributes
-end
-
-function scripts.installdataset(specification) -- global overload
- local method = specification.method
- local name = specification.name
- local dataset = specification.dataset
- if method and name and dataset then
- local parent = specification.parent or ""
- local handler = handlers[method]
- if handler then
- local datasets = handler.datasets
- if datasets then
- local defaultset = datasets.default
- if defaultset then
- if parent ~= "" then
- local p = datasets[parent]
- if p then
- defaultset = p
- else
- report_preprocessing("dataset, unknown parent %a for method %a",parent,method)
- end
- end
- setmetatable(dataset,defaultset)
- local existing = datasets[name]
- if existing then
- for k, v in next, existing do
- existing[k] = dataset
- end
- else
- datasets[name] = dataset
- end
- else
- report_preprocessing("dataset, no default for method %a",method)
- end
- else
- report_preprocessing("dataset, no datasets for method %a",method)
- end
- else
- report_preprocessing("dataset, no method %a",method)
- end
- else
- report_preprocessing("dataset, invalid specification") -- maybe report table
- end
-end
-
-local injectorenabled = false
-local splitterenabled = false
-
-function scripts.set(name,method,preset)
- local handler = handlers[method]
- if handler then
- if handler.injector then
- if not injectorenabled then
- enableaction("processors","scripts.injectors.handler")
- injectorenabled = true
- end
- texsetattribute(a_scriptinjection,handler.attributes[preset] or unsetvalue)
- end
- if handler.splitter then
- if not splitterenabled then
- enableaction("processors","scripts.splitters.handler")
- splitterenabled = true
- end
- texsetattribute(a_scriptsplitting,handler.attributes[preset] or unsetvalue)
- end
- if handler.initializer then
- handler.initializer(handler)
- handler.initializer = nil
- end
- else
- texsetattribute(a_scriptinjection,unsetvalue)
- texsetattribute(a_scriptsplitting,unsetvalue)
- end
-end
-
-function scripts.reset()
- texsetattribute(a_scriptinjection,unsetvalue)
- texsetattribute(a_scriptsplitting,unsetvalue)
-end
-
--- the following tables will become a proper installer (move to cjk/eth)
---
--- 0=gray 1=red 2=green 3=blue 4=yellow 5=magenta 6=cyan 7=x-yellow 8=x-magenta 9=x-cyan
-
-local scriptcolors = allocate { -- todo: just named colors
- korean = "trace:0",
- chinese = "trace:0",
- katakana = "trace:0",
- hiragana = "trace:0",
- full_width_open = "trace:1",
- full_width_close = "trace:2",
- half_width_open = "trace:3",
- half_width_close = "trace:4",
- full_width_punct = "trace:5",
- hyphen = "trace:5",
- non_starter = "trace:6",
- jamo_initial = "trace:7",
- jamo_medial = "trace:8",
- jamo_final = "trace:9",
- ethiopic_syllable = "trace:1",
- ethiopic_word = "trace:2",
- ethiopic_sentence = "trace:3",
-}
-
-scripts.colors = scriptcolors
-
-local numbertocategory = allocate { -- rather bound to cjk ... will be generalized
- "korean",
- "chinese",
- "katakana",
- "hiragana",
- "full_width_open",
- "full_width_close",
- "half_width_open",
- "half_width_close",
- "full_width_punct",
- "hyphen",
- "non_starter",
- "jamo_initial",
- "jamo_medial",
- "jamo_final",
- "ethiopic_syllable",
- "ethiopic_word",
- "ethiopic_sentence",
-}
-
-local categorytonumber = allocate(table.swapped(numbertocategory)) -- could be one table
-
-scripts.categorytonumber = categorytonumber
-scripts.numbertocategory = numbertocategory
-
-local function colorize(start,stop)
- for n in traverse_id(glyph_code,start) do
- local kind = numbertocategory[n[a_scriptstatus]]
- if kind then
- local ac = scriptcolors[kind]
- if ac then
- setnodecolor(n,ac)
- end
- end
- if n == stop then
- break
- end
- end
-end
-
-local function traced_process(head,first,last,process,a)
- if start ~= last then
- local f, l = first, last
- local name = numbertodataset[a]
- name = name and name.name or "?"
- report_preprocessing("before %s: %s",name,nodes.tosequence(f,l))
- process(head,first,last)
- report_preprocessing("after %s: %s", name,nodes.tosequence(f,l))
- end
-end
-
--- eventually we might end up with more extensive parsing
--- todo: pass t[start..stop] == original
---
--- one of the time consuming functions:
-
--- we can have a fonts.hashes.originals
-
-function scripts.injectors.handler(head)
- local start = first_glyph(head) -- we already have glyphs here (subtype 1)
- if not start then
- return head, false
- else
- local last_a, normal_process, lastfont, originals = nil, nil, nil, nil
- local done, first, last, ok = false, nil, nil, false
- while start do
- local id = start.id
- if id == glyph_code then
- local a = start[a_scriptinjection]
- if a then
- if a ~= last_a then
- if first then
- if ok then
- if trace_analyzing then
- colorize(first,last)
- end
- if trace_injections then
- traced_process(head,first,last,normal_process,last_a)
- else
- normal_process(head,first,last)
- end
- ok, done = false, true
- end
- first, last = nil, nil
- end
- last_a = a
- local handler = numbertohandler[a]
- normal_process = handler.injector
- end
- if normal_process then
- local f = start.font
- if f ~= lastfont then
- originals = fontdata[f].resources
- if resources then
- originals = resources.originals
- else
- -- can't happen
- end
- lastfont = f
- end
- local c = start.char
- if originals then
- c = originals[c] or c
- end
- local h = hash[c]
- if h then
- start[a_scriptstatus] = categorytonumber[h]
- if not first then
- first, last = start, start
- else
- last = start
- end
- -- if cjk == "chinese" or cjk == "korean" then -- we need to prevent too much ( ) processing
- ok = true
- -- end
- elseif first then
- if ok then
- if trace_analyzing then
- colorize(first,last)
- end
- if trace_injections then
- traced_process(head,first,last,normal_process,last_a)
- else
- normal_process(head,first,last)
- end
- ok, done = false, true
- end
- first, last = nil, nil
- end
- end
- elseif first then
- if ok then
- if trace_analyzing then
- colorize(first,last)
- end
- if trace_injections then
- traced_process(head,first,last,normal_process,last_a)
- else
- normal_process(head,first,last)
- end
- ok, done = false, true
- end
- first, last = nil, nil
- end
- elseif id == glue_code then
- if ok then
- -- continue
- elseif first then
- -- no chinese or korean
- first, last = nil, nil
- end
- elseif first then
- if ok then
- -- some chinese or korean
- if trace_analyzing then
- colorize(first,last)
- end
- if trace_injections then
- traced_process(head,first,last,normal_process,last_a)
- else
- normal_process(head,first,last)
- end
- first, last, ok, done = nil, nil, false, true
- elseif first then
- first, last = nil, nil
- end
- end
- start = start.next
- end
- if ok then
- if trace_analyzing then
- colorize(first,last)
- end
- if trace_injections then
- traced_process(head,first,last,normal_process,last_a)
- else
- normal_process(head,first,last)
- end
- done = true
- end
- return head, done
- end
-end
-
-function scripts.splitters.handler(head)
- return head, false
-end
-
--- new plugin:
-
-local registercontext = fonts.specifiers.registercontext
-local mergecontext = fonts.specifiers.mergecontext
-
-local otfscripts = characters.otfscripts
-
-local report_scripts = logs.reporter("scripts","auto feature")
-local trace_scripts = false trackers.register("scripts.autofeature",function(v) trace_scripts = v end)
-
-local autofontfeature = scripts.autofontfeature or { }
-scripts.autofontfeature = autofontfeature
-
-local cache_yes = { }
-local cache_nop = { }
-
-setmetatableindex(cache_yes,function(t,k) local v = { } t[k] = v return v end)
-setmetatableindex(cache_nop,function(t,k) local v = { } t[k] = v return v end)
-
--- beware: we need to tag a done (otherwise too many extra instances ... but how
--- often unpack? wait till we have a bitmap
---
--- we can consider merging this in handlers.characters(head) at some point as there
--- already check for the dynamic attribute so it saves a pass, however, then we also
--- need to check for a_scriptinjection there which nils the benefit
---
--- we can consider cheating: set all glyphs in a word as the first one but it's not
--- playing nice
-
-function autofontfeature.handler(head)
- for n in traverse_id(glyph_code,head) do
- -- if n[a_scriptinjection] then
- -- -- already tagged by script feature, maybe some day adapt
- -- else
- local char = n.char
- local script = otfscripts[char]
- if script then
- local dynamic = n[0] or 0
- local font = n.font
- if dynamic > 0 then
- local slot = cache_yes[font]
- local attr = slot[script]
- if not attr then
- attr = mergecontext(dynamic,name,2)
- slot[script] = attr
- if trace_scripts then
- report_scripts("script: %s, trigger %C, dynamic: %a, variant: %a",script,char,attr,"extended")
- end
- end
- if attr ~= 0 then
- n[0] = attr
- -- maybe set scriptinjection when associated
- end
- else
- local slot = cache_nop[font]
- local attr = slot[script]
- if not attr then
- attr = registercontext(font,script,2)
- slot[script] = attr
- if trace_scripts then
- report_scripts("script: %s, trigger %C, dynamic: %s, variant: %a",script,char,attr,"normal")
- end
- end
- if attr ~= 0 then
- n[0] = attr
- -- maybe set scriptinjection when associated
- end
- end
- end
- -- end
- end
- return head
-end
-
-function autofontfeature.enable()
- report_scripts("globally enabled")
- enableaction("processors","scripts.autofontfeature.handler")
-end
-
-function autofontfeature.disable()
- report_scripts("globally disabled")
- disableaction("processors","scripts.autofontfeature.handler")
-end
-
-commands.enableautofontscript = autofontfeature.enable
-commands.disableautofontscript = autofontfeature.disable
+if not modules then modules = { } end modules ['scrp-ini'] = {
+ version = 1.001,
+ comment = "companion to scrp-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- We need to rewrite this a bit ... rather old code ... will be done when japanese
+-- is finished.
+
+local attributes, nodes, node = attributes, nodes, node
+
+local trace_analyzing = false trackers.register("scripts.analyzing", function(v) trace_analyzing = v end)
+local trace_injections = false trackers.register("scripts.injections", function(v) trace_injections = v end)
+
+local report_preprocessing = logs.reporter("scripts","preprocessing")
+
+local utfchar = utf.char
+
+local first_glyph = node.first_glyph or node.first_character
+local traverse_id = node.traverse_id
+
+local texsetattribute = tex.setattribute
+
+local nodecodes = nodes.nodecodes
+local unsetvalue = attributes.unsetvalue
+
+local glyph_code = nodecodes.glyph
+local glue_code = nodecodes.glue
+
+local a_scriptinjection = attributes.private('scriptinjection')
+local a_scriptsplitting = attributes.private('scriptsplitting')
+local a_scriptstatus = attributes.private('scriptstatus')
+
+local fontdata = fonts.hashes.identifiers
+local allocate = utilities.storage.allocate
+local setnodecolor = nodes.tracers.colors.set
+local setmetatableindex = table.setmetatableindex
+
+local enableaction = nodes.tasks.enableaction
+local disableaction = nodes.tasks.disableaction
+
+scripts = scripts or { }
+local scripts = scripts
+
+scripts.hash = scripts.hash or { }
+local hash = scripts.hash
+
+local handlers = allocate()
+scripts.handlers = handlers
+
+local injectors = allocate()
+scripts.injectors = handlers
+
+local splitters = allocate()
+scripts.splitters = splitters
+
+local hash = { -- we could put these presets in char-def.lua
+ --
+ -- half width opening parenthesis
+ --
+ [0x0028] = "half_width_open",
+ [0x005B] = "half_width_open",
+ [0x007B] = "half_width_open",
+ [0x2018] = "half_width_open", -- ‘
+ [0x201C] = "half_width_open", -- “
+ --
+ -- full width opening parenthesis
+ --
+ [0x3008] = "full_width_open", -- 〈 Left book quote
+ [0x300A] = "full_width_open", -- 《 Left double book quote
+ [0x300C] = "full_width_open", -- 「 left quote
+ [0x300E] = "full_width_open", -- 『 left double quote
+ [0x3010] = "full_width_open", -- 【 left double book quote
+ [0x3014] = "full_width_open", -- 〔 left book quote
+ [0x3016] = "full_width_open", --〖 left double book quote
+ [0x3018] = "full_width_open", -- left tortoise bracket
+ [0x301A] = "full_width_open", -- left square bracket
+ [0x301D] = "full_width_open", -- reverse double prime qm
+ [0xFF08] = "full_width_open", -- ( left parenthesis
+ [0xFF3B] = "full_width_open", -- [ left square brackets
+ [0xFF5B] = "full_width_open", -- { left curve bracket
+ --
+ -- half width closing parenthesis
+ --
+ [0x0029] = "half_width_close",
+ [0x005D] = "half_width_close",
+ [0x007D] = "half_width_close",
+ [0x2019] = "half_width_close", -- ’ right quote, right
+ [0x201D] = "half_width_close", -- ” right double quote
+ --
+ -- full width closing parenthesis
+ --
+ [0x3009] = "full_width_close", -- 〉 book quote
+ [0x300B] = "full_width_close", -- 》 double book quote
+ [0x300D] = "full_width_close", -- 」 right quote, right
+ [0x300F] = "full_width_close", -- 』 right double quote
+ [0x3011] = "full_width_close", -- 】 right double book quote
+ [0x3015] = "full_width_close", -- 〕 right book quote
+ [0x3017] = "full_width_close", -- 〗 right double book quote
+ [0x3019] = "full_width_close", -- right tortoise bracket
+ [0x301B] = "full_width_close", -- right square bracket
+ [0x301E] = "full_width_close", -- double prime qm
+ [0x301F] = "full_width_close", -- low double prime qm
+ [0xFF09] = "full_width_close", -- ) right parenthesis
+ [0xFF3D] = "full_width_close", -- ] right square brackets
+ [0xFF5D] = "full_width_close", -- } right curve brackets
+ --
+ [0xFF62] = "half_width_open", -- left corner bracket
+ [0xFF63] = "half_width_close", -- right corner bracket
+ --
+ -- vertical opening vertical
+ --
+ -- 0xFE35, 0xFE37, 0xFE39, 0xFE3B, 0xFE3D, 0xFE3F, 0xFE41, 0xFE43, 0xFE47,
+ --
+ -- vertical closing
+ --
+ -- 0xFE36, 0xFE38, 0xFE3A, 0xFE3C, 0xFE3E, 0xFE40, 0xFE42, 0xFE44, 0xFE48,
+ --
+ -- half width opening punctuation
+ --
+ -- <empty>
+ --
+ -- full width opening punctuation
+ --
+ -- 0x2236, -- ∶
+ -- 0xFF0C, -- ,
+ --
+ -- half width closing punctuation_hw
+ --
+ [0x0021] = "half_width_close", -- !
+ [0x002C] = "half_width_close", -- ,
+ [0x002E] = "half_width_close", -- .
+ [0x003A] = "half_width_close", -- :
+ [0x003B] = "half_width_close", -- ;
+ [0x003F] = "half_width_close", -- ?
+ [0xFF61] = "half_width_close", -- hw full stop
+ --
+ -- full width closing punctuation
+ --
+ [0x3001] = "full_width_close", -- 、
+ [0x3002] = "full_width_close", -- 。
+ [0xFF0C] = "full_width_close", -- ,
+ [0xFF0E] = "full_width_close", --
+ --
+ -- depends on font
+ --
+ [0xFF01] = "full_width_close", -- !
+ [0xFF1F] = "full_width_close", -- ?
+ --
+ [0xFF1A] = "full_width_punct", -- :
+ [0xFF1B] = "full_width_punct", -- ;
+ --
+ -- non starter
+ --
+ [0x3005] = "non_starter", [0x3041] = "non_starter", [0x3043] = "non_starter", [0x3045] = "non_starter", [0x3047] = "non_starter",
+ [0x3049] = "non_starter", [0x3063] = "non_starter", [0x3083] = "non_starter", [0x3085] = "non_starter", [0x3087] = "non_starter",
+ [0x308E] = "non_starter", [0x3095] = "non_starter", [0x3096] = "non_starter", [0x309B] = "non_starter", [0x309C] = "non_starter",
+ [0x309D] = "non_starter", [0x309E] = "non_starter", [0x30A0] = "non_starter", [0x30A1] = "non_starter", [0x30A3] = "non_starter",
+ [0x30A5] = "non_starter", [0x30A7] = "non_starter", [0x30A9] = "non_starter", [0x30C3] = "non_starter", [0x30E3] = "non_starter",
+ [0x30E5] = "non_starter", [0x30E7] = "non_starter", [0x30EE] = "non_starter", [0x30F5] = "non_starter", [0x30F6] = "non_starter",
+ [0x30FC] = "non_starter", [0x30FD] = "non_starter", [0x30FE] = "non_starter", [0x31F0] = "non_starter", [0x31F1] = "non_starter",
+ [0x30F2] = "non_starter", [0x30F3] = "non_starter", [0x30F4] = "non_starter", [0x31F5] = "non_starter", [0x31F6] = "non_starter",
+ [0x30F7] = "non_starter", [0x30F8] = "non_starter", [0x30F9] = "non_starter", [0x31FA] = "non_starter", [0x31FB] = "non_starter",
+ [0x30FC] = "non_starter", [0x30FD] = "non_starter", [0x30FE] = "non_starter", [0x31FF] = "non_starter",
+ --
+ -- hyphenation
+ --
+ [0x2026] = "hyphen", -- … ellipsis
+ [0x2014] = "hyphen", -- — hyphen
+ --
+ [0x1361] = "ethiopic_word",
+ [0x1362] = "ethiopic_sentence",
+ --
+}
+
+local function provide(t,k)
+ local v
+ if not tonumber(k) then v = false
+ elseif (k >= 0x03040 and k <= 0x030FF)
+ or (k >= 0x031F0 and k <= 0x031FF)
+ or (k >= 0x032D0 and k <= 0x032FE)
+ or (k >= 0x0FF00 and k <= 0x0FFEF) then v = "katakana"
+ elseif (k >= 0x03400 and k <= 0x04DFF)
+ or (k >= 0x04E00 and k <= 0x09FFF)
+ or (k >= 0x0F900 and k <= 0x0FAFF)
+ or (k >= 0x20000 and k <= 0x2A6DF)
+ or (k >= 0x2F800 and k <= 0x2FA1F) then v = "chinese"
+ elseif (k >= 0x0AC00 and k <= 0x0D7A3) then v = "korean"
+ elseif (k >= 0x01100 and k <= 0x0115F) then v = "jamo_initial"
+ elseif (k >= 0x01160 and k <= 0x011A7) then v = "jamo_medial"
+ elseif (k >= 0x011A8 and k <= 0x011FF) then v = "jamo_final"
+ elseif (k >= 0x01200 and k <= 0x0139F) then v = "ethiopic_syllable"
+ else v = false
+ end
+ t[k] = v
+ return v
+end
+
+setmetatableindex(hash,provide)
+
+scripts.hash = hash
+
+local numbertodataset = allocate()
+local numbertohandler = allocate()
+
+--~ storage.register("scripts/hash", hash, "scripts.hash")
+
+scripts.numbertodataset = numbertodataset
+scripts.numbertohandler = numbertohandler
+
+local defaults = {
+ inter_char_shrink_factor = 0,
+ inter_char_shrink_factor = 0,
+ inter_char_stretch_factor = 0,
+ inter_char_half_shrink_factor = 0,
+ inter_char_half_stretch_factor = 0,
+ inter_char_quarter_shrink_factor = 0,
+ inter_char_quarter_stretch_factor = 0,
+ inter_char_hangul_penalty = 0,
+
+ inter_word_stretch_factor = 0,
+}
+
+scripts.defaults = defaults -- so we can add more
+
+function scripts.installmethod(handler)
+ local name = handler.name
+ handlers[name] = handler
+ local attributes = { }
+ local datasets = handler.datasets
+ if not datasets or not datasets.default then
+ report_preprocessing("missing (default) dataset in script %a",name)
+ datasets.default = { } -- slower but an error anyway
+ end
+ for k, v in next, datasets do
+ setmetatableindex(v,defaults)
+ end
+ setmetatable(attributes, {
+ __index = function(t,k)
+ local v = datasets[k] or datasets.default
+ local a = unsetvalue
+ if v then
+ v.name = name -- for tracing
+ a = #numbertodataset + 1
+ numbertodataset[a] = v
+ numbertohandler[a] = handler
+ end
+ t[k] = a
+ return a
+ end
+ } )
+ handler.attributes = attributes
+end
+
+function scripts.installdataset(specification) -- global overload
+ local method = specification.method
+ local name = specification.name
+ local dataset = specification.dataset
+ if method and name and dataset then
+ local parent = specification.parent or ""
+ local handler = handlers[method]
+ if handler then
+ local datasets = handler.datasets
+ if datasets then
+ local defaultset = datasets.default
+ if defaultset then
+ if parent ~= "" then
+ local p = datasets[parent]
+ if p then
+ defaultset = p
+ else
+ report_preprocessing("dataset, unknown parent %a for method %a",parent,method)
+ end
+ end
+ setmetatable(dataset,defaultset)
+ local existing = datasets[name]
+ if existing then
+ for k, v in next, existing do
+ existing[k] = dataset
+ end
+ else
+ datasets[name] = dataset
+ end
+ else
+ report_preprocessing("dataset, no default for method %a",method)
+ end
+ else
+ report_preprocessing("dataset, no datasets for method %a",method)
+ end
+ else
+ report_preprocessing("dataset, no method %a",method)
+ end
+ else
+ report_preprocessing("dataset, invalid specification") -- maybe report table
+ end
+end
+
+local injectorenabled = false
+local splitterenabled = false
+
+function scripts.set(name,method,preset)
+ local handler = handlers[method]
+ if handler then
+ if handler.injector then
+ if not injectorenabled then
+ enableaction("processors","scripts.injectors.handler")
+ injectorenabled = true
+ end
+ texsetattribute(a_scriptinjection,handler.attributes[preset] or unsetvalue)
+ end
+ if handler.splitter then
+ if not splitterenabled then
+ enableaction("processors","scripts.splitters.handler")
+ splitterenabled = true
+ end
+ texsetattribute(a_scriptsplitting,handler.attributes[preset] or unsetvalue)
+ end
+ if handler.initializer then
+ handler.initializer(handler)
+ handler.initializer = nil
+ end
+ else
+ texsetattribute(a_scriptinjection,unsetvalue)
+ texsetattribute(a_scriptsplitting,unsetvalue)
+ end
+end
+
+function scripts.reset()
+ texsetattribute(a_scriptinjection,unsetvalue)
+ texsetattribute(a_scriptsplitting,unsetvalue)
+end
+
+-- the following tables will become a proper installer (move to cjk/eth)
+--
+-- 0=gray 1=red 2=green 3=blue 4=yellow 5=magenta 6=cyan 7=x-yellow 8=x-magenta 9=x-cyan
+
+local scriptcolors = allocate { -- todo: just named colors
+ korean = "trace:0",
+ chinese = "trace:0",
+ katakana = "trace:0",
+ hiragana = "trace:0",
+ full_width_open = "trace:1",
+ full_width_close = "trace:2",
+ half_width_open = "trace:3",
+ half_width_close = "trace:4",
+ full_width_punct = "trace:5",
+ hyphen = "trace:5",
+ non_starter = "trace:6",
+ jamo_initial = "trace:7",
+ jamo_medial = "trace:8",
+ jamo_final = "trace:9",
+ ethiopic_syllable = "trace:1",
+ ethiopic_word = "trace:2",
+ ethiopic_sentence = "trace:3",
+}
+
+scripts.colors = scriptcolors
+
+local numbertocategory = allocate { -- rather bound to cjk ... will be generalized
+ "korean",
+ "chinese",
+ "katakana",
+ "hiragana",
+ "full_width_open",
+ "full_width_close",
+ "half_width_open",
+ "half_width_close",
+ "full_width_punct",
+ "hyphen",
+ "non_starter",
+ "jamo_initial",
+ "jamo_medial",
+ "jamo_final",
+ "ethiopic_syllable",
+ "ethiopic_word",
+ "ethiopic_sentence",
+}
+
+local categorytonumber = allocate(table.swapped(numbertocategory)) -- could be one table
+
+scripts.categorytonumber = categorytonumber
+scripts.numbertocategory = numbertocategory
+
+local function colorize(start,stop)
+ for n in traverse_id(glyph_code,start) do
+ local kind = numbertocategory[n[a_scriptstatus]]
+ if kind then
+ local ac = scriptcolors[kind]
+ if ac then
+ setnodecolor(n,ac)
+ end
+ end
+ if n == stop then
+ break
+ end
+ end
+end
+
+local function traced_process(head,first,last,process,a)
+ if start ~= last then
+ local f, l = first, last
+ local name = numbertodataset[a]
+ name = name and name.name or "?"
+ report_preprocessing("before %s: %s",name,nodes.tosequence(f,l))
+ process(head,first,last)
+ report_preprocessing("after %s: %s", name,nodes.tosequence(f,l))
+ end
+end
+
+-- eventually we might end up with more extensive parsing
+-- todo: pass t[start..stop] == original
+--
+-- one of the time consuming functions:
+
+-- we can have a fonts.hashes.originals
+
+function scripts.injectors.handler(head)
+ local start = first_glyph(head) -- we already have glyphs here (subtype 1)
+ if not start then
+ return head, false
+ else
+ local last_a, normal_process, lastfont, originals = nil, nil, nil, nil
+ local done, first, last, ok = false, nil, nil, false
+ while start do
+ local id = start.id
+ if id == glyph_code then
+ local a = start[a_scriptinjection]
+ if a then
+ if a ~= last_a then
+ if first then
+ if ok then
+ if trace_analyzing then
+ colorize(first,last)
+ end
+ if trace_injections then
+ traced_process(head,first,last,normal_process,last_a)
+ else
+ normal_process(head,first,last)
+ end
+ ok, done = false, true
+ end
+ first, last = nil, nil
+ end
+ last_a = a
+ local handler = numbertohandler[a]
+ normal_process = handler.injector
+ end
+ if normal_process then
+ local f = start.font
+ if f ~= lastfont then
+ originals = fontdata[f].resources
+ if resources then
+ originals = resources.originals
+ else
+ -- can't happen
+ end
+ lastfont = f
+ end
+ local c = start.char
+ if originals then
+ c = originals[c] or c
+ end
+ local h = hash[c]
+ if h then
+ start[a_scriptstatus] = categorytonumber[h]
+ if not first then
+ first, last = start, start
+ else
+ last = start
+ end
+ -- if cjk == "chinese" or cjk == "korean" then -- we need to prevent too much ( ) processing
+ ok = true
+ -- end
+ elseif first then
+ if ok then
+ if trace_analyzing then
+ colorize(first,last)
+ end
+ if trace_injections then
+ traced_process(head,first,last,normal_process,last_a)
+ else
+ normal_process(head,first,last)
+ end
+ ok, done = false, true
+ end
+ first, last = nil, nil
+ end
+ end
+ elseif first then
+ if ok then
+ if trace_analyzing then
+ colorize(first,last)
+ end
+ if trace_injections then
+ traced_process(head,first,last,normal_process,last_a)
+ else
+ normal_process(head,first,last)
+ end
+ ok, done = false, true
+ end
+ first, last = nil, nil
+ end
+ elseif id == glue_code then
+ if ok then
+ -- continue
+ elseif first then
+ -- no chinese or korean
+ first, last = nil, nil
+ end
+ elseif first then
+ if ok then
+ -- some chinese or korean
+ if trace_analyzing then
+ colorize(first,last)
+ end
+ if trace_injections then
+ traced_process(head,first,last,normal_process,last_a)
+ else
+ normal_process(head,first,last)
+ end
+ first, last, ok, done = nil, nil, false, true
+ elseif first then
+ first, last = nil, nil
+ end
+ end
+ start = start.next
+ end
+ if ok then
+ if trace_analyzing then
+ colorize(first,last)
+ end
+ if trace_injections then
+ traced_process(head,first,last,normal_process,last_a)
+ else
+ normal_process(head,first,last)
+ end
+ done = true
+ end
+ return head, done
+ end
+end
+
+function scripts.splitters.handler(head)
+ return head, false
+end
+
+-- new plugin:
+
+local registercontext = fonts.specifiers.registercontext
+local mergecontext = fonts.specifiers.mergecontext
+
+local otfscripts = characters.otfscripts
+
+local report_scripts = logs.reporter("scripts","auto feature")
+local trace_scripts = false trackers.register("scripts.autofeature",function(v) trace_scripts = v end)
+
+local autofontfeature = scripts.autofontfeature or { }
+scripts.autofontfeature = autofontfeature
+
+local cache_yes = { }
+local cache_nop = { }
+
+setmetatableindex(cache_yes,function(t,k) local v = { } t[k] = v return v end)
+setmetatableindex(cache_nop,function(t,k) local v = { } t[k] = v return v end)
+
+-- beware: we need to tag a done (otherwise too many extra instances ... but how
+-- often unpack? wait till we have a bitmap
+--
+-- we can consider merging this in handlers.characters(head) at some point as there
+-- already check for the dynamic attribute so it saves a pass, however, then we also
+-- need to check for a_scriptinjection there which nils the benefit
+--
+-- we can consider cheating: set all glyphs in a word as the first one but it's not
+-- playing nice
+
+function autofontfeature.handler(head)
+ for n in traverse_id(glyph_code,head) do
+ -- if n[a_scriptinjection] then
+ -- -- already tagged by script feature, maybe some day adapt
+ -- else
+ local char = n.char
+ local script = otfscripts[char]
+ if script then
+ local dynamic = n[0] or 0
+ local font = n.font
+ if dynamic > 0 then
+ local slot = cache_yes[font]
+ local attr = slot[script]
+ if not attr then
+ attr = mergecontext(dynamic,name,2)
+ slot[script] = attr
+ if trace_scripts then
+ report_scripts("script: %s, trigger %C, dynamic: %a, variant: %a",script,char,attr,"extended")
+ end
+ end
+ if attr ~= 0 then
+ n[0] = attr
+ -- maybe set scriptinjection when associated
+ end
+ else
+ local slot = cache_nop[font]
+ local attr = slot[script]
+ if not attr then
+ attr = registercontext(font,script,2)
+ slot[script] = attr
+ if trace_scripts then
+ report_scripts("script: %s, trigger %C, dynamic: %s, variant: %a",script,char,attr,"normal")
+ end
+ end
+ if attr ~= 0 then
+ n[0] = attr
+ -- maybe set scriptinjection when associated
+ end
+ end
+ end
+ -- end
+ end
+ return head
+end
+
+function autofontfeature.enable()
+ report_scripts("globally enabled")
+ enableaction("processors","scripts.autofontfeature.handler")
+end
+
+function autofontfeature.disable()
+ report_scripts("globally disabled")
+ disableaction("processors","scripts.autofontfeature.handler")
+end
+
+commands.enableautofontscript = autofontfeature.enable
+commands.disableautofontscript = autofontfeature.disable
diff --git a/tex/context/base/sort-ini.lua b/tex/context/base/sort-ini.lua
index a07cbc6d2..479d1c489 100644
--- a/tex/context/base/sort-ini.lua
+++ b/tex/context/base/sort-ini.lua
@@ -1,665 +1,665 @@
-if not modules then modules = { } end modules ['sort-ini'] = {
- version = 1.001,
- comment = "companion to sort-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- It took a while to get there, but with Fleetwood Mac's "Don't Stop"
--- playing in the background we sort of got it done.
-
---[[<p>The code here evolved from the rather old mkii approach. There
-we concatinate the key and (raw) entry into a new string. Numbers and
-special characters get some treatment so that they sort ok. In
-addition some normalization (lowercasing, accent stripping) takes
-place and again data is appended ror prepended. Eventually these
-strings are sorted using a regular string sorter. The relative order
-of character is dealt with by weighting them. It took a while to
-figure this all out but eventually it worked ok for most languages,
-given that the right datatables were provided.</p>
-
-<p>Here we do follow a similar approach but this time we don't append
-the manipulated keys and entries but create tables for each of them
-with entries being tables themselves having different properties. In
-these tables characters are represented by numbers and sorting takes
-place using these numbers. Strings are simplified using lowercasing
-as well as shape codes. Numbers are filtered and after getting an offset
-they end up at the right end of the spectrum (more clever parser will
-be added some day). There are definitely more solutions to the problem
-and it is a nice puzzle to solve.</p>
-
-<p>In the future more methods can be added, as there is practically no
-limit to what goes into the tables. For that we will provide hooks.</p>
-
-<p>Todo: decomposition with specific order of accents, this is
-relatively easy to do.</p>
-
-<p>Todo: investigate what standards and conventions there are and see
-how they map onto this mechanism. I've learned that users can come up
-with any demand so nothing here is frozen.</p>
-
-<p>In the future index entries will become more clever, i.e. they will
-have language etc properties that then can be used.</p>
-]]--
-
-local gsub, rep, sub, sort, concat = string.gsub, string.rep, string.sub, table.sort, table.concat
-local utfbyte, utfchar, utfcharacters, utfvalues = utf.byte, utf.char, utf.characters, utf.values
-local next, type, tonumber, rawget, rawset = next, type, tonumber, rawget, rawset
-
-local allocate = utilities.storage.allocate
-local setmetatableindex = table.setmetatableindex
-
-local trace_tests = false trackers.register("sorters.tests", function(v) trace_tests = v end)
-local trace_methods = false trackers.register("sorters.methods", function(v) trace_methods = v end)
-
-local report_sorters = logs.reporter("languages","sorters")
-
-local comparers = { }
-local splitters = { }
-local definitions = allocate()
-local tracers = allocate()
-local ignoredoffset = 0x10000 -- frozen
-local replacementoffset = 0x10000 -- frozen
-local digitsoffset = 0x20000 -- frozen
-local digitsmaximum = 0xFFFFF -- frozen
-
-local lccodes = characters.lccodes
-local lcchars = characters.lcchars
-local shchars = characters.shchars
-local fscodes = characters.fscodes
-local fschars = characters.fschars
-
-local decomposed = characters.decomposed
-
-local variables = interfaces.variables
-
-local v_numbers = variables.numbers
-local v_default = variables.default
-local v_before = variables.before
-local v_after = variables.after
-local v_first = variables.first
-local v_last = variables.last
-
-local validmethods = table.tohash {
- -- "ch", -- raw character
- "mm", -- minus mapping
- "zm", -- zero mapping
- "pm", -- plus mapping
- "mc", -- lower case - 1
- "zc", -- lower case
- "pc", -- lower case + 1
- "uc", -- unicode
-}
-
-local predefinedmethods = {
- [v_default] = "zc,pc,zm,pm,uc",
- [v_before] = "mm,mc,uc",
- [v_after] = "pm,mc,uc",
- [v_first] = "pc,mm,uc",
- [v_last] = "mc,mm,uc",
-}
-
-sorters = {
- comparers = comparers,
- splitters = splitters,
- definitions = definitions,
- tracers = tracers,
- constants = {
- ignoredoffset = ignoredoffset,
- replacementoffset = replacementoffset,
- digitsoffset = digitsoffset,
- digitsmaximum = digitsmaximum,
- defaultlanguage = v_default,
- defaultmethod = v_default,
- defaultdigits = v_numbers,
- }
-}
-
-local sorters = sorters
-local constants = sorters.constants
-
-local data, language, method, digits
-local replacements, m_mappings, z_mappings, p_mappings, entries, orders, lower, upper, method, sequence
-local thefirstofsplit
-
-local mte = { -- todo: assign to t
- __index = function(t,k)
- if k and k ~= "" and utfbyte(k) < digitsoffset then -- k check really needed (see s-lan-02)
- local el
- if k then
- local l = lower[k] or lcchars[k]
- el = rawget(t,l)
- end
- if not el then
- local l = shchars[k]
- if l and l ~= k then
- if #l > 1 then
- l = sub(l,1,1) -- todo
- end
- el = rawget(t,l)
- if not el then
- l = lower[k] or lcchars[l]
- if l then
- el = rawget(t,l)
- end
- end
- end
- el = el or k
- end
- -- rawset(t,k,el)
- return el
- else
- -- rawset(t,k,k)
- end
- end
-}
-
-local noorder = false
-
-local function preparetables(data)
- local orders, lower, m_mappings, z_mappings, p_mappings = data.orders, data.lower, { }, { }, { }
- for i=1,#orders do
- local oi = orders[i]
- local n = { 2 * i }
- m_mappings[oi], z_mappings[oi], p_mappings[oi] = n, n, n
- end
- local mtm = {
- __index = function(t,k)
- local n, nn
- if k then
- if trace_tests then
- report_sorters("simplifing character %C",k)
- end
- local l = lower[k] or lcchars[k]
- if l then
- if trace_tests then
- report_sorters(" 1 lower: %C",l)
- end
- local ml = rawget(t,l)
- if ml then
- n = { }
- nn = 0
- for i=1,#ml do
- nn = nn + 1
- n[nn] = ml[i] + (t.__delta or 0)
- end
- if trace_tests then
- report_sorters(" 2 order: % t",n)
- end
- end
- end
- if not n then
- local s = shchars[k] -- maybe all components?
- if s and s ~= k then
- if trace_tests then
- report_sorters(" 3 shape: %C",s)
- end
- n = { }
- nn = 0
- for l in utfcharacters(s) do
- local ml = rawget(t,l)
- if ml then
- if trace_tests then
- report_sorters(" 4 keep: %C",l)
- end
- if ml then
- for i=1,#ml do
- nn = nn + 1
- n[nn] = ml[i]
- end
- end
- else
- l = lower[l] or lcchars[l]
- if l then
- if trace_tests then
- report_sorters(" 5 lower: %C",l)
- end
- local ml = rawget(t,l)
- if ml then
- for i=1,#ml do
- nn = nn + 1
- n[nn] = ml[i] + (t.__delta or 0)
- end
- end
- end
- end
- end
- else
- -- -- we probably never enter this branch
- -- -- fschars returns a single char
- --
- -- s = fschars[k]
- -- if s and s ~= k then
- -- if trace_tests then
- -- report_sorters(" 6 split: %s",s)
- -- end
- -- local ml = rawget(t,s)
- -- if ml then
- -- n = { }
- -- nn = 0
- -- for i=1,#ml do
- -- nn = nn + 1
- -- n[nn] = ml[i]
- -- end
- -- end
- -- end
- local b = utfbyte(k)
- n = decomposed[b] or { b }
- if trace_tests then
- report_sorters(" 6 split: %s",utf.tostring(b)) -- todo
- end
- end
- if n then
- if trace_tests then
- report_sorters(" 7 order: % t",n)
- end
- else
- n = noorder
- if trace_tests then
- report_sorters(" 8 order: 0")
- end
- end
- end
- else
- n = noorder
- if trace_tests then
- report_sorters(" 9 order: 0")
- end
- end
- rawset(t,k,n)
- return n
- end
- }
- data.m_mappings = m_mappings
- data.z_mappings = z_mappings
- data.p_mappings = p_mappings
- m_mappings.__delta = -1
- z_mappings.__delta = 0
- p_mappings.__delta = 1
- setmetatable(data.entries,mte)
- setmetatable(data.m_mappings,mtm)
- setmetatable(data.z_mappings,mtm)
- setmetatable(data.p_mappings,mtm)
- thefirstofsplit = data.firstofsplit
-end
-
-local function update() -- prepare parent chains, needed when new languages are added
- for language, data in next, definitions do
- local parent = data.parent or "default"
- if language ~= "default" then
- setmetatableindex(data,definitions[parent] or definitions.default)
- end
- data.language = language
- data.parent = parent
- data.m_mappings = { } -- free temp data
- data.z_mappings = { } -- free temp data
- data.p_mappings = { } -- free temp data
- end
-end
-
-local function setlanguage(l,m,d,u)
- language = (l ~= "" and l) or constants.defaultlanguage
- data = definitions[language or constants.defaultlanguage] or definitions[constants.defaultlanguage]
- method = (m ~= "" and m) or data.method or constants.defaultmethod
- digits = (d ~= "" and d) or data.digits or constants.defaultdigits
- if trace_tests then
- report_sorters("setting language %a, method %a, digits %a",language,method,digits)
- end
- replacements = data.replacements
- entries = data.entries
- orders = data.orders
- lower = data.lower
- upper = data.upper
- preparetables(data)
- m_mappings = data.m_mappings
- z_mappings = data.z_mappings
- p_mappings = data.p_mappings
- --
- method = predefinedmethods[variables[method]] or method
- data.method = method
- --
- data.digits = digits
- --
- local seq = utilities.parsers.settings_to_array(method or "") -- check the list
- sequence = { }
- local nofsequence = 0
- for i=1,#seq do
- local s = seq[i]
- if validmethods[s] then
- nofsequence = nofsequence + 1
- sequence[nofsequence] = s
- else
- report_sorters("invalid sorter method %a in %a",s,method)
- end
- end
- data.sequence = sequence
- if trace_tests then
- report_sorters("using sort sequence: % t",sequence)
- end
- --
- return data
-end
-
-function sorters.update()
- update()
- setlanguage(language,method,numberorder) -- resync current language and method
-end
-
-function sorters.setlanguage(language,method,numberorder)
- update()
- setlanguage(language,method,numberorder) -- new language and method
-end
-
--- tricky: { 0, 0, 0 } vs { 0, 0, 0, 0 } => longer wins and mm, pm, zm can have them
-
-local function basicsort(sort_a,sort_b)
- if sort_a and sort_b then
- local na = #sort_a
- local nb = #sort_b
- if na > nb then
- na = nb
- end
- for i=1,na do
- local ai, bi = sort_a[i], sort_b[i]
- if ai > bi then
- return 1
- elseif ai < bi then
- return -1
- end
- end
- end
- return 0
-end
-
-function comparers.basic(a,b) -- trace ea and eb
- local ea, eb = a.split, b.split
- local na, nb = #ea, #eb
- if na == 0 and nb == 0 then
- -- simple variant (single word)
- local result = 0
- for j=1,#sequence do
- local m = sequence[j]
- result = basicsort(ea[m],eb[m])
- if result ~= 0 then
- return result
- end
- end
- if result == 0 then
- local la, lb = #ea.uc, #eb.uc
- if la > lb then
- return 1
- elseif lb > la then
- return -1
- else
- return 0
- end
- else
- return result
- end
- else
- -- complex variant, used in register (multiple words)
- local result = 0
- for i=1,nb < na and nb or na do
- local eai, ebi = ea[i], eb[i]
- for j=1,#sequence do
- local m = sequence[j]
- result = basicsort(eai[m],ebi[m])
- if result ~= 0 then
- return result
- end
- end
- if result == 0 then
- local la, lb = #eai.uc, #ebi.uc
- if la > lb then
- return 1
- elseif lb > la then
- return -1
- end
- else
- return result
- end
- end
- if result ~= 0 then
- return result
- elseif na > nb then
- return 1
- elseif nb > na then
- return -1
- else
- return 0
- end
- end
-end
-
-local function numify(s)
- s = digitsoffset + tonumber(s) -- alternatively we can create range
- if s > digitsmaximum then
- s = digitsmaximum
- end
- return utfchar(s)
-end
-
-function sorters.strip(str) -- todo: only letters and such
- if str and str ~= "" then
- -- todo: make a decent lpeg
- str = gsub(str,"\\[\"\'~^`]*","") -- \"e -- hm, too greedy
- str = gsub(str,"\\%S*","") -- the rest
- str = gsub(str,"%s","\001") -- can be option
- str = gsub(str,"[%s%[%](){}%$\"\']*","")
- if digits == v_numbers then
- str = gsub(str,"(%d+)",numify) -- sort numbers properly
- end
- return str
- else
- return ""
- end
-end
-
-local function firstofsplit(entry)
- -- numbers are left padded by spaces
- local split = entry.split
- if #split > 0 then
- split = split[1].ch
- else
- split = split.ch
- end
- local first = split and split[1] or ""
- if thefirstofsplit then
- return thefirstofsplit(first,data,entry) -- normally the first one is needed
- else
- return first, entries[first] or "\000" -- tag
- end
-end
-
-sorters.firstofsplit = firstofsplit
-
--- for the moment we use an inefficient bunch of tables but once
--- we know what combinations make sense we can optimize this
-
-function splitters.utf(str) -- we could append m and u but this is cleaner, s is for tracing
- if #replacements > 0 then
- -- todo make an lpeg for this
- for k=1,#replacements do
- local v = replacements[k]
- str = gsub(str,v[1],v[2])
- end
- end
- local m_case, z_case, p_case, m_mapping, z_mapping, p_mapping, char, byte, n = { }, { }, { }, { }, { }, { }, { }, { }, 0
- local nm, nz, np = 0, 0, 0
- for sc in utfcharacters(str) do
- local b = utfbyte(sc)
- if b >= digitsoffset then
- if n == 0 then
- -- we need to force number to the top
- z_case[1] = 0
- m_case[1] = 0
- p_case[1] = 0
- char[1] = sc
- byte[1] = 0
- m_mapping[1] = 0
- z_mapping[1] = 0
- p_mapping[1] = 0
- n = 2
- else
- n = n + 1
- end
- z_case[n] = b
- m_case[n] = b
- p_case[n] = b
- char[n] = sc
- byte[n] = b
- nm = nm + 1
- nz = nz + 1
- np = np + 1
- m_mapping[nm] = b
- z_mapping[nz] = b
- p_mapping[np] = b
- else
- n = n + 1
- local l = lower[sc]
- l = l and utfbyte(l) or lccodes[b]
- if type(l) == "table" then
- l = l[1] -- there are currently no tables in lccodes but it can be some, day
- end
- z_case[n] = l
- if l ~= b then
- m_case[n] = l - 1
- p_case[n] = l + 1
- else
- m_case[n] = l
- p_case[n] = l
- end
- char[n], byte[n] = sc, b
- local fs = fscodes[b] or b
- local msc = m_mappings[sc]
- if msc ~= noorder then
- if not msc then
- msc = m_mappings[fs]
- end
- for i=1,#msc do
- nm = nm + 1
- m_mapping[nm] = msc[i]
- end
- end
- local zsc = z_mappings[sc]
- if zsc ~= noorder then
- if not zsc then
- zsc = z_mappings[fs]
- end
- for i=1,#zsc do
- nz = nz + 1
- z_mapping[nz] = zsc[i]
- end
- end
- local psc = p_mappings[sc]
- if psc ~= noorder then
- if not psc then
- psc = p_mappings[fs]
- end
- for i=1,#psc do
- np = np + 1
- p_mapping[np] = psc[i]
- end
- end
- end
- end
- -- -- only those needed that are part of a sequence
- --
- -- local b = byte[1]
- -- if b then
- -- -- we set them to the first split code (korean)
- -- local fs = fscodes[b] or b
- -- if #m_mapping == 0 then
- -- m_mapping = { m_mappings[fs][1] }
- -- end
- -- if #z_mapping == 0 then
- -- z_mapping = { z_mappings[fs][1] }
- -- end
- -- if #p_mapping == 0 then
- -- p_mapping = { p_mappings[fs][1] }
- -- end
- -- end
- local t = {
- ch = char,
- uc = byte,
- mc = m_case,
- zc = z_case,
- pc = p_case,
- mm = m_mapping,
- zm = z_mapping,
- pm = p_mapping,
- }
-
- return t
-end
-
-local function packch(entry)
- local split = entry.split
- if #split > 0 then -- useless test
- local t = { }
- for i=1,#split do
- local tt, li = { }, split[i].ch
- for j=1,#li do
- local lij = li[j]
- tt[j] = utfbyte(lij) > ignoredoffset and "[]" or lij
- end
- t[i] = concat(tt)
- end
- return concat(t," + ")
- else
- local t, li = { }, split.ch
- for j=1,#li do
- local lij = li[j]
- t[j] = utfbyte(lij) > ignoredoffset and "[]" or lij
- end
- return concat(t)
- end
-end
-
-local function packuc(entry)
- local split = entry.split
- if #split > 0 then -- useless test
- local t = { }
- for i=1,#split do
- t[i] = concat(split[i].uc, " ")
- end
- return concat(t," + ")
- else
- return concat(split.uc," ")
- end
-end
-
-function sorters.sort(entries,cmp)
- if trace_tests or trace_methods then
- local nofentries = #entries
- report_sorters("entries: %s, language: %s, method: %s, digits: %s",nofentries,language,method,tostring(digits))
- for i=1,nofentries do
- report_sorters("entry %s",table.serialize(entries[i].split,i,true,true,true))
- end
- end
- if trace_tests then
- sort(entries,function(a,b)
- local r = cmp(a,b)
- local e = (not r and "?") or (r<0 and "<") or (r>0 and ">") or "="
- report_sorters("%s %s %s | %s %s %s",packch(a),e,packch(b),packuc(a),e,packuc(b))
- return r == -1
- end)
- local s
- for i=1,#entries do
- local entry = entries[i]
- local letter, first = firstofsplit(entry)
- if first == s then
- first = " "
- else
- s = first
- report_sorters(">> %C (%C)",first,letter)
- end
- report_sorters(" %s | %s",packch(entry),packuc(entry))
- end
- else
- sort(entries,function(a,b)
- return cmp(a,b) == -1
- end)
- end
-end
+if not modules then modules = { } end modules ['sort-ini'] = {
+ version = 1.001,
+ comment = "companion to sort-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- It took a while to get there, but with Fleetwood Mac's "Don't Stop"
+-- playing in the background we sort of got it done.
+
+--[[<p>The code here evolved from the rather old mkii approach. There
+we concatinate the key and (raw) entry into a new string. Numbers and
+special characters get some treatment so that they sort ok. In
+addition some normalization (lowercasing, accent stripping) takes
+place and again data is appended ror prepended. Eventually these
+strings are sorted using a regular string sorter. The relative order
+of character is dealt with by weighting them. It took a while to
+figure this all out but eventually it worked ok for most languages,
+given that the right datatables were provided.</p>
+
+<p>Here we do follow a similar approach but this time we don't append
+the manipulated keys and entries but create tables for each of them
+with entries being tables themselves having different properties. In
+these tables characters are represented by numbers and sorting takes
+place using these numbers. Strings are simplified using lowercasing
+as well as shape codes. Numbers are filtered and after getting an offset
+they end up at the right end of the spectrum (more clever parser will
+be added some day). There are definitely more solutions to the problem
+and it is a nice puzzle to solve.</p>
+
+<p>In the future more methods can be added, as there is practically no
+limit to what goes into the tables. For that we will provide hooks.</p>
+
+<p>Todo: decomposition with specific order of accents, this is
+relatively easy to do.</p>
+
+<p>Todo: investigate what standards and conventions there are and see
+how they map onto this mechanism. I've learned that users can come up
+with any demand so nothing here is frozen.</p>
+
+<p>In the future index entries will become more clever, i.e. they will
+have language etc properties that then can be used.</p>
+]]--
+
+local gsub, rep, sub, sort, concat = string.gsub, string.rep, string.sub, table.sort, table.concat
+local utfbyte, utfchar, utfcharacters, utfvalues = utf.byte, utf.char, utf.characters, utf.values
+local next, type, tonumber, rawget, rawset = next, type, tonumber, rawget, rawset
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+
+local trace_tests = false trackers.register("sorters.tests", function(v) trace_tests = v end)
+local trace_methods = false trackers.register("sorters.methods", function(v) trace_methods = v end)
+
+local report_sorters = logs.reporter("languages","sorters")
+
+local comparers = { }
+local splitters = { }
+local definitions = allocate()
+local tracers = allocate()
+local ignoredoffset = 0x10000 -- frozen
+local replacementoffset = 0x10000 -- frozen
+local digitsoffset = 0x20000 -- frozen
+local digitsmaximum = 0xFFFFF -- frozen
+
+local lccodes = characters.lccodes
+local lcchars = characters.lcchars
+local shchars = characters.shchars
+local fscodes = characters.fscodes
+local fschars = characters.fschars
+
+local decomposed = characters.decomposed
+
+local variables = interfaces.variables
+
+local v_numbers = variables.numbers
+local v_default = variables.default
+local v_before = variables.before
+local v_after = variables.after
+local v_first = variables.first
+local v_last = variables.last
+
+local validmethods = table.tohash {
+ -- "ch", -- raw character
+ "mm", -- minus mapping
+ "zm", -- zero mapping
+ "pm", -- plus mapping
+ "mc", -- lower case - 1
+ "zc", -- lower case
+ "pc", -- lower case + 1
+ "uc", -- unicode
+}
+
+local predefinedmethods = {
+ [v_default] = "zc,pc,zm,pm,uc",
+ [v_before] = "mm,mc,uc",
+ [v_after] = "pm,mc,uc",
+ [v_first] = "pc,mm,uc",
+ [v_last] = "mc,mm,uc",
+}
+
+sorters = {
+ comparers = comparers,
+ splitters = splitters,
+ definitions = definitions,
+ tracers = tracers,
+ constants = {
+ ignoredoffset = ignoredoffset,
+ replacementoffset = replacementoffset,
+ digitsoffset = digitsoffset,
+ digitsmaximum = digitsmaximum,
+ defaultlanguage = v_default,
+ defaultmethod = v_default,
+ defaultdigits = v_numbers,
+ }
+}
+
+local sorters = sorters
+local constants = sorters.constants
+
+local data, language, method, digits
+local replacements, m_mappings, z_mappings, p_mappings, entries, orders, lower, upper, method, sequence
+local thefirstofsplit
+
+local mte = { -- todo: assign to t
+ __index = function(t,k)
+ if k and k ~= "" and utfbyte(k) < digitsoffset then -- k check really needed (see s-lan-02)
+ local el
+ if k then
+ local l = lower[k] or lcchars[k]
+ el = rawget(t,l)
+ end
+ if not el then
+ local l = shchars[k]
+ if l and l ~= k then
+ if #l > 1 then
+ l = sub(l,1,1) -- todo
+ end
+ el = rawget(t,l)
+ if not el then
+ l = lower[k] or lcchars[l]
+ if l then
+ el = rawget(t,l)
+ end
+ end
+ end
+ el = el or k
+ end
+ -- rawset(t,k,el)
+ return el
+ else
+ -- rawset(t,k,k)
+ end
+ end
+}
+
+local noorder = false
+
+local function preparetables(data)
+ local orders, lower, m_mappings, z_mappings, p_mappings = data.orders, data.lower, { }, { }, { }
+ for i=1,#orders do
+ local oi = orders[i]
+ local n = { 2 * i }
+ m_mappings[oi], z_mappings[oi], p_mappings[oi] = n, n, n
+ end
+ local mtm = {
+ __index = function(t,k)
+ local n, nn
+ if k then
+ if trace_tests then
+ report_sorters("simplifing character %C",k)
+ end
+ local l = lower[k] or lcchars[k]
+ if l then
+ if trace_tests then
+ report_sorters(" 1 lower: %C",l)
+ end
+ local ml = rawget(t,l)
+ if ml then
+ n = { }
+ nn = 0
+ for i=1,#ml do
+ nn = nn + 1
+ n[nn] = ml[i] + (t.__delta or 0)
+ end
+ if trace_tests then
+ report_sorters(" 2 order: % t",n)
+ end
+ end
+ end
+ if not n then
+ local s = shchars[k] -- maybe all components?
+ if s and s ~= k then
+ if trace_tests then
+ report_sorters(" 3 shape: %C",s)
+ end
+ n = { }
+ nn = 0
+ for l in utfcharacters(s) do
+ local ml = rawget(t,l)
+ if ml then
+ if trace_tests then
+ report_sorters(" 4 keep: %C",l)
+ end
+ if ml then
+ for i=1,#ml do
+ nn = nn + 1
+ n[nn] = ml[i]
+ end
+ end
+ else
+ l = lower[l] or lcchars[l]
+ if l then
+ if trace_tests then
+ report_sorters(" 5 lower: %C",l)
+ end
+ local ml = rawget(t,l)
+ if ml then
+ for i=1,#ml do
+ nn = nn + 1
+ n[nn] = ml[i] + (t.__delta or 0)
+ end
+ end
+ end
+ end
+ end
+ else
+ -- -- we probably never enter this branch
+ -- -- fschars returns a single char
+ --
+ -- s = fschars[k]
+ -- if s and s ~= k then
+ -- if trace_tests then
+ -- report_sorters(" 6 split: %s",s)
+ -- end
+ -- local ml = rawget(t,s)
+ -- if ml then
+ -- n = { }
+ -- nn = 0
+ -- for i=1,#ml do
+ -- nn = nn + 1
+ -- n[nn] = ml[i]
+ -- end
+ -- end
+ -- end
+ local b = utfbyte(k)
+ n = decomposed[b] or { b }
+ if trace_tests then
+ report_sorters(" 6 split: %s",utf.tostring(b)) -- todo
+ end
+ end
+ if n then
+ if trace_tests then
+ report_sorters(" 7 order: % t",n)
+ end
+ else
+ n = noorder
+ if trace_tests then
+ report_sorters(" 8 order: 0")
+ end
+ end
+ end
+ else
+ n = noorder
+ if trace_tests then
+ report_sorters(" 9 order: 0")
+ end
+ end
+ rawset(t,k,n)
+ return n
+ end
+ }
+ data.m_mappings = m_mappings
+ data.z_mappings = z_mappings
+ data.p_mappings = p_mappings
+ m_mappings.__delta = -1
+ z_mappings.__delta = 0
+ p_mappings.__delta = 1
+ setmetatable(data.entries,mte)
+ setmetatable(data.m_mappings,mtm)
+ setmetatable(data.z_mappings,mtm)
+ setmetatable(data.p_mappings,mtm)
+ thefirstofsplit = data.firstofsplit
+end
+
+local function update() -- prepare parent chains, needed when new languages are added
+ for language, data in next, definitions do
+ local parent = data.parent or "default"
+ if language ~= "default" then
+ setmetatableindex(data,definitions[parent] or definitions.default)
+ end
+ data.language = language
+ data.parent = parent
+ data.m_mappings = { } -- free temp data
+ data.z_mappings = { } -- free temp data
+ data.p_mappings = { } -- free temp data
+ end
+end
+
+local function setlanguage(l,m,d,u)
+ language = (l ~= "" and l) or constants.defaultlanguage
+ data = definitions[language or constants.defaultlanguage] or definitions[constants.defaultlanguage]
+ method = (m ~= "" and m) or data.method or constants.defaultmethod
+ digits = (d ~= "" and d) or data.digits or constants.defaultdigits
+ if trace_tests then
+ report_sorters("setting language %a, method %a, digits %a",language,method,digits)
+ end
+ replacements = data.replacements
+ entries = data.entries
+ orders = data.orders
+ lower = data.lower
+ upper = data.upper
+ preparetables(data)
+ m_mappings = data.m_mappings
+ z_mappings = data.z_mappings
+ p_mappings = data.p_mappings
+ --
+ method = predefinedmethods[variables[method]] or method
+ data.method = method
+ --
+ data.digits = digits
+ --
+ local seq = utilities.parsers.settings_to_array(method or "") -- check the list
+ sequence = { }
+ local nofsequence = 0
+ for i=1,#seq do
+ local s = seq[i]
+ if validmethods[s] then
+ nofsequence = nofsequence + 1
+ sequence[nofsequence] = s
+ else
+ report_sorters("invalid sorter method %a in %a",s,method)
+ end
+ end
+ data.sequence = sequence
+ if trace_tests then
+ report_sorters("using sort sequence: % t",sequence)
+ end
+ --
+ return data
+end
+
+function sorters.update()
+ update()
+ setlanguage(language,method,numberorder) -- resync current language and method
+end
+
+function sorters.setlanguage(language,method,numberorder)
+ update()
+ setlanguage(language,method,numberorder) -- new language and method
+end
+
+-- tricky: { 0, 0, 0 } vs { 0, 0, 0, 0 } => longer wins and mm, pm, zm can have them
+
+local function basicsort(sort_a,sort_b)
+ if sort_a and sort_b then
+ local na = #sort_a
+ local nb = #sort_b
+ if na > nb then
+ na = nb
+ end
+ for i=1,na do
+ local ai, bi = sort_a[i], sort_b[i]
+ if ai > bi then
+ return 1
+ elseif ai < bi then
+ return -1
+ end
+ end
+ end
+ return 0
+end
+
+function comparers.basic(a,b) -- trace ea and eb
+ local ea, eb = a.split, b.split
+ local na, nb = #ea, #eb
+ if na == 0 and nb == 0 then
+ -- simple variant (single word)
+ local result = 0
+ for j=1,#sequence do
+ local m = sequence[j]
+ result = basicsort(ea[m],eb[m])
+ if result ~= 0 then
+ return result
+ end
+ end
+ if result == 0 then
+ local la, lb = #ea.uc, #eb.uc
+ if la > lb then
+ return 1
+ elseif lb > la then
+ return -1
+ else
+ return 0
+ end
+ else
+ return result
+ end
+ else
+ -- complex variant, used in register (multiple words)
+ local result = 0
+ for i=1,nb < na and nb or na do
+ local eai, ebi = ea[i], eb[i]
+ for j=1,#sequence do
+ local m = sequence[j]
+ result = basicsort(eai[m],ebi[m])
+ if result ~= 0 then
+ return result
+ end
+ end
+ if result == 0 then
+ local la, lb = #eai.uc, #ebi.uc
+ if la > lb then
+ return 1
+ elseif lb > la then
+ return -1
+ end
+ else
+ return result
+ end
+ end
+ if result ~= 0 then
+ return result
+ elseif na > nb then
+ return 1
+ elseif nb > na then
+ return -1
+ else
+ return 0
+ end
+ end
+end
+
+local function numify(s)
+ s = digitsoffset + tonumber(s) -- alternatively we can create range
+ if s > digitsmaximum then
+ s = digitsmaximum
+ end
+ return utfchar(s)
+end
+
+function sorters.strip(str) -- todo: only letters and such
+ if str and str ~= "" then
+ -- todo: make a decent lpeg
+ str = gsub(str,"\\[\"\'~^`]*","") -- \"e -- hm, too greedy
+ str = gsub(str,"\\%S*","") -- the rest
+ str = gsub(str,"%s","\001") -- can be option
+ str = gsub(str,"[%s%[%](){}%$\"\']*","")
+ if digits == v_numbers then
+ str = gsub(str,"(%d+)",numify) -- sort numbers properly
+ end
+ return str
+ else
+ return ""
+ end
+end
+
+local function firstofsplit(entry)
+ -- numbers are left padded by spaces
+ local split = entry.split
+ if #split > 0 then
+ split = split[1].ch
+ else
+ split = split.ch
+ end
+ local first = split and split[1] or ""
+ if thefirstofsplit then
+ return thefirstofsplit(first,data,entry) -- normally the first one is needed
+ else
+ return first, entries[first] or "\000" -- tag
+ end
+end
+
+sorters.firstofsplit = firstofsplit
+
+-- for the moment we use an inefficient bunch of tables but once
+-- we know what combinations make sense we can optimize this
+
+function splitters.utf(str) -- we could append m and u but this is cleaner, s is for tracing
+ if #replacements > 0 then
+ -- todo make an lpeg for this
+ for k=1,#replacements do
+ local v = replacements[k]
+ str = gsub(str,v[1],v[2])
+ end
+ end
+ local m_case, z_case, p_case, m_mapping, z_mapping, p_mapping, char, byte, n = { }, { }, { }, { }, { }, { }, { }, { }, 0
+ local nm, nz, np = 0, 0, 0
+ for sc in utfcharacters(str) do
+ local b = utfbyte(sc)
+ if b >= digitsoffset then
+ if n == 0 then
+ -- we need to force number to the top
+ z_case[1] = 0
+ m_case[1] = 0
+ p_case[1] = 0
+ char[1] = sc
+ byte[1] = 0
+ m_mapping[1] = 0
+ z_mapping[1] = 0
+ p_mapping[1] = 0
+ n = 2
+ else
+ n = n + 1
+ end
+ z_case[n] = b
+ m_case[n] = b
+ p_case[n] = b
+ char[n] = sc
+ byte[n] = b
+ nm = nm + 1
+ nz = nz + 1
+ np = np + 1
+ m_mapping[nm] = b
+ z_mapping[nz] = b
+ p_mapping[np] = b
+ else
+ n = n + 1
+ local l = lower[sc]
+ l = l and utfbyte(l) or lccodes[b]
+ if type(l) == "table" then
+ l = l[1] -- there are currently no tables in lccodes but it can be some, day
+ end
+ z_case[n] = l
+ if l ~= b then
+ m_case[n] = l - 1
+ p_case[n] = l + 1
+ else
+ m_case[n] = l
+ p_case[n] = l
+ end
+ char[n], byte[n] = sc, b
+ local fs = fscodes[b] or b
+ local msc = m_mappings[sc]
+ if msc ~= noorder then
+ if not msc then
+ msc = m_mappings[fs]
+ end
+ for i=1,#msc do
+ nm = nm + 1
+ m_mapping[nm] = msc[i]
+ end
+ end
+ local zsc = z_mappings[sc]
+ if zsc ~= noorder then
+ if not zsc then
+ zsc = z_mappings[fs]
+ end
+ for i=1,#zsc do
+ nz = nz + 1
+ z_mapping[nz] = zsc[i]
+ end
+ end
+ local psc = p_mappings[sc]
+ if psc ~= noorder then
+ if not psc then
+ psc = p_mappings[fs]
+ end
+ for i=1,#psc do
+ np = np + 1
+ p_mapping[np] = psc[i]
+ end
+ end
+ end
+ end
+ -- -- only those needed that are part of a sequence
+ --
+ -- local b = byte[1]
+ -- if b then
+ -- -- we set them to the first split code (korean)
+ -- local fs = fscodes[b] or b
+ -- if #m_mapping == 0 then
+ -- m_mapping = { m_mappings[fs][1] }
+ -- end
+ -- if #z_mapping == 0 then
+ -- z_mapping = { z_mappings[fs][1] }
+ -- end
+ -- if #p_mapping == 0 then
+ -- p_mapping = { p_mappings[fs][1] }
+ -- end
+ -- end
+ local t = {
+ ch = char,
+ uc = byte,
+ mc = m_case,
+ zc = z_case,
+ pc = p_case,
+ mm = m_mapping,
+ zm = z_mapping,
+ pm = p_mapping,
+ }
+
+ return t
+end
+
+local function packch(entry)
+ local split = entry.split
+ if #split > 0 then -- useless test
+ local t = { }
+ for i=1,#split do
+ local tt, li = { }, split[i].ch
+ for j=1,#li do
+ local lij = li[j]
+ tt[j] = utfbyte(lij) > ignoredoffset and "[]" or lij
+ end
+ t[i] = concat(tt)
+ end
+ return concat(t," + ")
+ else
+ local t, li = { }, split.ch
+ for j=1,#li do
+ local lij = li[j]
+ t[j] = utfbyte(lij) > ignoredoffset and "[]" or lij
+ end
+ return concat(t)
+ end
+end
+
+local function packuc(entry)
+ local split = entry.split
+ if #split > 0 then -- useless test
+ local t = { }
+ for i=1,#split do
+ t[i] = concat(split[i].uc, " ")
+ end
+ return concat(t," + ")
+ else
+ return concat(split.uc," ")
+ end
+end
+
+function sorters.sort(entries,cmp)
+ if trace_tests or trace_methods then
+ local nofentries = #entries
+ report_sorters("entries: %s, language: %s, method: %s, digits: %s",nofentries,language,method,tostring(digits))
+ for i=1,nofentries do
+ report_sorters("entry %s",table.serialize(entries[i].split,i,true,true,true))
+ end
+ end
+ if trace_tests then
+ sort(entries,function(a,b)
+ local r = cmp(a,b)
+ local e = (not r and "?") or (r<0 and "<") or (r>0 and ">") or "="
+ report_sorters("%s %s %s | %s %s %s",packch(a),e,packch(b),packuc(a),e,packuc(b))
+ return r == -1
+ end)
+ local s
+ for i=1,#entries do
+ local entry = entries[i]
+ local letter, first = firstofsplit(entry)
+ if first == s then
+ first = " "
+ else
+ s = first
+ report_sorters(">> %C (%C)",first,letter)
+ end
+ report_sorters(" %s | %s",packch(entry),packuc(entry))
+ end
+ else
+ sort(entries,function(a,b)
+ return cmp(a,b) == -1
+ end)
+ end
+end
diff --git a/tex/context/base/sort-lan.lua b/tex/context/base/sort-lan.lua
index e0c6376dc..d2fa276d7 100644
--- a/tex/context/base/sort-lan.lua
+++ b/tex/context/base/sort-lan.lua
@@ -1,925 +1,925 @@
-if not modules then modules = { } end modules ['sort-lan'] = {
- version = 1.001,
- comment = "companion to sort-lan.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files",
- dataonly = true,
-}
-
--- todo: look into uts#10 (2012) ... some experiments ... something
--- to finish in winter.
-
--- Many vectors were supplied by Wolfgang Schuster and Philipp
--- Gesang. However this is a quite adapted and reformatted variant
--- so it needs some checking. Other users provides tables and
--- corrections as well.
-
-local utfchar, utfbyte = utf.char, utf.byte
-local sorters = sorters
-local definitions = sorters.definitions
-local replacementoffset = sorters.constants.replacementoffset
-local variables = interfaces.variables
-
-definitions["default"] = {
- method = variables.before,
- replacements = {
- -- no replacements
- },
- entries = {
- ["a"] = "a", ["b"] = "b", ["c"] = "c", ["d"] = "d", ["e"] = "e",
- ["f"] = "f", ["g"] = "g", ["h"] = "h", ["i"] = "i", ["j"] = "j",
- ["k"] = "k", ["l"] = "l", ["m"] = "m", ["n"] = "n", ["o"] = "o",
- ["p"] = "p", ["q"] = "q", ["r"] = "r", ["s"] = "s", ["t"] = "t",
- ["u"] = "u", ["v"] = "v", ["w"] = "w", ["x"] = "x", ["y"] = "y",
- ["z"] = "z",
- },
- orders = {
- "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
- "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
- "u", "v", "w", "x", "y", "z",
- },
- lower = {
- -- no replacements
- },
- upper = {
- -- no replacements
- }
-}
-
-sorters.setlanguage("default")
-
--- english
-
-definitions["en"] = { parent = "default" }
-
--- dutch
-
-definitions['nl'] = {
- parent = 'default',
- replacements = {
- { "ij", 'y' }, { "IJ", 'Y' },
- },
-}
-
--- French
-
-definitions['fr'] = { parent = 'default' }
-
--- German (by Wolfgang Schuster)
-
--- DIN 5007-1
-
-definitions['DIN 5007-1'] = { parent = 'default' }
-
--- DIN 5007-2
-
-definitions['DIN 5007-2'] = {
- parent = 'default',
- replacements = {
- { "ä", 'ae' }, { "Ä", 'Ae' },
- { "ö", 'oe' }, { "Ö", 'Oe' },
- { "ü", 'ue' }, { "Ü", 'Ue' },
- },
-}
-
--- Duden
-
-definitions['Duden'] = {
- parent = 'default',
- replacements = { { "ß", 's' } },
-}
-
--- definitions['de'] = { parent = 'default' } -- new german
-
-definitions['de'] = {
- parent = 'default',
- replacements = {
- { "ä", 'ae' }, { "Ä", 'Ae' },
- { "ö", 'oe' }, { "Ö", 'Oe' },
- { "ü", 'ue' }, { "Ü", 'Ue' },
- { "ß", 's' },
- },
-}
-
-definitions['deo'] = { parent = 'de' } -- old german
-
-definitions['de-DE'] = { parent = 'de' } -- german - Germany
-definitions['de-CH'] = { parent = 'de' } -- german - Swiss
-
--- german - Austria
-
-definitions['de-AT'] = {
- entries = {
- ["a"] = "a", ["ä"] = "ä", ["b"] = "b", ["c"] = "c", ["d"] = "d",
- ["e"] = "e", ["f"] = "f", ["g"] = "g", ["h"] = "h", ["i"] = "i",
- ["j"] = "j", ["k"] = "k", ["l"] = "l", ["m"] = "m", ["n"] = "n",
- ["o"] = "o", ["ö"] = "ö", ["p"] = "p", ["q"] = "q", ["r"] = "r",
- ["s"] = "s", ["t"] = "t", ["u"] = "u", ["ü"] = "ü", ["v"] = "v",
- ["w"] = "w", ["x"] = "x", ["y"] = "y", ["z"] = "z",
- },
- orders = {
- "a", "ä", "b", "c", "d", "e", "f", "g", "h", "i",
- "j", "k", "l", "m", "n", "o", "ö", "p", "q", "r",
- "s", "t", "u", "ü", "v", "w", "x", "y", "z",
- },
-}
-
--- finish (by Wolfgang Schuster)
-
-definitions['fi'] = {
- entries = {
- ["a"] = "a", ["b"] = "b", ["c"] = "c", ["d"] = "d", ["e"] = "e",
- ["f"] = "f", ["g"] = "g", ["h"] = "h", ["i"] = "i", ["j"] = "j",
- ["k"] = "k", ["l"] = "l", ["m"] = "m", ["n"] = "n", ["o"] = "o",
- ["p"] = "p", ["q"] = "q", ["r"] = "r", ["s"] = "s", ["t"] = "t",
- ["u"] = "u", ["v"] = "v", ["w"] = "w", ["x"] = "x", ["y"] = "y",
- ["z"] = "z", ["å"] = "å", ["ä"] = "ä", ["ö"] = "ö",
- },
- orders = {
- "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
- "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
- "u", "v", "w", "x", "y", "z", "å", "ä", "ö",
- }
-}
-
--- slovenian by MM: this will change since we need to add accented vowels
-
-definitions['sl'] = {
- entries = {
- ["a"] = "a", ["b"] = "b", ["c"] = "c", ["č"] = "č", ["ć"] = "ć", ["d"] = "d",
- ["đ"] = "đ", ["e"] = "e", ["f"] = "f", ["g"] = "g", ["h"] = "h", ["i"] = "i",
- ["j"] = "j", ["k"] = "k", ["l"] = "l", ["m"] = "m", ["n"] = "n", ["o"] = "o",
- ["p"] = "p", ["q"] = "q", ["r"] = "r", ["s"] = "s", ["š"] = "š", ["t"] = "t",
- ["u"] = "u", ["v"] = "v", ["w"] = "w", ["x"] = "x", ["y"] = "y", ["z"] = "z",
- ["ž"] = "ž",
- },
- orders = {
- "a", "b", "c", "č", "ć", "d", "đ", "e", "f", "g", "h", "i",
- "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "š", "t",
- "u", "v", "w", "x", "y", "z", "ž",
- }
-}
-
--- The following data was provided by Philipp Gesang.
-
-definitions["ru"] = {
- entries = {
- ["а"] = "а", ["б"] = "б", ["в"] = "в", ["г"] = "г", ["д"] = "д",
- ["е"] = "е", ["ё"] = "е", ["ж"] = "ж", ["з"] = "з", ["и"] = "и",
- ["і"] = "и", ["й"] = "й", ["к"] = "к", ["л"] = "л", ["м"] = "м",
- ["н"] = "н", ["о"] = "о", ["п"] = "п", ["р"] = "р", ["с"] = "с",
- ["т"] = "т", ["у"] = "у", ["ф"] = "ф", ["х"] = "х", ["ц"] = "ц",
- ["ч"] = "ч", ["ш"] = "ш", ["щ"] = "щ", ["ъ"] = "ъ", ["ы"] = "ы",
- ["ь"] = "ь", ["ѣ"] = "ѣ", ["э"] = "э", ["ю"] = "ю", ["я"] = "я",
- ["ѳ"] = "ѳ", ["ѵ"] = "ѵ",
- },
- orders = {
- "а", "б", "в", "г", "д", "е", "ё", "ж", "з", "и",
- "і", "й", "к", "л", "м", "н", "о", "п", "р", "с",
- "т", "у", "ф", "х", "ц", "ч", "ш", "щ", "ъ", "ы",
- "ь", "ѣ", "э", "ю", "я", "ѳ", "ѵ",
- }
-}
-
---- Basic Ukrainian
-
-definitions["uk"] = {
- entries = {
- ["а"] = "а", ["б"] = "б", ["в"] = "в", ["г"] = "г", ["ґ"] = "ґ",
- ["д"] = "д", ["е"] = "е", ["є"] = "є", ["ж"] = "ж", ["з"] = "з",
- ["и"] = "и", ["і"] = "і", ["ї"] = "ї", ["й"] = "й", ["к"] = "к",
- ["л"] = "л", ["м"] = "м", ["н"] = "н", ["о"] = "о", ["п"] = "п",
- ["р"] = "р", ["с"] = "с", ["т"] = "т", ["у"] = "у", ["ф"] = "ф",
- ["х"] = "х", ["ц"] = "ц", ["ч"] = "ч", ["ш"] = "ш", ["щ"] = "щ",
- ["ь"] = "ь", ["ю"] = "ю", ["я"] = "я",
- },
- orders = {
- "а", "б", "в", "г", "ґ", "д", "е", "є", "ж", "з", "и", "і",
- "ї", "й", "к", "л", "м", "н", "о", "п", "р", "с", "т", "у",
- "ф", "х", "ц", "ч", "ш", "щ", "ь", "ю", "я",
- }
-}
-
---- Belarusian
-
-definitions["be"] = {
- entries = {
- ["а"] = "а", ["б"] = "б", ["в"] = "в", ["г"] = "г", ["д"] = "д",
- ["е"] = "е", ["ё"] = "е", ["ж"] = "ж", ["з"] = "з", ["і"] = "і",
- ["й"] = "й", ["к"] = "к", ["л"] = "л", ["м"] = "м", ["н"] = "н",
- ["о"] = "о", ["п"] = "п", ["р"] = "р", ["с"] = "с", ["т"] = "т",
- ["у"] = "у", ["ў"] = "ў", ["ф"] = "ф", ["х"] = "х", ["ц"] = "ц",
- ["ч"] = "ч", ["ш"] = "ш", ["ы"] = "ы", ["ь"] = "ь", ["э"] = "э",
- ["ю"] = "ю", ["я"] = "я",
- },
- orders = {
- "а", "б", "в", "г", "д", "е", "ё", "ж", "з", "і",
- "й", "к", "л", "м", "н", "о", "п", "р", "с", "т",
- "у", "ў", "ф", "х", "ц", "ч", "ш", "ы", "ь", "э",
- "ю", "я",
- }
-}
-
---- Bulgarian
-
-definitions["bg"] = {
- entries = {
- ["а"] = "а", ["б"] = "б", ["в"] = "в", ["г"] = "г", ["д"] = "д",
- ["е"] = "е", ["ж"] = "ж", ["з"] = "з", ["и"] = "и", ["й"] = "й",
- ["к"] = "к", ["a"] = "a", ["л"] = "л", ["a"] = "a", ["м"] = "м",
- ["н"] = "н", ["о"] = "о", ["п"] = "п", ["р"] = "р", ["с"] = "с",
- ["т"] = "т", ["у"] = "у", ["ф"] = "ф", ["х"] = "х", ["ц"] = "ц",
- ["ч"] = "ч", ["ш"] = "ш", ["щ"] = "щ", ["ъ"] = "ъ", ["ь"] = "ь",
- ["ю"] = "ю", ["я"] = "я",
- },
- orders = {
- "а", "б", "в", "г", "д", "е", "ж", "з","и", "й",
- "к", "a", "л", "a", "м", "н", "о", "п", "р", "с",
- "т", "у", "ф", "х", "ц", "ч", "ш", "щ", "ъ", "ь",
- "ю", "я",
- }
-}
-
---- Old Church Slavonic
-
--- The language symbol “cu” is taken from the Wikipedia subdomain
--- cu.wikipedia.org.
-
-local uk, UK = utfchar(replacementoffset + 1), utfchar(replacementoffset + 11)
-
-definitions["cu"] = {
- replacements = {
- { "оу", uk }, { "ОУ", UK },
- },
- entries = {
- ["а"] = "а", ["б"] = "б", ["в"] = "в", ["г"] = "г", ["д"] = "д",
- ["є"] = "є", ["ж"] = "ж", ["ѕ"] = "ѕ", ["ꙃ"] = "ѕ", ["з"] = "з",
- ["ꙁ"] = "з", ["и"] = "и", ["і"] = "и", ["ї"] = "и", ["ћ"] = "ћ",
- ["к"] = "к", ["л"] = "л", ["м"] = "м", ["н"] = "н", ["о"] = "о",
- ["п"] = "п", ["р"] = "р", ["с"] = "с", ["т"] = "т", ["у"] = "у",
- ["ѹ"] = "у", ["ꙋ"] = "у", [uk] = "у", ["ф"] = "ф", ["х"] = "х",
- ["ѡ"] = "ѡ", ["ѿ"] = "ѡ", ["ѽ"] = "ѡ", ["ꙍ"] = "ѡ", ["ц"] = "ц",
- ["ч"] = "ч", ["ш"] = "ш", ["щ"] = "щ", ["ъ"] = "ъ", ["ы"] = "ы",
- ["ꙑ"] = "ы", ["ь"] = "ь", ["ѣ"] = "ѣ", ["ю"] = "ю", ["ꙗ"] = "ꙗ",
- ["ѥ"] = "ѥ", ["ѧ"] = "ѧ", ["ѩ"] = "ѩ", ["ѫ"] = "ѫ", ["ѭ"] = "ѭ",
- ["ѯ"] = "ѯ", ["ѱ"] = "ѱ", ["ѳ"] = "ѳ", ["ѵ"] = "ѵ", ["ѷ"] = "ѵ",
- },
- orders = {
- "а", "б", "в", "г", "д", "є", "ж", "ѕ", "ꙃ", "з", -- Dzělo, U+0292, alternative: dz U+01f3
- "ꙁ", "и", "і", "ї", "ћ", "к", "л", "м", "н", "о", -- Zemlja
- "п", "р", "с", "т", "у", "ѹ", "ꙋ", uk, "ф", "х", -- U+0478 uk, horizontal ligature, U+0479 uk, vertical ligature
- "ѡ", "ѿ", "ѽ", "ꙍ", "ц", "ч", "ш", "щ", "ъ", "ы", -- "ō", U+047f \, U+047d > Omega variants, U+064D /
- "ꙑ", "ь", "ѣ", "ю", "ꙗ", "ѥ", "ѧ", "ѩ", "ѫ", "ѭ", -- Old jery (U+a651) as used e.g. by the OCS Wikipedia. IOTIFIED A
- "ѯ", "ѱ", "ѳ", "ѵ", "ѷ",
- },
- upper = {
- uk = UK,
- },
- lower = {
- UK = uk,
- }
-}
-
---- Polish (including the letters q, v, x) Cf. ftp://ftp.gust.org.pl/pub/GUST/bulletin/03/02-bl.pdf.
-
-definitions["pl"] = {
- entries = {
- ["a"] = "a", ["ą"] = "ą", ["b"] = "b", ["c"] = "c", ["ć"] = "ć",
- ["d"] = "d", ["e"] = "e", ["ę"] = "ę", ["f"] = "f", ["g"] = "g",
- ["h"] = "h", ["i"] = "i", ["j"] = "j", ["k"] = "k", ["l"] = "l",
- ["ł"] = "ł", ["m"] = "m", ["n"] = "n", ["ń"] = "ń", ["o"] = "o",
- ["ó"] = "ó", ["p"] = "p", ["q"] = "q", ["r"] = "r", ["s"] = "s",
- ["ś"] = "ś", ["t"] = "t", ["u"] = "u", ["v"] = "v", ["w"] = "w",
- ["x"] = "x", ["y"] = "y", ["z"] = "z", ["ź"] = "ź", ["ż"] = "ż",
- },
- orders = {
- "a", "ą", "b", "c", "ć", "d", "e", "ę", "f", "g",
- "h", "i", "j", "k", "l", "ł", "m", "n", "ń", "o",
- "ó", "p", "q", "r", "s", "ś", "t", "u", "v", "w",
- "x", "y", "z", "ź", "ż",
- },
-}
-
--- Czech, modified to treat quantities and other secondary characteristics indifferently. Cf.
--- http://racek.vlada.cz/usneseni/usneseni_webtest.nsf/WebGovRes/0AD8FEF4CC04B7A4C12571B6006D69D0?OpenDocument
--- (2.4.3; via <http://cs.wikipedia.org/wiki/Abecední_řazení#.C4.8Ce.C5.A1tina>)
-
-local ch, CH = utfchar(replacementoffset + 1), utfchar(replacementoffset + 11)
-
-definitions["cz"] = {
- replacements = {
- { "ch", ch }, { "CH", CH }
- },
- entries = {
- ["a"] = "a", ["á"] = "a", ["b"] = "b", ["c"] = "c", ["č"] = "č",
- ["d"] = "d", ["ď"] = "d", ["e"] = "e", ["é"] = "e", ["ě"] = "e",
- ["f"] = "f", ["g"] = "g", ["h"] = "h", [ch] = "ch", ["i"] = "i",
- ["í"] = "i", ["j"] = "j", ["k"] = "k", ["l"] = "l", ["m"] = "m",
- ["n"] = "n", ["ň"] = "n", ["o"] = "o", ["ó"] = "o", ["p"] = "p",
- ["q"] = "q", ["r"] = "r", ["ř"] = "ř", ["s"] = "s", ["š"] = "š",
- ["t"] = "t", ["ť"] = "t", ["u"] = "u", ["ú"] = "u", ["ů"] = "u",
- ["v"] = "v", ["w"] = "w", ["x"] = "x", ["y"] = "y", ["ý"] = "y",
- ["z"] = "z", ["ž"] = "ž",
- },
- orders = {
- "a", "á", "b", "c", "č", "d", "ď", "e", "é", "ě",
- "f", "g", "h", ch, "i", "í", "j", "k", "l", "m",
- "n", "ň", "o", "ó", "p", "q", "r", "ř", "s", "š",
- "t", "ť", "u", "ú", "ů", "v", "w", "x", "y", "ý",
- "z", "ž",
- },
- upper = {
- ch = CH,
- },
- lower = {
- CH = ch,
- }
-}
-
-definitions["cs"] = { parent = "cz" }
-
---- Slovak.
-
--- Vowel and consonant quantities, "ď", "ľ", "ň", "ť", "ô", and "ä" are treated
--- indifferently as their base character, as in my dictionary. If you prefer them
--- to affect collation order, then use the values given in the comments. We could
--- define an additional vector for that.
-
-local dz, DZ = utfchar(replacementoffset + 1), utfchar(replacementoffset + 11)
-local dzh, DZH = utfchar(replacementoffset + 2), utfchar(replacementoffset + 12)
-local ch, CH = utfchar(replacementoffset + 3), utfchar(replacementoffset + 13)
-
-definitions["sk"] = {
- replacements = {
- { "dz", dz }, { "dz", DZ },
- { "dž", dzh }, { "dž", DZH },
- { "ch", ch }, { "ch", CH },
- },
- entries = {
- ["a"] = "a", ["á"] = "a", ["ä"] = "a", ["b"] = "b", ["c"] = "c",
- ["č"] = "č", ["d"] = "d", ["ď"] = "d", [dz] = "dz", [dzh] = "dž",
- ["e"] = "e", ["é"] = "e", ["f"] = "f", ["g"] = "g", ["h"] = "h",
- [ch] = "ch", ["i"] = "i", ["í"] = "i", ["j"] = "j", ["k"] = "k",
- ["l"] = "l", ["ĺ"] = "l", ["ľ"] = "l", ["m"] = "m", ["n"] = "n",
- ["ň"] = "n", ["o"] = "o", ["ó"] = "o", ["ô"] = "o", ["p"] = "p",
- ["q"] = "q", ["r"] = "r", ["ŕ"] = "r", ["s"] = "s", ["š"] = "š",
- ["t"] = "t", ["ť"] = "t", ["u"] = "u", ["ú"] = "u", ["v"] = "v",
- ["w"] = "w", ["x"] = "x", ["y"] = "y", ["ý"] = "y", ["z"] = "z",
- ["ž"] = "ž",
- },
- orders = {
- "a", "á", "ä", "b", "c", "č", "d", "ď", dz, dzh,
- "e", "é", "f", "g", "h", ch, "i", "í", "j", "k",
- "l", "ĺ", "ľ", "m", "n", "ň", "o", "ó", "ô", "p",
- "q", "r", "ŕ", "s", "š", "t", "ť", "u", "ú", "v",
- "w", "x", "y", "ý", "z", "ž",
- },
- upper = {
- dz = DZ, dzh = DZH, ch = CH,
- },
- lower = {
- DZ = dz, DZH = dzh, CH = ch,
- }
-}
-
---- Croatian
-
-local dzh, DZH = utfchar(replacementoffset + 1), utfchar(replacementoffset + 11)
-local lj, LJ = utfchar(replacementoffset + 2), utfchar(replacementoffset + 12)
-local nj, NJ = utfchar(replacementoffset + 3), utfchar(replacementoffset + 13)
-
-definitions["hr"] = {
- replacements = {
- { "dž", dzh }, { "DŽ", DZH },
- { "lj", lj }, { "LJ", LJ },
- { "nj", nj }, { "NJ", NJ },
- },
- entries = {
- ["a"] = "a", ["b"] = "b", ["c"] = "c", ["č"] = "č", ["ć"] = "ć",
- ["d"] = "d", [dzh] = "dž", ["đ"] = "đ", ["e"] = "e", ["f"] = "f",
- ["g"] = "g", ["h"] = "h", ["i"] = "i", ["j"] = "j", ["k"] = "k",
- ["l"] = "l", [lj] = "lj", ["m"] = "m", ["n"] = "n", [nj] = "nj",
- ["o"] = "o", ["p"] = "p", ["r"] = "r", ["s"] = "s", ["š"] = "š",
- ["t"] = "t", ["u"] = "u", ["v"] = "v", ["z"] = "z", ["ž"] = "ž",
- },
- orders = {
- "a", "b", "c", "č", "ć", "d", dzh, "đ", "e", "f",
- "g", "h", "i", "j", "k", "l", lj, "m", "n", nj,
- "o", "p", "r", "s", "š", "t", "u", "v", "z", "ž",
- },
- upper = {
- dzh = DZH, lj = LJ, nj = NJ,
- },
- lower = {
- DZH = dzh, LJ = lj, NJ = nj,
- }
-}
-
-
---- Serbian
-
-definitions["sr"] = {
- entries = {
- ["а"] = "а", ["б"] = "б", ["в"] = "в", ["г"] = "г", ["д"] = "д",
- ["ђ"] = "ђ", ["е"] = "е", ["ж"] = "ж", ["з"] = "з", ["и"] = "и",
- ["ј"] = "ј", ["к"] = "к", ["л"] = "л", ["љ"] = "љ", ["м"] = "м",
- ["н"] = "н", ["њ"] = "њ", ["о"] = "о", ["п"] = "п", ["р"] = "р",
- ["с"] = "с", ["т"] = "т", ["ћ"] = "ћ", ["у"] = "у", ["ф"] = "ф",
- ["х"] = "х", ["ц"] = "ц", ["ч"] = "ч", ["џ"] = "џ",
- ["ш"] = "ш",
- },
- orders = {
- "а", "б", "в", "г", "д", "ђ", "е", "ж", "з", "и",
- "ј", "к", "л", "љ", "м", "н", "њ", "о", "п", "р",
- "с", "т", "ћ", "у", "ф", "х", "ц", "ч", "џ", "ш",
- }
-}
-
---- Transliteration: Russian|ISO9-1995
-
--- Keeping the same collation order as Russian (v.s.).
--- Matches the tables from:
--- http://bitbucket.org/phg/transliterator/src/tip/tex/context/third/transliterator/trans_tables_iso9.lua
-
-local yer = utfchar(replacementoffset + 1)
-
-definitions["ru-iso9"] = {
- replacements = {
- { "''", yer },
- },
- entries = {
- ["a"] = "a", ["b"] = "b", ["v"] = "v", ["g"] = "g", ["d"] = "d",
- ["e"] = "e", ["ë"] = "ë", ["ž"] = "ž", ["z"] = "z", ["i"] = "i",
- ["ì"] = "ì", ["j"] = "j", ["k"] = "k", ["l"] = "l", ["m"] = "m",
- ["n"] = "n", ["o"] = "o", ["p"] = "p", ["r"] = "r", ["s"] = "s",
- ["t"] = "t", ["u"] = "u", ["f"] = "f", ["h"] = "h", ["c"] = "c",
- ["č"] = "č", ["š"] = "š", ["ŝ"] = "ŝ", ["ʺ"] = "ʺ", [yer] = "ʺ",
- ["y"] = "y", ["ʹ"] = "ʹ", ["'"] = "ʹ", ["ě"] = "ě", ["è"] = "è",
- ["û"] = "û", ["â"] = "â", ["û"] = "û", ["â"] = "â",
- },
- orders = {
- "a", "b", "v", "g", "d", "e", "ë", "ž", "z", "i",
- "ì", "j", "k", "l", "m", "n", "o", "p", "r", "s",
- "t", "u", "f", "h", "c", "č", "š", "ŝ", "ʺ", yer,
- "y", "ʹ", "'", "ě", "è", "û", "â", "û", "â",
- }
-}
-
---- Transliteration: Old Slavonic|scientific
-
--- Matches the tables from:
--- http://bitbucket.org/phg/transliterator/src/tip/tex/context/third/transliterator/trans_tables_scntfc.lua
-
-local uk, UK = utfchar(replacementoffset + 1), utfchar(replacementoffset + 21)
-local tshe, TSHE = utfchar(replacementoffset + 2), utfchar(replacementoffset + 22)
-local sht, SHT = utfchar(replacementoffset + 3), utfchar(replacementoffset + 23)
-local ju, JU = utfchar(replacementoffset + 4), utfchar(replacementoffset + 24)
-local ja, JA = utfchar(replacementoffset + 5), utfchar(replacementoffset + 25)
-local je, JE = utfchar(replacementoffset + 6), utfchar(replacementoffset + 26)
-local ijus, IJUS = utfchar(replacementoffset + 7), utfchar(replacementoffset + 27)
-local ibigjus, IBIGJUS = utfchar(replacementoffset + 8), utfchar(replacementoffset + 28)
-local xi, XI = utfchar(replacementoffset + 9), utfchar(replacementoffset + 29)
-local psi, PSI = utfchar(replacementoffset + 10), utfchar(replacementoffset + 30)
-local theta, THETA = utfchar(replacementoffset + 11), utfchar(replacementoffset + 31)
-local shch, SHCH = utfchar(replacementoffset + 12), utfchar(replacementoffset + 32)
-
-definitions["ocs-scn"] = {
- replacements = {
- { "ou", uk }, { "OU", UK },
- { "g’", tshe }, { "G’", TSHE },
- { "št", sht }, { "ŠT", SHT },
- { "ju", ju }, { "JU", JU },
- { "ja", ja }, { "JA", JA },
- { "je", je }, { "JE", JE },
- { "ję", ijus }, { "JĘ", IJUS },
- { "jǫ", ibigjus }, { "JǪ", IBIGJUS },
- { "ks", xi }, { "KS", XI },
- { "ps", psi }, { "PS", PSI },
- { "th", theta }, { "TH", THETA },
- { "šč", shch }, { "ŠČ", SHCH },
- },
- entries = {
- ["a"] = "a", ["b"] = "b", ["v"] = "v", ["g"] = "g", ["d"] = "d",
- ["e"] = "e", ["ž"] = "ž", ["ʒ"] = "ʒ", ["z"] = "z", ["i"] = "i",
- ["ï"] = "ï", [tshe] = "g’", ["k"] = "k", ["l"] = "l", ["m"] = "m",
- ["n"] = "n", ["o"] = "o", ["p"] = "p", ["r"] = "r", ["s"] = "s",
- ["t"] = "t", ["u"] = "u", ["f"] = "f", ["x"] = "x", ["o"] = "o",
- ["c"] = "c", ["č"] = "č", ["š"] = "š", [sht] = "št", [shch] = "šč",
- ["ъ"] = "ъ", ["y"] = "y", [uk] = "y", ["ь"] = "ь", ["ě"] = "ě",
- [ju] = "ju", [ja] = "ja", [je] = "je", ["ę"] = "ę", [ijus] = "ję",
- ["ǫ"] = "ǫ", [ibigjus] = "jǫ", [xi] = "ks", [psi] = "ps", [theta] = "th",
- ["ü"] = "ü",
- },
- orders = {
- "a", "b", "v", "g", "d", "e", "ž", "ʒ", "z", "i", "ï",
- tshe, "k", "l", "m", "n", "o", "p", "r", "s", "t", "u",
- "f", "x", "o", "c", "č", "š", sht, shch, "ъ", "y", uk,
- "ь", "ě", ju, ja, je, "ę", ijus, "ǫ", ibigjus, xi, psi,
- theta, "ü",
- },
- upper = {
- uk = UK, tshe = TSHE, sht = SHT, ju = JU, ja = JA, je = JE, ijus = IJUS, ibigjus = IBIGJUS, xi = XI, psi = PSI, theta = THETA, shch = SHCH,
- },
- lower = {
- UK = uk, TSHE = tshe, SHT = sht, JU = ju, JA = ja, JE = je, IJUS = ijus, IBIGJUS = ibigjus, XI = xi, PSI = psi, THETA = theta, SHCH = shch,
- },
-}
-
-
---- Norwegian (bokmål).
-
-definitions["no"] = {
- entries = {
- ["a"] = "a", ["b"] = "b", ["c"] = "c", ["d"] = "d", ["e"] = "e",
- ["f"] = "f", ["g"] = "g", ["h"] = "h", ["i"] = "i", ["j"] = "j",
- ["k"] = "k", ["l"] = "l", ["m"] = "m", ["n"] = "n", ["o"] = "o",
- ["p"] = "p", ["q"] = "q", ["r"] = "r", ["s"] = "s", ["t"] = "t",
- ["u"] = "u", ["v"] = "v", ["w"] = "w", ["x"] = "x", ["y"] = "y",
- ["z"] = "z", ["æ"] = "æ", ["ø"] = "ø", ["å"] = "å",
- },
- orders = {
- "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
- "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
- "u", "v", "w", "x", "y", "z", "æ", "ø", "å",
- }
-}
-
---- Danish (-> Norwegian).
-
-definitions["da"] = { parent = "no" }
-
---- Swedish
-
-definitions["sv"] = {
- entries = {
- ["a"] = "a", ["b"] = "b", ["c"] = "c", ["d"] = "d", ["e"] = "e",
- ["f"] = "f", ["g"] = "g", ["h"] = "h", ["i"] = "i", ["j"] = "j",
- ["k"] = "k", ["l"] = "l", ["m"] = "m", ["n"] = "n", ["o"] = "o",
- ["p"] = "p", ["q"] = "q", ["r"] = "r", ["s"] = "s", ["t"] = "t",
- ["u"] = "u", ["v"] = "v", ["w"] = "w", ["x"] = "x", ["y"] = "y",
- ["z"] = "z", ["å"] = "å", ["ä"] = "ä", ["ö"] = "ö",
- },
- orders = {
- "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
- "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
- "u", "v", "w", "x", "y", "z", "å", "ä", "ö",
- }
-}
-
---- Icelandic
-
--- Treating quantities as allographs.
-
-definitions["is"] = {
- entries = {
- ["a"] = "a", ["á"] = "a", ["b"] = "b", ["d"] = "d", ["ð"] = "ð",
- ["e"] = "e", ["é"] = "e", ["f"] = "f", ["g"] = "g", ["h"] = "h",
- ["i"] = "i", ["í"] = "i", ["j"] = "j", ["k"] = "k", ["l"] = "l",
- ["m"] = "m", ["n"] = "n", ["o"] = "o", ["ó"] = "o", ["p"] = "p",
- ["r"] = "r", ["s"] = "s", ["t"] = "t", ["u"] = "u", ["ú"] = "u",
- ["v"] = "v", ["x"] = "x", ["y"] = "y", ["ý"] = "y", ["þ"] = "þ",
- ["æ"] = "æ", ["ö"] = "ö",
- },
- orders = {
- "a", "á", "b", "d", "ð", "e", "é", "f", "g", "h",
- "i", "í", "j", "k", "l", "m", "n", "o", "ó", "p",
- "r", "s", "t", "u", "ú", "v", "x", "y", "ý", "þ",
- "æ", "ö",
- },
-}
-
---- Greek
-
-definitions["gr"] = {
- entries = {
- ["α"] = "α", ["ά"] = "α", ["ὰ"] = "α", ["ᾶ"] = "α", ["ᾳ"] = "α",
- ["ἀ"] = "α", ["ἁ"] = "α", ["ἄ"] = "α", ["ἂ"] = "α", ["ἆ"] = "α",
- ["ἁ"] = "α", ["ἅ"] = "α", ["ἃ"] = "α", ["ἇ"] = "α", ["ᾁ"] = "α",
- ["ᾴ"] = "α", ["ᾲ"] = "α", ["ᾷ"] = "α", ["ᾄ"] = "α", ["ᾂ"] = "α",
- ["ᾅ"] = "α", ["ᾃ"] = "α", ["ᾆ"] = "α", ["ᾇ"] = "α", ["β"] = "β",
- ["γ"] = "γ", ["δ"] = "δ", ["ε"] = "ε", ["έ"] = "ε", ["ὲ"] = "ε",
- ["ἐ"] = "ε", ["ἔ"] = "ε", ["ἒ"] = "ε", ["ἑ"] = "ε", ["ἕ"] = "ε",
- ["ἓ"] = "ε", ["ζ"] = "ζ", ["η"] = "η", ["η"] = "η", ["ή"] = "η",
- ["ὴ"] = "η", ["ῆ"] = "η", ["ῃ"] = "η", ["ἠ"] = "η", ["ἤ"] = "η",
- ["ἢ"] = "η", ["ἦ"] = "η", ["ᾐ"] = "η", ["ἡ"] = "η", ["ἥ"] = "η",
- ["ἣ"] = "η", ["ἧ"] = "η", ["ᾑ"] = "η", ["ῄ"] = "η", ["ῂ"] = "η",
- ["ῇ"] = "η", ["ᾔ"] = "η", ["ᾒ"] = "η", ["ᾕ"] = "η", ["ᾓ"] = "η",
- ["ᾖ"] = "η", ["ᾗ"] = "η", ["θ"] = "θ", ["ι"] = "ι", ["ί"] = "ι",
- ["ὶ"] = "ι", ["ῖ"] = "ι", ["ἰ"] = "ι", ["ἴ"] = "ι", ["ἲ"] = "ι",
- ["ἶ"] = "ι", ["ἱ"] = "ι", ["ἵ"] = "ι", ["ἳ"] = "ι", ["ἷ"] = "ι",
- ["ϊ"] = "ι", ["ΐ"] = "ι", ["ῒ"] = "ι", ["ῗ"] = "ι", ["κ"] = "κ",
- ["λ"] = "λ", ["μ"] = "μ", ["ν"] = "ν", ["ξ"] = "ξ", ["ο"] = "ο",
- ["ό"] = "ο", ["ὸ"] = "ο", ["ὀ"] = "ο", ["ὄ"] = "ο", ["ὂ"] = "ο",
- ["ὁ"] = "ο", ["ὅ"] = "ο", ["ὃ"] = "ο", ["π"] = "π", ["ρ"] = "ρ",
- ["ῤ"] = "ῤ", ["ῥ"] = "ῥ", ["σ"] = "σ", ["ς"] = "ς", ["τ"] = "τ",
- ["υ"] = "υ", ["ύ"] = "υ", ["ὺ"] = "υ", ["ῦ"] = "υ", ["ὐ"] = "υ",
- ["ὔ"] = "υ", ["ὒ"] = "υ", ["ὖ"] = "υ", ["ὑ"] = "υ", ["ὕ"] = "υ",
- ["ὓ"] = "υ", ["ὗ"] = "υ", ["ϋ"] = "υ", ["ΰ"] = "υ", ["ῢ"] = "υ",
- ["ῧ"] = "υ", ["φ"] = "φ", ["χ"] = "χ", ["ψ"] = "ω", ["ω"] = "ω",
- ["ώ"] = "ω", ["ὼ"] = "ω", ["ῶ"] = "ω", ["ῳ"] = "ω", ["ὠ"] = "ω",
- ["ὤ"] = "ω", ["ὢ"] = "ω", ["ὦ"] = "ω", ["ᾠ"] = "ω", ["ὡ"] = "ω",
- ["ὥ"] = "ω", ["ὣ"] = "ω", ["ὧ"] = "ω", ["ᾡ"] = "ω", ["ῴ"] = "ω",
- ["ῲ"] = "ω", ["ῷ"] = "ω", ["ᾤ"] = "ω", ["ᾢ"] = "ω", ["ᾥ"] = "ω",
- ["ᾣ"] = "ω", ["ᾦ"] = "ω", ["ᾧ"] = "ω",
- },
- orders = {
- "α", "ά", "ὰ", "ᾶ", "ᾳ", "ἀ", "ἁ", "ἄ", "ἂ", "ἆ",
- "ἁ", "ἅ", "ἃ", "ἇ", "ᾁ", "ᾴ", "ᾲ", "ᾷ", "ᾄ", "ᾂ",
- "ᾅ", "ᾃ", "ᾆ", "ᾇ", "β", "γ", "δ", "ε", "έ", "ὲ",
- "ἐ", "ἔ", "ἒ", "ἑ", "ἕ", "ἓ", "ζ", "η", "η", "ή",
- "ὴ", "ῆ", "ῃ", "ἠ", "ἤ", "ἢ", "ἦ", "ᾐ", "ἡ", "ἥ",
- "ἣ", "ἧ", "ᾑ", "ῄ", "ῂ", "ῇ", "ᾔ", "ᾒ", "ᾕ", "ᾓ",
- "ᾖ", "ᾗ", "θ", "ι", "ί", "ὶ", "ῖ", "ἰ", "ἴ", "ἲ",
- "ἶ", "ἱ", "ἵ", "ἳ", "ἷ", "ϊ", "ΐ", "ῒ", "ῗ", "κ",
- "λ", "μ", "ν", "ξ", "ο", "ό", "ὸ", "ὀ", "ὄ", "ὂ",
- "ὁ", "ὅ", "ὃ", "π", "ρ", "ῤ", "ῥ", "σ", "ς", "τ",
- "υ", "ύ", "ὺ", "ῦ", "ὐ", "ὔ", "ὒ", "ὖ", "ὑ", "ὕ",
- "ὓ", "ὗ", "ϋ", "ΰ", "ῢ", "ῧ", "φ", "χ", "ψ", "ω",
- "ώ", "ὼ", "ῶ", "ῳ", "ὠ", "ὤ", "ὢ", "ὦ", "ᾠ", "ὡ",
- "ὥ", "ὣ", "ὧ", "ᾡ", "ῴ", "ῲ", "ῷ", "ᾤ", "ᾢ", "ᾥ",
- "ᾣ", "ᾦ", "ᾧ",
- },
-}
-
---- Latin
-
--- Treating the post-classical fricatives “j” and “v” as “i” and “u”
--- respectively.
-
-definitions["la"] = {
- replacements = {
- { "æ", "ae" }, { "Æ", "AE" },
- },
- entries = {
- ["a"] = "a", ["ā"] = "a", ["ă"] = "a", ["b"] = "b", ["c"] = "c",
- ["d"] = "d", ["e"] = "e", ["ē"] = "e", ["ĕ"] = "e", ["f"] = "f",
- ["g"] = "g", ["h"] = "h", ["i"] = "i", ["ī"] = "i", ["ĭ"] = "i",
- ["j"] = "i", ["k"] = "k", ["l"] = "l", ["m"] = "m", ["n"] = "n",
- ["o"] = "o", ["ō"] = "o", ["ŏ"] = "o", ["p"] = "p", ["q"] = "q",
- ["r"] = "r", ["s"] = "s", ["t"] = "t", ["u"] = "u", ["ū"] = "u",
- ["ŭ"] = "u", ["v"] = "u", ["w"] = "w", ["x"] = "x", ["y"] = "y",
- ["ȳ"] = "y", ["y̆"] = "y", ["z"] = "z",
- },
- orders = {
- "a", "ā", "ă", "b", "c", "d", "e", "ē", "ĕ", "f",
- "g", "h", "i", "ī", "ĭ", "j", "k", "l", "m", "n",
- "o", "ō", "ŏ", "p", "q", "r", "s", "t", "u", "ū",
- "ŭ", "v", "w", "x", "y", "ȳ", "y̆", "z",
- }
-}
-
---- Italian
-
-definitions["it"] = {
- entries = {
- ["a"] = "a", ["á"] = "a", ["b"] = "b", ["c"] = "c", ["d"] = "d",
- ["e"] = "e", ["é"] = "e", ["è"] = "e", ["f"] = "f", ["g"] = "g",
- ["h"] = "h", ["i"] = "i", ["í"] = "i", ["ì"] = "i", ["j"] = "i",
- ["k"] = "k", ["l"] = "l", ["m"] = "m", ["n"] = "n", ["o"] = "o",
- ["ó"] = "o", ["ò"] = "o", ["p"] = "p", ["q"] = "q", ["r"] = "r",
- ["s"] = "s", ["t"] = "t", ["u"] = "u", ["ú"] = "u", ["ù"] = "u",
- ["v"] = "u", ["w"] = "w", ["x"] = "x", ["y"] = "y", ["z"] = "z",
- },
- orders = {
- "a", "á", "b", "c", "d", "e", "é", "è", "f", "g",
- "h", "i", "í", "ì", "j", "k", "l", "m", "n", "o",
- "ó", "ò", "p", "q", "r", "s", "t", "u", "ú", "ù",
- "v", "w", "x", "y", "z",
- }
-}
-
---- Romanian
-
-definitions["ro"] = {
- entries = {
- ["a"] = "a", ["ă"] = "ă", ["â"] = "â", ["b"] = "b", ["c"] = "c",
- ["d"] = "d", ["e"] = "e", ["f"] = "f", ["g"] = "g", ["h"] = "h",
- ["i"] = "i", ["î"] = "î", ["j"] = "j", ["k"] = "k", ["l"] = "l",
- ["m"] = "m", ["n"] = "n", ["o"] = "o", ["p"] = "p", ["q"] = "q",
- ["r"] = "r", ["s"] = "s", ["ș"] = "ș", ["t"] = "t", ["ț"] = "ț",
- ["u"] = "u", ["v"] = "v", ["w"] = "w", ["x"] = "x", ["y"] = "y",
- ["z"] = "z",
- },
- orders = {
- "a", "ă", "â", "b", "c", "d", "e", "f", "g", "h",
- "i", "î", "j", "k", "l", "m", "n", "o", "p", "q",
- "r", "s", "ș", "t", "ț", "u", "v", "w", "x", "y",
- "z",
- }
-}
-
---- Spanish
-
-definitions["es"] = {
- entries = {
- ["a"] = "a", ["á"] = "a", ["b"] = "b", ["c"] = "c", ["d"] = "d",
- ["e"] = "e", ["é"] = "e", ["f"] = "f", ["g"] = "g", ["h"] = "h",
- ["i"] = "i", ["í"] = "i", ["j"] = "j", ["k"] = "k", ["l"] = "l",
- ["m"] = "m", ["n"] = "n", ["ñ"] = "ñ", ["o"] = "o", ["ó"] = "o",
- ["p"] = "p", ["q"] = "q", ["r"] = "r", ["s"] = "s", ["t"] = "t",
- ["u"] = "u", ["ú"] = "u", ["ü"] = "u", ["v"] = "v", ["w"] = "w",
- ["x"] = "x", ["y"] = "y", ["z"] = "z",
- },
- orders = {
- "a", "á", "b", "c", "d", "e", "é", "f", "g", "h",
- "i", "í", "j", "k", "l", "m", "n", "ñ", "o", "ó",
- "p", "q", "r", "s", "t", "u", "ú", "ü", "v", "w",
- "x", "y", "z",
- }
-}
-
---- Portuguese
-
-definitions["pt"] = {
- entries = {
- ["a"] = "a", ["á"] = "a", ["â"] = "a", ["ã"] = "a", ["à"] = "a",
- ["b"] = "b", ["c"] = "c", ["ç"] = "c", ["d"] = "d", ["e"] = "e",
- ["é"] = "e", ["ê"] = "e", ["f"] = "f", ["g"] = "g", ["h"] = "h",
- ["i"] = "i", ["í"] = "i", ["j"] = "j", ["k"] = "k", ["l"] = "l",
- ["m"] = "m", ["n"] = "n", ["o"] = "o", ["ó"] = "o", ["ô"] = "o",
- ["õ"] = "o", ["p"] = "p", ["q"] = "q", ["r"] = "r", ["s"] = "s",
- ["t"] = "t", ["u"] = "u", ["ú"] = "u", ["ü"] = "u", ["v"] = "v",
- ["w"] = "w", ["x"] = "x", ["y"] = "y", ["z"] = "z",
- },
- orders = {
- "a", "á", "â", "ã", "à", "b", "c", "ç", "d", "e",
- "é", "ê", "f", "g", "h", "i", "í", "j", "k", "l",
- "m", "n", "o", "ó", "ô", "õ", "p", "q", "r", "s",
- "t", "u", "ú", "ü", "v", "w", "x", "y", "z",
- }
-}
-
---- Lithuanian
-
-local ch, CH = utfchar(replacementoffset + 1), utfchar(replacementoffset + 11)
-
-definitions["lt"] = {
- replacements = {
- { "ch", ch }, { "CH", CH}
- },
- entries = {
- ["a"] = "a", ["ą"] = "a", ["b"] = "b", ["c"] = "c", [ch ] = "c",
- ["č"] = "č", ["d"] = "d", ["e"] = "e", ["ę"] = "e", ["ė"] = "e",
- ["f"] = "f", ["g"] = "g", ["h"] = "h", ["i"] = "i", ["į"] = "i",
- ["y"] = "i", ["j"] = "j", ["k"] = "k", ["l"] = "l", ["m"] = "m",
- ["n"] = "n", ["o"] = "o", ["p"] = "p", ["r"] = "r", ["s"] = "s",
- ["š"] = "š", ["t"] = "t", ["u"] = "u", ["ų"] = "u", ["ū"] = "u",
- ["v"] = "v", ["z"] = "z", ["ž"] = "ž",
- },
- orders = {
- "a", "ą", "b", "c", ch, "č", "d", "e", "ę", "ė",
- "f", "g", "h", "i", "į", "y", "j", "k", "l", "m",
- "n", "o", "p", "r", "s", "š", "t", "u", "ų", "ū",
- "v", "z", "ž",
- },
- lower = {
- ch = CH,
- },
- upper = {
- CH = ch,
- },
-}
-
---- Latvian
-
-definitions["lv"] = {
- entries = {
- ["a"] = "a", ["ā"] = "a", ["b"] = "b", ["c"] = "c", ["č"] = "č",
- ["d"] = "d", ["e"] = "e", ["ē"] = "e", ["f"] = "f", ["g"] = "g",
- ["ģ"] = "ģ", ["h"] = "h", ["i"] = "i", ["ī"] = "i", ["j"] = "j",
- ["k"] = "k", ["ķ"] = "ķ", ["l"] = "l", ["ļ"] = "ļ", ["m"] = "m",
- ["n"] = "n", ["ņ"] = "ņ", ["o"] = "o", ["ō"] = "o", ["p"] = "p",
- ["r"] = "r", ["ŗ"] = "ŗ", ["s"] = "s", ["š"] = "š", ["t"] = "t",
- ["u"] = "u", ["ū"] = "u", ["v"] = "v", ["z"] = "z", ["ž"] = "ž",
- },
- orders = {
- "a", "ā", "b", "c", "č", "d", "e", "ē", "f", "g",
- "ģ", "h", "i", "ī", "j", "k", "ķ", "l", "ļ", "m",
- "n", "ņ", "o", "ō", "p", "r", "ŗ", "s", "š", "t",
- "u", "ū", "v", "z", "ž",
- }
-}
-
---- Hungarian
-
--- Helpful but disturbing:
--- http://en.wikipedia.org/wiki/Hungarian_alphabet#Alphabetical_ordering_.28collation.29
--- (In short: you'd have to analyse word-compounds to realize a correct order
--- for sequences like “nny”, “ssz”, and “zsz”. This is left as an exercise to
--- the reader…)
-
-local cs, CS = utfchar(replacementoffset + 1), utfchar(replacementoffset + 11)
-local dz, DZ = utfchar(replacementoffset + 2), utfchar(replacementoffset + 12)
-local dzs, DZS = utfchar(replacementoffset + 3), utfchar(replacementoffset + 13)
-local gy, GY = utfchar(replacementoffset + 4), utfchar(replacementoffset + 14)
-local ly, LY = utfchar(replacementoffset + 5), utfchar(replacementoffset + 15)
-local ny, NY = utfchar(replacementoffset + 6), utfchar(replacementoffset + 16)
-local sz, SZ = utfchar(replacementoffset + 7), utfchar(replacementoffset + 17)
-local ty, TY = utfchar(replacementoffset + 8), utfchar(replacementoffset + 18)
-local zs, ZS = utfchar(replacementoffset + 9), utfchar(replacementoffset + 19)
-
-definitions["hu"] = {
- replacements = {
- { "cs", cs }, { "CS", CS },
- { "dz", dz }, { "DZ", DZ },
- { "dzs", dzs }, { "DZS", DZS },
- { "gy", gy }, { "GY", GY },
- { "ly", ly }, { "LY", LY },
- { "ny", ny }, { "NY", NY },
- { "sz", sz }, { "SZ", SZ },
- { "ty", ty }, { "TY", TY },
- { "zs", zs }, { "ZS", ZS },
- },
- entries = {
- ["a"] = "a", ["á"] = "a", ["b"] = "b", ["c"] = "c", [cs ] = "cs",
- ["d"] = "d", [dz ] = "dz", [dzs] = "dzs", ["e"] = "e", ["é"] = "e",
- ["f"] = "f", ["g"] = "g", [gy ] = "gy", ["h"] = "h", ["i"] = "i",
- ["í"] = "i", ["j"] = "j", ["k"] = "k", ["l"] = "l", [ly ] = "ly",
- ["m"] = "m", ["n"] = "n", [ny ] = "ny", ["o"] = "o", ["ó"] = "o",
- ["ö"] = "ö", ["ő"] = "ö", ["p"] = "p", ["q"] = "q", ["r"] = "r",
- ["s"] = "s", [sz ] = "sz", ["t"] = "t", [ty ] = "ty", ["u"] = "u",
- ["ú"] = "u", ["ü"] = "ü", ["ű"] = "ü", ["v"] = "v", ["w"] = "w",
- ["x"] = "x", ["y"] = "y", ["z"] = "z", [zs ] = "zs",
- },
- orders = {
- "a", "á", "b", "c", cs, "d", dz, dzs, "e", "é",
- "f", "g", gy, "h", "i", "í", "j", "k", "l", ly,
- "m", "n", ny, "o", "ó", "ö", "ő", "p", "q", "r",
- "s", sz, "t", ty, "u", "ú", "ü", "ű", "v", "w",
- "x", "y", "z", zs,
- },
- lower = {
- CS = cs, DZ = dz, DZS = dzs, GY = gy, LY = ly, NY = ny, SZ = sz, TY = ty, ZS = zs,
- },
- upper = {
- cs = CS, dz = DZ, dzs = DZS, gy = GY, ly = LY, ny = NY, sz = SZ, ty = TY, zs = ZS,
- },
-}
-
---- Estonian
-
-definitions["et"] = {
- entries = { -- w x y are used for foreign words only
- ["a"] = "a", ["b"] = "b", ["d"] = "d", ["e"] = "e", ["f"] = "f",
- ["g"] = "g", ["h"] = "h", ["i"] = "i", ["j"] = "j", ["k"] = "k",
- ["l"] = "l", ["m"] = "m", ["n"] = "n", ["o"] = "o", ["p"] = "p",
- ["r"] = "r", ["s"] = "s", ["š"] = "š", ["z"] = "z", ["ž"] = "ž",
- ["t"] = "t", ["u"] = "u", ["v"] = "v", ["w"] = "v", ["õ"] = "õ",
- ["ä"] = "ä", ["ö"] = "ö", ["ü"] = "ü", ["x"] = "x", ["y"] = "y",
- },
- orders = {
- "a", "b", "d", "e", "f", "g", "h", "i", "j", "k",
- "l", "m", "n", "o", "p", "r", "s", "š", "z", "ž",
- "t", "u", "v", "w", "õ", "ä", "ö", "ü", "x", "y",
- }
-}
-
---- Korean
-
-local fschars = characters.fschars
-
-local function firstofsplit(first)
- local fs = fschars[first] or first -- leadconsonant
- return fs, fs -- entry, tag
-end
-
-definitions["kr"] = {
- firstofsplit = firstofsplit,
- orders = {
- "ㄱ", "ㄴ", "ㄷ", "ㄹ", "ㅁ", "ㅂ", "ㅅ", "ㅇ", "ㅈ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ",
- "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
- "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
- }
-}
-
--- Japanese
-
-definitions["jp"] = {
- replacements = {
- { "ぁ", "あ" }, { "ぃ", "い" },
- { "ぅ", "う" }, { "ぇ", "え" },
- { "ぉ", "お" }, { "っ", "つ" },
- { "ゃ", "や" }, { "ゅ", "ゆ" },
- { "ょ", "よ" },
- },
- entries = {
- ["あ"] = "あ", ["い"] = "い", ["う"] = "う", ["え"] = "え", ["お"] = "お",
- ["か"] = "か", ["き"] = "き", ["く"] = "く", ["け"] = "け", ["こ"] = "こ",
- ["さ"] = "さ", ["し"] = "し", ["す"] = "す", ["せ"] = "せ", ["そ"] = "そ",
- ["た"] = "た", ["ち"] = "ち", ["つ"] = "つ", ["て"] = "て", ["と"] = "と",
- ["な"] = "な", ["に"] = "に", ["ぬ"] = "ぬ", ["ね"] = "ね", ["の"] = "の",
- ["は"] = "は", ["ひ"] = "ひ", ["ふ"] = "ふ", ["へ"] = "へ", ["ほ"] = "ほ",
- ["ま"] = "ま", ["み"] = "み", ["む"] = "む", ["め"] = "め", ["も"] = "も",
- ["や"] = "や", ["ゆ"] = "ゆ", ["よ"] = "よ",
- ["ら"] = "ら", ["り"] = "り", ["る"] = "る", ["れ"] = "れ", ["ろ"] = "ろ",
- ["わ"] = "わ", ["ゐ"] = "ゐ", ["ゑ"] = "ゑ", ["を"] = "を", ["ん"] = "ん",
- },
- orders = {
- "あ", "い", "う", "え", "お", "か", "き", "く", "け", "こ",
- "さ", "し", "す", "せ", "そ", "た", "ち", "つ", "て", "と",
- "な", "に", "ぬ", "ね", "の", "は", "ひ", "ふ", "へ", "ほ",
- "ま", "み", "む", "め", "も", "や", "ゆ", "よ",
- "ら", "り", "る", "れ", "ろ", "わ", "ゐ", "ゑ", "を", "ん",
- }
-}
+if not modules then modules = { } end modules ['sort-lan'] = {
+ version = 1.001,
+ comment = "companion to sort-lan.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files",
+ dataonly = true,
+}
+
+-- todo: look into uts#10 (2012) ... some experiments ... something
+-- to finish in winter.
+
+-- Many vectors were supplied by Wolfgang Schuster and Philipp
+-- Gesang. However this is a quite adapted and reformatted variant
+-- so it needs some checking. Other users provides tables and
+-- corrections as well.
+
+local utfchar, utfbyte = utf.char, utf.byte
+local sorters = sorters
+local definitions = sorters.definitions
+local replacementoffset = sorters.constants.replacementoffset
+local variables = interfaces.variables
+
+definitions["default"] = {
+ method = variables.before,
+ replacements = {
+ -- no replacements
+ },
+ entries = {
+ ["a"] = "a", ["b"] = "b", ["c"] = "c", ["d"] = "d", ["e"] = "e",
+ ["f"] = "f", ["g"] = "g", ["h"] = "h", ["i"] = "i", ["j"] = "j",
+ ["k"] = "k", ["l"] = "l", ["m"] = "m", ["n"] = "n", ["o"] = "o",
+ ["p"] = "p", ["q"] = "q", ["r"] = "r", ["s"] = "s", ["t"] = "t",
+ ["u"] = "u", ["v"] = "v", ["w"] = "w", ["x"] = "x", ["y"] = "y",
+ ["z"] = "z",
+ },
+ orders = {
+ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
+ "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
+ "u", "v", "w", "x", "y", "z",
+ },
+ lower = {
+ -- no replacements
+ },
+ upper = {
+ -- no replacements
+ }
+}
+
+sorters.setlanguage("default")
+
+-- english
+
+definitions["en"] = { parent = "default" }
+
+-- dutch
+
+definitions['nl'] = {
+ parent = 'default',
+ replacements = {
+ { "ij", 'y' }, { "IJ", 'Y' },
+ },
+}
+
+-- French
+
+definitions['fr'] = { parent = 'default' }
+
+-- German (by Wolfgang Schuster)
+
+-- DIN 5007-1
+
+definitions['DIN 5007-1'] = { parent = 'default' }
+
+-- DIN 5007-2
+
+definitions['DIN 5007-2'] = {
+ parent = 'default',
+ replacements = {
+ { "ä", 'ae' }, { "Ä", 'Ae' },
+ { "ö", 'oe' }, { "Ö", 'Oe' },
+ { "ü", 'ue' }, { "Ü", 'Ue' },
+ },
+}
+
+-- Duden
+
+definitions['Duden'] = {
+ parent = 'default',
+ replacements = { { "ß", 's' } },
+}
+
+-- definitions['de'] = { parent = 'default' } -- new german
+
+definitions['de'] = {
+ parent = 'default',
+ replacements = {
+ { "ä", 'ae' }, { "Ä", 'Ae' },
+ { "ö", 'oe' }, { "Ö", 'Oe' },
+ { "ü", 'ue' }, { "Ü", 'Ue' },
+ { "ß", 's' },
+ },
+}
+
+definitions['deo'] = { parent = 'de' } -- old german
+
+definitions['de-DE'] = { parent = 'de' } -- german - Germany
+definitions['de-CH'] = { parent = 'de' } -- german - Swiss
+
+-- german - Austria
+
+definitions['de-AT'] = {
+ entries = {
+ ["a"] = "a", ["ä"] = "ä", ["b"] = "b", ["c"] = "c", ["d"] = "d",
+ ["e"] = "e", ["f"] = "f", ["g"] = "g", ["h"] = "h", ["i"] = "i",
+ ["j"] = "j", ["k"] = "k", ["l"] = "l", ["m"] = "m", ["n"] = "n",
+ ["o"] = "o", ["ö"] = "ö", ["p"] = "p", ["q"] = "q", ["r"] = "r",
+ ["s"] = "s", ["t"] = "t", ["u"] = "u", ["ü"] = "ü", ["v"] = "v",
+ ["w"] = "w", ["x"] = "x", ["y"] = "y", ["z"] = "z",
+ },
+ orders = {
+ "a", "ä", "b", "c", "d", "e", "f", "g", "h", "i",
+ "j", "k", "l", "m", "n", "o", "ö", "p", "q", "r",
+ "s", "t", "u", "ü", "v", "w", "x", "y", "z",
+ },
+}
+
+-- finish (by Wolfgang Schuster)
+
+definitions['fi'] = {
+ entries = {
+ ["a"] = "a", ["b"] = "b", ["c"] = "c", ["d"] = "d", ["e"] = "e",
+ ["f"] = "f", ["g"] = "g", ["h"] = "h", ["i"] = "i", ["j"] = "j",
+ ["k"] = "k", ["l"] = "l", ["m"] = "m", ["n"] = "n", ["o"] = "o",
+ ["p"] = "p", ["q"] = "q", ["r"] = "r", ["s"] = "s", ["t"] = "t",
+ ["u"] = "u", ["v"] = "v", ["w"] = "w", ["x"] = "x", ["y"] = "y",
+ ["z"] = "z", ["å"] = "å", ["ä"] = "ä", ["ö"] = "ö",
+ },
+ orders = {
+ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
+ "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
+ "u", "v", "w", "x", "y", "z", "å", "ä", "ö",
+ }
+}
+
+-- slovenian by MM: this will change since we need to add accented vowels
+
+definitions['sl'] = {
+ entries = {
+ ["a"] = "a", ["b"] = "b", ["c"] = "c", ["č"] = "č", ["ć"] = "ć", ["d"] = "d",
+ ["đ"] = "đ", ["e"] = "e", ["f"] = "f", ["g"] = "g", ["h"] = "h", ["i"] = "i",
+ ["j"] = "j", ["k"] = "k", ["l"] = "l", ["m"] = "m", ["n"] = "n", ["o"] = "o",
+ ["p"] = "p", ["q"] = "q", ["r"] = "r", ["s"] = "s", ["š"] = "š", ["t"] = "t",
+ ["u"] = "u", ["v"] = "v", ["w"] = "w", ["x"] = "x", ["y"] = "y", ["z"] = "z",
+ ["ž"] = "ž",
+ },
+ orders = {
+ "a", "b", "c", "č", "ć", "d", "đ", "e", "f", "g", "h", "i",
+ "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "š", "t",
+ "u", "v", "w", "x", "y", "z", "ž",
+ }
+}
+
+-- The following data was provided by Philipp Gesang.
+
+definitions["ru"] = {
+ entries = {
+ ["а"] = "а", ["б"] = "б", ["в"] = "в", ["г"] = "г", ["д"] = "д",
+ ["е"] = "е", ["ё"] = "е", ["ж"] = "ж", ["з"] = "з", ["и"] = "и",
+ ["і"] = "и", ["й"] = "й", ["к"] = "к", ["л"] = "л", ["м"] = "м",
+ ["н"] = "н", ["о"] = "о", ["п"] = "п", ["р"] = "р", ["с"] = "с",
+ ["т"] = "т", ["у"] = "у", ["ф"] = "ф", ["х"] = "х", ["ц"] = "ц",
+ ["ч"] = "ч", ["ш"] = "ш", ["щ"] = "щ", ["ъ"] = "ъ", ["ы"] = "ы",
+ ["ь"] = "ь", ["ѣ"] = "ѣ", ["э"] = "э", ["ю"] = "ю", ["я"] = "я",
+ ["ѳ"] = "ѳ", ["ѵ"] = "ѵ",
+ },
+ orders = {
+ "а", "б", "в", "г", "д", "е", "ё", "ж", "з", "и",
+ "і", "й", "к", "л", "м", "н", "о", "п", "р", "с",
+ "т", "у", "ф", "х", "ц", "ч", "ш", "щ", "ъ", "ы",
+ "ь", "ѣ", "э", "ю", "я", "ѳ", "ѵ",
+ }
+}
+
+--- Basic Ukrainian
+
+definitions["uk"] = {
+ entries = {
+ ["а"] = "а", ["б"] = "б", ["в"] = "в", ["г"] = "г", ["ґ"] = "ґ",
+ ["д"] = "д", ["е"] = "е", ["є"] = "є", ["ж"] = "ж", ["з"] = "з",
+ ["и"] = "и", ["і"] = "і", ["ї"] = "ї", ["й"] = "й", ["к"] = "к",
+ ["л"] = "л", ["м"] = "м", ["н"] = "н", ["о"] = "о", ["п"] = "п",
+ ["р"] = "р", ["с"] = "с", ["т"] = "т", ["у"] = "у", ["ф"] = "ф",
+ ["х"] = "х", ["ц"] = "ц", ["ч"] = "ч", ["ш"] = "ш", ["щ"] = "щ",
+ ["ь"] = "ь", ["ю"] = "ю", ["я"] = "я",
+ },
+ orders = {
+ "а", "б", "в", "г", "ґ", "д", "е", "є", "ж", "з", "и", "і",
+ "ї", "й", "к", "л", "м", "н", "о", "п", "р", "с", "т", "у",
+ "ф", "х", "ц", "ч", "ш", "щ", "ь", "ю", "я",
+ }
+}
+
+--- Belarusian
+
+definitions["be"] = {
+ entries = {
+ ["а"] = "а", ["б"] = "б", ["в"] = "в", ["г"] = "г", ["д"] = "д",
+ ["е"] = "е", ["ё"] = "е", ["ж"] = "ж", ["з"] = "з", ["і"] = "і",
+ ["й"] = "й", ["к"] = "к", ["л"] = "л", ["м"] = "м", ["н"] = "н",
+ ["о"] = "о", ["п"] = "п", ["р"] = "р", ["с"] = "с", ["т"] = "т",
+ ["у"] = "у", ["ў"] = "ў", ["ф"] = "ф", ["х"] = "х", ["ц"] = "ц",
+ ["ч"] = "ч", ["ш"] = "ш", ["ы"] = "ы", ["ь"] = "ь", ["э"] = "э",
+ ["ю"] = "ю", ["я"] = "я",
+ },
+ orders = {
+ "а", "б", "в", "г", "д", "е", "ё", "ж", "з", "і",
+ "й", "к", "л", "м", "н", "о", "п", "р", "с", "т",
+ "у", "ў", "ф", "х", "ц", "ч", "ш", "ы", "ь", "э",
+ "ю", "я",
+ }
+}
+
+--- Bulgarian
+
+definitions["bg"] = {
+ entries = {
+ ["а"] = "а", ["б"] = "б", ["в"] = "в", ["г"] = "г", ["д"] = "д",
+ ["е"] = "е", ["ж"] = "ж", ["з"] = "з", ["и"] = "и", ["й"] = "й",
+ ["к"] = "к", ["a"] = "a", ["л"] = "л", ["a"] = "a", ["м"] = "м",
+ ["н"] = "н", ["о"] = "о", ["п"] = "п", ["р"] = "р", ["с"] = "с",
+ ["т"] = "т", ["у"] = "у", ["ф"] = "ф", ["х"] = "х", ["ц"] = "ц",
+ ["ч"] = "ч", ["ш"] = "ш", ["щ"] = "щ", ["ъ"] = "ъ", ["ь"] = "ь",
+ ["ю"] = "ю", ["я"] = "я",
+ },
+ orders = {
+ "а", "б", "в", "г", "д", "е", "ж", "з","и", "й",
+ "к", "a", "л", "a", "м", "н", "о", "п", "р", "с",
+ "т", "у", "ф", "х", "ц", "ч", "ш", "щ", "ъ", "ь",
+ "ю", "я",
+ }
+}
+
+--- Old Church Slavonic
+
+-- The language symbol “cu” is taken from the Wikipedia subdomain
+-- cu.wikipedia.org.
+
+local uk, UK = utfchar(replacementoffset + 1), utfchar(replacementoffset + 11)
+
+definitions["cu"] = {
+ replacements = {
+ { "оу", uk }, { "ОУ", UK },
+ },
+ entries = {
+ ["а"] = "а", ["б"] = "б", ["в"] = "в", ["г"] = "г", ["д"] = "д",
+ ["є"] = "є", ["ж"] = "ж", ["ѕ"] = "ѕ", ["ꙃ"] = "ѕ", ["з"] = "з",
+ ["ꙁ"] = "з", ["и"] = "и", ["і"] = "и", ["ї"] = "и", ["ћ"] = "ћ",
+ ["к"] = "к", ["л"] = "л", ["м"] = "м", ["н"] = "н", ["о"] = "о",
+ ["п"] = "п", ["р"] = "р", ["с"] = "с", ["т"] = "т", ["у"] = "у",
+ ["ѹ"] = "у", ["ꙋ"] = "у", [uk] = "у", ["ф"] = "ф", ["х"] = "х",
+ ["ѡ"] = "ѡ", ["ѿ"] = "ѡ", ["ѽ"] = "ѡ", ["ꙍ"] = "ѡ", ["ц"] = "ц",
+ ["ч"] = "ч", ["ш"] = "ш", ["щ"] = "щ", ["ъ"] = "ъ", ["ы"] = "ы",
+ ["ꙑ"] = "ы", ["ь"] = "ь", ["ѣ"] = "ѣ", ["ю"] = "ю", ["ꙗ"] = "ꙗ",
+ ["ѥ"] = "ѥ", ["ѧ"] = "ѧ", ["ѩ"] = "ѩ", ["ѫ"] = "ѫ", ["ѭ"] = "ѭ",
+ ["ѯ"] = "ѯ", ["ѱ"] = "ѱ", ["ѳ"] = "ѳ", ["ѵ"] = "ѵ", ["ѷ"] = "ѵ",
+ },
+ orders = {
+ "а", "б", "в", "г", "д", "є", "ж", "ѕ", "ꙃ", "з", -- Dzělo, U+0292, alternative: dz U+01f3
+ "ꙁ", "и", "і", "ї", "ћ", "к", "л", "м", "н", "о", -- Zemlja
+ "п", "р", "с", "т", "у", "ѹ", "ꙋ", uk, "ф", "х", -- U+0478 uk, horizontal ligature, U+0479 uk, vertical ligature
+ "ѡ", "ѿ", "ѽ", "ꙍ", "ц", "ч", "ш", "щ", "ъ", "ы", -- "ō", U+047f \, U+047d > Omega variants, U+064D /
+ "ꙑ", "ь", "ѣ", "ю", "ꙗ", "ѥ", "ѧ", "ѩ", "ѫ", "ѭ", -- Old jery (U+a651) as used e.g. by the OCS Wikipedia. IOTIFIED A
+ "ѯ", "ѱ", "ѳ", "ѵ", "ѷ",
+ },
+ upper = {
+ uk = UK,
+ },
+ lower = {
+ UK = uk,
+ }
+}
+
+--- Polish (including the letters q, v, x) Cf. ftp://ftp.gust.org.pl/pub/GUST/bulletin/03/02-bl.pdf.
+
+definitions["pl"] = {
+ entries = {
+ ["a"] = "a", ["ą"] = "ą", ["b"] = "b", ["c"] = "c", ["ć"] = "ć",
+ ["d"] = "d", ["e"] = "e", ["ę"] = "ę", ["f"] = "f", ["g"] = "g",
+ ["h"] = "h", ["i"] = "i", ["j"] = "j", ["k"] = "k", ["l"] = "l",
+ ["ł"] = "ł", ["m"] = "m", ["n"] = "n", ["ń"] = "ń", ["o"] = "o",
+ ["ó"] = "ó", ["p"] = "p", ["q"] = "q", ["r"] = "r", ["s"] = "s",
+ ["ś"] = "ś", ["t"] = "t", ["u"] = "u", ["v"] = "v", ["w"] = "w",
+ ["x"] = "x", ["y"] = "y", ["z"] = "z", ["ź"] = "ź", ["ż"] = "ż",
+ },
+ orders = {
+ "a", "ą", "b", "c", "ć", "d", "e", "ę", "f", "g",
+ "h", "i", "j", "k", "l", "ł", "m", "n", "ń", "o",
+ "ó", "p", "q", "r", "s", "ś", "t", "u", "v", "w",
+ "x", "y", "z", "ź", "ż",
+ },
+}
+
+-- Czech, modified to treat quantities and other secondary characteristics indifferently. Cf.
+-- http://racek.vlada.cz/usneseni/usneseni_webtest.nsf/WebGovRes/0AD8FEF4CC04B7A4C12571B6006D69D0?OpenDocument
+-- (2.4.3; via <http://cs.wikipedia.org/wiki/Abecední_řazení#.C4.8Ce.C5.A1tina>)
+
+local ch, CH = utfchar(replacementoffset + 1), utfchar(replacementoffset + 11)
+
+definitions["cz"] = {
+ replacements = {
+ { "ch", ch }, { "CH", CH }
+ },
+ entries = {
+ ["a"] = "a", ["á"] = "a", ["b"] = "b", ["c"] = "c", ["č"] = "č",
+ ["d"] = "d", ["ď"] = "d", ["e"] = "e", ["é"] = "e", ["ě"] = "e",
+ ["f"] = "f", ["g"] = "g", ["h"] = "h", [ch] = "ch", ["i"] = "i",
+ ["í"] = "i", ["j"] = "j", ["k"] = "k", ["l"] = "l", ["m"] = "m",
+ ["n"] = "n", ["ň"] = "n", ["o"] = "o", ["ó"] = "o", ["p"] = "p",
+ ["q"] = "q", ["r"] = "r", ["ř"] = "ř", ["s"] = "s", ["š"] = "š",
+ ["t"] = "t", ["ť"] = "t", ["u"] = "u", ["ú"] = "u", ["ů"] = "u",
+ ["v"] = "v", ["w"] = "w", ["x"] = "x", ["y"] = "y", ["ý"] = "y",
+ ["z"] = "z", ["ž"] = "ž",
+ },
+ orders = {
+ "a", "á", "b", "c", "č", "d", "ď", "e", "é", "ě",
+ "f", "g", "h", ch, "i", "í", "j", "k", "l", "m",
+ "n", "ň", "o", "ó", "p", "q", "r", "ř", "s", "š",
+ "t", "ť", "u", "ú", "ů", "v", "w", "x", "y", "ý",
+ "z", "ž",
+ },
+ upper = {
+ ch = CH,
+ },
+ lower = {
+ CH = ch,
+ }
+}
+
+definitions["cs"] = { parent = "cz" }
+
+--- Slovak.
+
+-- Vowel and consonant quantities, "ď", "ľ", "ň", "ť", "ô", and "ä" are treated
+-- indifferently as their base character, as in my dictionary. If you prefer them
+-- to affect collation order, then use the values given in the comments. We could
+-- define an additional vector for that.
+
+local dz, DZ = utfchar(replacementoffset + 1), utfchar(replacementoffset + 11)
+local dzh, DZH = utfchar(replacementoffset + 2), utfchar(replacementoffset + 12)
+local ch, CH = utfchar(replacementoffset + 3), utfchar(replacementoffset + 13)
+
+definitions["sk"] = {
+ replacements = {
+ { "dz", dz }, { "dz", DZ },
+ { "dž", dzh }, { "dž", DZH },
+ { "ch", ch }, { "ch", CH },
+ },
+ entries = {
+ ["a"] = "a", ["á"] = "a", ["ä"] = "a", ["b"] = "b", ["c"] = "c",
+ ["č"] = "č", ["d"] = "d", ["ď"] = "d", [dz] = "dz", [dzh] = "dž",
+ ["e"] = "e", ["é"] = "e", ["f"] = "f", ["g"] = "g", ["h"] = "h",
+ [ch] = "ch", ["i"] = "i", ["í"] = "i", ["j"] = "j", ["k"] = "k",
+ ["l"] = "l", ["ĺ"] = "l", ["ľ"] = "l", ["m"] = "m", ["n"] = "n",
+ ["ň"] = "n", ["o"] = "o", ["ó"] = "o", ["ô"] = "o", ["p"] = "p",
+ ["q"] = "q", ["r"] = "r", ["ŕ"] = "r", ["s"] = "s", ["š"] = "š",
+ ["t"] = "t", ["ť"] = "t", ["u"] = "u", ["ú"] = "u", ["v"] = "v",
+ ["w"] = "w", ["x"] = "x", ["y"] = "y", ["ý"] = "y", ["z"] = "z",
+ ["ž"] = "ž",
+ },
+ orders = {
+ "a", "á", "ä", "b", "c", "č", "d", "ď", dz, dzh,
+ "e", "é", "f", "g", "h", ch, "i", "í", "j", "k",
+ "l", "ĺ", "ľ", "m", "n", "ň", "o", "ó", "ô", "p",
+ "q", "r", "ŕ", "s", "š", "t", "ť", "u", "ú", "v",
+ "w", "x", "y", "ý", "z", "ž",
+ },
+ upper = {
+ dz = DZ, dzh = DZH, ch = CH,
+ },
+ lower = {
+ DZ = dz, DZH = dzh, CH = ch,
+ }
+}
+
+--- Croatian
+
+local dzh, DZH = utfchar(replacementoffset + 1), utfchar(replacementoffset + 11)
+local lj, LJ = utfchar(replacementoffset + 2), utfchar(replacementoffset + 12)
+local nj, NJ = utfchar(replacementoffset + 3), utfchar(replacementoffset + 13)
+
+definitions["hr"] = {
+ replacements = {
+ { "dž", dzh }, { "DŽ", DZH },
+ { "lj", lj }, { "LJ", LJ },
+ { "nj", nj }, { "NJ", NJ },
+ },
+ entries = {
+ ["a"] = "a", ["b"] = "b", ["c"] = "c", ["č"] = "č", ["ć"] = "ć",
+ ["d"] = "d", [dzh] = "dž", ["đ"] = "đ", ["e"] = "e", ["f"] = "f",
+ ["g"] = "g", ["h"] = "h", ["i"] = "i", ["j"] = "j", ["k"] = "k",
+ ["l"] = "l", [lj] = "lj", ["m"] = "m", ["n"] = "n", [nj] = "nj",
+ ["o"] = "o", ["p"] = "p", ["r"] = "r", ["s"] = "s", ["š"] = "š",
+ ["t"] = "t", ["u"] = "u", ["v"] = "v", ["z"] = "z", ["ž"] = "ž",
+ },
+ orders = {
+ "a", "b", "c", "č", "ć", "d", dzh, "đ", "e", "f",
+ "g", "h", "i", "j", "k", "l", lj, "m", "n", nj,
+ "o", "p", "r", "s", "š", "t", "u", "v", "z", "ž",
+ },
+ upper = {
+ dzh = DZH, lj = LJ, nj = NJ,
+ },
+ lower = {
+ DZH = dzh, LJ = lj, NJ = nj,
+ }
+}
+
+
+--- Serbian
+
+definitions["sr"] = {
+ entries = {
+ ["а"] = "а", ["б"] = "б", ["в"] = "в", ["г"] = "г", ["д"] = "д",
+ ["ђ"] = "ђ", ["е"] = "е", ["ж"] = "ж", ["з"] = "з", ["и"] = "и",
+ ["ј"] = "ј", ["к"] = "к", ["л"] = "л", ["љ"] = "љ", ["м"] = "м",
+ ["н"] = "н", ["њ"] = "њ", ["о"] = "о", ["п"] = "п", ["р"] = "р",
+ ["с"] = "с", ["т"] = "т", ["ћ"] = "ћ", ["у"] = "у", ["ф"] = "ф",
+ ["х"] = "х", ["ц"] = "ц", ["ч"] = "ч", ["џ"] = "џ",
+ ["ш"] = "ш",
+ },
+ orders = {
+ "а", "б", "в", "г", "д", "ђ", "е", "ж", "з", "и",
+ "ј", "к", "л", "љ", "м", "н", "њ", "о", "п", "р",
+ "с", "т", "ћ", "у", "ф", "х", "ц", "ч", "џ", "ш",
+ }
+}
+
+--- Transliteration: Russian|ISO9-1995
+
+-- Keeping the same collation order as Russian (v.s.).
+-- Matches the tables from:
+-- http://bitbucket.org/phg/transliterator/src/tip/tex/context/third/transliterator/trans_tables_iso9.lua
+
+local yer = utfchar(replacementoffset + 1)
+
+definitions["ru-iso9"] = {
+ replacements = {
+ { "''", yer },
+ },
+ entries = {
+ ["a"] = "a", ["b"] = "b", ["v"] = "v", ["g"] = "g", ["d"] = "d",
+ ["e"] = "e", ["ë"] = "ë", ["ž"] = "ž", ["z"] = "z", ["i"] = "i",
+ ["ì"] = "ì", ["j"] = "j", ["k"] = "k", ["l"] = "l", ["m"] = "m",
+ ["n"] = "n", ["o"] = "o", ["p"] = "p", ["r"] = "r", ["s"] = "s",
+ ["t"] = "t", ["u"] = "u", ["f"] = "f", ["h"] = "h", ["c"] = "c",
+ ["č"] = "č", ["š"] = "š", ["ŝ"] = "ŝ", ["ʺ"] = "ʺ", [yer] = "ʺ",
+ ["y"] = "y", ["ʹ"] = "ʹ", ["'"] = "ʹ", ["ě"] = "ě", ["è"] = "è",
+ ["û"] = "û", ["â"] = "â", ["û"] = "û", ["â"] = "â",
+ },
+ orders = {
+ "a", "b", "v", "g", "d", "e", "ë", "ž", "z", "i",
+ "ì", "j", "k", "l", "m", "n", "o", "p", "r", "s",
+ "t", "u", "f", "h", "c", "č", "š", "ŝ", "ʺ", yer,
+ "y", "ʹ", "'", "ě", "è", "û", "â", "û", "â",
+ }
+}
+
+--- Transliteration: Old Slavonic|scientific
+
+-- Matches the tables from:
+-- http://bitbucket.org/phg/transliterator/src/tip/tex/context/third/transliterator/trans_tables_scntfc.lua
+
+local uk, UK = utfchar(replacementoffset + 1), utfchar(replacementoffset + 21)
+local tshe, TSHE = utfchar(replacementoffset + 2), utfchar(replacementoffset + 22)
+local sht, SHT = utfchar(replacementoffset + 3), utfchar(replacementoffset + 23)
+local ju, JU = utfchar(replacementoffset + 4), utfchar(replacementoffset + 24)
+local ja, JA = utfchar(replacementoffset + 5), utfchar(replacementoffset + 25)
+local je, JE = utfchar(replacementoffset + 6), utfchar(replacementoffset + 26)
+local ijus, IJUS = utfchar(replacementoffset + 7), utfchar(replacementoffset + 27)
+local ibigjus, IBIGJUS = utfchar(replacementoffset + 8), utfchar(replacementoffset + 28)
+local xi, XI = utfchar(replacementoffset + 9), utfchar(replacementoffset + 29)
+local psi, PSI = utfchar(replacementoffset + 10), utfchar(replacementoffset + 30)
+local theta, THETA = utfchar(replacementoffset + 11), utfchar(replacementoffset + 31)
+local shch, SHCH = utfchar(replacementoffset + 12), utfchar(replacementoffset + 32)
+
+definitions["ocs-scn"] = {
+ replacements = {
+ { "ou", uk }, { "OU", UK },
+ { "g’", tshe }, { "G’", TSHE },
+ { "št", sht }, { "ŠT", SHT },
+ { "ju", ju }, { "JU", JU },
+ { "ja", ja }, { "JA", JA },
+ { "je", je }, { "JE", JE },
+ { "ję", ijus }, { "JĘ", IJUS },
+ { "jǫ", ibigjus }, { "JǪ", IBIGJUS },
+ { "ks", xi }, { "KS", XI },
+ { "ps", psi }, { "PS", PSI },
+ { "th", theta }, { "TH", THETA },
+ { "šč", shch }, { "ŠČ", SHCH },
+ },
+ entries = {
+ ["a"] = "a", ["b"] = "b", ["v"] = "v", ["g"] = "g", ["d"] = "d",
+ ["e"] = "e", ["ž"] = "ž", ["ʒ"] = "ʒ", ["z"] = "z", ["i"] = "i",
+ ["ï"] = "ï", [tshe] = "g’", ["k"] = "k", ["l"] = "l", ["m"] = "m",
+ ["n"] = "n", ["o"] = "o", ["p"] = "p", ["r"] = "r", ["s"] = "s",
+ ["t"] = "t", ["u"] = "u", ["f"] = "f", ["x"] = "x", ["o"] = "o",
+ ["c"] = "c", ["č"] = "č", ["š"] = "š", [sht] = "št", [shch] = "šč",
+ ["ъ"] = "ъ", ["y"] = "y", [uk] = "y", ["ь"] = "ь", ["ě"] = "ě",
+ [ju] = "ju", [ja] = "ja", [je] = "je", ["ę"] = "ę", [ijus] = "ję",
+ ["ǫ"] = "ǫ", [ibigjus] = "jǫ", [xi] = "ks", [psi] = "ps", [theta] = "th",
+ ["ü"] = "ü",
+ },
+ orders = {
+ "a", "b", "v", "g", "d", "e", "ž", "ʒ", "z", "i", "ï",
+ tshe, "k", "l", "m", "n", "o", "p", "r", "s", "t", "u",
+ "f", "x", "o", "c", "č", "š", sht, shch, "ъ", "y", uk,
+ "ь", "ě", ju, ja, je, "ę", ijus, "ǫ", ibigjus, xi, psi,
+ theta, "ü",
+ },
+ upper = {
+ uk = UK, tshe = TSHE, sht = SHT, ju = JU, ja = JA, je = JE, ijus = IJUS, ibigjus = IBIGJUS, xi = XI, psi = PSI, theta = THETA, shch = SHCH,
+ },
+ lower = {
+ UK = uk, TSHE = tshe, SHT = sht, JU = ju, JA = ja, JE = je, IJUS = ijus, IBIGJUS = ibigjus, XI = xi, PSI = psi, THETA = theta, SHCH = shch,
+ },
+}
+
+
+--- Norwegian (bokmål).
+
+definitions["no"] = {
+ entries = {
+ ["a"] = "a", ["b"] = "b", ["c"] = "c", ["d"] = "d", ["e"] = "e",
+ ["f"] = "f", ["g"] = "g", ["h"] = "h", ["i"] = "i", ["j"] = "j",
+ ["k"] = "k", ["l"] = "l", ["m"] = "m", ["n"] = "n", ["o"] = "o",
+ ["p"] = "p", ["q"] = "q", ["r"] = "r", ["s"] = "s", ["t"] = "t",
+ ["u"] = "u", ["v"] = "v", ["w"] = "w", ["x"] = "x", ["y"] = "y",
+ ["z"] = "z", ["æ"] = "æ", ["ø"] = "ø", ["å"] = "å",
+ },
+ orders = {
+ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
+ "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
+ "u", "v", "w", "x", "y", "z", "æ", "ø", "å",
+ }
+}
+
+--- Danish (-> Norwegian).
+
+definitions["da"] = { parent = "no" }
+
+--- Swedish
+
+definitions["sv"] = {
+ entries = {
+ ["a"] = "a", ["b"] = "b", ["c"] = "c", ["d"] = "d", ["e"] = "e",
+ ["f"] = "f", ["g"] = "g", ["h"] = "h", ["i"] = "i", ["j"] = "j",
+ ["k"] = "k", ["l"] = "l", ["m"] = "m", ["n"] = "n", ["o"] = "o",
+ ["p"] = "p", ["q"] = "q", ["r"] = "r", ["s"] = "s", ["t"] = "t",
+ ["u"] = "u", ["v"] = "v", ["w"] = "w", ["x"] = "x", ["y"] = "y",
+ ["z"] = "z", ["å"] = "å", ["ä"] = "ä", ["ö"] = "ö",
+ },
+ orders = {
+ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
+ "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
+ "u", "v", "w", "x", "y", "z", "å", "ä", "ö",
+ }
+}
+
+--- Icelandic
+
+-- Treating quantities as allographs.
+
+definitions["is"] = {
+ entries = {
+ ["a"] = "a", ["á"] = "a", ["b"] = "b", ["d"] = "d", ["ð"] = "ð",
+ ["e"] = "e", ["é"] = "e", ["f"] = "f", ["g"] = "g", ["h"] = "h",
+ ["i"] = "i", ["í"] = "i", ["j"] = "j", ["k"] = "k", ["l"] = "l",
+ ["m"] = "m", ["n"] = "n", ["o"] = "o", ["ó"] = "o", ["p"] = "p",
+ ["r"] = "r", ["s"] = "s", ["t"] = "t", ["u"] = "u", ["ú"] = "u",
+ ["v"] = "v", ["x"] = "x", ["y"] = "y", ["ý"] = "y", ["þ"] = "þ",
+ ["æ"] = "æ", ["ö"] = "ö",
+ },
+ orders = {
+ "a", "á", "b", "d", "ð", "e", "é", "f", "g", "h",
+ "i", "í", "j", "k", "l", "m", "n", "o", "ó", "p",
+ "r", "s", "t", "u", "ú", "v", "x", "y", "ý", "þ",
+ "æ", "ö",
+ },
+}
+
+--- Greek
+
+definitions["gr"] = {
+ entries = {
+ ["α"] = "α", ["ά"] = "α", ["ὰ"] = "α", ["ᾶ"] = "α", ["ᾳ"] = "α",
+ ["ἀ"] = "α", ["ἁ"] = "α", ["ἄ"] = "α", ["ἂ"] = "α", ["ἆ"] = "α",
+ ["ἁ"] = "α", ["ἅ"] = "α", ["ἃ"] = "α", ["ἇ"] = "α", ["ᾁ"] = "α",
+ ["ᾴ"] = "α", ["ᾲ"] = "α", ["ᾷ"] = "α", ["ᾄ"] = "α", ["ᾂ"] = "α",
+ ["ᾅ"] = "α", ["ᾃ"] = "α", ["ᾆ"] = "α", ["ᾇ"] = "α", ["β"] = "β",
+ ["γ"] = "γ", ["δ"] = "δ", ["ε"] = "ε", ["έ"] = "ε", ["ὲ"] = "ε",
+ ["ἐ"] = "ε", ["ἔ"] = "ε", ["ἒ"] = "ε", ["ἑ"] = "ε", ["ἕ"] = "ε",
+ ["ἓ"] = "ε", ["ζ"] = "ζ", ["η"] = "η", ["η"] = "η", ["ή"] = "η",
+ ["ὴ"] = "η", ["ῆ"] = "η", ["ῃ"] = "η", ["ἠ"] = "η", ["ἤ"] = "η",
+ ["ἢ"] = "η", ["ἦ"] = "η", ["ᾐ"] = "η", ["ἡ"] = "η", ["ἥ"] = "η",
+ ["ἣ"] = "η", ["ἧ"] = "η", ["ᾑ"] = "η", ["ῄ"] = "η", ["ῂ"] = "η",
+ ["ῇ"] = "η", ["ᾔ"] = "η", ["ᾒ"] = "η", ["ᾕ"] = "η", ["ᾓ"] = "η",
+ ["ᾖ"] = "η", ["ᾗ"] = "η", ["θ"] = "θ", ["ι"] = "ι", ["ί"] = "ι",
+ ["ὶ"] = "ι", ["ῖ"] = "ι", ["ἰ"] = "ι", ["ἴ"] = "ι", ["ἲ"] = "ι",
+ ["ἶ"] = "ι", ["ἱ"] = "ι", ["ἵ"] = "ι", ["ἳ"] = "ι", ["ἷ"] = "ι",
+ ["ϊ"] = "ι", ["ΐ"] = "ι", ["ῒ"] = "ι", ["ῗ"] = "ι", ["κ"] = "κ",
+ ["λ"] = "λ", ["μ"] = "μ", ["ν"] = "ν", ["ξ"] = "ξ", ["ο"] = "ο",
+ ["ό"] = "ο", ["ὸ"] = "ο", ["ὀ"] = "ο", ["ὄ"] = "ο", ["ὂ"] = "ο",
+ ["ὁ"] = "ο", ["ὅ"] = "ο", ["ὃ"] = "ο", ["π"] = "π", ["ρ"] = "ρ",
+ ["ῤ"] = "ῤ", ["ῥ"] = "ῥ", ["σ"] = "σ", ["ς"] = "ς", ["τ"] = "τ",
+ ["υ"] = "υ", ["ύ"] = "υ", ["ὺ"] = "υ", ["ῦ"] = "υ", ["ὐ"] = "υ",
+ ["ὔ"] = "υ", ["ὒ"] = "υ", ["ὖ"] = "υ", ["ὑ"] = "υ", ["ὕ"] = "υ",
+ ["ὓ"] = "υ", ["ὗ"] = "υ", ["ϋ"] = "υ", ["ΰ"] = "υ", ["ῢ"] = "υ",
+ ["ῧ"] = "υ", ["φ"] = "φ", ["χ"] = "χ", ["ψ"] = "ω", ["ω"] = "ω",
+ ["ώ"] = "ω", ["ὼ"] = "ω", ["ῶ"] = "ω", ["ῳ"] = "ω", ["ὠ"] = "ω",
+ ["ὤ"] = "ω", ["ὢ"] = "ω", ["ὦ"] = "ω", ["ᾠ"] = "ω", ["ὡ"] = "ω",
+ ["ὥ"] = "ω", ["ὣ"] = "ω", ["ὧ"] = "ω", ["ᾡ"] = "ω", ["ῴ"] = "ω",
+ ["ῲ"] = "ω", ["ῷ"] = "ω", ["ᾤ"] = "ω", ["ᾢ"] = "ω", ["ᾥ"] = "ω",
+ ["ᾣ"] = "ω", ["ᾦ"] = "ω", ["ᾧ"] = "ω",
+ },
+ orders = {
+ "α", "ά", "ὰ", "ᾶ", "ᾳ", "ἀ", "ἁ", "ἄ", "ἂ", "ἆ",
+ "ἁ", "ἅ", "ἃ", "ἇ", "ᾁ", "ᾴ", "ᾲ", "ᾷ", "ᾄ", "ᾂ",
+ "ᾅ", "ᾃ", "ᾆ", "ᾇ", "β", "γ", "δ", "ε", "έ", "ὲ",
+ "ἐ", "ἔ", "ἒ", "ἑ", "ἕ", "ἓ", "ζ", "η", "η", "ή",
+ "ὴ", "ῆ", "ῃ", "ἠ", "ἤ", "ἢ", "ἦ", "ᾐ", "ἡ", "ἥ",
+ "ἣ", "ἧ", "ᾑ", "ῄ", "ῂ", "ῇ", "ᾔ", "ᾒ", "ᾕ", "ᾓ",
+ "ᾖ", "ᾗ", "θ", "ι", "ί", "ὶ", "ῖ", "ἰ", "ἴ", "ἲ",
+ "ἶ", "ἱ", "ἵ", "ἳ", "ἷ", "ϊ", "ΐ", "ῒ", "ῗ", "κ",
+ "λ", "μ", "ν", "ξ", "ο", "ό", "ὸ", "ὀ", "ὄ", "ὂ",
+ "ὁ", "ὅ", "ὃ", "π", "ρ", "ῤ", "ῥ", "σ", "ς", "τ",
+ "υ", "ύ", "ὺ", "ῦ", "ὐ", "ὔ", "ὒ", "ὖ", "ὑ", "ὕ",
+ "ὓ", "ὗ", "ϋ", "ΰ", "ῢ", "ῧ", "φ", "χ", "ψ", "ω",
+ "ώ", "ὼ", "ῶ", "ῳ", "ὠ", "ὤ", "ὢ", "ὦ", "ᾠ", "ὡ",
+ "ὥ", "ὣ", "ὧ", "ᾡ", "ῴ", "ῲ", "ῷ", "ᾤ", "ᾢ", "ᾥ",
+ "ᾣ", "ᾦ", "ᾧ",
+ },
+}
+
+--- Latin
+
+-- Treating the post-classical fricatives “j” and “v” as “i” and “u”
+-- respectively.
+
+definitions["la"] = {
+ replacements = {
+ { "æ", "ae" }, { "Æ", "AE" },
+ },
+ entries = {
+ ["a"] = "a", ["ā"] = "a", ["ă"] = "a", ["b"] = "b", ["c"] = "c",
+ ["d"] = "d", ["e"] = "e", ["ē"] = "e", ["ĕ"] = "e", ["f"] = "f",
+ ["g"] = "g", ["h"] = "h", ["i"] = "i", ["ī"] = "i", ["ĭ"] = "i",
+ ["j"] = "i", ["k"] = "k", ["l"] = "l", ["m"] = "m", ["n"] = "n",
+ ["o"] = "o", ["ō"] = "o", ["ŏ"] = "o", ["p"] = "p", ["q"] = "q",
+ ["r"] = "r", ["s"] = "s", ["t"] = "t", ["u"] = "u", ["ū"] = "u",
+ ["ŭ"] = "u", ["v"] = "u", ["w"] = "w", ["x"] = "x", ["y"] = "y",
+ ["ȳ"] = "y", ["y̆"] = "y", ["z"] = "z",
+ },
+ orders = {
+ "a", "ā", "ă", "b", "c", "d", "e", "ē", "ĕ", "f",
+ "g", "h", "i", "ī", "ĭ", "j", "k", "l", "m", "n",
+ "o", "ō", "ŏ", "p", "q", "r", "s", "t", "u", "ū",
+ "ŭ", "v", "w", "x", "y", "ȳ", "y̆", "z",
+ }
+}
+
+--- Italian
+
+definitions["it"] = {
+ entries = {
+ ["a"] = "a", ["á"] = "a", ["b"] = "b", ["c"] = "c", ["d"] = "d",
+ ["e"] = "e", ["é"] = "e", ["è"] = "e", ["f"] = "f", ["g"] = "g",
+ ["h"] = "h", ["i"] = "i", ["í"] = "i", ["ì"] = "i", ["j"] = "i",
+ ["k"] = "k", ["l"] = "l", ["m"] = "m", ["n"] = "n", ["o"] = "o",
+ ["ó"] = "o", ["ò"] = "o", ["p"] = "p", ["q"] = "q", ["r"] = "r",
+ ["s"] = "s", ["t"] = "t", ["u"] = "u", ["ú"] = "u", ["ù"] = "u",
+ ["v"] = "u", ["w"] = "w", ["x"] = "x", ["y"] = "y", ["z"] = "z",
+ },
+ orders = {
+ "a", "á", "b", "c", "d", "e", "é", "è", "f", "g",
+ "h", "i", "í", "ì", "j", "k", "l", "m", "n", "o",
+ "ó", "ò", "p", "q", "r", "s", "t", "u", "ú", "ù",
+ "v", "w", "x", "y", "z",
+ }
+}
+
+--- Romanian
+
+definitions["ro"] = {
+ entries = {
+ ["a"] = "a", ["ă"] = "ă", ["â"] = "â", ["b"] = "b", ["c"] = "c",
+ ["d"] = "d", ["e"] = "e", ["f"] = "f", ["g"] = "g", ["h"] = "h",
+ ["i"] = "i", ["î"] = "î", ["j"] = "j", ["k"] = "k", ["l"] = "l",
+ ["m"] = "m", ["n"] = "n", ["o"] = "o", ["p"] = "p", ["q"] = "q",
+ ["r"] = "r", ["s"] = "s", ["ș"] = "ș", ["t"] = "t", ["ț"] = "ț",
+ ["u"] = "u", ["v"] = "v", ["w"] = "w", ["x"] = "x", ["y"] = "y",
+ ["z"] = "z",
+ },
+ orders = {
+ "a", "ă", "â", "b", "c", "d", "e", "f", "g", "h",
+ "i", "î", "j", "k", "l", "m", "n", "o", "p", "q",
+ "r", "s", "ș", "t", "ț", "u", "v", "w", "x", "y",
+ "z",
+ }
+}
+
+--- Spanish
+
+definitions["es"] = {
+ entries = {
+ ["a"] = "a", ["á"] = "a", ["b"] = "b", ["c"] = "c", ["d"] = "d",
+ ["e"] = "e", ["é"] = "e", ["f"] = "f", ["g"] = "g", ["h"] = "h",
+ ["i"] = "i", ["í"] = "i", ["j"] = "j", ["k"] = "k", ["l"] = "l",
+ ["m"] = "m", ["n"] = "n", ["ñ"] = "ñ", ["o"] = "o", ["ó"] = "o",
+ ["p"] = "p", ["q"] = "q", ["r"] = "r", ["s"] = "s", ["t"] = "t",
+ ["u"] = "u", ["ú"] = "u", ["ü"] = "u", ["v"] = "v", ["w"] = "w",
+ ["x"] = "x", ["y"] = "y", ["z"] = "z",
+ },
+ orders = {
+ "a", "á", "b", "c", "d", "e", "é", "f", "g", "h",
+ "i", "í", "j", "k", "l", "m", "n", "ñ", "o", "ó",
+ "p", "q", "r", "s", "t", "u", "ú", "ü", "v", "w",
+ "x", "y", "z",
+ }
+}
+
+--- Portuguese
+
+definitions["pt"] = {
+ entries = {
+ ["a"] = "a", ["á"] = "a", ["â"] = "a", ["ã"] = "a", ["à"] = "a",
+ ["b"] = "b", ["c"] = "c", ["ç"] = "c", ["d"] = "d", ["e"] = "e",
+ ["é"] = "e", ["ê"] = "e", ["f"] = "f", ["g"] = "g", ["h"] = "h",
+ ["i"] = "i", ["í"] = "i", ["j"] = "j", ["k"] = "k", ["l"] = "l",
+ ["m"] = "m", ["n"] = "n", ["o"] = "o", ["ó"] = "o", ["ô"] = "o",
+ ["õ"] = "o", ["p"] = "p", ["q"] = "q", ["r"] = "r", ["s"] = "s",
+ ["t"] = "t", ["u"] = "u", ["ú"] = "u", ["ü"] = "u", ["v"] = "v",
+ ["w"] = "w", ["x"] = "x", ["y"] = "y", ["z"] = "z",
+ },
+ orders = {
+ "a", "á", "â", "ã", "à", "b", "c", "ç", "d", "e",
+ "é", "ê", "f", "g", "h", "i", "í", "j", "k", "l",
+ "m", "n", "o", "ó", "ô", "õ", "p", "q", "r", "s",
+ "t", "u", "ú", "ü", "v", "w", "x", "y", "z",
+ }
+}
+
+--- Lithuanian
+
+local ch, CH = utfchar(replacementoffset + 1), utfchar(replacementoffset + 11)
+
+definitions["lt"] = {
+ replacements = {
+ { "ch", ch }, { "CH", CH}
+ },
+ entries = {
+ ["a"] = "a", ["ą"] = "a", ["b"] = "b", ["c"] = "c", [ch ] = "c",
+ ["č"] = "č", ["d"] = "d", ["e"] = "e", ["ę"] = "e", ["ė"] = "e",
+ ["f"] = "f", ["g"] = "g", ["h"] = "h", ["i"] = "i", ["į"] = "i",
+ ["y"] = "i", ["j"] = "j", ["k"] = "k", ["l"] = "l", ["m"] = "m",
+ ["n"] = "n", ["o"] = "o", ["p"] = "p", ["r"] = "r", ["s"] = "s",
+ ["š"] = "š", ["t"] = "t", ["u"] = "u", ["ų"] = "u", ["ū"] = "u",
+ ["v"] = "v", ["z"] = "z", ["ž"] = "ž",
+ },
+ orders = {
+ "a", "ą", "b", "c", ch, "č", "d", "e", "ę", "ė",
+ "f", "g", "h", "i", "į", "y", "j", "k", "l", "m",
+ "n", "o", "p", "r", "s", "š", "t", "u", "ų", "ū",
+ "v", "z", "ž",
+ },
+ lower = {
+ ch = CH,
+ },
+ upper = {
+ CH = ch,
+ },
+}
+
+--- Latvian
+
+definitions["lv"] = {
+ entries = {
+ ["a"] = "a", ["ā"] = "a", ["b"] = "b", ["c"] = "c", ["č"] = "č",
+ ["d"] = "d", ["e"] = "e", ["ē"] = "e", ["f"] = "f", ["g"] = "g",
+ ["ģ"] = "ģ", ["h"] = "h", ["i"] = "i", ["ī"] = "i", ["j"] = "j",
+ ["k"] = "k", ["ķ"] = "ķ", ["l"] = "l", ["ļ"] = "ļ", ["m"] = "m",
+ ["n"] = "n", ["ņ"] = "ņ", ["o"] = "o", ["ō"] = "o", ["p"] = "p",
+ ["r"] = "r", ["ŗ"] = "ŗ", ["s"] = "s", ["š"] = "š", ["t"] = "t",
+ ["u"] = "u", ["ū"] = "u", ["v"] = "v", ["z"] = "z", ["ž"] = "ž",
+ },
+ orders = {
+ "a", "ā", "b", "c", "č", "d", "e", "ē", "f", "g",
+ "ģ", "h", "i", "ī", "j", "k", "ķ", "l", "ļ", "m",
+ "n", "ņ", "o", "ō", "p", "r", "ŗ", "s", "š", "t",
+ "u", "ū", "v", "z", "ž",
+ }
+}
+
+--- Hungarian
+
+-- Helpful but disturbing:
+-- http://en.wikipedia.org/wiki/Hungarian_alphabet#Alphabetical_ordering_.28collation.29
+-- (In short: you'd have to analyse word-compounds to realize a correct order
+-- for sequences like “nny”, “ssz”, and “zsz”. This is left as an exercise to
+-- the reader…)
+
+local cs, CS = utfchar(replacementoffset + 1), utfchar(replacementoffset + 11)
+local dz, DZ = utfchar(replacementoffset + 2), utfchar(replacementoffset + 12)
+local dzs, DZS = utfchar(replacementoffset + 3), utfchar(replacementoffset + 13)
+local gy, GY = utfchar(replacementoffset + 4), utfchar(replacementoffset + 14)
+local ly, LY = utfchar(replacementoffset + 5), utfchar(replacementoffset + 15)
+local ny, NY = utfchar(replacementoffset + 6), utfchar(replacementoffset + 16)
+local sz, SZ = utfchar(replacementoffset + 7), utfchar(replacementoffset + 17)
+local ty, TY = utfchar(replacementoffset + 8), utfchar(replacementoffset + 18)
+local zs, ZS = utfchar(replacementoffset + 9), utfchar(replacementoffset + 19)
+
+definitions["hu"] = {
+ replacements = {
+ { "cs", cs }, { "CS", CS },
+ { "dz", dz }, { "DZ", DZ },
+ { "dzs", dzs }, { "DZS", DZS },
+ { "gy", gy }, { "GY", GY },
+ { "ly", ly }, { "LY", LY },
+ { "ny", ny }, { "NY", NY },
+ { "sz", sz }, { "SZ", SZ },
+ { "ty", ty }, { "TY", TY },
+ { "zs", zs }, { "ZS", ZS },
+ },
+ entries = {
+ ["a"] = "a", ["á"] = "a", ["b"] = "b", ["c"] = "c", [cs ] = "cs",
+ ["d"] = "d", [dz ] = "dz", [dzs] = "dzs", ["e"] = "e", ["é"] = "e",
+ ["f"] = "f", ["g"] = "g", [gy ] = "gy", ["h"] = "h", ["i"] = "i",
+ ["í"] = "i", ["j"] = "j", ["k"] = "k", ["l"] = "l", [ly ] = "ly",
+ ["m"] = "m", ["n"] = "n", [ny ] = "ny", ["o"] = "o", ["ó"] = "o",
+ ["ö"] = "ö", ["ő"] = "ö", ["p"] = "p", ["q"] = "q", ["r"] = "r",
+ ["s"] = "s", [sz ] = "sz", ["t"] = "t", [ty ] = "ty", ["u"] = "u",
+ ["ú"] = "u", ["ü"] = "ü", ["ű"] = "ü", ["v"] = "v", ["w"] = "w",
+ ["x"] = "x", ["y"] = "y", ["z"] = "z", [zs ] = "zs",
+ },
+ orders = {
+ "a", "á", "b", "c", cs, "d", dz, dzs, "e", "é",
+ "f", "g", gy, "h", "i", "í", "j", "k", "l", ly,
+ "m", "n", ny, "o", "ó", "ö", "ő", "p", "q", "r",
+ "s", sz, "t", ty, "u", "ú", "ü", "ű", "v", "w",
+ "x", "y", "z", zs,
+ },
+ lower = {
+ CS = cs, DZ = dz, DZS = dzs, GY = gy, LY = ly, NY = ny, SZ = sz, TY = ty, ZS = zs,
+ },
+ upper = {
+ cs = CS, dz = DZ, dzs = DZS, gy = GY, ly = LY, ny = NY, sz = SZ, ty = TY, zs = ZS,
+ },
+}
+
+--- Estonian
+
+definitions["et"] = {
+ entries = { -- w x y are used for foreign words only
+ ["a"] = "a", ["b"] = "b", ["d"] = "d", ["e"] = "e", ["f"] = "f",
+ ["g"] = "g", ["h"] = "h", ["i"] = "i", ["j"] = "j", ["k"] = "k",
+ ["l"] = "l", ["m"] = "m", ["n"] = "n", ["o"] = "o", ["p"] = "p",
+ ["r"] = "r", ["s"] = "s", ["š"] = "š", ["z"] = "z", ["ž"] = "ž",
+ ["t"] = "t", ["u"] = "u", ["v"] = "v", ["w"] = "v", ["õ"] = "õ",
+ ["ä"] = "ä", ["ö"] = "ö", ["ü"] = "ü", ["x"] = "x", ["y"] = "y",
+ },
+ orders = {
+ "a", "b", "d", "e", "f", "g", "h", "i", "j", "k",
+ "l", "m", "n", "o", "p", "r", "s", "š", "z", "ž",
+ "t", "u", "v", "w", "õ", "ä", "ö", "ü", "x", "y",
+ }
+}
+
+--- Korean
+
+local fschars = characters.fschars
+
+local function firstofsplit(first)
+ local fs = fschars[first] or first -- leadconsonant
+ return fs, fs -- entry, tag
+end
+
+definitions["kr"] = {
+ firstofsplit = firstofsplit,
+ orders = {
+ "ㄱ", "ㄴ", "ㄷ", "ㄹ", "ㅁ", "ㅂ", "ㅅ", "ㅇ", "ㅈ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ",
+ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
+ "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
+ }
+}
+
+-- Japanese
+
+definitions["jp"] = {
+ replacements = {
+ { "ぁ", "あ" }, { "ぃ", "い" },
+ { "ぅ", "う" }, { "ぇ", "え" },
+ { "ぉ", "お" }, { "っ", "つ" },
+ { "ゃ", "や" }, { "ゅ", "ゆ" },
+ { "ょ", "よ" },
+ },
+ entries = {
+ ["あ"] = "あ", ["い"] = "い", ["う"] = "う", ["え"] = "え", ["お"] = "お",
+ ["か"] = "か", ["き"] = "き", ["く"] = "く", ["け"] = "け", ["こ"] = "こ",
+ ["さ"] = "さ", ["し"] = "し", ["す"] = "す", ["せ"] = "せ", ["そ"] = "そ",
+ ["た"] = "た", ["ち"] = "ち", ["つ"] = "つ", ["て"] = "て", ["と"] = "と",
+ ["な"] = "な", ["に"] = "に", ["ぬ"] = "ぬ", ["ね"] = "ね", ["の"] = "の",
+ ["は"] = "は", ["ひ"] = "ひ", ["ふ"] = "ふ", ["へ"] = "へ", ["ほ"] = "ほ",
+ ["ま"] = "ま", ["み"] = "み", ["む"] = "む", ["め"] = "め", ["も"] = "も",
+ ["や"] = "や", ["ゆ"] = "ゆ", ["よ"] = "よ",
+ ["ら"] = "ら", ["り"] = "り", ["る"] = "る", ["れ"] = "れ", ["ろ"] = "ろ",
+ ["わ"] = "わ", ["ゐ"] = "ゐ", ["ゑ"] = "ゑ", ["を"] = "を", ["ん"] = "ん",
+ },
+ orders = {
+ "あ", "い", "う", "え", "お", "か", "き", "く", "け", "こ",
+ "さ", "し", "す", "せ", "そ", "た", "ち", "つ", "て", "と",
+ "な", "に", "ぬ", "ね", "の", "は", "ひ", "ふ", "へ", "ほ",
+ "ま", "み", "む", "め", "も", "や", "ゆ", "よ",
+ "ら", "り", "る", "れ", "ろ", "わ", "ゐ", "ゑ", "を", "ん",
+ }
+}
diff --git a/tex/context/base/spac-adj.lua b/tex/context/base/spac-adj.lua
index 6dff0dede..c87a9d17f 100644
--- a/tex/context/base/spac-adj.lua
+++ b/tex/context/base/spac-adj.lua
@@ -1,58 +1,58 @@
-if not modules then modules = { } end modules ['spac-adj'] = {
- version = 1.001,
- comment = "companion to spac-adj.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- sort of obsolete code
-
-local a_vadjust = attributes.private('graphicvadjust')
-
-local nodecodes = nodes.nodecodes
-
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-
-local remove_node = nodes.remove
-local hpack_node = node.hpack
-local vpack_node = node.vpack
-
-function nodes.handlers.graphicvadjust(head,groupcode) -- we can make an actionchain for mvl only
- if groupcode == "" then -- mvl only
- local h, p, done = head, nil, false
- while h do
- local id = h.id
- if id == hlist_code or id == vlist_code then
- local a = h[a_vadjust]
- if a then
- if p then
- local n
- head, h, n = remove_node(head,h)
- local pl = p.list
- if n.width ~= 0 then
- n = hpack_node(n,0,'exactly') -- todo: dir
- end
- if pl then
- pl.prev = n
- n.next = pl
- end
- p.list = n
- done = true
- else
- -- can't happen
- end
- else
- p = h
- h = h.next
- end
- else
- h = h.next
- end
- end
- return head, done
- else
- return head, false
- end
-end
+if not modules then modules = { } end modules ['spac-adj'] = {
+ version = 1.001,
+ comment = "companion to spac-adj.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- sort of obsolete code
+
+local a_vadjust = attributes.private('graphicvadjust')
+
+local nodecodes = nodes.nodecodes
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+
+local remove_node = nodes.remove
+local hpack_node = node.hpack
+local vpack_node = node.vpack
+
+function nodes.handlers.graphicvadjust(head,groupcode) -- we can make an actionchain for mvl only
+ if groupcode == "" then -- mvl only
+ local h, p, done = head, nil, false
+ while h do
+ local id = h.id
+ if id == hlist_code or id == vlist_code then
+ local a = h[a_vadjust]
+ if a then
+ if p then
+ local n
+ head, h, n = remove_node(head,h)
+ local pl = p.list
+ if n.width ~= 0 then
+ n = hpack_node(n,0,'exactly') -- todo: dir
+ end
+ if pl then
+ pl.prev = n
+ n.next = pl
+ end
+ p.list = n
+ done = true
+ else
+ -- can't happen
+ end
+ else
+ p = h
+ h = h.next
+ end
+ else
+ h = h.next
+ end
+ end
+ return head, done
+ else
+ return head, false
+ end
+end
diff --git a/tex/context/base/spac-ali.lua b/tex/context/base/spac-ali.lua
index 6357f0f15..ceb278433 100644
--- a/tex/context/base/spac-ali.lua
+++ b/tex/context/base/spac-ali.lua
@@ -1,134 +1,134 @@
-if not modules then modules = { } end modules ['spac-ali'] = {
- version = 1.001,
- 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 appendaction = tasks.appendaction
-local prependaction = tasks.prependaction
-local disableaction = tasks.disableaction
-local enableaction = tasks.enableaction
-
-local slide_nodes = node.slide
-local hpack_nodes = node.hpack -- nodes.fasthpack not really faster here
-
-local unsetvalue = attributes.unsetvalue
-
-local concat_nodes = nodes.concat
-
-local nodecodes = nodes.nodecodes
-local listcodes = nodes.listcodes
-
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-local line_code = listcodes.line
-
-local nodepool = nodes.pool
-
-local new_stretch = nodepool.stretch
-
-local a_realign = attributes.private("realign")
-
-local texattribute = tex.attribute
-local texcount = tex.count
-
-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)
- local current = head
- local done = false
- while current do
- local id = current.id
- if id == hlist_code then
- if current.subtype == line_code then
- local a = current[a_realign]
- if not a or a == 0 then
- -- skip
- else
- local align = a % 10
- local pageno = div(a,10)
- if pageno == realpageno then
- -- already ok
- else
- 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
- if action == 1 then
- current.list = hpack_nodes(concat_nodes{current.list,new_stretch(3)},current.width,"exactly")
- if trace_realign then
- report_realign("flushing left, align %a, page %a, realpage %a",align,pageno,realpageno)
- end
- elseif action == 2 then
- current.list = hpack_nodes(concat_nodes{new_stretch(3),current.list},current.width,"exactly")
- 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
- done = true
- nofrealigned = nofrealigned + 1
- end
- current[a_realign] = unsetvalue
- end
- end
- handler(current.list,leftpage,realpageno)
- elseif id == vlist_code then
- handler(current.list,leftpage,realpageno)
- end
- current = current.next
- end
- return head, done
-end
-
-function alignments.handler(head)
- local leftpage = isleftpage(true,false)
- local realpageno = texcount.realpageno
- return handler(head,leftpage,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
- texattribute[a_realign] = texcount.realpageno * 10 + n
-end
-
-commands.setrealign = alignments.set
-
-statistics.register("realigning", function()
- if nofrealigned > 0 then
- return format("%s processed",nofrealigned)
- else
- return nil
- end
-end)
+if not modules then modules = { } end modules ['spac-ali'] = {
+ version = 1.001,
+ 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 appendaction = tasks.appendaction
+local prependaction = tasks.prependaction
+local disableaction = tasks.disableaction
+local enableaction = tasks.enableaction
+
+local slide_nodes = node.slide
+local hpack_nodes = node.hpack -- nodes.fasthpack not really faster here
+
+local unsetvalue = attributes.unsetvalue
+
+local concat_nodes = nodes.concat
+
+local nodecodes = nodes.nodecodes
+local listcodes = nodes.listcodes
+
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+local line_code = listcodes.line
+
+local nodepool = nodes.pool
+
+local new_stretch = nodepool.stretch
+
+local a_realign = attributes.private("realign")
+
+local texattribute = tex.attribute
+local texcount = tex.count
+
+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)
+ local current = head
+ local done = false
+ while current do
+ local id = current.id
+ if id == hlist_code then
+ if current.subtype == line_code then
+ local a = current[a_realign]
+ if not a or a == 0 then
+ -- skip
+ else
+ local align = a % 10
+ local pageno = div(a,10)
+ if pageno == realpageno then
+ -- already ok
+ else
+ 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
+ if action == 1 then
+ current.list = hpack_nodes(concat_nodes{current.list,new_stretch(3)},current.width,"exactly")
+ if trace_realign then
+ report_realign("flushing left, align %a, page %a, realpage %a",align,pageno,realpageno)
+ end
+ elseif action == 2 then
+ current.list = hpack_nodes(concat_nodes{new_stretch(3),current.list},current.width,"exactly")
+ 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
+ done = true
+ nofrealigned = nofrealigned + 1
+ end
+ current[a_realign] = unsetvalue
+ end
+ end
+ handler(current.list,leftpage,realpageno)
+ elseif id == vlist_code then
+ handler(current.list,leftpage,realpageno)
+ end
+ current = current.next
+ end
+ return head, done
+end
+
+function alignments.handler(head)
+ local leftpage = isleftpage(true,false)
+ local realpageno = texcount.realpageno
+ return handler(head,leftpage,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
+ texattribute[a_realign] = texcount.realpageno * 10 + n
+end
+
+commands.setrealign = alignments.set
+
+statistics.register("realigning", function()
+ if nofrealigned > 0 then
+ return format("%s processed",nofrealigned)
+ else
+ return nil
+ end
+end)
diff --git a/tex/context/base/spac-chr.lua b/tex/context/base/spac-chr.lua
index 6c9cb82df..24364978a 100644
--- a/tex/context/base/spac-chr.lua
+++ b/tex/context/base/spac-chr.lua
@@ -1,200 +1,200 @@
-if not modules then modules = { } end modules ['spac-chr'] = {
- version = 1.001,
- 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
-
-trace_characters = false trackers.register("typesetters.characters", function(v) trace_characters = v end)
-
-report_characters = logs.reporter("typesetting","characters")
-
-local nodes, node = nodes, node
-
-local insert_node_after = node.insert_after
-local remove_node = nodes.remove -- ! nodes
-local copy_node_list = node.copy_list
-
-local nodepool = nodes.pool
-local tasks = nodes.tasks
-
-local new_penalty = nodepool.penalty
-local new_glue = nodepool.glue
-
-local nodecodes = nodes.nodecodes
-local skipcodes = nodes.skipcodes
-local glyph_code = nodecodes.glyph
-local glue_code = nodecodes.glue
-
-local space_skip_code = skipcodes["spaceskip"]
-
-local chardata = characters.data
-
-local typesetters = typesetters
-
-local characters = { }
-typesetters.characters = characters
-
-local fonthashes = fonts.hashes
-local fontparameters = fonthashes.parameters
-local fontcharacters = fonthashes.characters
-local fontquads = fonthashes.quads
-
-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)
- local attr = current.attr
- if fraction ~= 0 then
- fraction = fraction * fontquads[current.font]
- end
- local glue = new_glue(fraction)
--- glue.attr = copy_node_list(attr)
- glue.attr = attr
- current.attr = nil
- glue[a_character] = unicode
- head, current = insert_node_after(head,current,glue)
- return head, current
-end
-
-local function inject_char_space(unicode,head,current,parent)
- local attr = current.attr
- local font = current.font
- local char = fontcharacters[font][parent]
- local glue = new_glue(char and char.width or fontparameters[font].space)
- -- glue.attr = copy_node_list(current.attr)
- glue.attr = current.attr
- current.attr = nil
- glue[a_character] = unicode
- head, current = insert_node_after(head,current,glue)
- return head, current
-end
-
-local function inject_nobreak_space(unicode,head,current,space,spacestretch,spaceshrink)
- local attr = current.attr
- local glue = new_glue(space,spacestretch,spaceshrink)
- local penalty = new_penalty(10000)
- -- glue.attr = copy_node_list(attr)
- glue.attr = attr
- current.attr = nil
- -- penalty.attr = attr
- glue[a_character] = unicode
- head, current = insert_node_after(head,current,penalty)
- head, current = insert_node_after(head,current,glue)
- return head, current
-end
-
-local methods = {
-
- -- The next one uses an attribute assigned to the character but still we
- -- don't have the 'local' value.
-
- [0x00A0] = function(head,current) -- nbsp
- local para = fontparameters[current.font]
- if current[a_alignstate] == 1 then -- flushright
- head, current = inject_nobreak_space(0x00A0,head,current,para.space,0,0)
- current.subtype = space_skip_code
- else
- head, current = inject_nobreak_space(0x00A0,head,current,para.space,para.spacestretch,para.spaceshrink)
- end
- return head, 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[current.font]/8)
- end,
-
- [0x205F] = function(head,current) -- math thinspace
- return inject_nobreak_space(0x205F,head,current,fontparameters[current.font].space/8)
- end,
-
- -- [0xFEFF] = function(head,current) -- zerowidthnobreakspace
- -- return head, current
- -- end,
-
-}
-
-function characters.handler(head)
- local current = head
- local done = false
- while current do
- local id = current.id
- if id == glyph_code then
- local next = current.next
- local char = current.char
- local method = methods[char]
- if method then
- if trace_characters then
- report_characters("replacing character %C, description %a",char,lower(chardata[char].description))
- end
- head = method(head,current)
- head = remove_node(head,current,true)
- done = true
- end
- current = next
- else
- current = current.next
- end
- end
- return head, done
-end
+if not modules then modules = { } end modules ['spac-chr'] = {
+ version = 1.001,
+ 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
+
+trace_characters = false trackers.register("typesetters.characters", function(v) trace_characters = v end)
+
+report_characters = logs.reporter("typesetting","characters")
+
+local nodes, node = nodes, node
+
+local insert_node_after = node.insert_after
+local remove_node = nodes.remove -- ! nodes
+local copy_node_list = node.copy_list
+
+local nodepool = nodes.pool
+local tasks = nodes.tasks
+
+local new_penalty = nodepool.penalty
+local new_glue = nodepool.glue
+
+local nodecodes = nodes.nodecodes
+local skipcodes = nodes.skipcodes
+local glyph_code = nodecodes.glyph
+local glue_code = nodecodes.glue
+
+local space_skip_code = skipcodes["spaceskip"]
+
+local chardata = characters.data
+
+local typesetters = typesetters
+
+local characters = { }
+typesetters.characters = characters
+
+local fonthashes = fonts.hashes
+local fontparameters = fonthashes.parameters
+local fontcharacters = fonthashes.characters
+local fontquads = fonthashes.quads
+
+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)
+ local attr = current.attr
+ if fraction ~= 0 then
+ fraction = fraction * fontquads[current.font]
+ end
+ local glue = new_glue(fraction)
+-- glue.attr = copy_node_list(attr)
+ glue.attr = attr
+ current.attr = nil
+ glue[a_character] = unicode
+ head, current = insert_node_after(head,current,glue)
+ return head, current
+end
+
+local function inject_char_space(unicode,head,current,parent)
+ local attr = current.attr
+ local font = current.font
+ local char = fontcharacters[font][parent]
+ local glue = new_glue(char and char.width or fontparameters[font].space)
+ -- glue.attr = copy_node_list(current.attr)
+ glue.attr = current.attr
+ current.attr = nil
+ glue[a_character] = unicode
+ head, current = insert_node_after(head,current,glue)
+ return head, current
+end
+
+local function inject_nobreak_space(unicode,head,current,space,spacestretch,spaceshrink)
+ local attr = current.attr
+ local glue = new_glue(space,spacestretch,spaceshrink)
+ local penalty = new_penalty(10000)
+ -- glue.attr = copy_node_list(attr)
+ glue.attr = attr
+ current.attr = nil
+ -- penalty.attr = attr
+ glue[a_character] = unicode
+ head, current = insert_node_after(head,current,penalty)
+ head, current = insert_node_after(head,current,glue)
+ return head, current
+end
+
+local methods = {
+
+ -- The next one uses an attribute assigned to the character but still we
+ -- don't have the 'local' value.
+
+ [0x00A0] = function(head,current) -- nbsp
+ local para = fontparameters[current.font]
+ if current[a_alignstate] == 1 then -- flushright
+ head, current = inject_nobreak_space(0x00A0,head,current,para.space,0,0)
+ current.subtype = space_skip_code
+ else
+ head, current = inject_nobreak_space(0x00A0,head,current,para.space,para.spacestretch,para.spaceshrink)
+ end
+ return head, 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[current.font]/8)
+ end,
+
+ [0x205F] = function(head,current) -- math thinspace
+ return inject_nobreak_space(0x205F,head,current,fontparameters[current.font].space/8)
+ end,
+
+ -- [0xFEFF] = function(head,current) -- zerowidthnobreakspace
+ -- return head, current
+ -- end,
+
+}
+
+function characters.handler(head)
+ local current = head
+ local done = false
+ while current do
+ local id = current.id
+ if id == glyph_code then
+ local next = current.next
+ local char = current.char
+ local method = methods[char]
+ if method then
+ if trace_characters then
+ report_characters("replacing character %C, description %a",char,lower(chardata[char].description))
+ end
+ head = method(head,current)
+ head = remove_node(head,current,true)
+ done = true
+ end
+ current = next
+ else
+ current = current.next
+ end
+ end
+ return head, done
+end
diff --git a/tex/context/base/spac-hor.lua b/tex/context/base/spac-hor.lua
index 36802bfbb..09920bd46 100644
--- a/tex/context/base/spac-hor.lua
+++ b/tex/context/base/spac-hor.lua
@@ -1,31 +1,31 @@
-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 match = string.match
-local utfbyte = utf.byte
-local chardata = characters.data
-
-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
-}
-
-function commands.autonextspace(str) -- todo: use nexttoken
- local ch = match(str,"the letter (.)") or match(str,"the character (.)")
- ch = ch and chardata[utfbyte(ch)]
- if ch and can_have_space[ch.category] then
- context.space()
- end
-end
+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 match = string.match
+local utfbyte = utf.byte
+local chardata = characters.data
+
+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
+}
+
+function commands.autonextspace(str) -- todo: use nexttoken
+ local ch = match(str,"the letter (.)") or match(str,"the character (.)")
+ ch = ch and chardata[utfbyte(ch)]
+ if ch and can_have_space[ch.category] then
+ context.space()
+ end
+end
diff --git a/tex/context/base/spac-ver.lua b/tex/context/base/spac-ver.lua
index a042945f2..7d030ab1a 100644
--- a/tex/context/base/spac-ver.lua
+++ b/tex/context/base/spac-ver.lua
@@ -1,1358 +1,1358 @@
-if not modules then modules = { } end modules ['spac-ver'] = {
- version = 1.001,
- 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!
-
--- todo: directly set skips
-
--- this code dates from the beginning and is kind of experimental; it
--- will be optimized and improved soon
---
--- 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, max, min, round, abs = math.ceil, math.floor, math.max, math.min, math.round, math.abs
-local texlists, texdimen, texbox = tex.lists, tex.dimen, tex.box
-local lpegmatch = lpeg.match
-local unpack = unpack or table.unpack
-local allocate = utilities.storage.allocate
-local todimen = string.todimen
-local formatters = string.formatters
-
-local P, C, R, S, Cc = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cc
-
-local nodes, node, trackers, attributes, context = nodes, node, trackers, attributes, context
-
-local variables = interfaces.variables
-
-local starttiming = statistics.starttiming
-local stoptiming = statistics.stoptiming
-
--- 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_vpacking = false trackers.register("vspacing.packing", function(v) trace_vpacking = 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_page_builder = logs.reporter("builders","page")
-
-local a_skipcategory = attributes.private('skipcategory')
-local a_skippenalty = attributes.private('skippenalty')
-local a_skiporder = attributes.private('skiporder')
------ snap_category = attributes.private('snapcategory')
-local a_snapmethod = attributes.private('snapmethod')
-local a_snapvbox = attributes.private('snapvbox')
-
-local find_node_tail = node.tail
-local free_node = node.free
-local free_node_list = node.flush_list
-local copy_node = node.copy
-local traverse_nodes = node.traverse
-local traverse_nodes_id = node.traverse_id
-local insert_node_before = node.insert_before
-local insert_node_after = node.insert_after
-local remove_node = nodes.remove
-local count_nodes = nodes.count
-local nodeidstostring = nodes.idstostring
-local hpack_node = node.hpack
-local vpack_node = node.vpack
-local writable_spec = nodes.writable_spec
-local listtoutf = nodes.listtoutf
-
-local nodepool = nodes.pool
-
-local new_penalty = nodepool.penalty
-local new_kern = nodepool.kern
-local new_rule = nodepool.rule
-local new_gluespec = nodepool.gluespec
-
-local nodecodes = nodes.nodecodes
-local skipcodes = nodes.skipcodes
-local fillcodes = nodes.fillcodes
-
-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 whatsit_code = nodecodes.whatsit
-
-local userskip_code = skipcodes.userskip
-
-local vspacing = builders.vspacing or { }
-builders.vspacing = vspacing
-
-local vspacingdata = vspacing.data or { }
-vspacing.data = vspacingdata
-
-vspacingdata.snapmethods = vspacingdata.snapmethods or { }
-local snapmethods = vspacingdata.snapmethods --maybe some older code can go
-
-storage.register("builders/vspacing/data/snapmethods", snapmethods, "builders.vspacing.data.snapmethods")
-
-local default = {
- maxheight = true,
- maxdepth = true,
- strut = true,
- hfraction = 1,
- dfraction = 1,
-}
-
-local fractions = {
- minheight = "hfraction", maxheight = "hfraction",
- mindepth = "dfraction", maxdepth = "dfraction",
- top = "tlines", bottom = "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.hfraction, t.dfraction = detail, detail
- end
- end
- end
- if next(t) then
- t.hfraction = t.hfraction or 1
- t.dfraction = t.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, t.specification = name, method
- context(n)
-end
-
--- local rule_id = nodecodes.rule
--- local vlist_id = nodecodes.vlist
--- function nodes.makevtop(n)
--- if n.id == vlist_id then
--- local list = n.list
--- local height = (list and list.id <= rule_id and list.height) or 0
--- n.depth = n.depth - height + n.height
--- n.height = height
--- end
--- end
-
-local reference = nodes.reference
-
-local function validvbox(parentid,list)
- if parentid == hlist_code then
- local id = list.id
- if id == whatsit_code then -- check for initial par subtype
- list = list.next
- if not next then
- return nil
- end
- end
- local done = nil
- for n in traverse_nodes(list) do
- local id = n.id
- 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 = done.id
- if id == hlist_code then
- return validvbox(id,done.list)
- 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 = list.id
- if id == whatsit_code then -- check for initial par subtype
- list = list.next
- if not next then
- return false
- end
- end
---~ local i = 0
- for n in traverse_nodes(list) do
- local id = n.id
---~ i = i + 1 print(i,nodecodes[id],n[a_snapmethod])
- if id == hlist_code or id == vlist_code then
- local a = n[a_snapmethod]
- if not a then
- -- return true -- not snapped at all
- elseif a == 0 then
- return true -- already snapped
- end
- elseif id == glue_code or id == penalty_code then -- whatsit is weak spot
- -- 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 snap_hlist(where,current,method,height,depth) -- method.strut is default
- local list = current.list
- local t = trace_vsnapping and { }
- if t then
- t[#t+1] = formatters["list content: %s"](nodes.toutf(list))
- t[#t+1] = formatters["parent id: %s"](reference(current))
- t[#t+1] = formatters["snap method: %s"](method.name)
- t[#t+1] = formatters["specification: %s"](method.specification)
- end
- local snapht, snapdp
- if method["local"] then
- -- snapping is done immediately here
- snapht, snapdp = texdimen.bodyfontstrutheight, texdimen.bodyfontstrutdepth
- if t then
- t[#t+1] = formatters["local: snapht %p snapdp %p"](snapht,snapdp)
- end
- elseif method["global"] then
- snapht, snapdp = texdimen.globalbodyfontstrutheight, texdimen.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, snapdp = texdimen.globalbodyfontstrutheight, texdimen.globalbodyfontstrutdepth
- local lsnapht, lsnapdp = texdimen.bodyfontstrutheight, texdimen.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 h, d = height or current.height, depth or current.depth
- local hr, dr, ch, cd = method.hfraction or 1, method.dfraction or 1, h, d
- local tlines, blines = method.tlines or 1, method.blines or 1
- local done, plusht, plusdp = false, snapht, snapdp
- local snaphtdp = snapht + snapdp
-
- if method.none then
- plusht, plusdp = 0, 0
- if t then
- t[#t+1] = "none: plusht 0pt plusdp 0pt"
- end
- end
- if method.halfline then -- extra halfline
- plusht, plusdp = plusht + snaphtdp/2, plusdp + snaphtdp/2
- if t then
- t[#t+1] = formatters["halfline: plusht %p plusdp %p"](plusht,plusdp)
- end
- end
- if method.line then -- extra line
- plusht, plusdp = plusht + snaphtdp, plusdp + snaphtdp
- if t then
- t[#t+1] = formatters["line: plusht %p plusdp %p"](plusht,plusdp)
- end
- end
-
- if method.first then
- local thebox = current
- local id = thebox.id
- if id == hlist_code then
- thebox = validvbox(id,thebox.list)
- id = thebox and thebox.id
- end
- if thebox and id == vlist_code then
- local list, lh, ld = thebox.list
- for n in traverse_nodes_id(hlist_code,list) do
- lh, ld = n.height, n.depth
- break
- end
- if lh then
- local ht, dp = thebox.height, thebox.depth
- 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(current.list)
- shifted.shift = delta
- current.list = 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.last then
- local thebox = current
- local id = thebox.id
- if id == hlist_code then
- thebox = validvbox(id,thebox.list)
- id = thebox and thebox.id
- end
- if thebox and id == vlist_code then
- local list, lh, ld = thebox.list
- for n in traverse_nodes_id(hlist_code,list) do
- lh, ld = n.height, n.depth
- end
- if lh then
- local ht, dp = thebox.height, thebox.depth
- 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(current.list)
- shifted.shift = delta
- current.list = 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.minheight then
- ch = floored((h-hr*snapht)/snaphtdp)*snaphtdp + plusht
- if t then
- t[#t+1] = formatters["minheight: %p"](ch)
- end
- elseif method.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.mindepth then
- cd = floored((d-dr*snapdp)/snaphtdp)*snaphtdp + plusdp
- if t then
- t[#t+1] = formatters["mindepth: %p"](cd)
- end
- elseif method.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.top then
- ch = ch + tlines * snaphtdp
- if t then
- t[#t+1] = formatters["top height: %p"](ch)
- end
- end
- if method.bottom then
- cd = cd + blines * snaphtdp
- if t then
- t[#t+1] = formatters["bottom depth: %p"](cd)
- end
- end
-
- local offset = method.offset
- if offset then
- -- we need to set the attr
- if t then
- t[#t+1] = formatters["before offset: %p (width %p height %p depth %p)"](offset,current.width,current.height,current.depth)
- end
- local shifted = hpack_node(current.list)
- shifted.shift = offset
- current.list = shifted
- if t then
- t[#t+1] = formatters["after offset: %p (width %p height %p depth %p)"](offset,current.width,current.height,current.depth)
- end
- shifted[a_snapmethod] = 0
- current[a_snapmethod] = 0
- end
- if not height then
- current.height = ch
- if t then
- t[#t+1] = formatters["forced height: %p"](ch)
- end
- end
- if not depth then
- current.depth = 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)/(texdimen.globalbodyfontstrutheight + texdimen.globalbodyfontstrutdepth)
- t[#t+1] = formatters["final lines: %s -> %s (%s)"](original,lines,whatever)
- t[#t+1] = formatters["final height: %p -> %p"](h,ch)
- t[#t+1] = formatters["final depth: %p -> %p"](d,cd)
- end
- if t then
- report_snapper("trace: %s type %s\n\t%\n\tt",where,nodecodes[current.id],t)
- end
- return h, d, ch, cd, lines
-end
-
-local function snap_topskip(current,method)
- local spec = current.spec
- local w = spec.width
- local wd = w
- if spec.writable then
- spec.width, wd = 0, 0
- end
- return w, wd
-end
-
-local categories = allocate {
- [0] = 'discard',
- [1] = 'largest',
- [2] = 'force' ,
- [3] = 'penalty',
- [4] = 'add' ,
- [5] = 'disable',
- [6] = 'nowhite',
- [7] = 'goback',
- [8] = 'together'
-}
-
-vspacing.categories = categories
-
-function vspacing.tocategories(str)
- local t = { }
- for s in gmatch(str,"[^, ]") do
- local n = tonumber(s)
- if n then
- t[categories[n]] = true
- else
- t[b] = true
- end
- end
- return t
-end
-
-function vspacing.tocategory(str)
- 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 -- todo: interface.variables
-
- vspacing.fixed = false
-
- local map = vspacingdata.map
- local skip = vspacingdata.skip
-
- local multiplier = C(S("+-")^0 * R("09")^1) * P("*")
- local category = P(":") * C(P(1)^1)
- local keyword = C((1-category)^1)
- local splitter = (multiplier + Cc(1)) * keyword * (category + Cc(false))
-
- local k_fixed, k_flexible, k_category, k_penalty, k_order = variables.fixed, variables.flexible, "category", "penalty", "order"
-
- -- This will change: just node.write and we can store the values in skips which
- -- then obeys grouping
-
- local fixedblankskip = context.fixedblankskip
- local flexibleblankskip = context.flexibleblankskip
- local setblankcategory = context.setblankcategory
- local setblankorder = context.setblankorder
- local setblankpenalty = context.setblankpenalty
- local setblankhandling = context.setblankhandling
- local flushblankhandling = context.flushblankhandling
- local addpredefinedblankskip = context.addpredefinedblankskip
- local addaskedblankskip = context.addaskedblankskip
-
- local function analyze(str,oldcategory) -- we could use shorter names
- for s in gmatch(str,"([^ ,]+)") do
- local amount, keyword, detail = lpegmatch(splitter,s) -- the comma splitter can be merged
- if not keyword then
- report_vspacing("unknown directive %a",s)
- else
- local mk = map[keyword]
- if mk then
- category = analyze(mk,category)
- elseif keyword == k_fixed then
- fixedblankskip()
- elseif keyword == k_flexible then
- flexibleblankskip()
- elseif keyword == k_category then
- local category = tonumber(detail)
- if category then
- setblankcategory(category)
- if category ~= oldcategory then
- flushblankhandling()
- oldcategory = category
- end
- end
- elseif keyword == k_order and detail then
- local order = tonumber(detail)
- if order then
- setblankorder(order)
- end
- elseif keyword == k_penalty and detail then
- local penalty = tonumber(detail)
- if penalty then
- setblankpenalty(penalty)
- end
- else
- amount = tonumber(amount) or 1
- local sk = skip[keyword]
- if sk then
- addpredefinedblankskip(amount,keyword)
- else -- no check
- addaskedblankskip(amount,keyword)
- end
- end
- end
- end
- return category
- end
-
- local pushlogger = context.pushlogger
- local startblankhandling = context.startblankhandling
- local stopblankhandling = context.stopblankhandling
- local poplogger = context.poplogger
-
- function vspacing.analyze(str)
- if trace_vspacing then
- pushlogger(report_vspacing)
- startblankhandling()
- analyze(str,1)
- stopblankhandling()
- poplogger()
- else
- startblankhandling()
- analyze(str,1)
- stopblankhandling()
- end
- end
-
- --
-
- 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
-
-end
-
--- implementation
-
-local trace_list, tracing_info, before, after = { }, false, "", ""
-
-local function nodes_to_string(head)
- local current, t = head, { }
- while current do
- local id = current.id
- local ty = nodecodes[id]
- if id == penalty_code then
- t[#t+1] = formatters["%s:%s"](ty,current.penalty)
- elseif id == glue_code then -- or id == kern_code then -- to be tested
- t[#t+1] = formatters["%s:%p"](ty,current)
- elseif id == kern_code then
- t[#t+1] = formatters["%s:%p"](ty,current.kern)
- else
- t[#t+1] = ty
- end
- current = current.next
- 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, 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, 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[what.id]
- 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 trace_done(str,data)
- if data.id == penalty_code then
- trace_list[#trace_list+1] = { "penalty", formatters["%s | %s"](str,data.penalty) }
- else
- trace_list[#trace_list+1] = { "glue", formatters["%s | %p"](str,data) }
- end
- tracing_info = true
-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
-
--- alignment box begin_of_par vmode_par hmode_par insert penalty before_display after_display
-
-local skipcodes = nodes.skipcodes
-
-local userskip_code = skipcodes.userskip
-local lineskip_code = skipcodes.lineskip
-local baselineskip_code = skipcodes.baselineskip
-local parskip_code = skipcodes.parskip
-local abovedisplayskip_code = skipcodes.abovedisplayskip
-local belowdisplayskip_code = skipcodes.belowdisplayskip
-local abovedisplayshortskip_code = skipcodes.abovedisplayshortskip
-local belowdisplayshortskip_code = skipcodes.belowdisplayshortskip
-local topskip_code = skipcodes.topskip
-local splittopskip_code = skipcodes.splittopskip
-
-local free_glue_node = free_node
-local discard, largest, force, penalty, add, disable, nowhite, goback, together = 0, 1, 2, 3, 4, 5, 6, 7, 8
-
--- local function free_glue_node(n)
--- -- free_node(n.spec)
--- print("before",n)
--- logs.flush()
--- free_node(n)
--- print("after")
--- logs.flush()
--- end
-
-function vspacing.snapbox(n,how)
- local sv = snapmethods[how]
- if sv then
- local box = texbox[n]
- local list = box.list
- if list then
- local s = list[a_snapmethod]
- if s == 0 then
- if trace_vsnapping then
- -- report_snapper("box list not snapped, already done")
- end
- else
- local ht, dp = box.height, box.depth
- 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 = snap_hlist("box",box,sv,ht,dp)
- box.height, box.depth = 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
- box[a_snapmethod] = 0 --
- list[a_snapmethod] = 0 -- yes or no
- end
- end
- end
- end
-end
-
-local function forced_skip(head,current,width,where,trace)
- if where == "after" then
- head, current = insert_node_after(head,current,new_rule(0,0,0))
- head, current = insert_node_after(head,current,new_kern(width))
- head, current = insert_node_after(head,current,new_rule(0,0,0))
- else
- local c = current
- head, current = insert_node_before(head,current,new_rule(0,0,0))
- head, current = insert_node_before(head,current,new_kern(width))
- head, current = insert_node_before(head,current,new_rule(0,0,0))
- 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 function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also pass tail
- if trace then
- reset_tracing(head)
- end
- local current, oldhead = head, head
- local glue_order, glue_data, force_glue = 0, nil, false
- local penalty_order, penalty_data, natural_penalty = 0, nil, nil
- local parskip, ignore_parskip, ignore_following, ignore_whitespace, keep_together = nil, false, false, false, false
- --
- -- todo: keep_together: between headers
- --
- 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
- head = insert_node_before(head,current,p)
- end
- if glue_data then
- if force_glue then
- if trace then trace_done("flushed due to " .. why,glue_data) end
- head = forced_skip(head,current,glue_data.spec.width,"before",trace)
- free_glue_node(glue_data)
- elseif glue_data.spec.writable then
- if trace then trace_done("flushed due to " .. why,glue_data) end
- head = insert_node_before(head,current,glue_data)
- else
- free_glue_node(glue_data)
- 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",
- texdimen.globalbodyfontstrutheight, texdimen.globalbodyfontstrutdepth,
- texdimen.bodyfontstrutheight, texdimen.bodyfontstrutdepth)
- end
- if trace then trace_info("start analyzing",where,what) end
- while current do
- local id = current.id
- if id == hlist_code or id == vlist_code then
- -- needs checking, why so many calls
- if snap then
- local list = current.list
- local s = 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
- if list and already_done(id,list,a_snapmethod) then
- local ht, dp = current.height, current.depth
- -- assume that the box is already snapped
- if trace_vsnapping then
- report_snapper("mvl list already snapped at (%p,%p): %s",ht,dp,listtoutf(list))
- end
- else
- local h, d, ch, cd, lines = snap_hlist("mvl",current,sv)
- 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
- current[a_snapmethod] = 0
- end
- else
- --
- end
- -- tex.prevdepth = 0
- flush("list")
- current = current.next
- elseif id == penalty_code then
- -- natural_penalty = current.penalty
- -- if trace then trace_done("removed penalty",current) end
- -- head, current = remove_node(head, current, true)
- current = current.next
- elseif id == kern_code then
- if snap and trace_vsnapping and current.kern ~= 0 then
- report_snapper("kern of %p kept",current.kern)
- end
- flush("kern")
- current = current.next
- elseif id == glue_code then
- local subtype = current.subtype
- if subtype == userskip_code then
- local sc = current[a_skipcategory] -- has no default, no unset (yet)
- local so = current[a_skiporder] or 1 -- has 1 default, no unset (yet)
- local sp = current[a_skippenalty] -- has no default, no unset (yet)
- if sp and sc == penalty then
- 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 = current.next
- else
- -- not look back across head
- local previous = current.prev
- if previous and previous.id == glue_code and previous.subtype == userskip_code then
- local ps = previous.spec
- if ps.writable then
- local cs = current.spec
- if cs.writable and ps.stretch_order == 0 and ps.shrink_order == 0 and cs.stretch_order == 0 and cs.shrink_order == 0 then
- local pw, pp, pm = ps.width, ps.stretch, ps.shrink
- local cw, cp, cm = cs.width, cs.stretch, cs.shrink
- -- ps = writable_spec(previous) -- no writable needed here
- -- ps.width, ps.stretch, ps.shrink = pw + cw, pp + cp, pm + cm
- previous.spec = new_gluespec(pw + cw, pp + cp, pm + cm) -- else topskip can disappear
- if trace then trace_natural("removed",current) end
- head, current = remove_node(head, current, true)
- -- current = previous
- if trace then trace_natural("collapsed",previous) end
- -- current = current.next
- else
- if trace then trace_natural("filler",current) end
- current = current.next
- end
- else
- if trace then trace_natural("natural (no prev spec)",current) end
- current = current.next
- end
- else
- if trace then trace_natural("natural (no prev)",current) end
- current = current.next
- end
- end
- glue_order, glue_data = 0, nil
- elseif sc == disable then
- ignore_following = true
- if trace then trace_skip("disable",sc,so,sp,current) end
- head, current = remove_node(head, current, true)
- elseif sc == together then
- keep_together = true
- if trace then trace_skip("together",sc,so,sp,current) end
- head, current = remove_node(head, current, true)
- elseif sc == nowhite then
- ignore_whitespace = true
- head, current = remove_node(head, current, true)
- elseif sc == discard then
- if trace then trace_skip("discard",sc,so,sp,current) end
- head, current = remove_node(head, current, true)
- 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
- free_glue_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 cs, gs = current.spec, glue_data.spec
- local cw, gw = cs.width, gs.width
- if cw > gw then
- if trace then trace_skip("largest",sc,so,sp,current) end
- free_glue_node(glue_data) -- also free spec
- 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
- free_glue_node(glue_data) -- also free spec
- 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
- free_glue_node(glue_data) -- also free spec
- head, current, glue_data = remove_node(head, current)
- elseif sc == penalty then
- if trace then trace_skip("penalty",sc,so,sp,current) end
- free_glue_node(glue_data) -- also free spec
- 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 old, new = glue_data.spec, current.spec
- local old, new = writable_spec(glue_data), current.spec
- old.width = old.width + new.width
- old.stretch = old.stretch + new.stretch
- old.shrink = old.shrink + new.shrink
- -- 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 = current[a_snapmethod]
- if s and s ~= 0 then
- current[a_snapmethod] = 0
- if current.spec.writable then
- local spec = writable_spec(current)
- spec.width = 0
- if trace_vsnapping then
- report_snapper("lineskip set to zero")
- end
- 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 = current.next
- elseif subtype == baselineskip_code then
- if snap then
- local s = current[a_snapmethod]
- if s and s ~= 0 then
- current[a_snapmethod] = 0
- if current.spec.writable then
- local spec = writable_spec(current)
- spec.width = 0
- if trace_vsnapping then
- report_snapper("baselineskip set to zero")
- end
- 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 = current.next
- 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 ps, gs = current.spec, glue_data.spec
- if ps.writable and gs.writable and ps.width > gs.width then
- glue_data.spec = copy_node(ps)
- if trace then trace_natural("taking parskip",current) end
- else
- if trace then trace_natural("removed parskip",current) end
- end
- head, current = remove_node(head, current, true)
- 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
- if snap then
- local s = current[a_snapmethod]
- if s and s ~= 0 then
- 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 = current.next
- elseif subtype == abovedisplayskip_code then
- --
- if trace then trace_skip("above display skip (normal)",sc,so,sp,current) end
- flush("above display skip (normal)")
- current = current.next
- --
- elseif subtype == belowdisplayskip_code then
- --
- if trace then trace_skip("below display skip (normal)",sc,so,sp,current) end
- flush("below display skip (normal)")
- current = current.next
- --
- elseif subtype == abovedisplayshortskip_code then
- --
- if trace then trace_skip("above display skip (short)",sc,so,sp,current) end
- flush("above display skip (short)")
- current = current.next
- --
- elseif subtype == belowdisplayshortskip_code then
- --
- if trace then trace_skip("below display skip (short)",sc,so,sp,current) end
- flush("below display skip (short)")
- current = current.next
- --
- else -- other glue
- if snap and trace_vsnapping and current.spec.writable and current.spec.width ~= 0 then
- report_snapper("glue %p of type %a kept",current.spec.width,skipcodes[subtype])
- --~ current.spec.width = 0
- end
- if trace then trace_skip(formatted["glue of type %a"](subtype),sc,so,sp,current) end
- flush("some glue")
- current = current.next
- end
- else
- flush("something else")
- current = current.next
- 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
- head, tail = insert_node_after(head,tail,p)
- 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,glue_data.spec.width,"after",trace)
- free_glue_node(glue_data)
- else
- head, tail = insert_node_after(head,tail,glue_data)
- end
- 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[oldhead.id],nodecodes[head.id])
- end
- end
- return head, true
-end
-
--- alignment after_output end box new_graf vmode_par hmode_par insert penalty before_display after_display
--- \par -> vmode_par
---
--- status.best_page_break
--- tex.lists.best_page_break
--- tex.lists.best_size (natural size to best_page_break)
--- tex.lists.least_page_cost (badness of best_page_break)
--- tex.lists.page_head
--- tex.lists.contrib_head
-
-local stackhead, stacktail, stackhack = nil, nil, false
-
-local function report(message,lst)
- report_vspacing(message,count_nodes(lst,true),nodeidstostring(lst))
-end
-
-function vspacing.pagehandler(newhead,where)
- -- local newhead = texlists.contrib_head
- 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
- for n in traverse_nodes(newhead) do -- we could just look for glue nodes
- local id = n.id
- if id ~= glue_code then
- flush = true
- elseif n.subtype == userskip_code then
- if n[a_skipcategory] then
- stackhack = true
- else
- flush = true
- end
- else
- -- tricky
- end
- end
- if flush then
- if stackhead then
- if trace_collect_vspacing then report("appending %s nodes to stack (final): %s",newhead) end
- stacktail.next = newhead
- newhead.prev = stacktail
- newhead = stackhead
- stackhead, stacktail = nil, nil
- end
- if stackhack then
- stackhack = false
- if trace_collect_vspacing then report("processing %s nodes: %s",newhead) end
---~ texlists.contrib_head = collapser(newhead,"page",where,trace_page_vspacing,true,a_snapmethod)
- newhead = collapser(newhead,"page",where,trace_page_vspacing,true,a_snapmethod)
- else
- if trace_collect_vspacing then report("flushing %s nodes: %s",newhead) end
---~ texlists.contrib_head = newhead
- end
- else
- if stackhead then
- if trace_collect_vspacing then report("appending %s nodes to stack (intermediate): %s",newhead) end
- stacktail.next = newhead
- newhead.prev = stacktail
- else
- if trace_collect_vspacing then report("storing %s nodes in stack (initial): %s",newhead) end
- stackhead = newhead
- end
- stacktail = newtail
- -- texlists.contrib_head = nil
- newhead = nil
- end
- end
- return newhead
-end
-
-local ignore = table.tohash {
- "split_keep",
- "split_off",
- -- "vbox",
-}
-
-function vspacing.vboxhandler(head,where)
- if head and not ignore[where] and head.next then
- -- starttiming(vspacing)
- head = collapser(head,"vbox",where,trace_vbox_vspacing,true,a_snapvbox) -- todo: local snapper
- -- stoptiming(vspacing)
- end
- return head
-end
-
-function vspacing.collapsevbox(n) -- for boxes but using global a_snapmethod
- local list = texbox[n].list
- if list then
- -- starttiming(vspacing)
- texbox[n].list = vpack_node(collapser(list,"snapper","vbox",trace_vbox_vspacing,true,a_snapmethod))
- -- stoptiming(vspacing)
- end
-end
-
--- We will split this module so a few locals are repeated. Also this will be
--- rewritten.
-
-nodes.builders = nodes.builder or { }
-local builders = nodes.builders
-
-local actions = nodes.tasks.actions("vboxbuilders")
-
-function builders.vpack_filter(head,groupcode,size,packtype,maxdepth,direction)
- local done = false
- if head then
- starttiming(builders)
- if trace_vpacking then
- local before = nodes.count(head)
- head, done = actions(head,groupcode,size,packtype,maxdepth,direction)
- local after = nodes.count(head)
- if done then
- nodes.processors.tracer("vpack","changed",head,groupcode,before,after,true)
- else
- nodes.processors.tracer("vpack","unchanged",head,groupcode,before,after,true)
- end
- else
- head, done = actions(head,groupcode)
- end
- 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 actions = nodes.tasks.actions("mvlbuilders")
-
-local function report(groupcode,head)
- report_page_builder("trigger: %s",groupcode)
- report_page_builder(" vsize : %p",tex.vsize)
- report_page_builder(" pagegoal : %p",tex.pagegoal)
- report_page_builder(" pagetotal: %p",tex.pagetotal)
- report_page_builder(" list : %s",head and nodeidstostring(head) or "<empty>")
-end
-
-function builders.buildpage_filter(groupcode)
- local head, done = texlists.contrib_head, false
- -- if head and head.next and head.next.id == hlist_code and head.next.width == 1 then
- -- report_page_builder("trigger otr calculations")
- -- free_node_list(head)
- -- head = nil
- -- end
- if head then
- starttiming(builders)
- if trace_page_builder then
- report(groupcode,head)
- end
- head, done = actions(head,groupcode)
- stoptiming(builders)
- -- -- doesn't work here (not passed on?)
- -- tex.pagegoal = tex.vsize - tex.dimen.d_page_floats_inserted_top - tex.dimen.d_page_floats_inserted_bottom
- texlists.contrib_head = head
- return done and head or true
- else
- if trace_page_builder then
- report(groupcode)
- end
- return nil, false
- end
-end
-
-callbacks.register('vpack_filter', builders.vpack_filter, "vertical spacing etc")
-callbacks.register('buildpage_filter', builders.buildpage_filter, "vertical spacing etc (mvl)")
-
-statistics.register("v-node processing time", function()
- return statistics.elapsedseconds(builders)
-end)
-
--- interface
-
-commands.vspacing = vspacing.analyze
-commands.vspacingsetamount = vspacing.setskip
-commands.vspacingdefine = vspacing.setmap
-commands.vspacingcollapse = vspacing.collapsevbox
-commands.vspacingsnap = vspacing.snapbox
+if not modules then modules = { } end modules ['spac-ver'] = {
+ version = 1.001,
+ 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!
+
+-- todo: directly set skips
+
+-- this code dates from the beginning and is kind of experimental; it
+-- will be optimized and improved soon
+--
+-- 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, max, min, round, abs = math.ceil, math.floor, math.max, math.min, math.round, math.abs
+local texlists, texdimen, texbox = tex.lists, tex.dimen, tex.box
+local lpegmatch = lpeg.match
+local unpack = unpack or table.unpack
+local allocate = utilities.storage.allocate
+local todimen = string.todimen
+local formatters = string.formatters
+
+local P, C, R, S, Cc = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cc
+
+local nodes, node, trackers, attributes, context = nodes, node, trackers, attributes, context
+
+local variables = interfaces.variables
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+
+-- 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_vpacking = false trackers.register("vspacing.packing", function(v) trace_vpacking = 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_page_builder = logs.reporter("builders","page")
+
+local a_skipcategory = attributes.private('skipcategory')
+local a_skippenalty = attributes.private('skippenalty')
+local a_skiporder = attributes.private('skiporder')
+----- snap_category = attributes.private('snapcategory')
+local a_snapmethod = attributes.private('snapmethod')
+local a_snapvbox = attributes.private('snapvbox')
+
+local find_node_tail = node.tail
+local free_node = node.free
+local free_node_list = node.flush_list
+local copy_node = node.copy
+local traverse_nodes = node.traverse
+local traverse_nodes_id = node.traverse_id
+local insert_node_before = node.insert_before
+local insert_node_after = node.insert_after
+local remove_node = nodes.remove
+local count_nodes = nodes.count
+local nodeidstostring = nodes.idstostring
+local hpack_node = node.hpack
+local vpack_node = node.vpack
+local writable_spec = nodes.writable_spec
+local listtoutf = nodes.listtoutf
+
+local nodepool = nodes.pool
+
+local new_penalty = nodepool.penalty
+local new_kern = nodepool.kern
+local new_rule = nodepool.rule
+local new_gluespec = nodepool.gluespec
+
+local nodecodes = nodes.nodecodes
+local skipcodes = nodes.skipcodes
+local fillcodes = nodes.fillcodes
+
+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 whatsit_code = nodecodes.whatsit
+
+local userskip_code = skipcodes.userskip
+
+local vspacing = builders.vspacing or { }
+builders.vspacing = vspacing
+
+local vspacingdata = vspacing.data or { }
+vspacing.data = vspacingdata
+
+vspacingdata.snapmethods = vspacingdata.snapmethods or { }
+local snapmethods = vspacingdata.snapmethods --maybe some older code can go
+
+storage.register("builders/vspacing/data/snapmethods", snapmethods, "builders.vspacing.data.snapmethods")
+
+local default = {
+ maxheight = true,
+ maxdepth = true,
+ strut = true,
+ hfraction = 1,
+ dfraction = 1,
+}
+
+local fractions = {
+ minheight = "hfraction", maxheight = "hfraction",
+ mindepth = "dfraction", maxdepth = "dfraction",
+ top = "tlines", bottom = "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.hfraction, t.dfraction = detail, detail
+ end
+ end
+ end
+ if next(t) then
+ t.hfraction = t.hfraction or 1
+ t.dfraction = t.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, t.specification = name, method
+ context(n)
+end
+
+-- local rule_id = nodecodes.rule
+-- local vlist_id = nodecodes.vlist
+-- function nodes.makevtop(n)
+-- if n.id == vlist_id then
+-- local list = n.list
+-- local height = (list and list.id <= rule_id and list.height) or 0
+-- n.depth = n.depth - height + n.height
+-- n.height = height
+-- end
+-- end
+
+local reference = nodes.reference
+
+local function validvbox(parentid,list)
+ if parentid == hlist_code then
+ local id = list.id
+ if id == whatsit_code then -- check for initial par subtype
+ list = list.next
+ if not next then
+ return nil
+ end
+ end
+ local done = nil
+ for n in traverse_nodes(list) do
+ local id = n.id
+ 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 = done.id
+ if id == hlist_code then
+ return validvbox(id,done.list)
+ 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 = list.id
+ if id == whatsit_code then -- check for initial par subtype
+ list = list.next
+ if not next then
+ return false
+ end
+ end
+--~ local i = 0
+ for n in traverse_nodes(list) do
+ local id = n.id
+--~ i = i + 1 print(i,nodecodes[id],n[a_snapmethod])
+ if id == hlist_code or id == vlist_code then
+ local a = n[a_snapmethod]
+ if not a then
+ -- return true -- not snapped at all
+ elseif a == 0 then
+ return true -- already snapped
+ end
+ elseif id == glue_code or id == penalty_code then -- whatsit is weak spot
+ -- 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 snap_hlist(where,current,method,height,depth) -- method.strut is default
+ local list = current.list
+ local t = trace_vsnapping and { }
+ if t then
+ t[#t+1] = formatters["list content: %s"](nodes.toutf(list))
+ t[#t+1] = formatters["parent id: %s"](reference(current))
+ t[#t+1] = formatters["snap method: %s"](method.name)
+ t[#t+1] = formatters["specification: %s"](method.specification)
+ end
+ local snapht, snapdp
+ if method["local"] then
+ -- snapping is done immediately here
+ snapht, snapdp = texdimen.bodyfontstrutheight, texdimen.bodyfontstrutdepth
+ if t then
+ t[#t+1] = formatters["local: snapht %p snapdp %p"](snapht,snapdp)
+ end
+ elseif method["global"] then
+ snapht, snapdp = texdimen.globalbodyfontstrutheight, texdimen.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, snapdp = texdimen.globalbodyfontstrutheight, texdimen.globalbodyfontstrutdepth
+ local lsnapht, lsnapdp = texdimen.bodyfontstrutheight, texdimen.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 h, d = height or current.height, depth or current.depth
+ local hr, dr, ch, cd = method.hfraction or 1, method.dfraction or 1, h, d
+ local tlines, blines = method.tlines or 1, method.blines or 1
+ local done, plusht, plusdp = false, snapht, snapdp
+ local snaphtdp = snapht + snapdp
+
+ if method.none then
+ plusht, plusdp = 0, 0
+ if t then
+ t[#t+1] = "none: plusht 0pt plusdp 0pt"
+ end
+ end
+ if method.halfline then -- extra halfline
+ plusht, plusdp = plusht + snaphtdp/2, plusdp + snaphtdp/2
+ if t then
+ t[#t+1] = formatters["halfline: plusht %p plusdp %p"](plusht,plusdp)
+ end
+ end
+ if method.line then -- extra line
+ plusht, plusdp = plusht + snaphtdp, plusdp + snaphtdp
+ if t then
+ t[#t+1] = formatters["line: plusht %p plusdp %p"](plusht,plusdp)
+ end
+ end
+
+ if method.first then
+ local thebox = current
+ local id = thebox.id
+ if id == hlist_code then
+ thebox = validvbox(id,thebox.list)
+ id = thebox and thebox.id
+ end
+ if thebox and id == vlist_code then
+ local list, lh, ld = thebox.list
+ for n in traverse_nodes_id(hlist_code,list) do
+ lh, ld = n.height, n.depth
+ break
+ end
+ if lh then
+ local ht, dp = thebox.height, thebox.depth
+ 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(current.list)
+ shifted.shift = delta
+ current.list = 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.last then
+ local thebox = current
+ local id = thebox.id
+ if id == hlist_code then
+ thebox = validvbox(id,thebox.list)
+ id = thebox and thebox.id
+ end
+ if thebox and id == vlist_code then
+ local list, lh, ld = thebox.list
+ for n in traverse_nodes_id(hlist_code,list) do
+ lh, ld = n.height, n.depth
+ end
+ if lh then
+ local ht, dp = thebox.height, thebox.depth
+ 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(current.list)
+ shifted.shift = delta
+ current.list = 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.minheight then
+ ch = floored((h-hr*snapht)/snaphtdp)*snaphtdp + plusht
+ if t then
+ t[#t+1] = formatters["minheight: %p"](ch)
+ end
+ elseif method.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.mindepth then
+ cd = floored((d-dr*snapdp)/snaphtdp)*snaphtdp + plusdp
+ if t then
+ t[#t+1] = formatters["mindepth: %p"](cd)
+ end
+ elseif method.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.top then
+ ch = ch + tlines * snaphtdp
+ if t then
+ t[#t+1] = formatters["top height: %p"](ch)
+ end
+ end
+ if method.bottom then
+ cd = cd + blines * snaphtdp
+ if t then
+ t[#t+1] = formatters["bottom depth: %p"](cd)
+ end
+ end
+
+ local offset = method.offset
+ if offset then
+ -- we need to set the attr
+ if t then
+ t[#t+1] = formatters["before offset: %p (width %p height %p depth %p)"](offset,current.width,current.height,current.depth)
+ end
+ local shifted = hpack_node(current.list)
+ shifted.shift = offset
+ current.list = shifted
+ if t then
+ t[#t+1] = formatters["after offset: %p (width %p height %p depth %p)"](offset,current.width,current.height,current.depth)
+ end
+ shifted[a_snapmethod] = 0
+ current[a_snapmethod] = 0
+ end
+ if not height then
+ current.height = ch
+ if t then
+ t[#t+1] = formatters["forced height: %p"](ch)
+ end
+ end
+ if not depth then
+ current.depth = 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)/(texdimen.globalbodyfontstrutheight + texdimen.globalbodyfontstrutdepth)
+ t[#t+1] = formatters["final lines: %s -> %s (%s)"](original,lines,whatever)
+ t[#t+1] = formatters["final height: %p -> %p"](h,ch)
+ t[#t+1] = formatters["final depth: %p -> %p"](d,cd)
+ end
+ if t then
+ report_snapper("trace: %s type %s\n\t%\n\tt",where,nodecodes[current.id],t)
+ end
+ return h, d, ch, cd, lines
+end
+
+local function snap_topskip(current,method)
+ local spec = current.spec
+ local w = spec.width
+ local wd = w
+ if spec.writable then
+ spec.width, wd = 0, 0
+ end
+ return w, wd
+end
+
+local categories = allocate {
+ [0] = 'discard',
+ [1] = 'largest',
+ [2] = 'force' ,
+ [3] = 'penalty',
+ [4] = 'add' ,
+ [5] = 'disable',
+ [6] = 'nowhite',
+ [7] = 'goback',
+ [8] = 'together'
+}
+
+vspacing.categories = categories
+
+function vspacing.tocategories(str)
+ local t = { }
+ for s in gmatch(str,"[^, ]") do
+ local n = tonumber(s)
+ if n then
+ t[categories[n]] = true
+ else
+ t[b] = true
+ end
+ end
+ return t
+end
+
+function vspacing.tocategory(str)
+ 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 -- todo: interface.variables
+
+ vspacing.fixed = false
+
+ local map = vspacingdata.map
+ local skip = vspacingdata.skip
+
+ local multiplier = C(S("+-")^0 * R("09")^1) * P("*")
+ local category = P(":") * C(P(1)^1)
+ local keyword = C((1-category)^1)
+ local splitter = (multiplier + Cc(1)) * keyword * (category + Cc(false))
+
+ local k_fixed, k_flexible, k_category, k_penalty, k_order = variables.fixed, variables.flexible, "category", "penalty", "order"
+
+ -- This will change: just node.write and we can store the values in skips which
+ -- then obeys grouping
+
+ local fixedblankskip = context.fixedblankskip
+ local flexibleblankskip = context.flexibleblankskip
+ local setblankcategory = context.setblankcategory
+ local setblankorder = context.setblankorder
+ local setblankpenalty = context.setblankpenalty
+ local setblankhandling = context.setblankhandling
+ local flushblankhandling = context.flushblankhandling
+ local addpredefinedblankskip = context.addpredefinedblankskip
+ local addaskedblankskip = context.addaskedblankskip
+
+ local function analyze(str,oldcategory) -- we could use shorter names
+ for s in gmatch(str,"([^ ,]+)") do
+ local amount, keyword, detail = lpegmatch(splitter,s) -- the comma splitter can be merged
+ if not keyword then
+ report_vspacing("unknown directive %a",s)
+ else
+ local mk = map[keyword]
+ if mk then
+ category = analyze(mk,category)
+ elseif keyword == k_fixed then
+ fixedblankskip()
+ elseif keyword == k_flexible then
+ flexibleblankskip()
+ elseif keyword == k_category then
+ local category = tonumber(detail)
+ if category then
+ setblankcategory(category)
+ if category ~= oldcategory then
+ flushblankhandling()
+ oldcategory = category
+ end
+ end
+ elseif keyword == k_order and detail then
+ local order = tonumber(detail)
+ if order then
+ setblankorder(order)
+ end
+ elseif keyword == k_penalty and detail then
+ local penalty = tonumber(detail)
+ if penalty then
+ setblankpenalty(penalty)
+ end
+ else
+ amount = tonumber(amount) or 1
+ local sk = skip[keyword]
+ if sk then
+ addpredefinedblankskip(amount,keyword)
+ else -- no check
+ addaskedblankskip(amount,keyword)
+ end
+ end
+ end
+ end
+ return category
+ end
+
+ local pushlogger = context.pushlogger
+ local startblankhandling = context.startblankhandling
+ local stopblankhandling = context.stopblankhandling
+ local poplogger = context.poplogger
+
+ function vspacing.analyze(str)
+ if trace_vspacing then
+ pushlogger(report_vspacing)
+ startblankhandling()
+ analyze(str,1)
+ stopblankhandling()
+ poplogger()
+ else
+ startblankhandling()
+ analyze(str,1)
+ stopblankhandling()
+ end
+ end
+
+ --
+
+ 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
+
+end
+
+-- implementation
+
+local trace_list, tracing_info, before, after = { }, false, "", ""
+
+local function nodes_to_string(head)
+ local current, t = head, { }
+ while current do
+ local id = current.id
+ local ty = nodecodes[id]
+ if id == penalty_code then
+ t[#t+1] = formatters["%s:%s"](ty,current.penalty)
+ elseif id == glue_code then -- or id == kern_code then -- to be tested
+ t[#t+1] = formatters["%s:%p"](ty,current)
+ elseif id == kern_code then
+ t[#t+1] = formatters["%s:%p"](ty,current.kern)
+ else
+ t[#t+1] = ty
+ end
+ current = current.next
+ 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, 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, 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[what.id]
+ 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 trace_done(str,data)
+ if data.id == penalty_code then
+ trace_list[#trace_list+1] = { "penalty", formatters["%s | %s"](str,data.penalty) }
+ else
+ trace_list[#trace_list+1] = { "glue", formatters["%s | %p"](str,data) }
+ end
+ tracing_info = true
+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
+
+-- alignment box begin_of_par vmode_par hmode_par insert penalty before_display after_display
+
+local skipcodes = nodes.skipcodes
+
+local userskip_code = skipcodes.userskip
+local lineskip_code = skipcodes.lineskip
+local baselineskip_code = skipcodes.baselineskip
+local parskip_code = skipcodes.parskip
+local abovedisplayskip_code = skipcodes.abovedisplayskip
+local belowdisplayskip_code = skipcodes.belowdisplayskip
+local abovedisplayshortskip_code = skipcodes.abovedisplayshortskip
+local belowdisplayshortskip_code = skipcodes.belowdisplayshortskip
+local topskip_code = skipcodes.topskip
+local splittopskip_code = skipcodes.splittopskip
+
+local free_glue_node = free_node
+local discard, largest, force, penalty, add, disable, nowhite, goback, together = 0, 1, 2, 3, 4, 5, 6, 7, 8
+
+-- local function free_glue_node(n)
+-- -- free_node(n.spec)
+-- print("before",n)
+-- logs.flush()
+-- free_node(n)
+-- print("after")
+-- logs.flush()
+-- end
+
+function vspacing.snapbox(n,how)
+ local sv = snapmethods[how]
+ if sv then
+ local box = texbox[n]
+ local list = box.list
+ if list then
+ local s = list[a_snapmethod]
+ if s == 0 then
+ if trace_vsnapping then
+ -- report_snapper("box list not snapped, already done")
+ end
+ else
+ local ht, dp = box.height, box.depth
+ 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 = snap_hlist("box",box,sv,ht,dp)
+ box.height, box.depth = 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
+ box[a_snapmethod] = 0 --
+ list[a_snapmethod] = 0 -- yes or no
+ end
+ end
+ end
+ end
+end
+
+local function forced_skip(head,current,width,where,trace)
+ if where == "after" then
+ head, current = insert_node_after(head,current,new_rule(0,0,0))
+ head, current = insert_node_after(head,current,new_kern(width))
+ head, current = insert_node_after(head,current,new_rule(0,0,0))
+ else
+ local c = current
+ head, current = insert_node_before(head,current,new_rule(0,0,0))
+ head, current = insert_node_before(head,current,new_kern(width))
+ head, current = insert_node_before(head,current,new_rule(0,0,0))
+ 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 function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also pass tail
+ if trace then
+ reset_tracing(head)
+ end
+ local current, oldhead = head, head
+ local glue_order, glue_data, force_glue = 0, nil, false
+ local penalty_order, penalty_data, natural_penalty = 0, nil, nil
+ local parskip, ignore_parskip, ignore_following, ignore_whitespace, keep_together = nil, false, false, false, false
+ --
+ -- todo: keep_together: between headers
+ --
+ 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
+ head = insert_node_before(head,current,p)
+ end
+ if glue_data then
+ if force_glue then
+ if trace then trace_done("flushed due to " .. why,glue_data) end
+ head = forced_skip(head,current,glue_data.spec.width,"before",trace)
+ free_glue_node(glue_data)
+ elseif glue_data.spec.writable then
+ if trace then trace_done("flushed due to " .. why,glue_data) end
+ head = insert_node_before(head,current,glue_data)
+ else
+ free_glue_node(glue_data)
+ 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",
+ texdimen.globalbodyfontstrutheight, texdimen.globalbodyfontstrutdepth,
+ texdimen.bodyfontstrutheight, texdimen.bodyfontstrutdepth)
+ end
+ if trace then trace_info("start analyzing",where,what) end
+ while current do
+ local id = current.id
+ if id == hlist_code or id == vlist_code then
+ -- needs checking, why so many calls
+ if snap then
+ local list = current.list
+ local s = 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
+ if list and already_done(id,list,a_snapmethod) then
+ local ht, dp = current.height, current.depth
+ -- assume that the box is already snapped
+ if trace_vsnapping then
+ report_snapper("mvl list already snapped at (%p,%p): %s",ht,dp,listtoutf(list))
+ end
+ else
+ local h, d, ch, cd, lines = snap_hlist("mvl",current,sv)
+ 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
+ current[a_snapmethod] = 0
+ end
+ else
+ --
+ end
+ -- tex.prevdepth = 0
+ flush("list")
+ current = current.next
+ elseif id == penalty_code then
+ -- natural_penalty = current.penalty
+ -- if trace then trace_done("removed penalty",current) end
+ -- head, current = remove_node(head, current, true)
+ current = current.next
+ elseif id == kern_code then
+ if snap and trace_vsnapping and current.kern ~= 0 then
+ report_snapper("kern of %p kept",current.kern)
+ end
+ flush("kern")
+ current = current.next
+ elseif id == glue_code then
+ local subtype = current.subtype
+ if subtype == userskip_code then
+ local sc = current[a_skipcategory] -- has no default, no unset (yet)
+ local so = current[a_skiporder] or 1 -- has 1 default, no unset (yet)
+ local sp = current[a_skippenalty] -- has no default, no unset (yet)
+ if sp and sc == penalty then
+ 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 = current.next
+ else
+ -- not look back across head
+ local previous = current.prev
+ if previous and previous.id == glue_code and previous.subtype == userskip_code then
+ local ps = previous.spec
+ if ps.writable then
+ local cs = current.spec
+ if cs.writable and ps.stretch_order == 0 and ps.shrink_order == 0 and cs.stretch_order == 0 and cs.shrink_order == 0 then
+ local pw, pp, pm = ps.width, ps.stretch, ps.shrink
+ local cw, cp, cm = cs.width, cs.stretch, cs.shrink
+ -- ps = writable_spec(previous) -- no writable needed here
+ -- ps.width, ps.stretch, ps.shrink = pw + cw, pp + cp, pm + cm
+ previous.spec = new_gluespec(pw + cw, pp + cp, pm + cm) -- else topskip can disappear
+ if trace then trace_natural("removed",current) end
+ head, current = remove_node(head, current, true)
+ -- current = previous
+ if trace then trace_natural("collapsed",previous) end
+ -- current = current.next
+ else
+ if trace then trace_natural("filler",current) end
+ current = current.next
+ end
+ else
+ if trace then trace_natural("natural (no prev spec)",current) end
+ current = current.next
+ end
+ else
+ if trace then trace_natural("natural (no prev)",current) end
+ current = current.next
+ end
+ end
+ glue_order, glue_data = 0, nil
+ elseif sc == disable then
+ ignore_following = true
+ if trace then trace_skip("disable",sc,so,sp,current) end
+ head, current = remove_node(head, current, true)
+ elseif sc == together then
+ keep_together = true
+ if trace then trace_skip("together",sc,so,sp,current) end
+ head, current = remove_node(head, current, true)
+ elseif sc == nowhite then
+ ignore_whitespace = true
+ head, current = remove_node(head, current, true)
+ elseif sc == discard then
+ if trace then trace_skip("discard",sc,so,sp,current) end
+ head, current = remove_node(head, current, true)
+ 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
+ free_glue_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 cs, gs = current.spec, glue_data.spec
+ local cw, gw = cs.width, gs.width
+ if cw > gw then
+ if trace then trace_skip("largest",sc,so,sp,current) end
+ free_glue_node(glue_data) -- also free spec
+ 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
+ free_glue_node(glue_data) -- also free spec
+ 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
+ free_glue_node(glue_data) -- also free spec
+ head, current, glue_data = remove_node(head, current)
+ elseif sc == penalty then
+ if trace then trace_skip("penalty",sc,so,sp,current) end
+ free_glue_node(glue_data) -- also free spec
+ 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 old, new = glue_data.spec, current.spec
+ local old, new = writable_spec(glue_data), current.spec
+ old.width = old.width + new.width
+ old.stretch = old.stretch + new.stretch
+ old.shrink = old.shrink + new.shrink
+ -- 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 = current[a_snapmethod]
+ if s and s ~= 0 then
+ current[a_snapmethod] = 0
+ if current.spec.writable then
+ local spec = writable_spec(current)
+ spec.width = 0
+ if trace_vsnapping then
+ report_snapper("lineskip set to zero")
+ end
+ 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 = current.next
+ elseif subtype == baselineskip_code then
+ if snap then
+ local s = current[a_snapmethod]
+ if s and s ~= 0 then
+ current[a_snapmethod] = 0
+ if current.spec.writable then
+ local spec = writable_spec(current)
+ spec.width = 0
+ if trace_vsnapping then
+ report_snapper("baselineskip set to zero")
+ end
+ 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 = current.next
+ 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 ps, gs = current.spec, glue_data.spec
+ if ps.writable and gs.writable and ps.width > gs.width then
+ glue_data.spec = copy_node(ps)
+ if trace then trace_natural("taking parskip",current) end
+ else
+ if trace then trace_natural("removed parskip",current) end
+ end
+ head, current = remove_node(head, current, true)
+ 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
+ if snap then
+ local s = current[a_snapmethod]
+ if s and s ~= 0 then
+ 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 = current.next
+ elseif subtype == abovedisplayskip_code then
+ --
+ if trace then trace_skip("above display skip (normal)",sc,so,sp,current) end
+ flush("above display skip (normal)")
+ current = current.next
+ --
+ elseif subtype == belowdisplayskip_code then
+ --
+ if trace then trace_skip("below display skip (normal)",sc,so,sp,current) end
+ flush("below display skip (normal)")
+ current = current.next
+ --
+ elseif subtype == abovedisplayshortskip_code then
+ --
+ if trace then trace_skip("above display skip (short)",sc,so,sp,current) end
+ flush("above display skip (short)")
+ current = current.next
+ --
+ elseif subtype == belowdisplayshortskip_code then
+ --
+ if trace then trace_skip("below display skip (short)",sc,so,sp,current) end
+ flush("below display skip (short)")
+ current = current.next
+ --
+ else -- other glue
+ if snap and trace_vsnapping and current.spec.writable and current.spec.width ~= 0 then
+ report_snapper("glue %p of type %a kept",current.spec.width,skipcodes[subtype])
+ --~ current.spec.width = 0
+ end
+ if trace then trace_skip(formatted["glue of type %a"](subtype),sc,so,sp,current) end
+ flush("some glue")
+ current = current.next
+ end
+ else
+ flush("something else")
+ current = current.next
+ 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
+ head, tail = insert_node_after(head,tail,p)
+ 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,glue_data.spec.width,"after",trace)
+ free_glue_node(glue_data)
+ else
+ head, tail = insert_node_after(head,tail,glue_data)
+ end
+ 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[oldhead.id],nodecodes[head.id])
+ end
+ end
+ return head, true
+end
+
+-- alignment after_output end box new_graf vmode_par hmode_par insert penalty before_display after_display
+-- \par -> vmode_par
+--
+-- status.best_page_break
+-- tex.lists.best_page_break
+-- tex.lists.best_size (natural size to best_page_break)
+-- tex.lists.least_page_cost (badness of best_page_break)
+-- tex.lists.page_head
+-- tex.lists.contrib_head
+
+local stackhead, stacktail, stackhack = nil, nil, false
+
+local function report(message,lst)
+ report_vspacing(message,count_nodes(lst,true),nodeidstostring(lst))
+end
+
+function vspacing.pagehandler(newhead,where)
+ -- local newhead = texlists.contrib_head
+ 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
+ for n in traverse_nodes(newhead) do -- we could just look for glue nodes
+ local id = n.id
+ if id ~= glue_code then
+ flush = true
+ elseif n.subtype == userskip_code then
+ if n[a_skipcategory] then
+ stackhack = true
+ else
+ flush = true
+ end
+ else
+ -- tricky
+ end
+ end
+ if flush then
+ if stackhead then
+ if trace_collect_vspacing then report("appending %s nodes to stack (final): %s",newhead) end
+ stacktail.next = newhead
+ newhead.prev = stacktail
+ newhead = stackhead
+ stackhead, stacktail = nil, nil
+ end
+ if stackhack then
+ stackhack = false
+ if trace_collect_vspacing then report("processing %s nodes: %s",newhead) end
+--~ texlists.contrib_head = collapser(newhead,"page",where,trace_page_vspacing,true,a_snapmethod)
+ newhead = collapser(newhead,"page",where,trace_page_vspacing,true,a_snapmethod)
+ else
+ if trace_collect_vspacing then report("flushing %s nodes: %s",newhead) end
+--~ texlists.contrib_head = newhead
+ end
+ else
+ if stackhead then
+ if trace_collect_vspacing then report("appending %s nodes to stack (intermediate): %s",newhead) end
+ stacktail.next = newhead
+ newhead.prev = stacktail
+ else
+ if trace_collect_vspacing then report("storing %s nodes in stack (initial): %s",newhead) end
+ stackhead = newhead
+ end
+ stacktail = newtail
+ -- texlists.contrib_head = nil
+ newhead = nil
+ end
+ end
+ return newhead
+end
+
+local ignore = table.tohash {
+ "split_keep",
+ "split_off",
+ -- "vbox",
+}
+
+function vspacing.vboxhandler(head,where)
+ if head and not ignore[where] and head.next then
+ -- starttiming(vspacing)
+ head = collapser(head,"vbox",where,trace_vbox_vspacing,true,a_snapvbox) -- todo: local snapper
+ -- stoptiming(vspacing)
+ end
+ return head
+end
+
+function vspacing.collapsevbox(n) -- for boxes but using global a_snapmethod
+ local list = texbox[n].list
+ if list then
+ -- starttiming(vspacing)
+ texbox[n].list = vpack_node(collapser(list,"snapper","vbox",trace_vbox_vspacing,true,a_snapmethod))
+ -- stoptiming(vspacing)
+ end
+end
+
+-- We will split this module so a few locals are repeated. Also this will be
+-- rewritten.
+
+nodes.builders = nodes.builder or { }
+local builders = nodes.builders
+
+local actions = nodes.tasks.actions("vboxbuilders")
+
+function builders.vpack_filter(head,groupcode,size,packtype,maxdepth,direction)
+ local done = false
+ if head then
+ starttiming(builders)
+ if trace_vpacking then
+ local before = nodes.count(head)
+ head, done = actions(head,groupcode,size,packtype,maxdepth,direction)
+ local after = nodes.count(head)
+ if done then
+ nodes.processors.tracer("vpack","changed",head,groupcode,before,after,true)
+ else
+ nodes.processors.tracer("vpack","unchanged",head,groupcode,before,after,true)
+ end
+ else
+ head, done = actions(head,groupcode)
+ end
+ 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 actions = nodes.tasks.actions("mvlbuilders")
+
+local function report(groupcode,head)
+ report_page_builder("trigger: %s",groupcode)
+ report_page_builder(" vsize : %p",tex.vsize)
+ report_page_builder(" pagegoal : %p",tex.pagegoal)
+ report_page_builder(" pagetotal: %p",tex.pagetotal)
+ report_page_builder(" list : %s",head and nodeidstostring(head) or "<empty>")
+end
+
+function builders.buildpage_filter(groupcode)
+ local head, done = texlists.contrib_head, false
+ -- if head and head.next and head.next.id == hlist_code and head.next.width == 1 then
+ -- report_page_builder("trigger otr calculations")
+ -- free_node_list(head)
+ -- head = nil
+ -- end
+ if head then
+ starttiming(builders)
+ if trace_page_builder then
+ report(groupcode,head)
+ end
+ head, done = actions(head,groupcode)
+ stoptiming(builders)
+ -- -- doesn't work here (not passed on?)
+ -- tex.pagegoal = tex.vsize - tex.dimen.d_page_floats_inserted_top - tex.dimen.d_page_floats_inserted_bottom
+ texlists.contrib_head = head
+ return done and head or true
+ else
+ if trace_page_builder then
+ report(groupcode)
+ end
+ return nil, false
+ end
+end
+
+callbacks.register('vpack_filter', builders.vpack_filter, "vertical spacing etc")
+callbacks.register('buildpage_filter', builders.buildpage_filter, "vertical spacing etc (mvl)")
+
+statistics.register("v-node processing time", function()
+ return statistics.elapsedseconds(builders)
+end)
+
+-- interface
+
+commands.vspacing = vspacing.analyze
+commands.vspacingsetamount = vspacing.setskip
+commands.vspacingdefine = vspacing.setmap
+commands.vspacingcollapse = vspacing.collapsevbox
+commands.vspacingsnap = vspacing.snapbox
diff --git a/tex/context/base/status-files.pdf b/tex/context/base/status-files.pdf
index 043d5740e..af025dafd 100644
--- a/tex/context/base/status-files.pdf
+++ b/tex/context/base/status-files.pdf
Binary files differ
diff --git a/tex/context/base/status-lua.pdf b/tex/context/base/status-lua.pdf
index 41276909d..515324e4b 100644
--- a/tex/context/base/status-lua.pdf
+++ b/tex/context/base/status-lua.pdf
Binary files differ
diff --git a/tex/context/base/strc-bkm.lua b/tex/context/base/strc-bkm.lua
index 76049f6cf..d9c268ce4 100644
--- a/tex/context/base/strc-bkm.lua
+++ b/tex/context/base/strc-bkm.lua
@@ -1,196 +1,196 @@
-if not modules then modules = { } end modules ['strc-bkm'] = {
- version = 0.200,
- comment = "companion to strc-bkm.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- Future version will support adding arbitrary bookmarks with
--- associated complex actions (rather trivial to implement).
-
--- this should become proper separated backend code
-
--- we should hook the placement into everystoptext ... needs checking
-
-local format, concat, gsub = string.format, table.concat, string.gsub
-local utfvalues = utf.values
-local settings_to_hash = utilities.parsers.settings_to_hash
-
-local codeinjections = backends.codeinjections
-
-local trace_bookmarks = false trackers.register("references.bookmarks", function(v) trace_bookmarks = v end)
-
-local report_bookmarks = logs.reporter("structure","bookmarks")
-
-local structures = structures
-
-structures.bookmarks = structures.bookmarks or { }
-
-local bookmarks = structures.bookmarks
-local sections = structures.sections
-local lists = structures.lists
-
-local levelmap = sections.levelmap
-local variables = interfaces.variables
-
-bookmarks.method = "internal" -- or "page"
-
-local names, opened, forced, numbered = { }, { }, { }, { }
-
-function bookmarks.register(settings)
- local force = settings.force == variables.yes
- local number = settings.number == variables.yes
- local allopen = settings.opened == variables.all
- for k, v in next, settings_to_hash(settings.names or "") do
- names[k] = true
- if force then
- forced[k] = true
- if allopen then
- opened[k] = true
- end
- end
- if number then
- numbered[k] = true
- end
- end
- if not allopen then
- for k, v in next, settings_to_hash(settings.opened or "") do
- opened[k] = true
- end
- end
-end
-
-function bookmarks.overload(name,text)
- local l, ls = lists.tobesaved, nil
- if #l == 0 then
- -- no entries
- elseif name == "" then
- ls = l[#l]
- else
- for i=#l,0,-1 do
- local li = l[i]
- local metadata = li.metadata
- if metadata and not metadata.nolist and metadata.name == name then
- ls = li
- break
- end
- end
- end
- if ls then
- ls.titledata.bookmark = text
- end
-end
-
-local function stripped(str) -- kind of generic
- str = gsub(str,"\\([A-Z]+)","%1") -- \LOGO
- str = gsub(str,"\\ "," ") -- \
- str = gsub(str,"\\([A-Za-z]+) *{(.-)}","%1") -- \bla{...}
- str = gsub(str," +"," ") -- spaces
- return str
-end
-
--- todo: collect specs and collect later i.e. multiple places
-
-local numberspec = { }
-
-function bookmarks.setup(spec)
- -- table.merge(numberspec,spec)
- for k, v in next, spec do
- numberspec[k] = v
- end
-end
-
-function bookmarks.place()
- if next(names) then
- local list = lists.filtercollected(names,"all",nil,lists.collected,forced)
- if #list > 0 then
- local levels, noflevels, lastlevel = { }, 0, 1
- for i=1,#list do
- local li = list[i]
- local metadata = li.metadata
- local name = metadata.name
- if not metadata.nolist or forced[name] then -- and levelmap[name] then
- local titledata = li.titledata
- if titledata then
- local structural = levelmap[name]
- lastlevel = structural or lastlevel
- local title = titledata.bookmark
- if not title or title == "" then
- -- We could typeset the title and then convert it.
- if not structural then
- -- placeholder, todo: bookmarklabel
- title = name .. ": " .. (titledata.title or "?")
- else
- title = titledata.title or "?"
- end
- end
- if numbered[name] then
- local sectiondata = sections.collected[li.references.section]
- local numberdata = li.numberdata
- if sectiondata and numberdata and not numberdata.hidenumber then
- -- we could typeset the number and convert it
- title = concat(sections.typesetnumber(sectiondata,"direct",numberspec,sectiondata)) .. " " .. title
- end
- end
- noflevels = noflevels + 1
- levels[noflevels] = {
- lastlevel,
- stripped(title), -- can be replaced by converter
- li.references, -- has internal and realpage
- allopen or opened[name]
- }
- end
- end
- end
- bookmarks.finalize(levels)
- end
- function bookmarks.place() end -- prevent second run
- end
-end
-
-function bookmarks.flatten(levels)
- -- This function promotes leading structurelements with a higher level
- -- to the next lower level. Such situations are the result of lack of
- -- structure: a subject preceding a chapter in a sectionblock. So, the
- -- following code runs over section blocks as well. (bookmarks-007.tex)
- local noflevels = #levels
- if noflevels > 1 then
- local skip, start, one = false, 1, levels[1]
- local first, block = one[1], one[3].block
- for i=2,noflevels do
- local li = levels[i]
- local new, newblock = li[1], li[3].block
- if newblock ~= block then
- first, block, start, skip = new, newblock, i, false
- elseif skip then
- -- go on
- elseif new > first then
- skip = true
- elseif new < first then
- for j=start,i-1 do
- local lj = levels[j]
- local old = lj[1]
- lj[1] = new
- if trace_bookmarks then
- report_bookmarks("promoting entry %a from level %a to %a: %s",j,old,new,lj[2])
- end
- end
- skip = true
- end
- end
- end
-end
-
-function bookmarks.finalize(levels)
- -- This function can be overloaded by an optional converter
- -- that uses nodes.toutf on a typeset stream. This is something
- -- that we will support when the main loop has become a coroutine.
- codeinjections.addbookmarks(levels,bookmarks.method)
-end
-
--- interface
-
-commands.overloadbookmark = bookmarks.overload
-commands.registerbookmark = bookmarks.register
-commands.setupbookmarks = bookmarks.setup
+if not modules then modules = { } end modules ['strc-bkm'] = {
+ version = 0.200,
+ comment = "companion to strc-bkm.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Future version will support adding arbitrary bookmarks with
+-- associated complex actions (rather trivial to implement).
+
+-- this should become proper separated backend code
+
+-- we should hook the placement into everystoptext ... needs checking
+
+local format, concat, gsub = string.format, table.concat, string.gsub
+local utfvalues = utf.values
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+local codeinjections = backends.codeinjections
+
+local trace_bookmarks = false trackers.register("references.bookmarks", function(v) trace_bookmarks = v end)
+
+local report_bookmarks = logs.reporter("structure","bookmarks")
+
+local structures = structures
+
+structures.bookmarks = structures.bookmarks or { }
+
+local bookmarks = structures.bookmarks
+local sections = structures.sections
+local lists = structures.lists
+
+local levelmap = sections.levelmap
+local variables = interfaces.variables
+
+bookmarks.method = "internal" -- or "page"
+
+local names, opened, forced, numbered = { }, { }, { }, { }
+
+function bookmarks.register(settings)
+ local force = settings.force == variables.yes
+ local number = settings.number == variables.yes
+ local allopen = settings.opened == variables.all
+ for k, v in next, settings_to_hash(settings.names or "") do
+ names[k] = true
+ if force then
+ forced[k] = true
+ if allopen then
+ opened[k] = true
+ end
+ end
+ if number then
+ numbered[k] = true
+ end
+ end
+ if not allopen then
+ for k, v in next, settings_to_hash(settings.opened or "") do
+ opened[k] = true
+ end
+ end
+end
+
+function bookmarks.overload(name,text)
+ local l, ls = lists.tobesaved, nil
+ if #l == 0 then
+ -- no entries
+ elseif name == "" then
+ ls = l[#l]
+ else
+ for i=#l,0,-1 do
+ local li = l[i]
+ local metadata = li.metadata
+ if metadata and not metadata.nolist and metadata.name == name then
+ ls = li
+ break
+ end
+ end
+ end
+ if ls then
+ ls.titledata.bookmark = text
+ end
+end
+
+local function stripped(str) -- kind of generic
+ str = gsub(str,"\\([A-Z]+)","%1") -- \LOGO
+ str = gsub(str,"\\ "," ") -- \
+ str = gsub(str,"\\([A-Za-z]+) *{(.-)}","%1") -- \bla{...}
+ str = gsub(str," +"," ") -- spaces
+ return str
+end
+
+-- todo: collect specs and collect later i.e. multiple places
+
+local numberspec = { }
+
+function bookmarks.setup(spec)
+ -- table.merge(numberspec,spec)
+ for k, v in next, spec do
+ numberspec[k] = v
+ end
+end
+
+function bookmarks.place()
+ if next(names) then
+ local list = lists.filtercollected(names,"all",nil,lists.collected,forced)
+ if #list > 0 then
+ local levels, noflevels, lastlevel = { }, 0, 1
+ for i=1,#list do
+ local li = list[i]
+ local metadata = li.metadata
+ local name = metadata.name
+ if not metadata.nolist or forced[name] then -- and levelmap[name] then
+ local titledata = li.titledata
+ if titledata then
+ local structural = levelmap[name]
+ lastlevel = structural or lastlevel
+ local title = titledata.bookmark
+ if not title or title == "" then
+ -- We could typeset the title and then convert it.
+ if not structural then
+ -- placeholder, todo: bookmarklabel
+ title = name .. ": " .. (titledata.title or "?")
+ else
+ title = titledata.title or "?"
+ end
+ end
+ if numbered[name] then
+ local sectiondata = sections.collected[li.references.section]
+ local numberdata = li.numberdata
+ if sectiondata and numberdata and not numberdata.hidenumber then
+ -- we could typeset the number and convert it
+ title = concat(sections.typesetnumber(sectiondata,"direct",numberspec,sectiondata)) .. " " .. title
+ end
+ end
+ noflevels = noflevels + 1
+ levels[noflevels] = {
+ lastlevel,
+ stripped(title), -- can be replaced by converter
+ li.references, -- has internal and realpage
+ allopen or opened[name]
+ }
+ end
+ end
+ end
+ bookmarks.finalize(levels)
+ end
+ function bookmarks.place() end -- prevent second run
+ end
+end
+
+function bookmarks.flatten(levels)
+ -- This function promotes leading structurelements with a higher level
+ -- to the next lower level. Such situations are the result of lack of
+ -- structure: a subject preceding a chapter in a sectionblock. So, the
+ -- following code runs over section blocks as well. (bookmarks-007.tex)
+ local noflevels = #levels
+ if noflevels > 1 then
+ local skip, start, one = false, 1, levels[1]
+ local first, block = one[1], one[3].block
+ for i=2,noflevels do
+ local li = levels[i]
+ local new, newblock = li[1], li[3].block
+ if newblock ~= block then
+ first, block, start, skip = new, newblock, i, false
+ elseif skip then
+ -- go on
+ elseif new > first then
+ skip = true
+ elseif new < first then
+ for j=start,i-1 do
+ local lj = levels[j]
+ local old = lj[1]
+ lj[1] = new
+ if trace_bookmarks then
+ report_bookmarks("promoting entry %a from level %a to %a: %s",j,old,new,lj[2])
+ end
+ end
+ skip = true
+ end
+ end
+ end
+end
+
+function bookmarks.finalize(levels)
+ -- This function can be overloaded by an optional converter
+ -- that uses nodes.toutf on a typeset stream. This is something
+ -- that we will support when the main loop has become a coroutine.
+ codeinjections.addbookmarks(levels,bookmarks.method)
+end
+
+-- interface
+
+commands.overloadbookmark = bookmarks.overload
+commands.registerbookmark = bookmarks.register
+commands.setupbookmarks = bookmarks.setup
diff --git a/tex/context/base/strc-blk.lua b/tex/context/base/strc-blk.lua
index 16a621ad4..791f8f99b 100644
--- a/tex/context/base/strc-blk.lua
+++ b/tex/context/base/strc-blk.lua
@@ -1,152 +1,152 @@
-if not modules then modules = { } end modules ['strc-blk'] = {
- version = 1.001,
- comment = "companion to strc-blk.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- this one runs on top of buffers and structure
-
-local type = type
-local find, format, validstring = string.find, string.format, string.valid
-local settings_to_set, settings_to_array = utilities.parsers.settings_to_set, utilities.parsers.settings_to_array
-local allocate = utilities.storage.allocate
-
-local structures, context = structures, context
-
-structures.blocks = structures.blocks or { }
-
-local blocks = structures.blocks
-local sections = structures.sections
-local lists = structures.lists
-
-local collected = allocate()
-local tobesaved = allocate()
-local states = allocate()
-
-blocks.collected = collected
-blocks.tobesaved = tobesaved
-blocks.states = states
-
-local function initializer()
- collected = blocks.collected
- tobesaved = blocks.tobesaved
-end
-
-job.register('structures.blocks.collected', tobesaved, initializer)
-
-local listitem = utilities.parsers.listitem
-
-function blocks.print(name,data,hide)
- if hide then
- context.dostarthiddenblock(name)
- else
- context.dostartnormalblock(name)
- end
- context.viafile(data,format("block.%s",validstring(name,"noname")))
- if hide then
- context.dostophiddenblock()
- else
- context.dostopnormalblock()
- end
-end
-
-function blocks.define(name)
- states[name] = { all = "hide" }
-end
-
-function blocks.setstate(state,name,tag)
- local all = tag == ""
- local tags = not all and settings_to_array(tag)
- for n in listitem(name) do
- local sn = states[n]
- if not sn then
- -- error
- elseif all then
- sn.all = state
- else
- for _, tag in next, tags do
- sn[tag] = state
- end
- end
- end
-end
-
-function blocks.select(state,name,tag,criterium)
- criterium = criterium or "text"
- if find(tag,"=") then tag = "" end
- local names = settings_to_set(name)
- local all = tag == ""
- local tags = not all and settings_to_set(tag)
- local hide = state == "process"
- local n = sections.numberatdepth(criterium)
- local result = lists.filtercollected("all", criterium, n, collected, { })
- for i=1,#result do
- local ri = result[i]
- local metadata = ri.metadata
- if names[metadata.name] then
- if all then
- blocks.print(name,ri.data,hide)
- else
- local mtags = metadata.tags
- for tag, sta in next, tags do
- if mtags[tag] then
- blocks.print(name,ri.data,hide)
- break
- end
- end
- end
- end
- end
-end
-
-function blocks.save(name,tag,buffer) -- wrong, not yet adapted
- local data = buffers.getcontent(buffer)
- local tags = settings_to_set(tag)
- local plus, minus = false, false
- if tags['+'] then plus = true tags['+'] = nil end
- if tags['-'] then minus = true tags['-'] = nil end
- tobesaved[#tobesaved+1] = {
- metadata = {
- name = name,
- tags = tags,
- plus = plus,
- minus = minus,
- },
- references = {
- section = sections.currentid(),
- },
- data = data or "error",
- }
- local allstate = states[name].all
- if not next(tags) then
- if allstate ~= "hide" then
- blocks.print(name,data)
- elseif plus then
- blocks.print(name,data,true)
- end
- else
- local sn = states[name]
- for tag, _ in next, tags do
- if sn[tag] == nil then
- if allstate ~= "hide" then
- blocks.print(name,data)
- break
- end
- elseif sn[tag] ~= "hide" then
- blocks.print(name,data)
- break
- end
- end
- end
- buffers.erase(buffer)
-end
-
--- interface
-
-
-commands.definestructureblock = blocks.define
-commands.savestructureblock = blocks.save
-commands.selectstructureblock = blocks.select
-commands.setstructureblockstate = blocks.setstate
+if not modules then modules = { } end modules ['strc-blk'] = {
+ version = 1.001,
+ comment = "companion to strc-blk.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- this one runs on top of buffers and structure
+
+local type = type
+local find, format, validstring = string.find, string.format, string.valid
+local settings_to_set, settings_to_array = utilities.parsers.settings_to_set, utilities.parsers.settings_to_array
+local allocate = utilities.storage.allocate
+
+local structures, context = structures, context
+
+structures.blocks = structures.blocks or { }
+
+local blocks = structures.blocks
+local sections = structures.sections
+local lists = structures.lists
+
+local collected = allocate()
+local tobesaved = allocate()
+local states = allocate()
+
+blocks.collected = collected
+blocks.tobesaved = tobesaved
+blocks.states = states
+
+local function initializer()
+ collected = blocks.collected
+ tobesaved = blocks.tobesaved
+end
+
+job.register('structures.blocks.collected', tobesaved, initializer)
+
+local listitem = utilities.parsers.listitem
+
+function blocks.print(name,data,hide)
+ if hide then
+ context.dostarthiddenblock(name)
+ else
+ context.dostartnormalblock(name)
+ end
+ context.viafile(data,format("block.%s",validstring(name,"noname")))
+ if hide then
+ context.dostophiddenblock()
+ else
+ context.dostopnormalblock()
+ end
+end
+
+function blocks.define(name)
+ states[name] = { all = "hide" }
+end
+
+function blocks.setstate(state,name,tag)
+ local all = tag == ""
+ local tags = not all and settings_to_array(tag)
+ for n in listitem(name) do
+ local sn = states[n]
+ if not sn then
+ -- error
+ elseif all then
+ sn.all = state
+ else
+ for _, tag in next, tags do
+ sn[tag] = state
+ end
+ end
+ end
+end
+
+function blocks.select(state,name,tag,criterium)
+ criterium = criterium or "text"
+ if find(tag,"=") then tag = "" end
+ local names = settings_to_set(name)
+ local all = tag == ""
+ local tags = not all and settings_to_set(tag)
+ local hide = state == "process"
+ local n = sections.numberatdepth(criterium)
+ local result = lists.filtercollected("all", criterium, n, collected, { })
+ for i=1,#result do
+ local ri = result[i]
+ local metadata = ri.metadata
+ if names[metadata.name] then
+ if all then
+ blocks.print(name,ri.data,hide)
+ else
+ local mtags = metadata.tags
+ for tag, sta in next, tags do
+ if mtags[tag] then
+ blocks.print(name,ri.data,hide)
+ break
+ end
+ end
+ end
+ end
+ end
+end
+
+function blocks.save(name,tag,buffer) -- wrong, not yet adapted
+ local data = buffers.getcontent(buffer)
+ local tags = settings_to_set(tag)
+ local plus, minus = false, false
+ if tags['+'] then plus = true tags['+'] = nil end
+ if tags['-'] then minus = true tags['-'] = nil end
+ tobesaved[#tobesaved+1] = {
+ metadata = {
+ name = name,
+ tags = tags,
+ plus = plus,
+ minus = minus,
+ },
+ references = {
+ section = sections.currentid(),
+ },
+ data = data or "error",
+ }
+ local allstate = states[name].all
+ if not next(tags) then
+ if allstate ~= "hide" then
+ blocks.print(name,data)
+ elseif plus then
+ blocks.print(name,data,true)
+ end
+ else
+ local sn = states[name]
+ for tag, _ in next, tags do
+ if sn[tag] == nil then
+ if allstate ~= "hide" then
+ blocks.print(name,data)
+ break
+ end
+ elseif sn[tag] ~= "hide" then
+ blocks.print(name,data)
+ break
+ end
+ end
+ end
+ buffers.erase(buffer)
+end
+
+-- interface
+
+
+commands.definestructureblock = blocks.define
+commands.savestructureblock = blocks.save
+commands.selectstructureblock = blocks.select
+commands.setstructureblockstate = blocks.setstate
diff --git a/tex/context/base/strc-con.lua b/tex/context/base/strc-con.lua
index 42da72f64..29a1c0cb3 100644
--- a/tex/context/base/strc-con.lua
+++ b/tex/context/base/strc-con.lua
@@ -1,9 +1,9 @@
-if not modules then modules = { } end modules ['strc-con'] = {
- version = 1.001,
- comment = "companion to strc-con.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- empty
+if not modules then modules = { } end modules ['strc-con'] = {
+ version = 1.001,
+ comment = "companion to strc-con.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- empty
diff --git a/tex/context/base/strc-doc.lua b/tex/context/base/strc-doc.lua
index 37a16c414..50a9e67a0 100644
--- a/tex/context/base/strc-doc.lua
+++ b/tex/context/base/strc-doc.lua
@@ -1,956 +1,956 @@
-if not modules then modules = { } end modules ['strc-doc'] = {
- version = 1.001,
- comment = "companion to strc-doc.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- todo: associate counter with head
--- we need to better split the lua/tex end
--- we need to freeze and document this module
-
--- keep this as is:
---
--- in section titles by default a zero aborts, so there we need: sectionset=bagger with \definestructureprefixset [bagger] [section-2,section-4] []
--- in lists however zero's are ignored, so there numbersegments=2:4 gives result
-
-local next, type, tonumber, select = next, type, tonumber, select
-local format, gsub, find, gmatch, match = string.format, string.gsub, string.find, string.gmatch, string.match
-local concat, fastcopy = table.concat, table.fastcopy
-local max, min = math.max, math.min
-local allocate, mark, accesstable = utilities.storage.allocate, utilities.storage.mark, utilities.tables.accesstable
-local setmetatableindex = table.setmetatableindex
-
-local catcodenumbers = catcodes.numbers
-local ctxcatcodes = catcodenumbers.ctxcatcodes
-local variables = interfaces.variables
-
-local v_last = variables.last
-local v_first = variables.first
-local v_previous = variables.previous
-local v_next = variables.next
-local v_auto = variables.auto
-local v_strict = variables.strict
-local v_all = variables.all
-local v_positive = variables.positive
-local v_by = variables.by
-
-local trace_sectioning = false trackers.register("structures.sectioning", function(v) trace_sectioning = v end)
-local trace_detail = false trackers.register("structures.detail", function(v) trace_detail = v end)
-
-local report_structure = logs.reporter("structure","sectioning")
-
-local structures = structures
-local context = context
-
-local helpers = structures.helpers
-local documents = structures.documents
-local sections = structures.sections
-local lists = structures.lists
-local counters = structures.counters
-local sets = structures.sets
-local tags = structures.tags
-
-local processors = typesetters.processors
-local applyprocessor = processors.apply
-local startapplyprocessor = processors.startapply
-local stopapplyprocessor = processors.stopapply
-local strippedprocessor = processors.stripped
-
-local a_internal = attributes.private('internal')
-
--- -- -- document -- -- --
-
-local data -- the current state
-
-function documents.initialize()
- data = allocate { -- whole data is marked
- numbers = { },
- forced = { },
- ownnumbers = { },
- status = { },
- checkers = { },
- depth = 0,
- blocks = { },
- block = "",
- }
- documents.data = data
-end
-
-function documents.reset()
- data.numbers = { }
- data.forced = { }
- data.ownnumbers = { }
- data.status = { }
- -- data.checkers = { }
- data.depth = 0
-end
-
-documents.initialize()
-
--- -- -- components -- -- --
-
-function documents.preset(numbers)
- local nofnumbers = #numbers
- local ownnumbers = { }
- data.numbers = numbers
- data.ownnumbers = ownnumbers
- data.depth = nofnumbers
- for i=1,nofnumbers do
- ownnumbers[i] = ""
- end
- sections.setnumber(nofnumbers,"-1")
-end
-
--- -- -- sections -- -- --
-
-local collected = allocate()
-local tobesaved = allocate()
-
-sections.collected = collected
-sections.tobesaved = tobesaved
-
--- local function initializer()
--- collected = sections.collected
--- tobesaved = sections.tobesaved
--- end
---
--- job.register('structures.sections.collected', tobesaved, initializer)
-
-sections.registered = sections.registered or allocate()
-local registered = sections.registered
-
-storage.register("structures/sections/registered", registered, "structures.sections.registered")
-
-function sections.register(name,specification)
- registered[name] = specification
-end
-
-function sections.currentid()
- return #tobesaved
-end
-
-function sections.save(sectiondata)
--- local sectionnumber = helpers.simplify(section.sectiondata) -- maybe done earlier
- local numberdata = sectiondata.numberdata
- local ntobesaved = #tobesaved
- if not numberdata or sectiondata.metadata.nolist then
- return ntobesaved
- else
- ntobesaved = ntobesaved + 1
- tobesaved[ntobesaved] = numberdata
- if not collected[ntobesaved] then
- collected[ntobesaved] = numberdata
- end
- return ntobesaved
- end
-end
-
-function sections.load()
- setmetatableindex(collected,nil)
- local lists = lists.collected
- for i=1,#lists do
- local list = lists[i]
- local metadata = list.metadata
- if metadata and metadata.kind == "section" and not metadata.nolist then
- local numberdata = list.numberdata
- if numberdata then
- collected[#collected+1] = numberdata
- end
- end
- end
- sections.load = functions.dummy
-end
-
-table.setmetatableindex(collected, function(t,i)
- sections.load()
- return collected[i] or { }
-end)
-
---
-
-sections.levelmap = sections.levelmap or { }
-
-local levelmap = sections.levelmap
-
-storage.register("structures/sections/levelmap", sections.levelmap, "structures.sections.levelmap")
-
-sections.verbose = true
-
-levelmap.block = -1
-
-function sections.setlevel(name,level) -- level can be number or parent (=string)
- local l = tonumber(level)
- if not l then
- l = levelmap[level]
- end
- if l and l > 0 then
- levelmap[name] = l
- else
- -- error
- end
-end
-
-function sections.getlevel(name)
- return levelmap[name] or 0
-end
-
-function sections.setblock(name)
- local block = name or data.block or "unknown" -- can be used to set the default
- data.block = block
- return block
-end
-
-function sections.pushblock(name)
- counters.check(0) -- we assume sane usage of \page between blocks
- local block = name or data.block
- data.blocks[#data.blocks+1] = block
- data.block = block
- documents.reset()
- return block
-end
-
-function sections.popblock()
- data.blocks[#data.blocks] = nil
- local block = data.blocks[#data.blocks] or data.block
- data.block = block
- documents.reset()
- return block
-end
-
-function sections.currentblock()
- return data.block or data.blocks[#data.blocks] or "unknown"
-end
-
-function sections.currentlevel()
- return data.depth
-end
-
-function sections.getcurrentlevel()
- context(data.depth)
-end
-
-local saveset = { } -- experiment, see sections/tricky-001.tex
-
-function sections.somelevel(given)
- -- old number
- local numbers = data.numbers
-
- local ownnumbers = data.ownnumbers
- local forced = data.forced
- local status = data.status
- local olddepth = data.depth
- local givenname = given.metadata.name
- local mappedlevel = levelmap[givenname]
- local newdepth = tonumber(mappedlevel or (olddepth > 0 and olddepth) or 1) -- hm, levelmap only works for section-*
- local directives = given.directives
- local resetset = directives and directives.resetset or ""
- -- local resetter = sets.getall("structure:resets",data.block,resetset)
- -- a trick to permit userdata to overload title, ownnumber and reference
- -- normally these are passed as argument but nowadays we provide several
- -- interfaces (we need this because we want to be compatible)
- if trace_detail then
- report_structure("name %a, mapped level %a, old depth %a, new depth %a, reset set %a",
- givenname,mappedlevel,olddepth,newdepth,resetset)
- end
- local u = given.userdata
- if u then
- -- kind of obsolete as we can pass them directly anyway
- if u.reference and u.reference ~= "" then given.metadata.reference = u.reference ; u.reference = nil end
- if u.ownnumber and u.ownnumber ~= "" then given.numberdata.ownnumber = u.ownnumber ; u.ownnumber = nil end
- if u.title and u.title ~= "" then given.titledata.title = u.title ; u.title = nil end
- if u.bookmark and u.bookmark ~= "" then given.titledata.bookmark = u.bookmark ; u.bookmark = nil end
- if u.label and u.label ~= "" then given.titledata.label = u.label ; u.label = nil end
- end
- -- so far for the trick
- if saveset then
- saveset[newdepth] = (resetset ~= "" and resetset) or saveset[newdepth] or ""
- end
- if newdepth > olddepth then
- for i=olddepth+1,newdepth do
- local s = tonumber(sets.get("structure:resets",data.block,saveset and saveset[i] or resetset,i))
- if trace_detail then
- report_structure("new depth %s, old depth %s, reset set %a, reset value %a, current %a",olddepth,newdepth,resetset,s,numbers[i])
- end
- if not s or s == 0 then
- numbers[i] = numbers[i] or 0
- ownnumbers[i] = ownnumbers[i] or ""
- else
- numbers[i] = s - 1
- ownnumbers[i] = ""
- end
- status[i] = { }
- end
- elseif newdepth < olddepth then
- for i=olddepth,newdepth+1,-1 do
- local s = tonumber(sets.get("structure:resets",data.block,saveset and saveset[i] or resetset,i))
- if trace_detail then
- report_structure("new depth %s, old depth %s, reset set %a, reset value %a, current %a",olddepth,newdepth,resetset,s,numbers[i])
- end
- if not s or s == 0 then
- numbers[i] = numbers[i] or 0
- ownnumbers[i] = ownnumbers[i] or ""
- else
- numbers[i] = s - 1
- ownnumbers[i] = ""
- end
- status[i] = nil
- end
- end
- counters.check(newdepth)
- ownnumbers[newdepth] = given.numberdata.ownnumber or ""
- given.numberdata.ownnumber = nil
- data.depth = newdepth
- -- new number
- olddepth = newdepth
- if given.metadata.increment then
- local oldn, newn = numbers[newdepth] or 0, 0
- local fd = forced[newdepth]
- if fd then
- if fd[1] == "add" then
- newn = oldn + fd[2] + 1
- else
- newn = fd[2] + 1
- end
- if newn < 0 then
- newn = 1 -- maybe zero is nicer
- end
- forced[newdepth] = nil
- if trace_detail then
- report_structure("old depth %a, new depth %a, old n %a, new n %a, forced %t",olddepth,newdepth,oldn,newn,fd)
- end
- else
- newn = oldn + 1
- if trace_detail then
- report_structure("old depth %a, new depth %a, old n %a, new n %a, increment",olddepth,newdepth,oldn,newn)
- end
- end
- numbers[newdepth] = newn
- end
- status[newdepth] = given or { }
- for k, v in next, data.checkers do
- if v[1] == newdepth and v[2] then
- v[2](k)
- end
- end
- local numberdata= given.numberdata
- if not numberdata then
- -- probably simplified to nothing
- numberdata = { }
- given.numberdata = numberdata
- end
-
- local n = { }
- for i=1,newdepth do
- n[i] = numbers[i]
- end
- numberdata.numbers = n
--- numberdata.numbers = fastcopy(numbers)
-
- if #ownnumbers > 0 then
- numberdata.ownnumbers = fastcopy(ownnumbers)
- end
- if trace_detail then
- report_structure("name %a, numbers % a, own numbers % a",givenname,numberdata.numbers,numberdata.ownnumbers)
- end
-
- local metadata = given.metadata
- local references = given.references
-
- local tag = references.tag or tags.getid(metadata.kind,metadata.name)
- if tag and tag ~= "" and tag ~= "?" then
- references.tag = tag
- end
-
- local setcomponent = structures.references.setcomponent
- if setcomponent then
- setcomponent(given) -- might move to the tex end
- end
-
- references.section = sections.save(given)
- -- given.numberdata = nil
-end
-
-function sections.reportstructure()
- if sections.verbose then
- local numbers, ownnumbers, status, depth = data.numbers, data.ownnumbers, data.status, data.depth
- local d = status[depth]
- local o = concat(ownnumbers,".",1,depth)
- local n = (numbers and concat(numbers,".",1,min(depth,#numbers))) or 0
- local l = d.titledata.title or ""
- local t = (l ~= "" and l) or d.titledata.title or "[no title]"
- local m = d.metadata.name
- if o and not find(o,"^%.*$") then
- report_structure("%s @ level %i : (%s) %s -> %s",m,depth,n,o,t)
- elseif d.directives and d.directives.hidenumber then
- report_structure("%s @ level %i : (%s) -> %s",m,depth,n,t)
- else
- report_structure("%s @ level %i : %s -> %s",m,depth,n,t)
- end
- end
-end
-
-function sections.setnumber(depth,n)
- local forced, depth, new = data.forced, depth or data.depth, tonumber(n)
- if type(n) == "string" then
- if find(n,"^[%+%-]") then
- forced[depth] = { "add", new }
- else
- forced[depth] = { "set", new }
- end
- else
- forced[depth] = { "set", new }
- end
-end
-
-function sections.numberatdepth(depth)
- return data.numbers[tonumber(depth) or sections.getlevel(depth) or 0] or 0
-end
-
-function sections.numbers()
- return data.numbers
-end
-
-function sections.matchingtilldepth(depth,numbers,parentnumbers)
- local dn = parentnumbers or data.numbers
- local ok = false
- for i=1,depth do
- if dn[i] == numbers[i] then
- ok = true
- else
- return false
- end
- end
- return ok
-end
-
-function sections.getnumber(depth) -- redefined later ...
- context(data.numbers[depth] or 0)
-end
-
-function sections.set(key,value)
- data.status[data.depth][key] = value -- may be nil for a reset
-end
-
-function sections.cct()
- local metadata = data.status[data.depth].metadata
- context(metadata and metadata.catcodes or ctxcatcodes)
-end
-
--- this one will become: return catcode, d (etc)
-
-function sections.structuredata(depth,key,default,honorcatcodetable) -- todo: spec table and then also depth
- if depth then
- depth = levelmap[depth] or tonumber(depth)
- end
- if not depth or depth == 0 then
- depth = data.depth
- end
- local data = data.status[depth]
- local d
- if data then
- if find(key,"%.") then
- d = accesstable(key,data)
- else
- d = data.titledata
- d = d and d[key]
- end
- end
- if d and type(d) ~= "table" then
- if honorcatcodetable == true or honorcatcodetable == v_auto then
- local metadata = data.metadata
- local catcodes = metadata and metadata.catcodes
- if catcodes then
- context.sprint(catcodes,d)
- else
- context(d)
- end
- elseif not honorcatcodetable or honorcatcodetable == "" then
- context(d)
- else
- local catcodes = catcodenumbers[honorcatcodetable]
- if catcodes then
- context.sprint(catcodes,d)
- else
- context(d)
- end
- end
- elseif default then
- context(default)
- end
-end
-
-function sections.userdata(depth,key,default)
- if depth then
- depth = levelmap[depth] or tonumber(depth)
- end
- if not depth or depth == 0 then
- depth = data.depth
- end
- if depth > 0 then
- local userdata = data.status[depth]
- userdata = userdata and userdata.userdata
- userdata = (userdata and userdata[key]) or default
- if userdata then
- context(userdata)
- end
- end
-end
-
-function sections.setchecker(name,level,command) -- hm, checkers are not saved
- data.checkers[name] = (name and command and level >= 0 and { level, command }) or nil
-end
-
-function sections.current()
- return data.status[data.depth]
-end
-
-function sections.depthnumber(n)
- local depth = data.depth
- if not n or n == 0 then
- n = depth
- elseif n < 0 then
- n = depth + n
- end
- return context(data.numbers[n] or 0)
-end
-
-function sections.autodepth(numbers)
- for i=#numbers,1,-1 do
- if numbers[i] ~= 0 then
- return i
- end
- end
- return 0
-end
-
---
-
-function structures.currentsectionnumber() -- brr, namespace wrong
- local sc = sections.current()
- return sc and sc.numberdata
-end
-
--- \dorecurse{3} {
--- \chapter{Blabla} \subsection{bla 1 1} \subsection{bla 1 2}
--- \section{bla 2} \subsection{bla 2 1} \subsection{bla 2 2}
--- }
-
--- sign=all => also zero and negative
--- sign=positive => also zero
--- sign=hang => llap sign
-
---~ todo: test this
---~
-
-local function process(index,numbers,ownnumbers,criterium,separatorset,conversion,conversionset,index,entry,result,preceding,done)
- -- todo: too much (100 steps)
- local number = numbers and (numbers[index] or 0)
- local ownnumber = ownnumbers and ownnumbers[index] or ""
- if number > criterium or (ownnumber ~= "") then
- local block = (entry.block ~= "" and entry.block) or sections.currentblock() -- added
- if preceding then
- local separator = sets.get("structure:separators",block,separatorset,preceding,".")
- if separator then
- if result then
- result[#result+1] = strippedprocessor(separator)
- else
- applyprocessor(separator)
- end
- end
- preceding = false
- end
- if result then
- if ownnumber ~= "" then
- result[#result+1] = ownnumber
- elseif conversion and conversion ~= "" then -- traditional (e.g. used in itemgroups) .. inherited!
- result[#result+1] = converters.convert(conversion,number)
- else
- local theconversion = sets.get("structure:conversions",block,conversionset,index,"numbers")
- result[#result+1] = converters.convert(theconversion,number)
- end
- else
- if ownnumber ~= "" then
- applyprocessor(ownnumber)
- elseif conversion and conversion ~= "" then -- traditional (e.g. used in itemgroups)
- context.convertnumber(conversion,number)
- else
- local theconversion = sets.get("structure:conversions",block,conversionset,index,"numbers")
- local data = startapplyprocessor(theconversion)
- context.convertnumber(data or "numbers",number)
- stopapplyprocessor()
- end
- end
- return index, true
- else
- return preceding or false, done
- end
-end
-
-function sections.typesetnumber(entry,kind,...) -- kind='section','number','prefix'
- if entry and entry.hidenumber ~= true then -- can be nil
- local separatorset = ""
- local conversionset = ""
- local conversion = ""
- local groupsuffix = ""
- local stopper = ""
- local starter = ""
- local connector = ""
- local set = ""
- local segments = ""
- local criterium = ""
- for d=1,select("#",...) do
- local data = select(d,...) -- can be multiple parametersets
- if data then
- if separatorset == "" then separatorset = data.separatorset or "" end
- if conversionset == "" then conversionset = data.conversionset or "" end
- if conversion == "" then conversion = data.conversion or "" end
- if groupsuffix == "" then groupsuffix = data.groupsuffix or "" end
- if stopper == "" then stopper = data.stopper or "" end
- if starter == "" then starter = data.starter or "" end
- if connector == "" then connector = data.connector or "" end
- if set == "" then set = data.set or "" end
- if segments == "" then segments = data.segments or "" end
- if criterium == "" then criterium = data.criterium or "" end
- end
- end
- if separatorset == "" then separatorset = "default" end
- if conversionset == "" then conversionset = "default" end -- not used
- if conversion == "" then conversion = nil end
- if groupsuffix == "" then groupsuffix = nil end
- if stopper == "" then stopper = nil end
- if starter == "" then starter = nil end
- if connector == "" then connector = nil end
- if set == "" then set = "default" end
- if segments == "" then segments = nil end
- --
- if criterium == v_strict then
- criterium = 0
- elseif criterium == v_positive then
- criterium = -1
- elseif criterium == v_all then
- criterium = -1000000
- else
- criterium = 0
- end
- --
- local firstprefix, lastprefix = 0, 16
- if segments then
- local f, l = match(tostring(segments),"^(.-):(.+)$")
- if l == "*" then
- l = 100 -- new
- end
- if f and l then
- -- 0:100, chapter:subsubsection
- firstprefix = tonumber(f) or sections.getlevel(f) or 0
- lastprefix = tonumber(l) or sections.getlevel(l) or 100
- else
- -- 3, section
- local fl = tonumber(segments) or sections.getlevel(segments) -- generalize
- if fl then
- firstprefix = fl
- lastprefix = fl
- end
- end
- end
- --
- local numbers, ownnumbers = entry.numbers, entry.ownnumbers
- if numbers then
- local done, preceding = false, false
- --
- local result = kind == "direct" and { }
- if result then
- connector = false
- end
- --
- local prefixlist = set and sets.getall("structure:prefixes","",set) -- "" == block
- if starter then
- if result then
- result[#result+1] = strippedprocessor(starter)
- else
- applyprocessor(starter)
- end
- end
- if prefixlist and (kind == 'section' or kind == 'prefix' or kind == 'direct') then
- -- find valid set (problem: for sectionnumber we should pass the level)
- -- no holes
- local b, e, bb, ee = 1, #prefixlist, 0, 0
- -- find last valid number
- for k=e,b,-1 do
- local prefix = prefixlist[k]
- local index = sections.getlevel(prefix) or k
- if index >= firstprefix and index <= lastprefix then
- local number = numbers and numbers[index]
- if number then
- local ownnumber = ownnumbers and ownnumbers[index] or ""
- if number > 0 or (ownnumber ~= "") then
- break
- else
- e = k -1
- end
- end
- end
- end
- -- find valid range
- for k=b,e do
- local prefix = prefixlist[k]
- local index = sections.getlevel(prefix) or k
- if index >= firstprefix and index <= lastprefix then
- local number = numbers and numbers[index]
- if number then
- local ownnumber = ownnumbers and ownnumbers[index] or ""
- if number > 0 or (ownnumber ~= "") then
- if bb == 0 then bb = k end
- ee = k
- else
- bb, ee = 0, 0
- end
- else
- break
- end
- end
- end
- -- print valid range
- for k=bb,ee do
- local prefix = prefixlist[k]
- local index = sections.getlevel(prefix) or k
- if index >= firstprefix and index <= lastprefix then
- -- process(index,result)
- preceding, done = process(index,numbers,ownnumbers,criterium,separatorset,conversion,conversionset,index,entry,result,preceding,done)
- end
- end
- else
- -- also holes check
- for index=firstprefix,lastprefix do
- -- process(index,result)
- preceding, done = process(index,numbers,ownnumbers,criterium,separatorset,conversion,conversionset,index,entry,result,preceding,done)
- end
- end
- --
- if done then
- if connector and kind == 'prefix' then
- if result then
- -- can't happen as we're in 'direct'
- else
- applyprocessor(connector)
- end
- else
-if groupsuffix and kind ~= "prefix" then
- if result then
- result[#result+1] = strippedprocessor(groupsuffix)
- else
- applyprocessor(groupsuffix)
- end
-end
- if stopper then
- if result then
- result[#result+1] = strippedprocessor(stopper)
- else
- applyprocessor(stopper)
- end
- end
- end
- end
- return result -- a table !
- else
- -- report_structure("error: no numbers")
- end
- end
-end
-
-function sections.title()
- local sc = sections.current()
- if sc then
- helpers.title(sc.titledata.title,sc.metadata)
- end
-end
-
-function sections.findnumber(depth,what) -- needs checking (looks wrong and slow too)
- local data = data.status[depth or data.depth]
- if data then
- local index = data.references.section
- local collected = sections.collected
- local sectiondata = collected[index]
- if sectiondata and sectiondata.hidenumber ~= true then -- can be nil
- local quit = what == v_previous or what == v_next
- if what == v_first or what == v_previous then
- for i=index,1,-1 do
- local s = collected[i]
- if s then
- local n = s.numbers
- if #n == depth and n[depth] and n[depth] ~= 0 then
- sectiondata = s
- if quit then
- break
- end
- elseif #n < depth then
- break
- end
- end
- end
- elseif what == v_last or what == v_next then
- for i=index,#collected do
- local s = collected[i]
- if s then
- local n = s.numbers
- if #n == depth and n[depth] and n[depth] ~= 0 then
- sectiondata = s
- if quit then
- break
- end
- elseif #n < depth then
- break
- end
- end
- end
- end
- return sectiondata
- end
- end
-end
-
-function sections.finddata(depth,what)
- local data = data.status[depth or data.depth]
- if data then
- -- if sectiondata and sectiondata.hidenumber ~= true then -- can be nil
- local index = data.references.listindex
- if index then
- local collected = structures.lists.collected
- local quit = what == v_previous or what == v_next
- if what == v_first or what == v_previous then
- for i=index-1,1,-1 do
- local s = collected[i]
- if not s then
- break
- elseif s.metadata.kind == "section" then -- maybe check on name
- local n = s.numberdata.numbers
- if #n == depth and n[depth] and n[depth] ~= 0 then
- data = s
- if quit then
- break
- end
- elseif #n < depth then
- break
- end
- end
- end
- elseif what == v_last or what == v_next then
- for i=index+1,#collected do
- local s = collected[i]
- if not s then
- break
- elseif s.metadata.kind == "section" then -- maybe check on name
- local n = s.numberdata.numbers
- if #n == depth and n[depth] and n[depth] ~= 0 then
- data = s
- if quit then
- break
- end
- elseif #n < depth then
- break
- end
- end
- end
- end
- end
- return data
- end
-end
-
-function sections.internalreference(sectionname,what) -- to be used in pagebuilder (no marks used)
- local r = type(sectionname) == "number" and sectionname or registered[sectionname]
- if r then
- local data = sections.finddata(r.level,what)
- return data and data.references and data.references.internal
- end
-end
-
-function sections.fullnumber(depth,what)
- local sectiondata = sections.findnumber(depth,what)
- if sectiondata then
- sections.typesetnumber(sectiondata,'section',sectiondata)
- end
-end
-
-function sections.getnumber(depth,what) -- redefined here
- local sectiondata = sections.findnumber(depth,what)
- context((sectiondata and sectiondata.numbers[depth]) or 0)
-end
-
--- experimental
-
-local levels = { }
-
---~ function commands.autonextstructurelevel(level)
---~ if level > #levels then
---~ for i=#levels+1,level do
---~ levels[i] = ""
---~ end
---~ end
---~ local finish = concat(levels,"\n",level) or ""
---~ for i=level+1,#levels do
---~ levels[i] = ""
---~ end
---~ levels[level] = [[\finalizeautostructurelevel]]
---~ context(finish)
---~ end
-
---~ function commands.autofinishstructurelevels()
---~ local finish = concat(levels,"\n") or ""
---~ levels = { }
---~ context(finish)
---~ end
-
-function commands.autonextstructurelevel(level)
- if level > #levels then
- for i=#levels+1,level do
- levels[i] = false
- end
- else
- for i=level,#levels do
- if levels[i] then
- context.finalizeautostructurelevel()
- levels[i] = false
- end
- end
- end
- levels[level] = true
-end
-
-function commands.autofinishstructurelevels()
- for i=1,#levels do
- if levels[i] then
- context.finalizeautostructurelevel()
- end
- end
- levels = { }
-end
-
--- interface (some are actually already commands, like sections.fullnumber)
-
-commands.structurenumber = function() sections.fullnumber() end
-commands.structuretitle = function() sections.title () end
-
-commands.structurevariable = function(name) sections.structuredata(nil,name) end
-commands.structureuservariable = function(name) sections.userdata (nil,name) end
-commands.structurecatcodedget = function(name) sections.structuredata(nil,name,nil,true) end
-commands.structuregivencatcodedget = function(name,catcode) sections.structuredata(nil,name,nil,catcode) end
-commands.structureautocatcodedget = function(name,catcode) sections.structuredata(nil,name,nil,catcode) end
-
-commands.namedstructurevariable = function(depth,name) sections.structuredata(depth,name) end
-commands.namedstructureuservariable = function(depth,name) sections.userdata (depth,name) end
-
---
-
-function commands.setsectionblock (name) context(sections.setblock(name)) end
-function commands.pushsectionblock(name) context(sections.pushblock(name)) end
-function commands.popsectionblock () context(sections.popblock()) end
-
---
-
-local byway = "^" .. v_by -- ugly but downward compatible
-
-function commands.way(way)
- context((gsub(way,byway,"")))
-end
+if not modules then modules = { } end modules ['strc-doc'] = {
+ version = 1.001,
+ comment = "companion to strc-doc.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo: associate counter with head
+-- we need to better split the lua/tex end
+-- we need to freeze and document this module
+
+-- keep this as is:
+--
+-- in section titles by default a zero aborts, so there we need: sectionset=bagger with \definestructureprefixset [bagger] [section-2,section-4] []
+-- in lists however zero's are ignored, so there numbersegments=2:4 gives result
+
+local next, type, tonumber, select = next, type, tonumber, select
+local format, gsub, find, gmatch, match = string.format, string.gsub, string.find, string.gmatch, string.match
+local concat, fastcopy = table.concat, table.fastcopy
+local max, min = math.max, math.min
+local allocate, mark, accesstable = utilities.storage.allocate, utilities.storage.mark, utilities.tables.accesstable
+local setmetatableindex = table.setmetatableindex
+
+local catcodenumbers = catcodes.numbers
+local ctxcatcodes = catcodenumbers.ctxcatcodes
+local variables = interfaces.variables
+
+local v_last = variables.last
+local v_first = variables.first
+local v_previous = variables.previous
+local v_next = variables.next
+local v_auto = variables.auto
+local v_strict = variables.strict
+local v_all = variables.all
+local v_positive = variables.positive
+local v_by = variables.by
+
+local trace_sectioning = false trackers.register("structures.sectioning", function(v) trace_sectioning = v end)
+local trace_detail = false trackers.register("structures.detail", function(v) trace_detail = v end)
+
+local report_structure = logs.reporter("structure","sectioning")
+
+local structures = structures
+local context = context
+
+local helpers = structures.helpers
+local documents = structures.documents
+local sections = structures.sections
+local lists = structures.lists
+local counters = structures.counters
+local sets = structures.sets
+local tags = structures.tags
+
+local processors = typesetters.processors
+local applyprocessor = processors.apply
+local startapplyprocessor = processors.startapply
+local stopapplyprocessor = processors.stopapply
+local strippedprocessor = processors.stripped
+
+local a_internal = attributes.private('internal')
+
+-- -- -- document -- -- --
+
+local data -- the current state
+
+function documents.initialize()
+ data = allocate { -- whole data is marked
+ numbers = { },
+ forced = { },
+ ownnumbers = { },
+ status = { },
+ checkers = { },
+ depth = 0,
+ blocks = { },
+ block = "",
+ }
+ documents.data = data
+end
+
+function documents.reset()
+ data.numbers = { }
+ data.forced = { }
+ data.ownnumbers = { }
+ data.status = { }
+ -- data.checkers = { }
+ data.depth = 0
+end
+
+documents.initialize()
+
+-- -- -- components -- -- --
+
+function documents.preset(numbers)
+ local nofnumbers = #numbers
+ local ownnumbers = { }
+ data.numbers = numbers
+ data.ownnumbers = ownnumbers
+ data.depth = nofnumbers
+ for i=1,nofnumbers do
+ ownnumbers[i] = ""
+ end
+ sections.setnumber(nofnumbers,"-1")
+end
+
+-- -- -- sections -- -- --
+
+local collected = allocate()
+local tobesaved = allocate()
+
+sections.collected = collected
+sections.tobesaved = tobesaved
+
+-- local function initializer()
+-- collected = sections.collected
+-- tobesaved = sections.tobesaved
+-- end
+--
+-- job.register('structures.sections.collected', tobesaved, initializer)
+
+sections.registered = sections.registered or allocate()
+local registered = sections.registered
+
+storage.register("structures/sections/registered", registered, "structures.sections.registered")
+
+function sections.register(name,specification)
+ registered[name] = specification
+end
+
+function sections.currentid()
+ return #tobesaved
+end
+
+function sections.save(sectiondata)
+-- local sectionnumber = helpers.simplify(section.sectiondata) -- maybe done earlier
+ local numberdata = sectiondata.numberdata
+ local ntobesaved = #tobesaved
+ if not numberdata or sectiondata.metadata.nolist then
+ return ntobesaved
+ else
+ ntobesaved = ntobesaved + 1
+ tobesaved[ntobesaved] = numberdata
+ if not collected[ntobesaved] then
+ collected[ntobesaved] = numberdata
+ end
+ return ntobesaved
+ end
+end
+
+function sections.load()
+ setmetatableindex(collected,nil)
+ local lists = lists.collected
+ for i=1,#lists do
+ local list = lists[i]
+ local metadata = list.metadata
+ if metadata and metadata.kind == "section" and not metadata.nolist then
+ local numberdata = list.numberdata
+ if numberdata then
+ collected[#collected+1] = numberdata
+ end
+ end
+ end
+ sections.load = functions.dummy
+end
+
+table.setmetatableindex(collected, function(t,i)
+ sections.load()
+ return collected[i] or { }
+end)
+
+--
+
+sections.levelmap = sections.levelmap or { }
+
+local levelmap = sections.levelmap
+
+storage.register("structures/sections/levelmap", sections.levelmap, "structures.sections.levelmap")
+
+sections.verbose = true
+
+levelmap.block = -1
+
+function sections.setlevel(name,level) -- level can be number or parent (=string)
+ local l = tonumber(level)
+ if not l then
+ l = levelmap[level]
+ end
+ if l and l > 0 then
+ levelmap[name] = l
+ else
+ -- error
+ end
+end
+
+function sections.getlevel(name)
+ return levelmap[name] or 0
+end
+
+function sections.setblock(name)
+ local block = name or data.block or "unknown" -- can be used to set the default
+ data.block = block
+ return block
+end
+
+function sections.pushblock(name)
+ counters.check(0) -- we assume sane usage of \page between blocks
+ local block = name or data.block
+ data.blocks[#data.blocks+1] = block
+ data.block = block
+ documents.reset()
+ return block
+end
+
+function sections.popblock()
+ data.blocks[#data.blocks] = nil
+ local block = data.blocks[#data.blocks] or data.block
+ data.block = block
+ documents.reset()
+ return block
+end
+
+function sections.currentblock()
+ return data.block or data.blocks[#data.blocks] or "unknown"
+end
+
+function sections.currentlevel()
+ return data.depth
+end
+
+function sections.getcurrentlevel()
+ context(data.depth)
+end
+
+local saveset = { } -- experiment, see sections/tricky-001.tex
+
+function sections.somelevel(given)
+ -- old number
+ local numbers = data.numbers
+
+ local ownnumbers = data.ownnumbers
+ local forced = data.forced
+ local status = data.status
+ local olddepth = data.depth
+ local givenname = given.metadata.name
+ local mappedlevel = levelmap[givenname]
+ local newdepth = tonumber(mappedlevel or (olddepth > 0 and olddepth) or 1) -- hm, levelmap only works for section-*
+ local directives = given.directives
+ local resetset = directives and directives.resetset or ""
+ -- local resetter = sets.getall("structure:resets",data.block,resetset)
+ -- a trick to permit userdata to overload title, ownnumber and reference
+ -- normally these are passed as argument but nowadays we provide several
+ -- interfaces (we need this because we want to be compatible)
+ if trace_detail then
+ report_structure("name %a, mapped level %a, old depth %a, new depth %a, reset set %a",
+ givenname,mappedlevel,olddepth,newdepth,resetset)
+ end
+ local u = given.userdata
+ if u then
+ -- kind of obsolete as we can pass them directly anyway
+ if u.reference and u.reference ~= "" then given.metadata.reference = u.reference ; u.reference = nil end
+ if u.ownnumber and u.ownnumber ~= "" then given.numberdata.ownnumber = u.ownnumber ; u.ownnumber = nil end
+ if u.title and u.title ~= "" then given.titledata.title = u.title ; u.title = nil end
+ if u.bookmark and u.bookmark ~= "" then given.titledata.bookmark = u.bookmark ; u.bookmark = nil end
+ if u.label and u.label ~= "" then given.titledata.label = u.label ; u.label = nil end
+ end
+ -- so far for the trick
+ if saveset then
+ saveset[newdepth] = (resetset ~= "" and resetset) or saveset[newdepth] or ""
+ end
+ if newdepth > olddepth then
+ for i=olddepth+1,newdepth do
+ local s = tonumber(sets.get("structure:resets",data.block,saveset and saveset[i] or resetset,i))
+ if trace_detail then
+ report_structure("new depth %s, old depth %s, reset set %a, reset value %a, current %a",olddepth,newdepth,resetset,s,numbers[i])
+ end
+ if not s or s == 0 then
+ numbers[i] = numbers[i] or 0
+ ownnumbers[i] = ownnumbers[i] or ""
+ else
+ numbers[i] = s - 1
+ ownnumbers[i] = ""
+ end
+ status[i] = { }
+ end
+ elseif newdepth < olddepth then
+ for i=olddepth,newdepth+1,-1 do
+ local s = tonumber(sets.get("structure:resets",data.block,saveset and saveset[i] or resetset,i))
+ if trace_detail then
+ report_structure("new depth %s, old depth %s, reset set %a, reset value %a, current %a",olddepth,newdepth,resetset,s,numbers[i])
+ end
+ if not s or s == 0 then
+ numbers[i] = numbers[i] or 0
+ ownnumbers[i] = ownnumbers[i] or ""
+ else
+ numbers[i] = s - 1
+ ownnumbers[i] = ""
+ end
+ status[i] = nil
+ end
+ end
+ counters.check(newdepth)
+ ownnumbers[newdepth] = given.numberdata.ownnumber or ""
+ given.numberdata.ownnumber = nil
+ data.depth = newdepth
+ -- new number
+ olddepth = newdepth
+ if given.metadata.increment then
+ local oldn, newn = numbers[newdepth] or 0, 0
+ local fd = forced[newdepth]
+ if fd then
+ if fd[1] == "add" then
+ newn = oldn + fd[2] + 1
+ else
+ newn = fd[2] + 1
+ end
+ if newn < 0 then
+ newn = 1 -- maybe zero is nicer
+ end
+ forced[newdepth] = nil
+ if trace_detail then
+ report_structure("old depth %a, new depth %a, old n %a, new n %a, forced %t",olddepth,newdepth,oldn,newn,fd)
+ end
+ else
+ newn = oldn + 1
+ if trace_detail then
+ report_structure("old depth %a, new depth %a, old n %a, new n %a, increment",olddepth,newdepth,oldn,newn)
+ end
+ end
+ numbers[newdepth] = newn
+ end
+ status[newdepth] = given or { }
+ for k, v in next, data.checkers do
+ if v[1] == newdepth and v[2] then
+ v[2](k)
+ end
+ end
+ local numberdata= given.numberdata
+ if not numberdata then
+ -- probably simplified to nothing
+ numberdata = { }
+ given.numberdata = numberdata
+ end
+
+ local n = { }
+ for i=1,newdepth do
+ n[i] = numbers[i]
+ end
+ numberdata.numbers = n
+-- numberdata.numbers = fastcopy(numbers)
+
+ if #ownnumbers > 0 then
+ numberdata.ownnumbers = fastcopy(ownnumbers)
+ end
+ if trace_detail then
+ report_structure("name %a, numbers % a, own numbers % a",givenname,numberdata.numbers,numberdata.ownnumbers)
+ end
+
+ local metadata = given.metadata
+ local references = given.references
+
+ local tag = references.tag or tags.getid(metadata.kind,metadata.name)
+ if tag and tag ~= "" and tag ~= "?" then
+ references.tag = tag
+ end
+
+ local setcomponent = structures.references.setcomponent
+ if setcomponent then
+ setcomponent(given) -- might move to the tex end
+ end
+
+ references.section = sections.save(given)
+ -- given.numberdata = nil
+end
+
+function sections.reportstructure()
+ if sections.verbose then
+ local numbers, ownnumbers, status, depth = data.numbers, data.ownnumbers, data.status, data.depth
+ local d = status[depth]
+ local o = concat(ownnumbers,".",1,depth)
+ local n = (numbers and concat(numbers,".",1,min(depth,#numbers))) or 0
+ local l = d.titledata.title or ""
+ local t = (l ~= "" and l) or d.titledata.title or "[no title]"
+ local m = d.metadata.name
+ if o and not find(o,"^%.*$") then
+ report_structure("%s @ level %i : (%s) %s -> %s",m,depth,n,o,t)
+ elseif d.directives and d.directives.hidenumber then
+ report_structure("%s @ level %i : (%s) -> %s",m,depth,n,t)
+ else
+ report_structure("%s @ level %i : %s -> %s",m,depth,n,t)
+ end
+ end
+end
+
+function sections.setnumber(depth,n)
+ local forced, depth, new = data.forced, depth or data.depth, tonumber(n)
+ if type(n) == "string" then
+ if find(n,"^[%+%-]") then
+ forced[depth] = { "add", new }
+ else
+ forced[depth] = { "set", new }
+ end
+ else
+ forced[depth] = { "set", new }
+ end
+end
+
+function sections.numberatdepth(depth)
+ return data.numbers[tonumber(depth) or sections.getlevel(depth) or 0] or 0
+end
+
+function sections.numbers()
+ return data.numbers
+end
+
+function sections.matchingtilldepth(depth,numbers,parentnumbers)
+ local dn = parentnumbers or data.numbers
+ local ok = false
+ for i=1,depth do
+ if dn[i] == numbers[i] then
+ ok = true
+ else
+ return false
+ end
+ end
+ return ok
+end
+
+function sections.getnumber(depth) -- redefined later ...
+ context(data.numbers[depth] or 0)
+end
+
+function sections.set(key,value)
+ data.status[data.depth][key] = value -- may be nil for a reset
+end
+
+function sections.cct()
+ local metadata = data.status[data.depth].metadata
+ context(metadata and metadata.catcodes or ctxcatcodes)
+end
+
+-- this one will become: return catcode, d (etc)
+
+function sections.structuredata(depth,key,default,honorcatcodetable) -- todo: spec table and then also depth
+ if depth then
+ depth = levelmap[depth] or tonumber(depth)
+ end
+ if not depth or depth == 0 then
+ depth = data.depth
+ end
+ local data = data.status[depth]
+ local d
+ if data then
+ if find(key,"%.") then
+ d = accesstable(key,data)
+ else
+ d = data.titledata
+ d = d and d[key]
+ end
+ end
+ if d and type(d) ~= "table" then
+ if honorcatcodetable == true or honorcatcodetable == v_auto then
+ local metadata = data.metadata
+ local catcodes = metadata and metadata.catcodes
+ if catcodes then
+ context.sprint(catcodes,d)
+ else
+ context(d)
+ end
+ elseif not honorcatcodetable or honorcatcodetable == "" then
+ context(d)
+ else
+ local catcodes = catcodenumbers[honorcatcodetable]
+ if catcodes then
+ context.sprint(catcodes,d)
+ else
+ context(d)
+ end
+ end
+ elseif default then
+ context(default)
+ end
+end
+
+function sections.userdata(depth,key,default)
+ if depth then
+ depth = levelmap[depth] or tonumber(depth)
+ end
+ if not depth or depth == 0 then
+ depth = data.depth
+ end
+ if depth > 0 then
+ local userdata = data.status[depth]
+ userdata = userdata and userdata.userdata
+ userdata = (userdata and userdata[key]) or default
+ if userdata then
+ context(userdata)
+ end
+ end
+end
+
+function sections.setchecker(name,level,command) -- hm, checkers are not saved
+ data.checkers[name] = (name and command and level >= 0 and { level, command }) or nil
+end
+
+function sections.current()
+ return data.status[data.depth]
+end
+
+function sections.depthnumber(n)
+ local depth = data.depth
+ if not n or n == 0 then
+ n = depth
+ elseif n < 0 then
+ n = depth + n
+ end
+ return context(data.numbers[n] or 0)
+end
+
+function sections.autodepth(numbers)
+ for i=#numbers,1,-1 do
+ if numbers[i] ~= 0 then
+ return i
+ end
+ end
+ return 0
+end
+
+--
+
+function structures.currentsectionnumber() -- brr, namespace wrong
+ local sc = sections.current()
+ return sc and sc.numberdata
+end
+
+-- \dorecurse{3} {
+-- \chapter{Blabla} \subsection{bla 1 1} \subsection{bla 1 2}
+-- \section{bla 2} \subsection{bla 2 1} \subsection{bla 2 2}
+-- }
+
+-- sign=all => also zero and negative
+-- sign=positive => also zero
+-- sign=hang => llap sign
+
+--~ todo: test this
+--~
+
+local function process(index,numbers,ownnumbers,criterium,separatorset,conversion,conversionset,index,entry,result,preceding,done)
+ -- todo: too much (100 steps)
+ local number = numbers and (numbers[index] or 0)
+ local ownnumber = ownnumbers and ownnumbers[index] or ""
+ if number > criterium or (ownnumber ~= "") then
+ local block = (entry.block ~= "" and entry.block) or sections.currentblock() -- added
+ if preceding then
+ local separator = sets.get("structure:separators",block,separatorset,preceding,".")
+ if separator then
+ if result then
+ result[#result+1] = strippedprocessor(separator)
+ else
+ applyprocessor(separator)
+ end
+ end
+ preceding = false
+ end
+ if result then
+ if ownnumber ~= "" then
+ result[#result+1] = ownnumber
+ elseif conversion and conversion ~= "" then -- traditional (e.g. used in itemgroups) .. inherited!
+ result[#result+1] = converters.convert(conversion,number)
+ else
+ local theconversion = sets.get("structure:conversions",block,conversionset,index,"numbers")
+ result[#result+1] = converters.convert(theconversion,number)
+ end
+ else
+ if ownnumber ~= "" then
+ applyprocessor(ownnumber)
+ elseif conversion and conversion ~= "" then -- traditional (e.g. used in itemgroups)
+ context.convertnumber(conversion,number)
+ else
+ local theconversion = sets.get("structure:conversions",block,conversionset,index,"numbers")
+ local data = startapplyprocessor(theconversion)
+ context.convertnumber(data or "numbers",number)
+ stopapplyprocessor()
+ end
+ end
+ return index, true
+ else
+ return preceding or false, done
+ end
+end
+
+function sections.typesetnumber(entry,kind,...) -- kind='section','number','prefix'
+ if entry and entry.hidenumber ~= true then -- can be nil
+ local separatorset = ""
+ local conversionset = ""
+ local conversion = ""
+ local groupsuffix = ""
+ local stopper = ""
+ local starter = ""
+ local connector = ""
+ local set = ""
+ local segments = ""
+ local criterium = ""
+ for d=1,select("#",...) do
+ local data = select(d,...) -- can be multiple parametersets
+ if data then
+ if separatorset == "" then separatorset = data.separatorset or "" end
+ if conversionset == "" then conversionset = data.conversionset or "" end
+ if conversion == "" then conversion = data.conversion or "" end
+ if groupsuffix == "" then groupsuffix = data.groupsuffix or "" end
+ if stopper == "" then stopper = data.stopper or "" end
+ if starter == "" then starter = data.starter or "" end
+ if connector == "" then connector = data.connector or "" end
+ if set == "" then set = data.set or "" end
+ if segments == "" then segments = data.segments or "" end
+ if criterium == "" then criterium = data.criterium or "" end
+ end
+ end
+ if separatorset == "" then separatorset = "default" end
+ if conversionset == "" then conversionset = "default" end -- not used
+ if conversion == "" then conversion = nil end
+ if groupsuffix == "" then groupsuffix = nil end
+ if stopper == "" then stopper = nil end
+ if starter == "" then starter = nil end
+ if connector == "" then connector = nil end
+ if set == "" then set = "default" end
+ if segments == "" then segments = nil end
+ --
+ if criterium == v_strict then
+ criterium = 0
+ elseif criterium == v_positive then
+ criterium = -1
+ elseif criterium == v_all then
+ criterium = -1000000
+ else
+ criterium = 0
+ end
+ --
+ local firstprefix, lastprefix = 0, 16
+ if segments then
+ local f, l = match(tostring(segments),"^(.-):(.+)$")
+ if l == "*" then
+ l = 100 -- new
+ end
+ if f and l then
+ -- 0:100, chapter:subsubsection
+ firstprefix = tonumber(f) or sections.getlevel(f) or 0
+ lastprefix = tonumber(l) or sections.getlevel(l) or 100
+ else
+ -- 3, section
+ local fl = tonumber(segments) or sections.getlevel(segments) -- generalize
+ if fl then
+ firstprefix = fl
+ lastprefix = fl
+ end
+ end
+ end
+ --
+ local numbers, ownnumbers = entry.numbers, entry.ownnumbers
+ if numbers then
+ local done, preceding = false, false
+ --
+ local result = kind == "direct" and { }
+ if result then
+ connector = false
+ end
+ --
+ local prefixlist = set and sets.getall("structure:prefixes","",set) -- "" == block
+ if starter then
+ if result then
+ result[#result+1] = strippedprocessor(starter)
+ else
+ applyprocessor(starter)
+ end
+ end
+ if prefixlist and (kind == 'section' or kind == 'prefix' or kind == 'direct') then
+ -- find valid set (problem: for sectionnumber we should pass the level)
+ -- no holes
+ local b, e, bb, ee = 1, #prefixlist, 0, 0
+ -- find last valid number
+ for k=e,b,-1 do
+ local prefix = prefixlist[k]
+ local index = sections.getlevel(prefix) or k
+ if index >= firstprefix and index <= lastprefix then
+ local number = numbers and numbers[index]
+ if number then
+ local ownnumber = ownnumbers and ownnumbers[index] or ""
+ if number > 0 or (ownnumber ~= "") then
+ break
+ else
+ e = k -1
+ end
+ end
+ end
+ end
+ -- find valid range
+ for k=b,e do
+ local prefix = prefixlist[k]
+ local index = sections.getlevel(prefix) or k
+ if index >= firstprefix and index <= lastprefix then
+ local number = numbers and numbers[index]
+ if number then
+ local ownnumber = ownnumbers and ownnumbers[index] or ""
+ if number > 0 or (ownnumber ~= "") then
+ if bb == 0 then bb = k end
+ ee = k
+ else
+ bb, ee = 0, 0
+ end
+ else
+ break
+ end
+ end
+ end
+ -- print valid range
+ for k=bb,ee do
+ local prefix = prefixlist[k]
+ local index = sections.getlevel(prefix) or k
+ if index >= firstprefix and index <= lastprefix then
+ -- process(index,result)
+ preceding, done = process(index,numbers,ownnumbers,criterium,separatorset,conversion,conversionset,index,entry,result,preceding,done)
+ end
+ end
+ else
+ -- also holes check
+ for index=firstprefix,lastprefix do
+ -- process(index,result)
+ preceding, done = process(index,numbers,ownnumbers,criterium,separatorset,conversion,conversionset,index,entry,result,preceding,done)
+ end
+ end
+ --
+ if done then
+ if connector and kind == 'prefix' then
+ if result then
+ -- can't happen as we're in 'direct'
+ else
+ applyprocessor(connector)
+ end
+ else
+if groupsuffix and kind ~= "prefix" then
+ if result then
+ result[#result+1] = strippedprocessor(groupsuffix)
+ else
+ applyprocessor(groupsuffix)
+ end
+end
+ if stopper then
+ if result then
+ result[#result+1] = strippedprocessor(stopper)
+ else
+ applyprocessor(stopper)
+ end
+ end
+ end
+ end
+ return result -- a table !
+ else
+ -- report_structure("error: no numbers")
+ end
+ end
+end
+
+function sections.title()
+ local sc = sections.current()
+ if sc then
+ helpers.title(sc.titledata.title,sc.metadata)
+ end
+end
+
+function sections.findnumber(depth,what) -- needs checking (looks wrong and slow too)
+ local data = data.status[depth or data.depth]
+ if data then
+ local index = data.references.section
+ local collected = sections.collected
+ local sectiondata = collected[index]
+ if sectiondata and sectiondata.hidenumber ~= true then -- can be nil
+ local quit = what == v_previous or what == v_next
+ if what == v_first or what == v_previous then
+ for i=index,1,-1 do
+ local s = collected[i]
+ if s then
+ local n = s.numbers
+ if #n == depth and n[depth] and n[depth] ~= 0 then
+ sectiondata = s
+ if quit then
+ break
+ end
+ elseif #n < depth then
+ break
+ end
+ end
+ end
+ elseif what == v_last or what == v_next then
+ for i=index,#collected do
+ local s = collected[i]
+ if s then
+ local n = s.numbers
+ if #n == depth and n[depth] and n[depth] ~= 0 then
+ sectiondata = s
+ if quit then
+ break
+ end
+ elseif #n < depth then
+ break
+ end
+ end
+ end
+ end
+ return sectiondata
+ end
+ end
+end
+
+function sections.finddata(depth,what)
+ local data = data.status[depth or data.depth]
+ if data then
+ -- if sectiondata and sectiondata.hidenumber ~= true then -- can be nil
+ local index = data.references.listindex
+ if index then
+ local collected = structures.lists.collected
+ local quit = what == v_previous or what == v_next
+ if what == v_first or what == v_previous then
+ for i=index-1,1,-1 do
+ local s = collected[i]
+ if not s then
+ break
+ elseif s.metadata.kind == "section" then -- maybe check on name
+ local n = s.numberdata.numbers
+ if #n == depth and n[depth] and n[depth] ~= 0 then
+ data = s
+ if quit then
+ break
+ end
+ elseif #n < depth then
+ break
+ end
+ end
+ end
+ elseif what == v_last or what == v_next then
+ for i=index+1,#collected do
+ local s = collected[i]
+ if not s then
+ break
+ elseif s.metadata.kind == "section" then -- maybe check on name
+ local n = s.numberdata.numbers
+ if #n == depth and n[depth] and n[depth] ~= 0 then
+ data = s
+ if quit then
+ break
+ end
+ elseif #n < depth then
+ break
+ end
+ end
+ end
+ end
+ end
+ return data
+ end
+end
+
+function sections.internalreference(sectionname,what) -- to be used in pagebuilder (no marks used)
+ local r = type(sectionname) == "number" and sectionname or registered[sectionname]
+ if r then
+ local data = sections.finddata(r.level,what)
+ return data and data.references and data.references.internal
+ end
+end
+
+function sections.fullnumber(depth,what)
+ local sectiondata = sections.findnumber(depth,what)
+ if sectiondata then
+ sections.typesetnumber(sectiondata,'section',sectiondata)
+ end
+end
+
+function sections.getnumber(depth,what) -- redefined here
+ local sectiondata = sections.findnumber(depth,what)
+ context((sectiondata and sectiondata.numbers[depth]) or 0)
+end
+
+-- experimental
+
+local levels = { }
+
+--~ function commands.autonextstructurelevel(level)
+--~ if level > #levels then
+--~ for i=#levels+1,level do
+--~ levels[i] = ""
+--~ end
+--~ end
+--~ local finish = concat(levels,"\n",level) or ""
+--~ for i=level+1,#levels do
+--~ levels[i] = ""
+--~ end
+--~ levels[level] = [[\finalizeautostructurelevel]]
+--~ context(finish)
+--~ end
+
+--~ function commands.autofinishstructurelevels()
+--~ local finish = concat(levels,"\n") or ""
+--~ levels = { }
+--~ context(finish)
+--~ end
+
+function commands.autonextstructurelevel(level)
+ if level > #levels then
+ for i=#levels+1,level do
+ levels[i] = false
+ end
+ else
+ for i=level,#levels do
+ if levels[i] then
+ context.finalizeautostructurelevel()
+ levels[i] = false
+ end
+ end
+ end
+ levels[level] = true
+end
+
+function commands.autofinishstructurelevels()
+ for i=1,#levels do
+ if levels[i] then
+ context.finalizeautostructurelevel()
+ end
+ end
+ levels = { }
+end
+
+-- interface (some are actually already commands, like sections.fullnumber)
+
+commands.structurenumber = function() sections.fullnumber() end
+commands.structuretitle = function() sections.title () end
+
+commands.structurevariable = function(name) sections.structuredata(nil,name) end
+commands.structureuservariable = function(name) sections.userdata (nil,name) end
+commands.structurecatcodedget = function(name) sections.structuredata(nil,name,nil,true) end
+commands.structuregivencatcodedget = function(name,catcode) sections.structuredata(nil,name,nil,catcode) end
+commands.structureautocatcodedget = function(name,catcode) sections.structuredata(nil,name,nil,catcode) end
+
+commands.namedstructurevariable = function(depth,name) sections.structuredata(depth,name) end
+commands.namedstructureuservariable = function(depth,name) sections.userdata (depth,name) end
+
+--
+
+function commands.setsectionblock (name) context(sections.setblock(name)) end
+function commands.pushsectionblock(name) context(sections.pushblock(name)) end
+function commands.popsectionblock () context(sections.popblock()) end
+
+--
+
+local byway = "^" .. v_by -- ugly but downward compatible
+
+function commands.way(way)
+ context((gsub(way,byway,"")))
+end
diff --git a/tex/context/base/strc-flt.lua b/tex/context/base/strc-flt.lua
index 0fdadc583..466fd515e 100644
--- a/tex/context/base/strc-flt.lua
+++ b/tex/context/base/strc-flt.lua
@@ -1,9 +1,9 @@
-if not modules then modules = { } end modules ['strc-flt'] = {
- version = 1.001,
- comment = "companion to strc-flt.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- nothing
+if not modules then modules = { } end modules ['strc-flt'] = {
+ version = 1.001,
+ comment = "companion to strc-flt.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- nothing
diff --git a/tex/context/base/strc-ini.lua b/tex/context/base/strc-ini.lua
index 5c72f3158..fd7c10f79 100644
--- a/tex/context/base/strc-ini.lua
+++ b/tex/context/base/strc-ini.lua
@@ -1,338 +1,338 @@
-if not modules then modules = { } end modules ['strc-ini'] = {
- version = 1.001,
- comment = "companion to strc-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
---[[
-The restructuring is the (intermediate) result of quite some experiments. I started
-with the basic structure, followed by lists, numbers, enumerations, itemgroups
-and floats. All these have something in common, like pagenumbers and section
-prefixes. I played with some generic datastructure (in order to save space) but
-the code at both the lua and tex end then quickly becomes messy due to the fact
-that access to variables is too different. So, eventually I ended up with
-dedicated structures combined with sharing data. In lua this is quite efficient
-because tables are referenced. However, some precautions are to be taken in
-order to keep the utility file small. Utility data and process data share much
-but it does not make sense to store all processdata.
-
-]]--
-
-local formatters = string.formatters
-local lpegmatch = lpeg.match
-local count = tex.count
-local type, next, tonumber, select = type, next, tonumber, select
-local settings_to_array, settings_to_hash = utilities.parsers.settings_to_array, utilities.parsers.settings_to_hash
-local allocate = utilities.storage.allocate
-
-local catcodenumbers = catcodes.numbers -- better use the context(...) way to switch
-
-local ctxcatcodes = catcodenumbers.ctxcatcodes
-local xmlcatcodes = catcodenumbers.xmlcatcodes
-local notcatcodes = catcodenumbers.notcatcodes
-local txtcatcodes = catcodenumbers.txtcatcodes
-
-local context, commands = context, commands
-
-local pushcatcodes = context.pushcatcodes
-local popcatcodes = context.popcatcodes
-
-local trace_processors = false
-local report_processors = logs.reporter("processors","structure")
-
-trackers.register("typesetters.processors", function(v) trace_processors = v end)
-
--- -- -- namespace -- -- --
-
--- This is tricky: we have stored and initialized already some of
--- the job.registered tables so we have a forward reference!
-
-structures = structures or { }
-local structures = structures
-
-structures.blocks = structures.blocks or { }
-structures.sections = structures.sections or { }
-structures.pages = structures.pages or { }
-structures.registers = structures.registers or { }
-structures.references = structures.references or { }
-structures.lists = structures.lists or { }
-structures.helpers = structures.helpers or { }
-structures.documents = structures.documents or { }
-structures.notes = structures.notes or { }
-structures.descriptions = structures.descriptions or { }
-structures.itemgroups = structures.itemgroups or { }
-structures.specials = structures.specials or { }
-structures.counters = structures.counters or { }
-structures.tags = structures.tags or { }
-structures.formulas = structures.formulas or { }
-structures.sets = structures.sets or { }
-structures.marks = structures.marks or { }
-structures.floats = structures.floats or { }
-structures.synonyms = structures.synonyms or { }
-
---~ table.print(structures)
-
-local processors = typesetters.processors
-
--- -- -- specials -- -- --
-
--- we can store information and get back a reference; this permits
--- us to store rather raw data in references
-
-local specials = structures.specials
-
-local collected = allocate()
-local tobesaved = allocate()
-
-specials.collected = collected
-specials.tobesaved = tobesaved
-
-local function initializer()
- collected = specials.collected
- tobesaved = specials.tobesaved
-end
-
-if job then
- job.register('structures.specials.collected', tobesaved, initializer)
-end
-
-function specials.store(class,data)
- if class and data then
- local s = tobesaved[class]
- if not s then
- s = { }
- tobesaved[class] = s
- end
- s[#s+1] = data
- context(#s)
- else
- context(0)
- end
-end
-
-function specials.retrieve(class,n)
- if class and n then
- local c = collected[class]
- return c and c[n]
- end
-end
-
--- -- -- helpers -- -- --
-
-local helpers = structures.helpers
-
--- function helpers.touserdata(str)
--- local hash = str and str ~= "" and settings_to_hash(str)
--- if hash and next(hash) then
--- return hash
--- end
--- end
-
-function helpers.touserdata(data)
- if type(data) == "string" then
- if data == "" then
- return nil
- else
- data = settings_to_hash(data)
- end
- end
- if data and next(data) then
- return data
- end
-end
-
-local function simplify(d,nodefault)
- if d then
- local t = { }
- for k, v in next, d do
- local tv = type(v)
- if tv == "table" then
- if next(v) then t[k] = simplify(v) end
- elseif tv == "string" then
- if v ~= "" and v ~= "default" then t[k] = v end
- elseif tv == "boolean" then
- if v then t[k] = v end
- else
- t[k] = v
- end
- end
- return next(t) and t
- elseif nodefault then
- return nil
- else
- return { }
- end
-end
-
-helpers.simplify = simplify
-
-function helpers.merged(...)
- local t = { }
- for k=1, select("#",...) do
- local v = select(k,...)
- if v and v ~= "" and not t[k] then
- t[k] = v
- end
- end
- return t
-end
-
-local tags = {
- generic = "ctx:genericentry",
- section = "ctx:sectionentry",
- entry = "ctx:registerentry",
-}
-
--- We had the following but it overloads the main document so it's a no-go as we
--- no longer push and pop. So now we use the tag as buffername, namespace and also
--- (optionally) as a setups to be applied but keep in mind that document setups
--- also get applied (when they use #1's).
---
--- local command = formatters["\\xmlprocessbuffer{%s}{%s}{}"](metadata.xmlroot or "main",tag)
-
-local experiment = true
-
-function helpers.title(title,metadata) -- coding is xml is rather old and not that much needed now
- if title and title ~= "" then -- so it might disappear
- if metadata then
- local xmlsetup = metadata.xmlsetup
- if metadata.coding == "xml" then
- -- title can contain raw xml
- local tag = tags[metadata.kind] or tags.generic
- local xmldata = formatters["<?xml version='1.0'?><%s>%s</%s>"](tag,title,tag)
- if not experiment then
- buffers.assign(tag,xmldata)
- end
- if trace_processors then
- report_processors("putting xml data in buffer: %s",xmldata)
- report_processors("processing buffer with setup %a and tag %a",xmlsetup,tag)
- end
- if experiment then
- -- the question is: will this be forgotten ... better store in a via file
- local xmltable = lxml.convert("temp",xmldata or "")
- lxml.store("temp",xmltable)
- context.xmlsetup("temp",xmlsetup or "")
- else
- context.xmlprocessbuffer("dummy",tag,xmlsetup or "")
- end
- elseif xmlsetup then -- title is reference to node (so \xmlraw should have been used)
- if trace_processors then
- report_processors("feeding xmlsetup %a using node %a",xmlsetup,title)
- end
- context.xmlsetup(title,metadata.xmlsetup)
- else
- local catcodes = metadata.catcodes
- if catcodes == notcatcodes or catcodes == xmlcatcodes then
- if trace_processors then
- report_processors("catcodetable %a, overloads %a, text %a",ctxcatcodes,catcodes,title)
- end
- context(title) -- nasty
- else
- if trace_processors then
- report_processors("catcodetable %a, text %a",catcodes,title)
- end
- --
- -- context.sprint(catcodes,title)
- --
- -- doesn't work when a newline is in there \section{Test\ A} so we do
- -- it this way:
- --
- pushcatcodes(catcodes)
- context(title)
- popcatcodes()
- end
- end
- else
- context(title) -- no catcode switch, was: texsprint(title)
- end
- end
-end
-
--- -- -- sets -- -- --
-
-local sets = structures.sets
-
-sets.setlist = sets.setlist or { }
-
-storage.register("structures/sets/setlist", structures.sets.setlist, "structures.sets.setlist")
-
-local setlist = sets.setlist
-
-function sets.define(namespace,name,values,default,numbers)
- local dn = setlist[namespace]
- if not dn then
- dn = { }
- setlist[namespace] = dn
- end
- if values == "" then
- dn[name] = { { }, default }
- else
- local split = settings_to_array(values)
- if numbers then
- -- convert to numbers (e.g. for reset)
- for i=1,#split do
- split[i] = tonumber(split[i]) or 0
- end
- end
- dn[name] = { split, default }
- end
-end
-
-function sets.getall(namespace,block,name)
- local ds = setlist[namespace]
- if not ds then
- return { }
- else
- local dn
- if block and block ~= "" then
- dn = ds[block..":"..name] or ds[name] or ds[block] or ds.default
- else
- dn = ds[name] or ds.default
- end
- return (dn and dn[1]) or { }
- end
-end
-
--- messy (will be another keyword, fixedconversion)
-
-local splitter = lpeg.splitat("::")
-
-function sets.get(namespace,block,name,level,default) -- check if name is passed
- --fixed::R:a: ...
- local kind, rest = lpegmatch(splitter,name)
- if rest and kind == "fixed" then -- fixed::n,a,i
- local s = settings_to_array(rest)
- return s[level] or s[#s] or default
- end
- --
- local ds = setlist[namespace]
- if not ds then
- return default
- end
- local dn
- if name and name ~= "" then
- if block and block ~= "" then
- dn = ds[block..":"..name] or ds[name] or ds[block] or ds.default
- else
- dn = ds[name] or ds.default
- end
- else
- if block and block ~= "" then
- dn = ds[block] or ds[block..":default"] or ds.default
- else
- dn = ds.default
- end
- end
- if not dn then
- return default
- end
--- inspect(dn)
- local dl = dn[1][level]
- return dl or dn[2] or default
-end
-
--- interface
-
-commands.definestructureset = sets.define
+if not modules then modules = { } end modules ['strc-ini'] = {
+ version = 1.001,
+ comment = "companion to strc-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[
+The restructuring is the (intermediate) result of quite some experiments. I started
+with the basic structure, followed by lists, numbers, enumerations, itemgroups
+and floats. All these have something in common, like pagenumbers and section
+prefixes. I played with some generic datastructure (in order to save space) but
+the code at both the lua and tex end then quickly becomes messy due to the fact
+that access to variables is too different. So, eventually I ended up with
+dedicated structures combined with sharing data. In lua this is quite efficient
+because tables are referenced. However, some precautions are to be taken in
+order to keep the utility file small. Utility data and process data share much
+but it does not make sense to store all processdata.
+
+]]--
+
+local formatters = string.formatters
+local lpegmatch = lpeg.match
+local count = tex.count
+local type, next, tonumber, select = type, next, tonumber, select
+local settings_to_array, settings_to_hash = utilities.parsers.settings_to_array, utilities.parsers.settings_to_hash
+local allocate = utilities.storage.allocate
+
+local catcodenumbers = catcodes.numbers -- better use the context(...) way to switch
+
+local ctxcatcodes = catcodenumbers.ctxcatcodes
+local xmlcatcodes = catcodenumbers.xmlcatcodes
+local notcatcodes = catcodenumbers.notcatcodes
+local txtcatcodes = catcodenumbers.txtcatcodes
+
+local context, commands = context, commands
+
+local pushcatcodes = context.pushcatcodes
+local popcatcodes = context.popcatcodes
+
+local trace_processors = false
+local report_processors = logs.reporter("processors","structure")
+
+trackers.register("typesetters.processors", function(v) trace_processors = v end)
+
+-- -- -- namespace -- -- --
+
+-- This is tricky: we have stored and initialized already some of
+-- the job.registered tables so we have a forward reference!
+
+structures = structures or { }
+local structures = structures
+
+structures.blocks = structures.blocks or { }
+structures.sections = structures.sections or { }
+structures.pages = structures.pages or { }
+structures.registers = structures.registers or { }
+structures.references = structures.references or { }
+structures.lists = structures.lists or { }
+structures.helpers = structures.helpers or { }
+structures.documents = structures.documents or { }
+structures.notes = structures.notes or { }
+structures.descriptions = structures.descriptions or { }
+structures.itemgroups = structures.itemgroups or { }
+structures.specials = structures.specials or { }
+structures.counters = structures.counters or { }
+structures.tags = structures.tags or { }
+structures.formulas = structures.formulas or { }
+structures.sets = structures.sets or { }
+structures.marks = structures.marks or { }
+structures.floats = structures.floats or { }
+structures.synonyms = structures.synonyms or { }
+
+--~ table.print(structures)
+
+local processors = typesetters.processors
+
+-- -- -- specials -- -- --
+
+-- we can store information and get back a reference; this permits
+-- us to store rather raw data in references
+
+local specials = structures.specials
+
+local collected = allocate()
+local tobesaved = allocate()
+
+specials.collected = collected
+specials.tobesaved = tobesaved
+
+local function initializer()
+ collected = specials.collected
+ tobesaved = specials.tobesaved
+end
+
+if job then
+ job.register('structures.specials.collected', tobesaved, initializer)
+end
+
+function specials.store(class,data)
+ if class and data then
+ local s = tobesaved[class]
+ if not s then
+ s = { }
+ tobesaved[class] = s
+ end
+ s[#s+1] = data
+ context(#s)
+ else
+ context(0)
+ end
+end
+
+function specials.retrieve(class,n)
+ if class and n then
+ local c = collected[class]
+ return c and c[n]
+ end
+end
+
+-- -- -- helpers -- -- --
+
+local helpers = structures.helpers
+
+-- function helpers.touserdata(str)
+-- local hash = str and str ~= "" and settings_to_hash(str)
+-- if hash and next(hash) then
+-- return hash
+-- end
+-- end
+
+function helpers.touserdata(data)
+ if type(data) == "string" then
+ if data == "" then
+ return nil
+ else
+ data = settings_to_hash(data)
+ end
+ end
+ if data and next(data) then
+ return data
+ end
+end
+
+local function simplify(d,nodefault)
+ if d then
+ local t = { }
+ for k, v in next, d do
+ local tv = type(v)
+ if tv == "table" then
+ if next(v) then t[k] = simplify(v) end
+ elseif tv == "string" then
+ if v ~= "" and v ~= "default" then t[k] = v end
+ elseif tv == "boolean" then
+ if v then t[k] = v end
+ else
+ t[k] = v
+ end
+ end
+ return next(t) and t
+ elseif nodefault then
+ return nil
+ else
+ return { }
+ end
+end
+
+helpers.simplify = simplify
+
+function helpers.merged(...)
+ local t = { }
+ for k=1, select("#",...) do
+ local v = select(k,...)
+ if v and v ~= "" and not t[k] then
+ t[k] = v
+ end
+ end
+ return t
+end
+
+local tags = {
+ generic = "ctx:genericentry",
+ section = "ctx:sectionentry",
+ entry = "ctx:registerentry",
+}
+
+-- We had the following but it overloads the main document so it's a no-go as we
+-- no longer push and pop. So now we use the tag as buffername, namespace and also
+-- (optionally) as a setups to be applied but keep in mind that document setups
+-- also get applied (when they use #1's).
+--
+-- local command = formatters["\\xmlprocessbuffer{%s}{%s}{}"](metadata.xmlroot or "main",tag)
+
+local experiment = true
+
+function helpers.title(title,metadata) -- coding is xml is rather old and not that much needed now
+ if title and title ~= "" then -- so it might disappear
+ if metadata then
+ local xmlsetup = metadata.xmlsetup
+ if metadata.coding == "xml" then
+ -- title can contain raw xml
+ local tag = tags[metadata.kind] or tags.generic
+ local xmldata = formatters["<?xml version='1.0'?><%s>%s</%s>"](tag,title,tag)
+ if not experiment then
+ buffers.assign(tag,xmldata)
+ end
+ if trace_processors then
+ report_processors("putting xml data in buffer: %s",xmldata)
+ report_processors("processing buffer with setup %a and tag %a",xmlsetup,tag)
+ end
+ if experiment then
+ -- the question is: will this be forgotten ... better store in a via file
+ local xmltable = lxml.convert("temp",xmldata or "")
+ lxml.store("temp",xmltable)
+ context.xmlsetup("temp",xmlsetup or "")
+ else
+ context.xmlprocessbuffer("dummy",tag,xmlsetup or "")
+ end
+ elseif xmlsetup then -- title is reference to node (so \xmlraw should have been used)
+ if trace_processors then
+ report_processors("feeding xmlsetup %a using node %a",xmlsetup,title)
+ end
+ context.xmlsetup(title,metadata.xmlsetup)
+ else
+ local catcodes = metadata.catcodes
+ if catcodes == notcatcodes or catcodes == xmlcatcodes then
+ if trace_processors then
+ report_processors("catcodetable %a, overloads %a, text %a",ctxcatcodes,catcodes,title)
+ end
+ context(title) -- nasty
+ else
+ if trace_processors then
+ report_processors("catcodetable %a, text %a",catcodes,title)
+ end
+ --
+ -- context.sprint(catcodes,title)
+ --
+ -- doesn't work when a newline is in there \section{Test\ A} so we do
+ -- it this way:
+ --
+ pushcatcodes(catcodes)
+ context(title)
+ popcatcodes()
+ end
+ end
+ else
+ context(title) -- no catcode switch, was: texsprint(title)
+ end
+ end
+end
+
+-- -- -- sets -- -- --
+
+local sets = structures.sets
+
+sets.setlist = sets.setlist or { }
+
+storage.register("structures/sets/setlist", structures.sets.setlist, "structures.sets.setlist")
+
+local setlist = sets.setlist
+
+function sets.define(namespace,name,values,default,numbers)
+ local dn = setlist[namespace]
+ if not dn then
+ dn = { }
+ setlist[namespace] = dn
+ end
+ if values == "" then
+ dn[name] = { { }, default }
+ else
+ local split = settings_to_array(values)
+ if numbers then
+ -- convert to numbers (e.g. for reset)
+ for i=1,#split do
+ split[i] = tonumber(split[i]) or 0
+ end
+ end
+ dn[name] = { split, default }
+ end
+end
+
+function sets.getall(namespace,block,name)
+ local ds = setlist[namespace]
+ if not ds then
+ return { }
+ else
+ local dn
+ if block and block ~= "" then
+ dn = ds[block..":"..name] or ds[name] or ds[block] or ds.default
+ else
+ dn = ds[name] or ds.default
+ end
+ return (dn and dn[1]) or { }
+ end
+end
+
+-- messy (will be another keyword, fixedconversion)
+
+local splitter = lpeg.splitat("::")
+
+function sets.get(namespace,block,name,level,default) -- check if name is passed
+ --fixed::R:a: ...
+ local kind, rest = lpegmatch(splitter,name)
+ if rest and kind == "fixed" then -- fixed::n,a,i
+ local s = settings_to_array(rest)
+ return s[level] or s[#s] or default
+ end
+ --
+ local ds = setlist[namespace]
+ if not ds then
+ return default
+ end
+ local dn
+ if name and name ~= "" then
+ if block and block ~= "" then
+ dn = ds[block..":"..name] or ds[name] or ds[block] or ds.default
+ else
+ dn = ds[name] or ds.default
+ end
+ else
+ if block and block ~= "" then
+ dn = ds[block] or ds[block..":default"] or ds.default
+ else
+ dn = ds.default
+ end
+ end
+ if not dn then
+ return default
+ end
+-- inspect(dn)
+ local dl = dn[1][level]
+ return dl or dn[2] or default
+end
+
+-- interface
+
+commands.definestructureset = sets.define
diff --git a/tex/context/base/strc-itm.lua b/tex/context/base/strc-itm.lua
index 75e77767f..8a745f356 100644
--- a/tex/context/base/strc-itm.lua
+++ b/tex/context/base/strc-itm.lua
@@ -1,38 +1,38 @@
-if not modules then modules = { } end modules ['strc-itm'] = {
- version = 1.001,
- comment = "companion to strc-itm.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local structures = structures
-local itemgroups = structures.itemgroups
-local jobpasses = job.passes
-
-local setfield = jobpasses.save
-local getfield = jobpasses.getfield
-
-function itemgroups.register(name,nofitems,maxwidth)
- setfield("itemgroup", { nofitems, maxwidth })
-end
-
-function itemgroups.nofitems(name,index)
- return getfield("itemgroup", index, 1, 0)
-end
-
-function itemgroups.maxwidth(name,index)
- return getfield("itemgroup", index, 2, 0)
-end
-
--- interface (might become counter/dimension)
-
-commands.registeritemgroup = itemgroups.register
-
-function commands.nofitems(name,index)
- context(getfield("itemgroup", index, 1, 0))
-end
-
-function commands.maxitemwidth(name,index)
- context(getfield("itemgroup", index, 2, 0))
-end
+if not modules then modules = { } end modules ['strc-itm'] = {
+ version = 1.001,
+ comment = "companion to strc-itm.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local structures = structures
+local itemgroups = structures.itemgroups
+local jobpasses = job.passes
+
+local setfield = jobpasses.save
+local getfield = jobpasses.getfield
+
+function itemgroups.register(name,nofitems,maxwidth)
+ setfield("itemgroup", { nofitems, maxwidth })
+end
+
+function itemgroups.nofitems(name,index)
+ return getfield("itemgroup", index, 1, 0)
+end
+
+function itemgroups.maxwidth(name,index)
+ return getfield("itemgroup", index, 2, 0)
+end
+
+-- interface (might become counter/dimension)
+
+commands.registeritemgroup = itemgroups.register
+
+function commands.nofitems(name,index)
+ context(getfield("itemgroup", index, 1, 0))
+end
+
+function commands.maxitemwidth(name,index)
+ context(getfield("itemgroup", index, 2, 0))
+end
diff --git a/tex/context/base/strc-lev.lua b/tex/context/base/strc-lev.lua
index 016aa2039..50a63c938 100644
--- a/tex/context/base/strc-lev.lua
+++ b/tex/context/base/strc-lev.lua
@@ -1,51 +1,51 @@
-if not modules then modules = { } end modules ['strc-lev'] = {
- version = 1.001,
- comment = "companion to strc-lev.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local insert, remove = table.insert, table.remove
-
-local sections = structures.sections
-local default = interfaces.variables.default
-
-sections.levels = sections.levels or { }
-
-local level, levels, categories = 0, sections.levels, { }
-
-storage.register("structures/sections/levels", levels, "structures.sections.levels")
-
-local f_two_colon = string.formatters["%s:%s"]
-
-function commands.definesectionlevels(category,list)
- levels[category] = utilities.parsers.settings_to_array(list)
-end
-
-function commands.startsectionlevel(category)
- category = category ~= "" and category or default
- level = level + 1
- local lc = levels[category]
- if not lc or level > #lc then
- context.nostarthead { f_two_colon(category,level) }
- else
- context.dostarthead { lc[level] }
- end
- insert(categories,category)
-end
-
-function commands.stopsectionlevel()
- local category = remove(categories)
- if category then
- local lc = levels[category]
- if not lc or level > #lc then
- context.nostophead { f_two_colon(category,level) }
- else
- context.dostophead { lc[level] }
- end
- level = level - 1
- else
- -- error
- end
-end
+if not modules then modules = { } end modules ['strc-lev'] = {
+ version = 1.001,
+ comment = "companion to strc-lev.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local insert, remove = table.insert, table.remove
+
+local sections = structures.sections
+local default = interfaces.variables.default
+
+sections.levels = sections.levels or { }
+
+local level, levels, categories = 0, sections.levels, { }
+
+storage.register("structures/sections/levels", levels, "structures.sections.levels")
+
+local f_two_colon = string.formatters["%s:%s"]
+
+function commands.definesectionlevels(category,list)
+ levels[category] = utilities.parsers.settings_to_array(list)
+end
+
+function commands.startsectionlevel(category)
+ category = category ~= "" and category or default
+ level = level + 1
+ local lc = levels[category]
+ if not lc or level > #lc then
+ context.nostarthead { f_two_colon(category,level) }
+ else
+ context.dostarthead { lc[level] }
+ end
+ insert(categories,category)
+end
+
+function commands.stopsectionlevel()
+ local category = remove(categories)
+ if category then
+ local lc = levels[category]
+ if not lc or level > #lc then
+ context.nostophead { f_two_colon(category,level) }
+ else
+ context.dostophead { lc[level] }
+ end
+ level = level - 1
+ else
+ -- error
+ end
+end
diff --git a/tex/context/base/strc-lst.lua b/tex/context/base/strc-lst.lua
index 2395abb62..ad7dc0f54 100644
--- a/tex/context/base/strc-lst.lua
+++ b/tex/context/base/strc-lst.lua
@@ -1,845 +1,845 @@
-if not modules then modules = { } end modules ['strc-lst'] = {
- version = 1.001,
- comment = "companion to strc-lst.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- when all datastructures are stable a packer will be added which will
--- bring down memory consumption a bit; we can use for instance a pagenumber,
--- section, metadata cache (internal then has to move up one level) or a
--- shared cache [we can use a fast and stupid serializer]
-
--- todo: tag entry in list is crap
---
--- move more to commands
-
-local format, gmatch, gsub = string.format, string.gmatch, string.gsub
-local tonumber = tonumber
-local texcount = tex.count
-local concat, insert, remove = table.concat, table.insert, table.remove
-local lpegmatch = lpeg.match
-local simple_hash_to_string, settings_to_hash = utilities.parsers.simple_hash_to_string, utilities.parsers.settings_to_hash
-local allocate, checked = utilities.storage.allocate, utilities.storage.checked
-
-local trace_lists = false trackers.register("structures.lists", function(v) trace_lists = v end)
-
-local report_lists = logs.reporter("structure","lists")
-
-local structures = structures
-local lists = structures.lists
-local sections = structures.sections
-local helpers = structures.helpers
-local documents = structures.documents
-local pages = structures.pages
-local tags = structures.tags
-local references = structures.references
-
-local collected = allocate()
-local tobesaved = allocate()
-local cached = allocate()
-local pushed = allocate()
-
-lists.collected = collected
-lists.tobesaved = tobesaved
-
-lists.enhancers = lists.enhancers or { }
-lists.internals = allocate(lists.internals or { }) -- to be checked
-lists.ordered = allocate(lists.ordered or { }) -- to be checked
-lists.cached = cached
-lists.pushed = pushed
-
-references.specials = references.specials or { }
-
-local variables = interfaces.variables
-local matchingtilldepth = sections.matchingtilldepth
-local numberatdepth = sections.numberatdepth
-
--- -- -- -- -- --
-
-local function zerostrippedconcat(t,separator) -- for the moment not public
- local f, l = 1, #t
- for i=f,l do
- if t[i] == 0 then
- f = f + 1
- end
- end
- for i=l,f,-1 do
- if t[i] == 0 then
- l = l - 1
- end
- end
- return concat(t,separator,f,l)
-end
-
--- -- -- -- -- --
-
-local function initializer()
- -- create a cross reference between internal references
- -- and list entries
- local collected = lists.collected
- local internals = checked(references.internals)
- local ordered = lists.ordered
- for i=1,#collected do
- local c = collected[i]
- local m = c.metadata
- local r = c.references
- if m then
- -- access by internal reference
- local internal = r and r.internal
- if internal then
- internals[internal] = c
- end
- -- access by order in list
- local kind, name = m.kind, m.name
- if kind and name then
- local ok = ordered[kind]
- if ok then
- local on = ok[name]
- if on then
- on[#on+1] = c
- else
- ok[name] = { c }
- end
- else
- ordered[kind] = { [name] = { c } }
- end
- end
- end
- if r then
- r.listindex = i -- handy to have
- end
- end
-end
-
-job.register('structures.lists.collected', tobesaved, initializer)
-
-local groupindices = table.setmetatableindex("table")
-
-function lists.groupindex(name,group)
- local groupindex = groupindices[name]
- return groupindex and groupindex[group] or 0
-end
-
-function lists.addto(t)
- local m = t.metadata
- local u = t.userdata
- if u and type(u) == "string" then
- t.userdata = helpers.touserdata(u) -- nicer at the tex end
- end
- local numberdata = t.numberdata
- local group = numberdata and numberdata.group
- if not group then
- -- forget about it
- elseif group == "" then
- group, numberdata.group = nil, nil
- else
- local groupindex = groupindices[m.name][group]
- if groupindex then
- numberdata.numbers = cached[groupindex].numberdata.numbers
- end
- end
- local r = t.references
- local i = r and r.internal or 0 -- brrr
- local p = pushed[i]
- if not p then
- p = #cached + 1
- cached[p] = helpers.simplify(t)
- pushed[i] = p
- r.listindex = p
- end
- local setcomponent = references.setcomponent
- if setcomponent then
- setcomponent(t) -- might move to the tex end
- end
- if group then
- groupindices[m.name][group] = p
- end
- return p
-end
-
-function lists.discard(n)
- n = tonumber(n)
- if not n then
- -- maybe an error message
- elseif n == #cached then
- cached[n] = nil
- n = n -1
- while n > 0 and cached[n] == false do
- cached[n] = nil -- collect garbage
- n = n - 1
- end
- else
- cached[n] = false
- end
-end
-
-function lists.iscached(n)
- return cached[tonumber(n)]
-end
-
--- this is the main pagenumber enhancer
-
-function lists.enhance(n)
- -- todo: symbolic names for counters
- local l = cached[n]
- if l then
- local metadata = l.metadata
- local references = l.references
- --
- l.directives = nil -- might change
- -- save in the right order (happens at shipout)
- lists.tobesaved[#lists.tobesaved+1] = l
- -- default enhancer (cross referencing)
- references.realpage = texcount.realpageno
- -- tags
- local kind = metadata.kind
- local name = metadata.name
- if references then
- -- is this used ?
- local tag = tags.getid(kind,name)
- if tag and tag ~= "?" then
- references.tag = tag
- end
- --~ references.listindex = n
- end
- -- specific enhancer (kind of obsolete)
- local enhancer = kind and lists.enhancers[kind]
- if enhancer then
- enhancer(l)
- end
- return l
- end
-end
-
--- we can use level instead but we can also decide to remove level from the metadata
-
-local nesting = { }
-
-function lists.pushnesting(i)
- local parent = lists.result[i]
- local name = parent.metadata.name
- local numberdata = parent and parent.numberdata
- local numbers = numberdata and numberdata.numbers
- local number = numbers and numbers[sections.getlevel(name)] or 0
- insert(nesting, { number = number, name = name, result = lists.result, parent = parent })
-end
-
-function lists.popnesting()
- local old = remove(nesting)
- lists.result = old.result
-end
-
--- will be split
-
--- Historically we had blocks but in the mkiv approach that could as well be a level
--- which would simplify things a bit.
-
-local splitter = lpeg.splitat(":")
-
--- this will become filtercollected(specification) and then we'll also have sectionblock as key
-
-local sorters = {
- [variables.command] = function(a,b)
- if a.metadata.kind == "command" or b.metadata.kind == "command" then
- return a.references.internal < b.references.internal
- else
- return a.references.order < b.references.order
- end
- end,
- [variables.all] = function(a,b)
- return a.references.internal < b.references.internal
- end,
-}
-
--- some day soon we will pass a table .. also split the function
-
-local function filtercollected(names, criterium, number, collected, forced, nested, sortorder) -- names is hash or string
- local numbers, depth = documents.data.numbers, documents.data.depth
- local result, nofresult, detail = { }, 0, nil
- local block = false -- all
- criterium = gsub(criterium or ""," ","") -- not needed
- -- new, will be applied stepwise
- local wantedblock, wantedcriterium = lpegmatch(splitter,criterium) -- block:criterium
- if wantedblock == "" or wantedblock == variables.all or wantedblock == variables.text then
- criterium = wantedcriterium ~= "" and wantedcriterium or criterium
- elseif not wantedcriterium then
- block = documents.data.block
- else
- block, criterium = wantedblock, wantedcriterium
- end
- if block == "" then
- block = false
- end
--- print(">>",block,criterium)
- --
- forced = forced or { } -- todo: also on other branched, for the moment only needed for bookmarks
- if type(names) == "string" then
- names = settings_to_hash(names)
- end
- local all = not next(names) or names[variables.all] or false
- if trace_lists then
- report_lists("filtering names %a, criterium %a, block %a, number %a",names,criterium,block or "*",number)
- end
- if criterium == variables.intro then
- -- special case, no structure yet
- for i=1,#collected do
- local v = collected[i]
- local r = v.references
- if r and r.section == 0 then
- nofresult = nofresult + 1
- result[nofresult] = v
- end
- end
- elseif all or criterium == variables.all or criterium == variables.text then
- for i=1,#collected do
- local v = collected[i]
- local r = v.references
- if r and (not block or not r.block or block == r.block) then
- local metadata = v.metadata
- if metadata then
- local name = metadata.name or false
- local sectionnumber = (r.section == 0) or sections.collected[r.section]
- if forced[name] or (sectionnumber and not metadata.nolist and (all or names[name])) then -- and not sectionnumber.hidenumber then
- nofresult = nofresult + 1
- result[nofresult] = v
- end
- end
- end
- end
- elseif criterium == variables.current then
- if depth == 0 then
- return filtercollected(names,variables.intro,number,collected,forced,false,sortorder)
- else
- for i=1,#collected do
- local v = collected[i]
- local r = v.references
- if r and (not block or not r.block or block == r.block) then
- local sectionnumber = sections.collected[r.section]
- if sectionnumber then -- and not sectionnumber.hidenumber then
- local cnumbers = sectionnumber.numbers
- local metadata = v.metadata
- if cnumbers then
- if metadata and not metadata.nolist and (all or names[metadata.name or false]) and #cnumbers > depth then
- local ok = true
- for d=1,depth do
- local cnd = cnumbers[d]
- if not (cnd == 0 or cnd == numbers[d]) then
- ok = false
- break
- end
- end
- if ok then
- nofresult = nofresult + 1
- result[nofresult] = v
- end
- end
- end
- end
- end
- end
- end
- elseif criterium == variables.here then
- -- this is quite dirty ... as cnumbers is not sparse we can misuse #cnumbers
- if depth == 0 then
- return filtercollected(names,variables.intro,number,collected,forced,false,sortorder)
- else
- for i=1,#collected do
- local v = collected[i]
- local r = v.references
- if r then -- and (not block or not r.block or block == r.block) then
- local sectionnumber = sections.collected[r.section]
- if sectionnumber then -- and not sectionnumber.hidenumber then
- local cnumbers = sectionnumber.numbers
- local metadata = v.metadata
- if cnumbers then
- if metadata and not metadata.nolist and (all or names[metadata.name or false]) and #cnumbers >= depth then
- local ok = true
- for d=1,depth do
- local cnd = cnumbers[d]
- if not (cnd == 0 or cnd == numbers[d]) then
- ok = false
- break
- end
- end
- if ok then
- nofresult = nofresult + 1
- result[nofresult] = v
- end
- end
- end
- end
- end
- end
- end
- elseif criterium == variables.previous then
- if depth == 0 then
- return filtercollected(names,variables.intro,number,collected,forced,false,sortorder)
- else
- for i=1,#collected do
- local v = collected[i]
- local r = v.references
- if r and (not block or not r.block or block == r.block) then
- local sectionnumber = sections.collected[r.section]
- if sectionnumber then -- and not sectionnumber.hidenumber then
- local cnumbers = sectionnumber.numbers
- local metadata = v.metadata
- if cnumbers then
- if metadata and not metadata.nolist and (all or names[metadata.name or false]) and #cnumbers >= depth then
- local ok = true
- for d=1,depth-1 do
- local cnd = cnumbers[d]
- if not (cnd == 0 or cnd == numbers[d]) then
- ok = false
- break
- end
- end
- if ok then
- nofresult = nofresult + 1
- result[nofresult] = v
- end
- end
- end
- end
- end
- end
- end
- elseif criterium == variables["local"] then -- not yet ok
- local nested = nesting[#nesting]
- if nested then
- return filtercollected(names,nested.name,nested.number,collected,forced,nested,sortorder)
- elseif sections.autodepth(documents.data.numbers) == 0 then
- return filtercollected(names,variables.all,number,collected,forced,false,sortorder)
- else
- return filtercollected(names,variables.current,number,collected,forced,false,sortorder)
- end
- elseif criterium == variables.component then
- -- special case, no structure yet
- local component = resolvers.jobs.currentcomponent() or ""
- if component ~= "" then
- for i=1,#collected do
- local v = collected[i]
- local r = v.references
- local m = v.metadata
- if r and r.component == component and (m and names[m.name] or all) then
- nofresult = nofresult + 1
- result[nofresult] = v
- end
- end
- end
- else -- sectionname, number
- -- not the same as register
- local depth = sections.getlevel(criterium)
- local number = tonumber(number) or numberatdepth(depth) or 0
- if trace_lists then
- local t = sections.numbers()
- detail = format("depth %s, number %s, numbers %s, startset %s",depth,number,(#t>0 and concat(t,".",1,depth)) or "?",#collected)
- end
- if number > 0 then
- local pnumbers = nil
- local pblock = block
- local parent = nested and nested.parent
- if parent then
- pnumbers = parent.numberdata.numbers or pnumbers -- so local as well as nested
- pblock = parent.references.block or pblock
- end
- for i=1,#collected do
- local v = collected[i]
- local r = v.references
- if r and (not block or not r.block or pblock == r.block) then
- local sectionnumber = sections.collected[r.section]
- if sectionnumber then
- local metadata = v.metadata
- local cnumbers = sectionnumber.numbers
- if cnumbers then
- if (all or names[metadata.name or false]) and #cnumbers >= depth and matchingtilldepth(depth,cnumbers,pnumbers) then
- nofresult = nofresult + 1
- result[nofresult] = v
- end
- end
- end
- end
- end
- end
- end
- if trace_lists then
- report_lists("criterium %a, block %a, found %a, detail %a",criterium,block or "*",#result,detail)
- end
-
- if sortorder then -- experiment
- local sorter = sorters[sortorder]
- if sorter then
- if trace_lists then
- report_lists("sorting list using method %a",sortorder)
- end
- for i=1,#result do
- result[i].references.order = i
- end
- table.sort(result,sorter)
- end
- end
-
- return result
-end
-
-lists.filtercollected = filtercollected
-
-function lists.filter(specification)
- return filtercollected(
- specification.names,
- specification.criterium,
- specification.number,
- lists.collected,
- specification.forced,
- false,
- specification.order
- )
-end
-
-lists.result = { }
-
-function lists.process(specification)
- lists.result = lists.filter(specification)
- local specials = utilities.parsers.settings_to_hash(specification.extras or "")
- specials = next(specials) and specials or nil
- for i=1,#lists.result do
- local r = lists.result[i]
- local m = r.metadata
- local s = specials and r.numberdata and specials[zerostrippedconcat(r.numberdata.numbers,".")] or ""
- context.strclistsentryprocess(m.name,m.kind,i,s)
- end
-end
-
-function lists.analyze(specification)
- lists.result = lists.filter(specification)
-end
-
-function lists.userdata(name,r,tag) -- to tex (todo: xml)
- local result = lists.result[r]
- if result then
- local userdata, metadata = result.userdata, result.metadata
- local str = userdata and userdata[tag]
- if str then
- return str, metadata
- end
- end
-end
-
-function lists.uservalue(name,r,tag,default) -- to lua
- local str = lists.result[r]
- str = str and str.userdata
- str = str and str[tag]
- return str or default
-end
-
-function lists.size()
- return #lists.result
-end
-
-function lists.location(n)
- local l = lists.result[n]
- return l and l.references.internal or n
-end
-
-function lists.label(n,default)
- local l = lists.result[n]
- local t = l.titledata
- return t and t.label or default or ""
-end
-
-function lists.sectionnumber(name,n,spec)
- local data = lists.result[n]
- local sectiondata = sections.collected[data.references.section]
- -- hm, prefixnumber?
- sections.typesetnumber(sectiondata,"prefix",spec,sectiondata) -- data happens to contain the spec too
-end
-
--- some basics (todo: helpers for pages)
-
-function lists.title(name,n,tag) -- tag becomes obsolete
- local data = lists.result[n]
- if data then
- local titledata = data.titledata
- if titledata then
- helpers.title(titledata[tag] or titledata.list or titledata.title or "",data.metadata)
- end
- end
-end
-
-function lists.hastitledata(name,n,tag)
- local data = cached[tonumber(n)]
- if data then
- local titledata = data.titledata
- if titledata then
- return (titledata[tag] or titledata.title or "") == ""
- end
- end
- return false
-end
-
-function lists.haspagedata(name,n)
- local data = lists.result[n]
- if data then
- local references = data.references
- if references and references.realpage then -- or references.pagedata
- return true
- end
- end
- return false
-end
-
-function lists.hasnumberdata(name,n)
- local data = lists.result[n]
- if data then
- local numberdata = data.numberdata
- if numberdata and not numberdata.hidenumber then -- th ehide number is true
- return true
- end
- end
- return false
-end
-
-function lists.prefix(name,n,spec)
- helpers.prefix(lists.result[n],spec)
-end
-
-function lists.page(name,n,pagespec)
- helpers.page(lists.result[n],pagespec)
-end
-
-function lists.prefixedpage(name,n,prefixspec,pagespec)
- helpers.prefixpage(lists.result[n],prefixspec,pagespec)
-end
-
-function lists.realpage(name,n)
- local data = lists.result[n]
- if data then
- local references = data.references
- return references and references.realpage or 0
- else
- return 0
- end
-end
-
--- numbers stored in entry.numberdata + entry.numberprefix
-
-function lists.number(name,n,spec)
- local data = lists.result[n]
- if data then
- local numberdata = data.numberdata
- if numberdata then
- sections.typesetnumber(numberdata,"number",spec or false,numberdata or false)
- end
- end
-end
-
-function lists.prefixednumber(name,n,prefixspec,numberspec)
- local data = lists.result[n]
- if data then
- helpers.prefix(data,prefixspec)
- local numberdata = data.numberdata
- if numberdata then
- sections.typesetnumber(numberdata,"number",numberspec or false,numberdata or false)
- end
- end
-end
-
--- todo, do this in references namespace ordered instead (this is an experiment)
---
--- also see lpdf-ano (maybe move this there)
-
-local splitter = lpeg.splitat(":")
-
-function references.specials.order(var,actions) -- references.specials !
- local operation = var.operation
- if operation then
- local kind, name, n = lpegmatch(splitter,operation)
- local order = lists.ordered[kind]
- order = order and order[name]
- local v = order[tonumber(n)]
- local r = v and v.references.realpage
- if r then
- actions.realpage = r
- var.operation = r -- brrr, but test anyway
- return references.specials.page(var,actions)
- end
- end
-end
-
--- interface (maybe strclistpush etc)
-
-commands.pushlist = lists.pushnesting
-commands.poplist = lists.popnesting
-commands.enhancelist = lists.enhance
-commands.processlist = lists.process
-commands.analyzelist = lists.analyze
-commands.listtitle = lists.title
-commands.listprefixednumber = lists.prefixednumber
-commands.listprefixedpage = lists.prefixedpage
-
-
-function commands.addtolist (...) context(lists.addto (...)) end -- we could use variables instead of print
-function commands.listsize (...) context(lists.size (...)) end
-function commands.listlocation (...) context(lists.location (...)) end
-function commands.listlabel (...) context(lists.label (...)) end
-function commands.listrealpage (...) context(lists.realpage (...)) end
-function commands.listgroupindex(...) context(lists.groupindex(...)) end
-
-function commands.listuserdata(...)
- local str, metadata = lists.userdata(...)
- if str then
- -- local catcodes = metadata and metadata.catcodes
- -- if catcodes then
- -- context.sprint(catcodes,str)
- -- else
- -- context(str)
- -- end
- helpers.title(str,metadata)
- end
-end
-
--- we could also set variables .. names will change (when this module is done)
--- maybe strc_lists_savedtitle etc
-
-function commands.doiflisthastitleelse (...) commands.doifelse(lists.hastitledata (...)) end
-function commands.doiflisthaspageelse (...) commands.doifelse(lists.haspagedata (...)) end
-function commands.doiflisthasnumberelse(...) commands.doifelse(lists.hasnumberdata(...)) end
-function commands.doiflisthasentry (n) commands.doifelse(lists.iscached (n )) end
-
-function commands.savedlistnumber(name,n)
- local data = cached[tonumber(n)]
- if data then
- local numberdata = data.numberdata
- if numberdata then
- sections.typesetnumber(numberdata,"number",numberdata or false)
- end
- end
-end
-
-function commands.savedlisttitle(name,n,tag)
- local data = cached[tonumber(n)]
- if data then
- local titledata = data.titledata
- if titledata then
- helpers.title(titledata[tag] or titledata.title or "",data.metadata)
- end
- end
-end
-
--- function commands.savedlistprefixednumber(name,n)
--- local data = cached[tonumber(n)]
--- if data then
--- local numberdata = data.numberdata
--- if numberdata then
--- helpers.prefix(data,data.prefixdata)
--- sections.typesetnumber(numberdata,"number",numberdata or false)
--- end
--- end
--- end
-
-if not lists.reordered then
- function lists.reordered(data)
- return data.numberdata
- end
-end
-
-function commands.savedlistprefixednumber(name,n)
- local data = cached[tonumber(n)]
- if data then
- local numberdata = lists.reordered(data)
- if numberdata then
- helpers.prefix(data,data.prefixdata)
- sections.typesetnumber(numberdata,"number",numberdata or false)
- end
- end
-end
-
-commands.discardfromlist = lists.discard
-
--- new and experimental and therefore off by default
-
-local sort, setmetatableindex = table.sort, table.setmetatableindex
-
-lists.autoreorder = false -- true
-
-local function addlevel(t,k)
- local v = { }
- setmetatableindex(v,function(t,k)
- local v = { }
- t[k] = v
- return v
- end)
- t[k] = v
- return v
-end
-
-local internals = setmetatableindex({ }, function(t,k)
-
- local sublists = setmetatableindex({ },addlevel)
-
- local collected = lists.collected or { }
-
- for i=1,#collected do
- local entry = collected[i]
- local numberdata = entry.numberdata
- if numberdata then
- local metadata = entry.metadata
- if metadata then
- local references = entry.references
- if references then
- local kind = metadata.kind
- local name = numberdata.counter or metadata.name
- local internal = references.internal
- if kind and name and internal then
- local sublist = sublists[kind][name]
- sublist[#sublist + 1] = { internal, numberdata }
- end
- end
- end
- end
- end
-
- for k, v in next, sublists do
- for k, v in next, v do
- local tmp = { }
- for i=1,#v do
- tmp[i] = v[i]
- end
- sort(v,function(a,b) return a[1] < b[1] end)
- for i=1,#v do
- t[v[i][1]] = tmp[i][2]
- end
- end
- end
-
- setmetatableindex(t,nil)
-
- return t[k]
-
-end)
-
-function lists.reordered(entry)
- local numberdata = entry.numberdata
- if lists.autoreorder then
- if numberdata then
- local metadata = entry.metadata
- if metadata then
- local references = entry.references
- if references then
- local kind = metadata.kind
- local name = numberdata.counter or metadata.name
- local internal = references.internal
- if kind and name and internal then
- return internals[internal] or numberdata
- end
- end
- end
- end
- else
- function lists.reordered(entry)
- return entry.numberdata
- end
- end
- return numberdata
-end
+if not modules then modules = { } end modules ['strc-lst'] = {
+ version = 1.001,
+ comment = "companion to strc-lst.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- when all datastructures are stable a packer will be added which will
+-- bring down memory consumption a bit; we can use for instance a pagenumber,
+-- section, metadata cache (internal then has to move up one level) or a
+-- shared cache [we can use a fast and stupid serializer]
+
+-- todo: tag entry in list is crap
+--
+-- move more to commands
+
+local format, gmatch, gsub = string.format, string.gmatch, string.gsub
+local tonumber = tonumber
+local texcount = tex.count
+local concat, insert, remove = table.concat, table.insert, table.remove
+local lpegmatch = lpeg.match
+local simple_hash_to_string, settings_to_hash = utilities.parsers.simple_hash_to_string, utilities.parsers.settings_to_hash
+local allocate, checked = utilities.storage.allocate, utilities.storage.checked
+
+local trace_lists = false trackers.register("structures.lists", function(v) trace_lists = v end)
+
+local report_lists = logs.reporter("structure","lists")
+
+local structures = structures
+local lists = structures.lists
+local sections = structures.sections
+local helpers = structures.helpers
+local documents = structures.documents
+local pages = structures.pages
+local tags = structures.tags
+local references = structures.references
+
+local collected = allocate()
+local tobesaved = allocate()
+local cached = allocate()
+local pushed = allocate()
+
+lists.collected = collected
+lists.tobesaved = tobesaved
+
+lists.enhancers = lists.enhancers or { }
+lists.internals = allocate(lists.internals or { }) -- to be checked
+lists.ordered = allocate(lists.ordered or { }) -- to be checked
+lists.cached = cached
+lists.pushed = pushed
+
+references.specials = references.specials or { }
+
+local variables = interfaces.variables
+local matchingtilldepth = sections.matchingtilldepth
+local numberatdepth = sections.numberatdepth
+
+-- -- -- -- -- --
+
+local function zerostrippedconcat(t,separator) -- for the moment not public
+ local f, l = 1, #t
+ for i=f,l do
+ if t[i] == 0 then
+ f = f + 1
+ end
+ end
+ for i=l,f,-1 do
+ if t[i] == 0 then
+ l = l - 1
+ end
+ end
+ return concat(t,separator,f,l)
+end
+
+-- -- -- -- -- --
+
+local function initializer()
+ -- create a cross reference between internal references
+ -- and list entries
+ local collected = lists.collected
+ local internals = checked(references.internals)
+ local ordered = lists.ordered
+ for i=1,#collected do
+ local c = collected[i]
+ local m = c.metadata
+ local r = c.references
+ if m then
+ -- access by internal reference
+ local internal = r and r.internal
+ if internal then
+ internals[internal] = c
+ end
+ -- access by order in list
+ local kind, name = m.kind, m.name
+ if kind and name then
+ local ok = ordered[kind]
+ if ok then
+ local on = ok[name]
+ if on then
+ on[#on+1] = c
+ else
+ ok[name] = { c }
+ end
+ else
+ ordered[kind] = { [name] = { c } }
+ end
+ end
+ end
+ if r then
+ r.listindex = i -- handy to have
+ end
+ end
+end
+
+job.register('structures.lists.collected', tobesaved, initializer)
+
+local groupindices = table.setmetatableindex("table")
+
+function lists.groupindex(name,group)
+ local groupindex = groupindices[name]
+ return groupindex and groupindex[group] or 0
+end
+
+function lists.addto(t)
+ local m = t.metadata
+ local u = t.userdata
+ if u and type(u) == "string" then
+ t.userdata = helpers.touserdata(u) -- nicer at the tex end
+ end
+ local numberdata = t.numberdata
+ local group = numberdata and numberdata.group
+ if not group then
+ -- forget about it
+ elseif group == "" then
+ group, numberdata.group = nil, nil
+ else
+ local groupindex = groupindices[m.name][group]
+ if groupindex then
+ numberdata.numbers = cached[groupindex].numberdata.numbers
+ end
+ end
+ local r = t.references
+ local i = r and r.internal or 0 -- brrr
+ local p = pushed[i]
+ if not p then
+ p = #cached + 1
+ cached[p] = helpers.simplify(t)
+ pushed[i] = p
+ r.listindex = p
+ end
+ local setcomponent = references.setcomponent
+ if setcomponent then
+ setcomponent(t) -- might move to the tex end
+ end
+ if group then
+ groupindices[m.name][group] = p
+ end
+ return p
+end
+
+function lists.discard(n)
+ n = tonumber(n)
+ if not n then
+ -- maybe an error message
+ elseif n == #cached then
+ cached[n] = nil
+ n = n -1
+ while n > 0 and cached[n] == false do
+ cached[n] = nil -- collect garbage
+ n = n - 1
+ end
+ else
+ cached[n] = false
+ end
+end
+
+function lists.iscached(n)
+ return cached[tonumber(n)]
+end
+
+-- this is the main pagenumber enhancer
+
+function lists.enhance(n)
+ -- todo: symbolic names for counters
+ local l = cached[n]
+ if l then
+ local metadata = l.metadata
+ local references = l.references
+ --
+ l.directives = nil -- might change
+ -- save in the right order (happens at shipout)
+ lists.tobesaved[#lists.tobesaved+1] = l
+ -- default enhancer (cross referencing)
+ references.realpage = texcount.realpageno
+ -- tags
+ local kind = metadata.kind
+ local name = metadata.name
+ if references then
+ -- is this used ?
+ local tag = tags.getid(kind,name)
+ if tag and tag ~= "?" then
+ references.tag = tag
+ end
+ --~ references.listindex = n
+ end
+ -- specific enhancer (kind of obsolete)
+ local enhancer = kind and lists.enhancers[kind]
+ if enhancer then
+ enhancer(l)
+ end
+ return l
+ end
+end
+
+-- we can use level instead but we can also decide to remove level from the metadata
+
+local nesting = { }
+
+function lists.pushnesting(i)
+ local parent = lists.result[i]
+ local name = parent.metadata.name
+ local numberdata = parent and parent.numberdata
+ local numbers = numberdata and numberdata.numbers
+ local number = numbers and numbers[sections.getlevel(name)] or 0
+ insert(nesting, { number = number, name = name, result = lists.result, parent = parent })
+end
+
+function lists.popnesting()
+ local old = remove(nesting)
+ lists.result = old.result
+end
+
+-- will be split
+
+-- Historically we had blocks but in the mkiv approach that could as well be a level
+-- which would simplify things a bit.
+
+local splitter = lpeg.splitat(":")
+
+-- this will become filtercollected(specification) and then we'll also have sectionblock as key
+
+local sorters = {
+ [variables.command] = function(a,b)
+ if a.metadata.kind == "command" or b.metadata.kind == "command" then
+ return a.references.internal < b.references.internal
+ else
+ return a.references.order < b.references.order
+ end
+ end,
+ [variables.all] = function(a,b)
+ return a.references.internal < b.references.internal
+ end,
+}
+
+-- some day soon we will pass a table .. also split the function
+
+local function filtercollected(names, criterium, number, collected, forced, nested, sortorder) -- names is hash or string
+ local numbers, depth = documents.data.numbers, documents.data.depth
+ local result, nofresult, detail = { }, 0, nil
+ local block = false -- all
+ criterium = gsub(criterium or ""," ","") -- not needed
+ -- new, will be applied stepwise
+ local wantedblock, wantedcriterium = lpegmatch(splitter,criterium) -- block:criterium
+ if wantedblock == "" or wantedblock == variables.all or wantedblock == variables.text then
+ criterium = wantedcriterium ~= "" and wantedcriterium or criterium
+ elseif not wantedcriterium then
+ block = documents.data.block
+ else
+ block, criterium = wantedblock, wantedcriterium
+ end
+ if block == "" then
+ block = false
+ end
+-- print(">>",block,criterium)
+ --
+ forced = forced or { } -- todo: also on other branched, for the moment only needed for bookmarks
+ if type(names) == "string" then
+ names = settings_to_hash(names)
+ end
+ local all = not next(names) or names[variables.all] or false
+ if trace_lists then
+ report_lists("filtering names %a, criterium %a, block %a, number %a",names,criterium,block or "*",number)
+ end
+ if criterium == variables.intro then
+ -- special case, no structure yet
+ for i=1,#collected do
+ local v = collected[i]
+ local r = v.references
+ if r and r.section == 0 then
+ nofresult = nofresult + 1
+ result[nofresult] = v
+ end
+ end
+ elseif all or criterium == variables.all or criterium == variables.text then
+ for i=1,#collected do
+ local v = collected[i]
+ local r = v.references
+ if r and (not block or not r.block or block == r.block) then
+ local metadata = v.metadata
+ if metadata then
+ local name = metadata.name or false
+ local sectionnumber = (r.section == 0) or sections.collected[r.section]
+ if forced[name] or (sectionnumber and not metadata.nolist and (all or names[name])) then -- and not sectionnumber.hidenumber then
+ nofresult = nofresult + 1
+ result[nofresult] = v
+ end
+ end
+ end
+ end
+ elseif criterium == variables.current then
+ if depth == 0 then
+ return filtercollected(names,variables.intro,number,collected,forced,false,sortorder)
+ else
+ for i=1,#collected do
+ local v = collected[i]
+ local r = v.references
+ if r and (not block or not r.block or block == r.block) then
+ local sectionnumber = sections.collected[r.section]
+ if sectionnumber then -- and not sectionnumber.hidenumber then
+ local cnumbers = sectionnumber.numbers
+ local metadata = v.metadata
+ if cnumbers then
+ if metadata and not metadata.nolist and (all or names[metadata.name or false]) and #cnumbers > depth then
+ local ok = true
+ for d=1,depth do
+ local cnd = cnumbers[d]
+ if not (cnd == 0 or cnd == numbers[d]) then
+ ok = false
+ break
+ end
+ end
+ if ok then
+ nofresult = nofresult + 1
+ result[nofresult] = v
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ elseif criterium == variables.here then
+ -- this is quite dirty ... as cnumbers is not sparse we can misuse #cnumbers
+ if depth == 0 then
+ return filtercollected(names,variables.intro,number,collected,forced,false,sortorder)
+ else
+ for i=1,#collected do
+ local v = collected[i]
+ local r = v.references
+ if r then -- and (not block or not r.block or block == r.block) then
+ local sectionnumber = sections.collected[r.section]
+ if sectionnumber then -- and not sectionnumber.hidenumber then
+ local cnumbers = sectionnumber.numbers
+ local metadata = v.metadata
+ if cnumbers then
+ if metadata and not metadata.nolist and (all or names[metadata.name or false]) and #cnumbers >= depth then
+ local ok = true
+ for d=1,depth do
+ local cnd = cnumbers[d]
+ if not (cnd == 0 or cnd == numbers[d]) then
+ ok = false
+ break
+ end
+ end
+ if ok then
+ nofresult = nofresult + 1
+ result[nofresult] = v
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ elseif criterium == variables.previous then
+ if depth == 0 then
+ return filtercollected(names,variables.intro,number,collected,forced,false,sortorder)
+ else
+ for i=1,#collected do
+ local v = collected[i]
+ local r = v.references
+ if r and (not block or not r.block or block == r.block) then
+ local sectionnumber = sections.collected[r.section]
+ if sectionnumber then -- and not sectionnumber.hidenumber then
+ local cnumbers = sectionnumber.numbers
+ local metadata = v.metadata
+ if cnumbers then
+ if metadata and not metadata.nolist and (all or names[metadata.name or false]) and #cnumbers >= depth then
+ local ok = true
+ for d=1,depth-1 do
+ local cnd = cnumbers[d]
+ if not (cnd == 0 or cnd == numbers[d]) then
+ ok = false
+ break
+ end
+ end
+ if ok then
+ nofresult = nofresult + 1
+ result[nofresult] = v
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ elseif criterium == variables["local"] then -- not yet ok
+ local nested = nesting[#nesting]
+ if nested then
+ return filtercollected(names,nested.name,nested.number,collected,forced,nested,sortorder)
+ elseif sections.autodepth(documents.data.numbers) == 0 then
+ return filtercollected(names,variables.all,number,collected,forced,false,sortorder)
+ else
+ return filtercollected(names,variables.current,number,collected,forced,false,sortorder)
+ end
+ elseif criterium == variables.component then
+ -- special case, no structure yet
+ local component = resolvers.jobs.currentcomponent() or ""
+ if component ~= "" then
+ for i=1,#collected do
+ local v = collected[i]
+ local r = v.references
+ local m = v.metadata
+ if r and r.component == component and (m and names[m.name] or all) then
+ nofresult = nofresult + 1
+ result[nofresult] = v
+ end
+ end
+ end
+ else -- sectionname, number
+ -- not the same as register
+ local depth = sections.getlevel(criterium)
+ local number = tonumber(number) or numberatdepth(depth) or 0
+ if trace_lists then
+ local t = sections.numbers()
+ detail = format("depth %s, number %s, numbers %s, startset %s",depth,number,(#t>0 and concat(t,".",1,depth)) or "?",#collected)
+ end
+ if number > 0 then
+ local pnumbers = nil
+ local pblock = block
+ local parent = nested and nested.parent
+ if parent then
+ pnumbers = parent.numberdata.numbers or pnumbers -- so local as well as nested
+ pblock = parent.references.block or pblock
+ end
+ for i=1,#collected do
+ local v = collected[i]
+ local r = v.references
+ if r and (not block or not r.block or pblock == r.block) then
+ local sectionnumber = sections.collected[r.section]
+ if sectionnumber then
+ local metadata = v.metadata
+ local cnumbers = sectionnumber.numbers
+ if cnumbers then
+ if (all or names[metadata.name or false]) and #cnumbers >= depth and matchingtilldepth(depth,cnumbers,pnumbers) then
+ nofresult = nofresult + 1
+ result[nofresult] = v
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ if trace_lists then
+ report_lists("criterium %a, block %a, found %a, detail %a",criterium,block or "*",#result,detail)
+ end
+
+ if sortorder then -- experiment
+ local sorter = sorters[sortorder]
+ if sorter then
+ if trace_lists then
+ report_lists("sorting list using method %a",sortorder)
+ end
+ for i=1,#result do
+ result[i].references.order = i
+ end
+ table.sort(result,sorter)
+ end
+ end
+
+ return result
+end
+
+lists.filtercollected = filtercollected
+
+function lists.filter(specification)
+ return filtercollected(
+ specification.names,
+ specification.criterium,
+ specification.number,
+ lists.collected,
+ specification.forced,
+ false,
+ specification.order
+ )
+end
+
+lists.result = { }
+
+function lists.process(specification)
+ lists.result = lists.filter(specification)
+ local specials = utilities.parsers.settings_to_hash(specification.extras or "")
+ specials = next(specials) and specials or nil
+ for i=1,#lists.result do
+ local r = lists.result[i]
+ local m = r.metadata
+ local s = specials and r.numberdata and specials[zerostrippedconcat(r.numberdata.numbers,".")] or ""
+ context.strclistsentryprocess(m.name,m.kind,i,s)
+ end
+end
+
+function lists.analyze(specification)
+ lists.result = lists.filter(specification)
+end
+
+function lists.userdata(name,r,tag) -- to tex (todo: xml)
+ local result = lists.result[r]
+ if result then
+ local userdata, metadata = result.userdata, result.metadata
+ local str = userdata and userdata[tag]
+ if str then
+ return str, metadata
+ end
+ end
+end
+
+function lists.uservalue(name,r,tag,default) -- to lua
+ local str = lists.result[r]
+ str = str and str.userdata
+ str = str and str[tag]
+ return str or default
+end
+
+function lists.size()
+ return #lists.result
+end
+
+function lists.location(n)
+ local l = lists.result[n]
+ return l and l.references.internal or n
+end
+
+function lists.label(n,default)
+ local l = lists.result[n]
+ local t = l.titledata
+ return t and t.label or default or ""
+end
+
+function lists.sectionnumber(name,n,spec)
+ local data = lists.result[n]
+ local sectiondata = sections.collected[data.references.section]
+ -- hm, prefixnumber?
+ sections.typesetnumber(sectiondata,"prefix",spec,sectiondata) -- data happens to contain the spec too
+end
+
+-- some basics (todo: helpers for pages)
+
+function lists.title(name,n,tag) -- tag becomes obsolete
+ local data = lists.result[n]
+ if data then
+ local titledata = data.titledata
+ if titledata then
+ helpers.title(titledata[tag] or titledata.list or titledata.title or "",data.metadata)
+ end
+ end
+end
+
+function lists.hastitledata(name,n,tag)
+ local data = cached[tonumber(n)]
+ if data then
+ local titledata = data.titledata
+ if titledata then
+ return (titledata[tag] or titledata.title or "") == ""
+ end
+ end
+ return false
+end
+
+function lists.haspagedata(name,n)
+ local data = lists.result[n]
+ if data then
+ local references = data.references
+ if references and references.realpage then -- or references.pagedata
+ return true
+ end
+ end
+ return false
+end
+
+function lists.hasnumberdata(name,n)
+ local data = lists.result[n]
+ if data then
+ local numberdata = data.numberdata
+ if numberdata and not numberdata.hidenumber then -- th ehide number is true
+ return true
+ end
+ end
+ return false
+end
+
+function lists.prefix(name,n,spec)
+ helpers.prefix(lists.result[n],spec)
+end
+
+function lists.page(name,n,pagespec)
+ helpers.page(lists.result[n],pagespec)
+end
+
+function lists.prefixedpage(name,n,prefixspec,pagespec)
+ helpers.prefixpage(lists.result[n],prefixspec,pagespec)
+end
+
+function lists.realpage(name,n)
+ local data = lists.result[n]
+ if data then
+ local references = data.references
+ return references and references.realpage or 0
+ else
+ return 0
+ end
+end
+
+-- numbers stored in entry.numberdata + entry.numberprefix
+
+function lists.number(name,n,spec)
+ local data = lists.result[n]
+ if data then
+ local numberdata = data.numberdata
+ if numberdata then
+ sections.typesetnumber(numberdata,"number",spec or false,numberdata or false)
+ end
+ end
+end
+
+function lists.prefixednumber(name,n,prefixspec,numberspec)
+ local data = lists.result[n]
+ if data then
+ helpers.prefix(data,prefixspec)
+ local numberdata = data.numberdata
+ if numberdata then
+ sections.typesetnumber(numberdata,"number",numberspec or false,numberdata or false)
+ end
+ end
+end
+
+-- todo, do this in references namespace ordered instead (this is an experiment)
+--
+-- also see lpdf-ano (maybe move this there)
+
+local splitter = lpeg.splitat(":")
+
+function references.specials.order(var,actions) -- references.specials !
+ local operation = var.operation
+ if operation then
+ local kind, name, n = lpegmatch(splitter,operation)
+ local order = lists.ordered[kind]
+ order = order and order[name]
+ local v = order[tonumber(n)]
+ local r = v and v.references.realpage
+ if r then
+ actions.realpage = r
+ var.operation = r -- brrr, but test anyway
+ return references.specials.page(var,actions)
+ end
+ end
+end
+
+-- interface (maybe strclistpush etc)
+
+commands.pushlist = lists.pushnesting
+commands.poplist = lists.popnesting
+commands.enhancelist = lists.enhance
+commands.processlist = lists.process
+commands.analyzelist = lists.analyze
+commands.listtitle = lists.title
+commands.listprefixednumber = lists.prefixednumber
+commands.listprefixedpage = lists.prefixedpage
+
+
+function commands.addtolist (...) context(lists.addto (...)) end -- we could use variables instead of print
+function commands.listsize (...) context(lists.size (...)) end
+function commands.listlocation (...) context(lists.location (...)) end
+function commands.listlabel (...) context(lists.label (...)) end
+function commands.listrealpage (...) context(lists.realpage (...)) end
+function commands.listgroupindex(...) context(lists.groupindex(...)) end
+
+function commands.listuserdata(...)
+ local str, metadata = lists.userdata(...)
+ if str then
+ -- local catcodes = metadata and metadata.catcodes
+ -- if catcodes then
+ -- context.sprint(catcodes,str)
+ -- else
+ -- context(str)
+ -- end
+ helpers.title(str,metadata)
+ end
+end
+
+-- we could also set variables .. names will change (when this module is done)
+-- maybe strc_lists_savedtitle etc
+
+function commands.doiflisthastitleelse (...) commands.doifelse(lists.hastitledata (...)) end
+function commands.doiflisthaspageelse (...) commands.doifelse(lists.haspagedata (...)) end
+function commands.doiflisthasnumberelse(...) commands.doifelse(lists.hasnumberdata(...)) end
+function commands.doiflisthasentry (n) commands.doifelse(lists.iscached (n )) end
+
+function commands.savedlistnumber(name,n)
+ local data = cached[tonumber(n)]
+ if data then
+ local numberdata = data.numberdata
+ if numberdata then
+ sections.typesetnumber(numberdata,"number",numberdata or false)
+ end
+ end
+end
+
+function commands.savedlisttitle(name,n,tag)
+ local data = cached[tonumber(n)]
+ if data then
+ local titledata = data.titledata
+ if titledata then
+ helpers.title(titledata[tag] or titledata.title or "",data.metadata)
+ end
+ end
+end
+
+-- function commands.savedlistprefixednumber(name,n)
+-- local data = cached[tonumber(n)]
+-- if data then
+-- local numberdata = data.numberdata
+-- if numberdata then
+-- helpers.prefix(data,data.prefixdata)
+-- sections.typesetnumber(numberdata,"number",numberdata or false)
+-- end
+-- end
+-- end
+
+if not lists.reordered then
+ function lists.reordered(data)
+ return data.numberdata
+ end
+end
+
+function commands.savedlistprefixednumber(name,n)
+ local data = cached[tonumber(n)]
+ if data then
+ local numberdata = lists.reordered(data)
+ if numberdata then
+ helpers.prefix(data,data.prefixdata)
+ sections.typesetnumber(numberdata,"number",numberdata or false)
+ end
+ end
+end
+
+commands.discardfromlist = lists.discard
+
+-- new and experimental and therefore off by default
+
+local sort, setmetatableindex = table.sort, table.setmetatableindex
+
+lists.autoreorder = false -- true
+
+local function addlevel(t,k)
+ local v = { }
+ setmetatableindex(v,function(t,k)
+ local v = { }
+ t[k] = v
+ return v
+ end)
+ t[k] = v
+ return v
+end
+
+local internals = setmetatableindex({ }, function(t,k)
+
+ local sublists = setmetatableindex({ },addlevel)
+
+ local collected = lists.collected or { }
+
+ for i=1,#collected do
+ local entry = collected[i]
+ local numberdata = entry.numberdata
+ if numberdata then
+ local metadata = entry.metadata
+ if metadata then
+ local references = entry.references
+ if references then
+ local kind = metadata.kind
+ local name = numberdata.counter or metadata.name
+ local internal = references.internal
+ if kind and name and internal then
+ local sublist = sublists[kind][name]
+ sublist[#sublist + 1] = { internal, numberdata }
+ end
+ end
+ end
+ end
+ end
+
+ for k, v in next, sublists do
+ for k, v in next, v do
+ local tmp = { }
+ for i=1,#v do
+ tmp[i] = v[i]
+ end
+ sort(v,function(a,b) return a[1] < b[1] end)
+ for i=1,#v do
+ t[v[i][1]] = tmp[i][2]
+ end
+ end
+ end
+
+ setmetatableindex(t,nil)
+
+ return t[k]
+
+end)
+
+function lists.reordered(entry)
+ local numberdata = entry.numberdata
+ if lists.autoreorder then
+ if numberdata then
+ local metadata = entry.metadata
+ if metadata then
+ local references = entry.references
+ if references then
+ local kind = metadata.kind
+ local name = numberdata.counter or metadata.name
+ local internal = references.internal
+ if kind and name and internal then
+ return internals[internal] or numberdata
+ end
+ end
+ end
+ end
+ else
+ function lists.reordered(entry)
+ return entry.numberdata
+ end
+ end
+ return numberdata
+end
diff --git a/tex/context/base/strc-mar.lua b/tex/context/base/strc-mar.lua
index 4aa867992..7b3ac11e1 100644
--- a/tex/context/base/strc-mar.lua
+++ b/tex/context/base/strc-mar.lua
@@ -1,696 +1,696 @@
-if not modules then modules = { } end modules ['strc-mar'] = {
- version = 1.001,
- comment = "companion to strc-mar.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- todo: cleanup stack (structures.marks.reset(v_all) also does the job)
--- todo: only commands.* print to tex, native marks return values
-
-local insert, concat = table.insert, table.concat
-local tostring, next, rawget = tostring, next, rawget
-local lpegmatch = lpeg.match
-local match = string.match
-
-local allocate = utilities.storage.allocate
-local setmetatableindex = table.setmetatableindex
-
-local nodecodes = nodes.nodecodes
-local glyph_code = nodecodes.glyph
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-
-local traversenodes = node.traverse
-local texsetattribute = tex.setattribute
-local texbox = tex.box
-
-local a_marks = attributes.private("structure","marks")
-
-local trace_marks_set = false trackers.register("marks.set", function(v) trace_marks_set = v end)
-local trace_marks_get = false trackers.register("marks.get", function(v) trace_marks_get = v end)
-local trace_marks_all = false trackers.register("marks.detail", function(v) trace_marks_all = v end)
-
-local report_marks = logs.reporter("structure","marks")
-
-local variables = interfaces.variables
-
-local v_first = variables.first
-local v_last = variables.last
-local v_previous = variables.previous
-local v_next = variables.next
-local v_top = variables.top
-local v_bottom = variables.bottom
-local v_current = variables.current
-local v_default = variables.default
-local v_page = variables.page
-local v_all = variables.all
-local v_keep = variables.keep
-
-local v_nocheck_suffix = ":" .. variables.nocheck
-
-local v_first_nocheck = variables.first .. v_nocheck_suffix
-local v_last_nocheck = variables.last .. v_nocheck_suffix
-local v_previous_nocheck = variables.previous .. v_nocheck_suffix
-local v_next_nocheck = variables.next .. v_nocheck_suffix
-local v_top_nocheck = variables.top .. v_nocheck_suffix
-local v_bottom_nocheck = variables.bottom .. v_nocheck_suffix
-
-local structures = structures
-local marks = structures.marks
-local lists = structures.lists
-
-local settings_to_array = utilities.parsers.settings_to_array
-
-marks.data = marks.data or allocate()
-
-storage.register("structures/marks/data", marks.data, "structures.marks.data")
-
-local data = marks.data
-local stack, topofstack = { }, 0
-
-local ranges = {
- [v_page] = {
- first = 0,
- last = 0,
- },
-}
-
-local function resolve(t,k)
- if k then
- if trace_marks_set or trace_marks_get then
- report_marks("undefined mark, name %a",k)
- end
- local crap = { autodefined = true } -- maybe set = 0 and reset = 0
- t[k] = crap
- return crap
- else
- -- weird: k is nil
- end
-end
-
-setmetatableindex(data, resolve)
-
-function marks.exists(name)
- return rawget(data,name) ~= nil
-end
-
--- identify range
-
-local function sweep(head,first,last)
- for n in traversenodes(head) do
- local id = n.id
- if id == glyph_code then
- local a = n[a_marks]
- if not a then
- -- next
- elseif first == 0 then
- first, last = a, a
- elseif a > last then
- last = a
- end
- elseif id == hlist_code or id == vlist_code then
- local a = n[a_marks]
- if not a then
- -- next
- elseif first == 0 then
- first, last = a, a
- elseif a > last then
- last = a
- end
- local list = n.list
- if list then
- first, last = sweep(list, first, last)
- end
- end
- end
- return first, last
-end
-
-local classes = { }
-
-setmetatableindex(classes, function(t,k) local s = settings_to_array(k) t[k] = s return s end)
-
-local lasts = { }
-
-function marks.synchronize(class,n,option)
- local box = texbox[n]
- if box then
- local first, last = sweep(box.list,0,0)
- if option == v_keep and first == 0 and last == 0 then
- if trace_marks_get or trace_marks_set then
- report_marks("action %a, class %a, box %a","retain at synchronize",class,n)
- end
- -- todo: check if still valid firts/last in range
- first = lasts[class] or 0
- last = first
- else
- lasts[class] = last
- local classlist = classes[class]
- for i=1,#classlist do
- local class = classlist[i]
- local range = ranges[class]
- if not range then
- range = { }
- ranges[class] = range
- end
- range.first, range.last = first, last
- if trace_marks_get or trace_marks_set then
- report_marks("action %a, class %a, first %a, last %a","synchronize",class,range.first,range.last)
- end
- end
- end
- elseif trace_marks_get or trace_marks_set then
- report_marks("action %s, class %a, box %a","synchronize without content",class,n)
- end
-end
-
--- define etc
-
-local function resolve(t,k)
- if k == "fullchain" then
- local fullchain = { }
- local chain = t.chain
- while chain and chain ~= "" do
- insert(fullchain,1,chain)
- chain = data[chain].chain
- end
- t[k] = fullchain
- return fullchain
- elseif k == "chain" then
- t[k] = ""
- return ""
- elseif k == "reset" or k == "set" then
- t[k] = 0
- return 0
- elseif k == "parent" then
- t[k] = false
- return false
- end
-end
-
-function marks.define(name,settings)
- settings = settings or { }
- data[name] = settings
- local parent = settings.parent
- if parent == nil or parent == "" or parent == name then
- settings.parent = false
- else
- local dp = data[parent]
- if not dp then
- settings.parent = false
- elseif dp.parent then
- settings.parent = dp.parent
- end
- end
- setmetatableindex(settings, resolve)
-end
-
-for k, v in next, data do
- setmetatableindex(v,resolve) -- runtime loaded table
-end
-
-local function parentname(name)
- local dn = data[name]
- return dn and dn.parent or name
-end
-
-function marks.relate(name,chain)
- local dn = data[name]
- if dn and not dn.parent then
- if chain and chain ~= "" then
- dn.chain = chain
- local dc = data[chain]
- if dc then
- local children = dc.children
- if not children then
- children = { }
- dc.children = children
- end
- children[#children+1] = name
- end
- elseif trace_marks_set then
- report_marks("error: invalid relation, name %a, chain %a",name,chain)
- end
- end
-end
-
-local function resetchildren(new,name)
- local dn = data[name]
- if dn and not dn.parent then
- local children = dn.children
- if children then
- for i=1,#children do
- local ci = children[i]
- new[ci] = false
- if trace_marks_set then
- report_marks("action %a, parent %a, child %a","reset",name,ci)
- end
- resetchildren(new,ci)
- end
- end
- end
-end
-
-function marks.set(name,value)
- local dn = data[name]
- if dn then
- local child = name
- local parent = dn.parent
- if parent then
- name = parent
- dn = data[name]
- end
- dn.set = topofstack
- if not dn.reset then
- dn.reset = 0 -- in case of selfdefined
- end
- local top = stack[topofstack]
- local new = { }
- if top then
- for k, v in next, top do
- local d = data[k]
- local r = d.reset or 0
- local s = d.set or 0
- if r <= topofstack and s < r then
- new[k] = false
- else
- new[k] = v
- end
- end
- end
- resetchildren(new,name)
- new[name] = value
- topofstack = topofstack + 1
- stack[topofstack] = new
- if trace_marks_set then
- if name == child then
- report_marks("action %a, name %a, index %a, value %a","set",name,topofstack,value)
- else
- report_marks("action %a, parent %a, child %a, index %a, value %a","set",parent,child,topofstack,value)
- end
- end
- texsetattribute("global",a_marks,topofstack)
- end
-end
-
-local function reset(name)
- if v_all then
- if trace_marks_set then
- report_marks("action %a","reset all")
- end
- stack = { }
- for name, dn in next, data do
- local parent = dn.parent
- if parent then
- dn.reset = 0
- dn.set = 0
- end
- end
- else
- local dn = data[name]
- if dn then
- local parent = dn.parent
- if parent then
- name = parent
- dn = data[name]
- end
- if trace_marks_set then
- report_marks("action %a, name %a, index %a","reset",name,topofstack)
- end
- dn.reset = topofstack
- local children = dn.children
- if children then
- for i=1,#children do
- local ci = children[i]
- reset(ci)
- end
- end
- end
- end
-end
-
-marks.reset = reset
-
-function marks.get(n,name,value)
- local dn = data[name]
- if dn then
- name = dn.parent or name
- local top = stack[n]
- if top then
- context(top[name])
- end
- end
-end
-
-function marks.show(first,last)
- if first and last then
- for k=first,last do
- local v = stack[k]
- if v then
- report_marks("% 4i: %s",k,table.sequenced(v))
- end
- end
- else
- for k, v in table.sortedpairs(stack) do
- report_marks("% 4i: %s",k,table.sequenced(v))
- end
- end
-end
-
-local function resolve(name,first,last,strict,quitonfalse,notrace)
- local dn = data[name]
- if dn then
- local child = name
- local parent = dn.parent
- name = parent or child
- dn = data[name]
- local step, method
- if first > last then
- step, method = -1, "bottom-up"
- else
- step, method = 1, "top-down"
- end
- if trace_marks_get and not notrace then
- report_marks("action %a, strategy %a, name %a, parent %a, strict %a","request",method,child,parent,strict or false)
- end
- if trace_marks_all and not notrace then
- marks.show(first,last)
- end
- local r = dn.reset
- local s = dn.set
- if first <= last and first <= r then
- if trace_marks_get and not notrace then
- report_marks("action %a, name %a, first %a, last %a, reset %a, index %a","reset first",name,first,last,r,first)
- end
- elseif first >= last and last <= r then
- if trace_marks_get and not notrace then
- report_marks("action %a, name %a, first %a, last %a, reset %a, index %a","reset last",name,first,last,r,last)
- end
- elseif not stack[first] or not stack[last] then
- if trace_marks_get and not notrace then
- -- a previous or next method can give an out of range, which is valid
- report_marks("error: out of range, name %a, reset %a, index %a",name,r,first)
- end
- elseif strict then
- local top = stack[first]
- local fullchain = dn.fullchain
- if not fullchain or #fullchain == 0 then
- if trace_marks_get and not notrace then
- report_marks("warning: no full chain, trying again, name %a, first %a, last %a",name,first,last)
- end
- return resolve(name,first,last)
- else
- if trace_marks_get and not notrace then
- report_marks("found chain [ % => T ]",fullchain)
- end
- local chaindata, chainlength = { }, #fullchain
- for i=1,chainlength do
- local cname = fullchain[i]
- if data[cname].set > 0 then
- local value = resolve(cname,first,last,false,false,true)
- if value == "" then
- if trace_marks_get and not notrace then
- report_marks("quitting chain, name %a, reset %a, start %a",name,r,first)
- end
- return ""
- else
- chaindata[i] = value
- end
- end
- end
- if trace_marks_get and not notrace then
- report_marks("using chain [ % => T ]",chaindata)
- end
- local value, index, found = resolve(name,first,last,false,false,true)
- if value ~= "" then
- if trace_marks_get and not notrace then
- report_marks("following chain [ % => T ]",chaindata)
- end
- for i=1,chainlength do
- local cname = fullchain[i]
- if data[cname].set > 0 and chaindata[i] ~= found[cname] then
- if trace_marks_get and not notrace then
- report_marks("quiting chain, name %a, reset %a, index %a",name,r,first)
- end
- return ""
- end
- end
- if trace_marks_get and not notrace then
- report_marks("found in chain, name %a, reset %a, start %a, index %a, value %a",name,r,first,index,value)
- end
- return value, index, found
- elseif trace_marks_get and not notrace then
- report_marks("not found, name %a, reset %a",name,r)
- end
- end
- else
- for i=first,last,step do
- local current = stack[i]
- local value = current and current[name]
- if value == nil then
- -- search on
- elseif value == false then
- if quitonfalse then
- return ""
- end
- elseif value == true then
- if trace_marks_get and not notrace then
- report_marks("quitting steps, name %a, reset %a, start %a, index %a",name,r,first,i)
- end
- return ""
- elseif value ~= "" then
- if trace_marks_get and not notrace then
- report_marks("found in steps, name %a, reset %a, start %a, index %a, value %a",name,r,first,i,value)
- end
- return value, i, current
- end
- end
- if trace_marks_get and not notrace then
- report_marks("not found in steps, name %a, reset %a",name,r)
- end
- end
- end
- return ""
-end
-
--- todo: column:first column:last
-
-local methods = { }
-
-local function doresolve(name,rangename,swap,df,dl,strict)
- local range = ranges[rangename] or ranges[v_page]
- local first, last = range.first, range.last
- if trace_marks_get then
- report_marks("action %a, name %a, range %a, swap %a, first %a, last %a, df %a, dl %a, strict %a",
- "resolving",name,rangename,swap or false,first,last,df,dl,strict or false)
- end
- if swap then
- first, last = last + df, first + dl
- else
- first, last = first + df, last + dl
- end
- local value, index, found = resolve(name,first,last,strict)
- -- maybe something more
- return value, index, found
-end
-
--- previous : last before sync
--- next : first after sync
-
--- top : first in sync
--- bottom : last in sync
-
--- first : first not top in sync
--- last : last not bottom in sync
-
-methods[v_previous] = function(name,range) return doresolve(name,range,false,-1,0,true ) end -- strict
-methods[v_top] = function(name,range) return doresolve(name,range,false, 0,0,true ) end -- strict
-methods[v_bottom] = function(name,range) return doresolve(name,range,true , 0,0,true ) end -- strict
-methods[v_next] = function(name,range) return doresolve(name,range,true , 0,1,true ) end -- strict
-
-methods[v_previous_nocheck] = function(name,range) return doresolve(name,range,false,-1,0,false) end
-methods[v_top_nocheck] = function(name,range) return doresolve(name,range,false, 0,0,false) end
-methods[v_bottom_nocheck] = function(name,range) return doresolve(name,range,true , 0,0,false) end
-methods[v_next_nocheck] = function(name,range) return doresolve(name,range,true , 0,1,false) end
-
-local function do_first(name,range,check)
- if trace_marks_get then
- report_marks("action %a, name %a, range %a","resolving first",name,range)
- end
- local f_value, f_index, f_found = doresolve(name,range,false,0,0,check)
- if trace_marks_get then
- report_marks("action %a, name %a, range %a","resolving last",name,range)
- end
- local l_value, l_index, l_found = doresolve(name,range,true ,0,0,check)
- if f_found and l_found and l_index > f_index then
- local name = parentname(name)
- for i=f_index,l_index,1 do
- local si = stack[i]
- local sn = si[name]
- if sn and sn ~= false and sn ~= true and sn ~= "" and sn ~= f_value then
- if trace_marks_get then
- report_marks("action %a, name %a, range %a, index %a, value %a","resolving",name,range,i,sn)
- end
- return sn, i, si
- end
- end
- end
- if trace_marks_get then
- report_marks("resolved, name %a, range %a, using first",name,range)
- end
- return f_value, f_index, f_found
-end
-
-local function do_last(name,range,check)
- if trace_marks_get then
- report_marks("action %a, name %a, range %a","resolving first",name,range)
- end
- local f_value, f_index, f_found = doresolve(name,range,false,0,0,check)
- if trace_marks_get then
- report_marks("action %a, name %a, range %a","resolving last",name,range)
- end
- local l_value, l_index, l_found = doresolve(name,range,true ,0,0,check)
- if f_found and l_found and l_index > f_index then
- local name = parentname(name)
- for i=l_index,f_index,-1 do
- local si = stack[i]
- local sn = si[name]
- if sn and sn ~= false and sn ~= true and sn ~= "" and sn ~= l_value then
- if trace_marks_get then
- report_marks("action %a, name %a, range %a, index %a, value %a","resolving",name,range,i,sn)
- end
- return sn, i, si
- end
- end
- end
- if trace_marks_get then
- report_marks("resolved, name %a, range %a, using first",name,range)
- end
- return l_value, l_index, l_found
-end
-
-methods[v_first ] = function(name,range) return do_first(name,range,true ) end
-methods[v_last ] = function(name,range) return do_last (name,range,true ) end
-methods[v_first_nocheck] = function(name,range) return do_first(name,range,false) end
-methods[v_last_nocheck ] = function(name,range) return do_last (name,range,false) end
-
-methods[v_current] = function(name,range) -- range is ignored here
- local top = stack[topofstack]
- return top and top[parentname(name)] or ""
-end
-
-local function fetched(name,range,method)
- local value = (methods[method] or methods[v_first])(name,range) or ""
- if not trace_marks_get then
- -- no report
- elseif value == "" then
- report_marks("nothing fetched, name %a, range %a, method %a",name,range,method)
- else
- report_marks("marking fetched, name %a, range %a, method %a, value %a",name,range,method,value)
- end
- return value or ""
-end
-
--- can be used at the lua end:
-
-marks.fetched = fetched
-
--- this will move to a separate runtime modules
-
-marks.tracers = marks.tracers or { }
-
-function marks.tracers.showtable()
- context.starttabulate { "|l|l|l|lp|lp|" }
- context.tabulaterowbold("name","parent","chain","children","fullchain")
- context.ML()
- for k, v in table.sortedpairs(data) do
- local parent, chain, children, fullchain = v.parent or "", v.chain or "", v.children or { }, v.fullchain or { }
- table.sort(children) -- in-place but harmless
- context.tabulaterowtyp(k,parent,chain,concat(children," "),concat(fullchain," "))
- end
- context.stoptabulate()
-end
-
--- pushing to context:
-
-local separator = context.nested.markingseparator
-local command = context.nested.markingcommand
-local ctxconcat = context.concat
-
-local function fetchonemark(name,range,method)
- context(command(name,fetched(name,range,method)))
-end
-
-local function fetchtwomarks(name,range)
- ctxconcat( {
- command(name,fetched(name,range,v_first)),
- command(name,fetched(name,range,v_last)),
- }, separator(name))
-end
-
-local function fetchallmarks(name,range)
- ctxconcat( {
- command(name,fetched(name,range,v_previous)),
- command(name,fetched(name,range,v_first)),
- command(name,fetched(name,range,v_last)),
- }, separator(name))
-end
-
-function marks.fetch(name,range,method) -- chapter page first | chapter column:1 first
- if trace_marks_get then
- report_marks("marking requested, name %a, range %a, method %a",name,range,method)
- end
- if method == "" or method == v_default then
- fetchonemark(name,range,v_first)
- elseif method == v_both then
- fetchtwomarks(name,range)
- elseif method == v_all then
- fetchallmarks(name,range)
- else
- fetchonemark(name,range,method)
- end
-end
-
-function marks.fetchonemark (name,range,method) fetchonemark (name,range,method) end
-function marks.fetchtwomarks(name,range) fetchtwomarks(name,range ) end
-function marks.fetchallmarks(name,range) fetchallmarks(name,range ) end
-
--- here we have a few helpers .. will become commands.*
-
-function marks.title(tag,n)
- local listindex = match(n,"^li::(.-)$")
- if listindex then
- commands.savedlisttitle(tag,listindex,"marking")
- else
- context(n)
- end
-end
-
-function marks.number(tag,n) -- no spec
- local listindex = match(n,"^li::(.-)$")
- if listindex then
- commands.savedlistnumber(tag,listindex)
- else
- -- no prefix (as it is the prefix)
- context(n)
- end
-end
-
--- interface
-
-commands.definemarking = marks.define
-commands.relatemarking = marks.relate
-commands.setmarking = marks.set
-commands.resetmarking = marks.reset
-commands.synchronizemarking = marks.synchronize
-commands.getmarking = marks.fetch
-commands.fetchonemark = marks.fetchonemark
-commands.fetchtwomarks = marks.fetchtwomarks
-commands.fetchallmarks = marks.fetchallmarks
-
-function commands.doifelsemarking(str) -- can be shortcut
- commands.doifelse(marks.exists(str))
-end
-
+if not modules then modules = { } end modules ['strc-mar'] = {
+ version = 1.001,
+ comment = "companion to strc-mar.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo: cleanup stack (structures.marks.reset(v_all) also does the job)
+-- todo: only commands.* print to tex, native marks return values
+
+local insert, concat = table.insert, table.concat
+local tostring, next, rawget = tostring, next, rawget
+local lpegmatch = lpeg.match
+local match = string.match
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+
+local nodecodes = nodes.nodecodes
+local glyph_code = nodecodes.glyph
+local hlist_code = nodecodes.hlist
+local vlist_code = nodecodes.vlist
+
+local traversenodes = node.traverse
+local texsetattribute = tex.setattribute
+local texbox = tex.box
+
+local a_marks = attributes.private("structure","marks")
+
+local trace_marks_set = false trackers.register("marks.set", function(v) trace_marks_set = v end)
+local trace_marks_get = false trackers.register("marks.get", function(v) trace_marks_get = v end)
+local trace_marks_all = false trackers.register("marks.detail", function(v) trace_marks_all = v end)
+
+local report_marks = logs.reporter("structure","marks")
+
+local variables = interfaces.variables
+
+local v_first = variables.first
+local v_last = variables.last
+local v_previous = variables.previous
+local v_next = variables.next
+local v_top = variables.top
+local v_bottom = variables.bottom
+local v_current = variables.current
+local v_default = variables.default
+local v_page = variables.page
+local v_all = variables.all
+local v_keep = variables.keep
+
+local v_nocheck_suffix = ":" .. variables.nocheck
+
+local v_first_nocheck = variables.first .. v_nocheck_suffix
+local v_last_nocheck = variables.last .. v_nocheck_suffix
+local v_previous_nocheck = variables.previous .. v_nocheck_suffix
+local v_next_nocheck = variables.next .. v_nocheck_suffix
+local v_top_nocheck = variables.top .. v_nocheck_suffix
+local v_bottom_nocheck = variables.bottom .. v_nocheck_suffix
+
+local structures = structures
+local marks = structures.marks
+local lists = structures.lists
+
+local settings_to_array = utilities.parsers.settings_to_array
+
+marks.data = marks.data or allocate()
+
+storage.register("structures/marks/data", marks.data, "structures.marks.data")
+
+local data = marks.data
+local stack, topofstack = { }, 0
+
+local ranges = {
+ [v_page] = {
+ first = 0,
+ last = 0,
+ },
+}
+
+local function resolve(t,k)
+ if k then
+ if trace_marks_set or trace_marks_get then
+ report_marks("undefined mark, name %a",k)
+ end
+ local crap = { autodefined = true } -- maybe set = 0 and reset = 0
+ t[k] = crap
+ return crap
+ else
+ -- weird: k is nil
+ end
+end
+
+setmetatableindex(data, resolve)
+
+function marks.exists(name)
+ return rawget(data,name) ~= nil
+end
+
+-- identify range
+
+local function sweep(head,first,last)
+ for n in traversenodes(head) do
+ local id = n.id
+ if id == glyph_code then
+ local a = n[a_marks]
+ if not a then
+ -- next
+ elseif first == 0 then
+ first, last = a, a
+ elseif a > last then
+ last = a
+ end
+ elseif id == hlist_code or id == vlist_code then
+ local a = n[a_marks]
+ if not a then
+ -- next
+ elseif first == 0 then
+ first, last = a, a
+ elseif a > last then
+ last = a
+ end
+ local list = n.list
+ if list then
+ first, last = sweep(list, first, last)
+ end
+ end
+ end
+ return first, last
+end
+
+local classes = { }
+
+setmetatableindex(classes, function(t,k) local s = settings_to_array(k) t[k] = s return s end)
+
+local lasts = { }
+
+function marks.synchronize(class,n,option)
+ local box = texbox[n]
+ if box then
+ local first, last = sweep(box.list,0,0)
+ if option == v_keep and first == 0 and last == 0 then
+ if trace_marks_get or trace_marks_set then
+ report_marks("action %a, class %a, box %a","retain at synchronize",class,n)
+ end
+ -- todo: check if still valid firts/last in range
+ first = lasts[class] or 0
+ last = first
+ else
+ lasts[class] = last
+ local classlist = classes[class]
+ for i=1,#classlist do
+ local class = classlist[i]
+ local range = ranges[class]
+ if not range then
+ range = { }
+ ranges[class] = range
+ end
+ range.first, range.last = first, last
+ if trace_marks_get or trace_marks_set then
+ report_marks("action %a, class %a, first %a, last %a","synchronize",class,range.first,range.last)
+ end
+ end
+ end
+ elseif trace_marks_get or trace_marks_set then
+ report_marks("action %s, class %a, box %a","synchronize without content",class,n)
+ end
+end
+
+-- define etc
+
+local function resolve(t,k)
+ if k == "fullchain" then
+ local fullchain = { }
+ local chain = t.chain
+ while chain and chain ~= "" do
+ insert(fullchain,1,chain)
+ chain = data[chain].chain
+ end
+ t[k] = fullchain
+ return fullchain
+ elseif k == "chain" then
+ t[k] = ""
+ return ""
+ elseif k == "reset" or k == "set" then
+ t[k] = 0
+ return 0
+ elseif k == "parent" then
+ t[k] = false
+ return false
+ end
+end
+
+function marks.define(name,settings)
+ settings = settings or { }
+ data[name] = settings
+ local parent = settings.parent
+ if parent == nil or parent == "" or parent == name then
+ settings.parent = false
+ else
+ local dp = data[parent]
+ if not dp then
+ settings.parent = false
+ elseif dp.parent then
+ settings.parent = dp.parent
+ end
+ end
+ setmetatableindex(settings, resolve)
+end
+
+for k, v in next, data do
+ setmetatableindex(v,resolve) -- runtime loaded table
+end
+
+local function parentname(name)
+ local dn = data[name]
+ return dn and dn.parent or name
+end
+
+function marks.relate(name,chain)
+ local dn = data[name]
+ if dn and not dn.parent then
+ if chain and chain ~= "" then
+ dn.chain = chain
+ local dc = data[chain]
+ if dc then
+ local children = dc.children
+ if not children then
+ children = { }
+ dc.children = children
+ end
+ children[#children+1] = name
+ end
+ elseif trace_marks_set then
+ report_marks("error: invalid relation, name %a, chain %a",name,chain)
+ end
+ end
+end
+
+local function resetchildren(new,name)
+ local dn = data[name]
+ if dn and not dn.parent then
+ local children = dn.children
+ if children then
+ for i=1,#children do
+ local ci = children[i]
+ new[ci] = false
+ if trace_marks_set then
+ report_marks("action %a, parent %a, child %a","reset",name,ci)
+ end
+ resetchildren(new,ci)
+ end
+ end
+ end
+end
+
+function marks.set(name,value)
+ local dn = data[name]
+ if dn then
+ local child = name
+ local parent = dn.parent
+ if parent then
+ name = parent
+ dn = data[name]
+ end
+ dn.set = topofstack
+ if not dn.reset then
+ dn.reset = 0 -- in case of selfdefined
+ end
+ local top = stack[topofstack]
+ local new = { }
+ if top then
+ for k, v in next, top do
+ local d = data[k]
+ local r = d.reset or 0
+ local s = d.set or 0
+ if r <= topofstack and s < r then
+ new[k] = false
+ else
+ new[k] = v
+ end
+ end
+ end
+ resetchildren(new,name)
+ new[name] = value
+ topofstack = topofstack + 1
+ stack[topofstack] = new
+ if trace_marks_set then
+ if name == child then
+ report_marks("action %a, name %a, index %a, value %a","set",name,topofstack,value)
+ else
+ report_marks("action %a, parent %a, child %a, index %a, value %a","set",parent,child,topofstack,value)
+ end
+ end
+ texsetattribute("global",a_marks,topofstack)
+ end
+end
+
+local function reset(name)
+ if v_all then
+ if trace_marks_set then
+ report_marks("action %a","reset all")
+ end
+ stack = { }
+ for name, dn in next, data do
+ local parent = dn.parent
+ if parent then
+ dn.reset = 0
+ dn.set = 0
+ end
+ end
+ else
+ local dn = data[name]
+ if dn then
+ local parent = dn.parent
+ if parent then
+ name = parent
+ dn = data[name]
+ end
+ if trace_marks_set then
+ report_marks("action %a, name %a, index %a","reset",name,topofstack)
+ end
+ dn.reset = topofstack
+ local children = dn.children
+ if children then
+ for i=1,#children do
+ local ci = children[i]
+ reset(ci)
+ end
+ end
+ end
+ end
+end
+
+marks.reset = reset
+
+function marks.get(n,name,value)
+ local dn = data[name]
+ if dn then
+ name = dn.parent or name
+ local top = stack[n]
+ if top then
+ context(top[name])
+ end
+ end
+end
+
+function marks.show(first,last)
+ if first and last then
+ for k=first,last do
+ local v = stack[k]
+ if v then
+ report_marks("% 4i: %s",k,table.sequenced(v))
+ end
+ end
+ else
+ for k, v in table.sortedpairs(stack) do
+ report_marks("% 4i: %s",k,table.sequenced(v))
+ end
+ end
+end
+
+local function resolve(name,first,last,strict,quitonfalse,notrace)
+ local dn = data[name]
+ if dn then
+ local child = name
+ local parent = dn.parent
+ name = parent or child
+ dn = data[name]
+ local step, method
+ if first > last then
+ step, method = -1, "bottom-up"
+ else
+ step, method = 1, "top-down"
+ end
+ if trace_marks_get and not notrace then
+ report_marks("action %a, strategy %a, name %a, parent %a, strict %a","request",method,child,parent,strict or false)
+ end
+ if trace_marks_all and not notrace then
+ marks.show(first,last)
+ end
+ local r = dn.reset
+ local s = dn.set
+ if first <= last and first <= r then
+ if trace_marks_get and not notrace then
+ report_marks("action %a, name %a, first %a, last %a, reset %a, index %a","reset first",name,first,last,r,first)
+ end
+ elseif first >= last and last <= r then
+ if trace_marks_get and not notrace then
+ report_marks("action %a, name %a, first %a, last %a, reset %a, index %a","reset last",name,first,last,r,last)
+ end
+ elseif not stack[first] or not stack[last] then
+ if trace_marks_get and not notrace then
+ -- a previous or next method can give an out of range, which is valid
+ report_marks("error: out of range, name %a, reset %a, index %a",name,r,first)
+ end
+ elseif strict then
+ local top = stack[first]
+ local fullchain = dn.fullchain
+ if not fullchain or #fullchain == 0 then
+ if trace_marks_get and not notrace then
+ report_marks("warning: no full chain, trying again, name %a, first %a, last %a",name,first,last)
+ end
+ return resolve(name,first,last)
+ else
+ if trace_marks_get and not notrace then
+ report_marks("found chain [ % => T ]",fullchain)
+ end
+ local chaindata, chainlength = { }, #fullchain
+ for i=1,chainlength do
+ local cname = fullchain[i]
+ if data[cname].set > 0 then
+ local value = resolve(cname,first,last,false,false,true)
+ if value == "" then
+ if trace_marks_get and not notrace then
+ report_marks("quitting chain, name %a, reset %a, start %a",name,r,first)
+ end
+ return ""
+ else
+ chaindata[i] = value
+ end
+ end
+ end
+ if trace_marks_get and not notrace then
+ report_marks("using chain [ % => T ]",chaindata)
+ end
+ local value, index, found = resolve(name,first,last,false,false,true)
+ if value ~= "" then
+ if trace_marks_get and not notrace then
+ report_marks("following chain [ % => T ]",chaindata)
+ end
+ for i=1,chainlength do
+ local cname = fullchain[i]
+ if data[cname].set > 0 and chaindata[i] ~= found[cname] then
+ if trace_marks_get and not notrace then
+ report_marks("quiting chain, name %a, reset %a, index %a",name,r,first)
+ end
+ return ""
+ end
+ end
+ if trace_marks_get and not notrace then
+ report_marks("found in chain, name %a, reset %a, start %a, index %a, value %a",name,r,first,index,value)
+ end
+ return value, index, found
+ elseif trace_marks_get and not notrace then
+ report_marks("not found, name %a, reset %a",name,r)
+ end
+ end
+ else
+ for i=first,last,step do
+ local current = stack[i]
+ local value = current and current[name]
+ if value == nil then
+ -- search on
+ elseif value == false then
+ if quitonfalse then
+ return ""
+ end
+ elseif value == true then
+ if trace_marks_get and not notrace then
+ report_marks("quitting steps, name %a, reset %a, start %a, index %a",name,r,first,i)
+ end
+ return ""
+ elseif value ~= "" then
+ if trace_marks_get and not notrace then
+ report_marks("found in steps, name %a, reset %a, start %a, index %a, value %a",name,r,first,i,value)
+ end
+ return value, i, current
+ end
+ end
+ if trace_marks_get and not notrace then
+ report_marks("not found in steps, name %a, reset %a",name,r)
+ end
+ end
+ end
+ return ""
+end
+
+-- todo: column:first column:last
+
+local methods = { }
+
+local function doresolve(name,rangename,swap,df,dl,strict)
+ local range = ranges[rangename] or ranges[v_page]
+ local first, last = range.first, range.last
+ if trace_marks_get then
+ report_marks("action %a, name %a, range %a, swap %a, first %a, last %a, df %a, dl %a, strict %a",
+ "resolving",name,rangename,swap or false,first,last,df,dl,strict or false)
+ end
+ if swap then
+ first, last = last + df, first + dl
+ else
+ first, last = first + df, last + dl
+ end
+ local value, index, found = resolve(name,first,last,strict)
+ -- maybe something more
+ return value, index, found
+end
+
+-- previous : last before sync
+-- next : first after sync
+
+-- top : first in sync
+-- bottom : last in sync
+
+-- first : first not top in sync
+-- last : last not bottom in sync
+
+methods[v_previous] = function(name,range) return doresolve(name,range,false,-1,0,true ) end -- strict
+methods[v_top] = function(name,range) return doresolve(name,range,false, 0,0,true ) end -- strict
+methods[v_bottom] = function(name,range) return doresolve(name,range,true , 0,0,true ) end -- strict
+methods[v_next] = function(name,range) return doresolve(name,range,true , 0,1,true ) end -- strict
+
+methods[v_previous_nocheck] = function(name,range) return doresolve(name,range,false,-1,0,false) end
+methods[v_top_nocheck] = function(name,range) return doresolve(name,range,false, 0,0,false) end
+methods[v_bottom_nocheck] = function(name,range) return doresolve(name,range,true , 0,0,false) end
+methods[v_next_nocheck] = function(name,range) return doresolve(name,range,true , 0,1,false) end
+
+local function do_first(name,range,check)
+ if trace_marks_get then
+ report_marks("action %a, name %a, range %a","resolving first",name,range)
+ end
+ local f_value, f_index, f_found = doresolve(name,range,false,0,0,check)
+ if trace_marks_get then
+ report_marks("action %a, name %a, range %a","resolving last",name,range)
+ end
+ local l_value, l_index, l_found = doresolve(name,range,true ,0,0,check)
+ if f_found and l_found and l_index > f_index then
+ local name = parentname(name)
+ for i=f_index,l_index,1 do
+ local si = stack[i]
+ local sn = si[name]
+ if sn and sn ~= false and sn ~= true and sn ~= "" and sn ~= f_value then
+ if trace_marks_get then
+ report_marks("action %a, name %a, range %a, index %a, value %a","resolving",name,range,i,sn)
+ end
+ return sn, i, si
+ end
+ end
+ end
+ if trace_marks_get then
+ report_marks("resolved, name %a, range %a, using first",name,range)
+ end
+ return f_value, f_index, f_found
+end
+
+local function do_last(name,range,check)
+ if trace_marks_get then
+ report_marks("action %a, name %a, range %a","resolving first",name,range)
+ end
+ local f_value, f_index, f_found = doresolve(name,range,false,0,0,check)
+ if trace_marks_get then
+ report_marks("action %a, name %a, range %a","resolving last",name,range)
+ end
+ local l_value, l_index, l_found = doresolve(name,range,true ,0,0,check)
+ if f_found and l_found and l_index > f_index then
+ local name = parentname(name)
+ for i=l_index,f_index,-1 do
+ local si = stack[i]
+ local sn = si[name]
+ if sn and sn ~= false and sn ~= true and sn ~= "" and sn ~= l_value then
+ if trace_marks_get then
+ report_marks("action %a, name %a, range %a, index %a, value %a","resolving",name,range,i,sn)
+ end
+ return sn, i, si
+ end
+ end
+ end
+ if trace_marks_get then
+ report_marks("resolved, name %a, range %a, using first",name,range)
+ end
+ return l_value, l_index, l_found
+end
+
+methods[v_first ] = function(name,range) return do_first(name,range,true ) end
+methods[v_last ] = function(name,range) return do_last (name,range,true ) end
+methods[v_first_nocheck] = function(name,range) return do_first(name,range,false) end
+methods[v_last_nocheck ] = function(name,range) return do_last (name,range,false) end
+
+methods[v_current] = function(name,range) -- range is ignored here
+ local top = stack[topofstack]
+ return top and top[parentname(name)] or ""
+end
+
+local function fetched(name,range,method)
+ local value = (methods[method] or methods[v_first])(name,range) or ""
+ if not trace_marks_get then
+ -- no report
+ elseif value == "" then
+ report_marks("nothing fetched, name %a, range %a, method %a",name,range,method)
+ else
+ report_marks("marking fetched, name %a, range %a, method %a, value %a",name,range,method,value)
+ end
+ return value or ""
+end
+
+-- can be used at the lua end:
+
+marks.fetched = fetched
+
+-- this will move to a separate runtime modules
+
+marks.tracers = marks.tracers or { }
+
+function marks.tracers.showtable()
+ context.starttabulate { "|l|l|l|lp|lp|" }
+ context.tabulaterowbold("name","parent","chain","children","fullchain")
+ context.ML()
+ for k, v in table.sortedpairs(data) do
+ local parent, chain, children, fullchain = v.parent or "", v.chain or "", v.children or { }, v.fullchain or { }
+ table.sort(children) -- in-place but harmless
+ context.tabulaterowtyp(k,parent,chain,concat(children," "),concat(fullchain," "))
+ end
+ context.stoptabulate()
+end
+
+-- pushing to context:
+
+local separator = context.nested.markingseparator
+local command = context.nested.markingcommand
+local ctxconcat = context.concat
+
+local function fetchonemark(name,range,method)
+ context(command(name,fetched(name,range,method)))
+end
+
+local function fetchtwomarks(name,range)
+ ctxconcat( {
+ command(name,fetched(name,range,v_first)),
+ command(name,fetched(name,range,v_last)),
+ }, separator(name))
+end
+
+local function fetchallmarks(name,range)
+ ctxconcat( {
+ command(name,fetched(name,range,v_previous)),
+ command(name,fetched(name,range,v_first)),
+ command(name,fetched(name,range,v_last)),
+ }, separator(name))
+end
+
+function marks.fetch(name,range,method) -- chapter page first | chapter column:1 first
+ if trace_marks_get then
+ report_marks("marking requested, name %a, range %a, method %a",name,range,method)
+ end
+ if method == "" or method == v_default then
+ fetchonemark(name,range,v_first)
+ elseif method == v_both then
+ fetchtwomarks(name,range)
+ elseif method == v_all then
+ fetchallmarks(name,range)
+ else
+ fetchonemark(name,range,method)
+ end
+end
+
+function marks.fetchonemark (name,range,method) fetchonemark (name,range,method) end
+function marks.fetchtwomarks(name,range) fetchtwomarks(name,range ) end
+function marks.fetchallmarks(name,range) fetchallmarks(name,range ) end
+
+-- here we have a few helpers .. will become commands.*
+
+function marks.title(tag,n)
+ local listindex = match(n,"^li::(.-)$")
+ if listindex then
+ commands.savedlisttitle(tag,listindex,"marking")
+ else
+ context(n)
+ end
+end
+
+function marks.number(tag,n) -- no spec
+ local listindex = match(n,"^li::(.-)$")
+ if listindex then
+ commands.savedlistnumber(tag,listindex)
+ else
+ -- no prefix (as it is the prefix)
+ context(n)
+ end
+end
+
+-- interface
+
+commands.definemarking = marks.define
+commands.relatemarking = marks.relate
+commands.setmarking = marks.set
+commands.resetmarking = marks.reset
+commands.synchronizemarking = marks.synchronize
+commands.getmarking = marks.fetch
+commands.fetchonemark = marks.fetchonemark
+commands.fetchtwomarks = marks.fetchtwomarks
+commands.fetchallmarks = marks.fetchallmarks
+
+function commands.doifelsemarking(str) -- can be shortcut
+ commands.doifelse(marks.exists(str))
+end
+
diff --git a/tex/context/base/strc-not.lua b/tex/context/base/strc-not.lua
index a699fd8d5..882e00a44 100644
--- a/tex/context/base/strc-not.lua
+++ b/tex/context/base/strc-not.lua
@@ -1,447 +1,447 @@
-if not modules then modules = { } end modules ['strc-not'] = {
- version = 1.001,
- comment = "companion to strc-not.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 next = next
-local texcount = tex.count
-
-local trace_notes = false trackers.register("structures.notes", function(v) trace_notes = v end)
-local trace_references = false trackers.register("structures.notes.references", function(v) trace_references = v end)
-
-local report_notes = logs.reporter("structure","notes")
-
-local structures = structures
-local helpers = structures.helpers
-local lists = structures.lists
-local sections = structures.sections
-local counters = structures.counters
-local notes = structures.notes
-local references = structures.references
-local counterspecials = counters.specials
-
-notes.states = notes.states or { }
-lists.enhancers = lists.enhancers or { }
-
-storage.register("structures/notes/states", notes.states, "structures.notes.states")
-
-local notestates = notes.states
-local notedata = { }
-
-local variables = interfaces.variables
-local context = context
-local commands = commands
-
--- state: store, insert, postpone
-
-local function store(tag,n)
- -- somewhat weird but this is a cheap hook spot
- if not counterspecials[tag] then
- counterspecials[tag] = function(tag)
- context.doresetlinenotecompression(tag) -- maybe flag that controls it
- end
- end
- --
- local nd = notedata[tag]
- if not nd then
- nd = { }
- notedata[tag] = nd
- end
- local nnd = #nd + 1
- nd[nnd] = n
- local state = notestates[tag]
- if not state then
- report_notes("unknown state for %a",tag)
- elseif state.kind ~= "insert" then
- if trace_notes then
- report_notes("storing %a with state %a as %a",tag,state.kind,nnd)
- end
- state.start = state.start or nnd
- end
- return #nd
-end
-
-notes.store = store
-
-function commands.storenote(tag,n)
- context(store(tag,n))
-end
-
-local function get(tag,n) -- tricky ... only works when defined
- local nd = notedata[tag]
- if nd then
- n = n or #nd
- nd = nd[n]
- if nd then
- if trace_notes then
- report_notes("getting note %a of %a with listindex %a",n,tag,nd)
- end
- -- is this right?
--- local newdata = lists.collected[nd]
- local newdata = lists.cached[nd]
--- local newdata = lists.tobesaved[nd]
- return newdata
- end
- end
-end
-
-local function getn(tag)
- local nd = notedata[tag]
- return (nd and #nd) or 0
-end
-
-notes.get = get
-notes.getn = getn
-
--- we could make a special enhancer
-
-local function listindex(tag,n)
- local ndt = notedata[tag]
- return ndt and ndt[n]
-end
-
-notes.listindex = listindex
-
-function commands.notelistindex(tag,n)
- context(listindex(tag,n))
-end
-
-local function setstate(tag,newkind)
- local state = notestates[tag]
- if trace_notes then
- report_notes("setting state of %a from %s to %s",tag,(state and state.kind) or "unset",newkind)
- end
- if not state then
- state = {
- kind = newkind
- }
- notestates[tag] = state
- elseif newkind == "insert" then
- if not state.start then
- state.kind = newkind
- end
- else
--- if newkind == "postpone" and state.kind == "store" then
--- else
- state.kind = newkind
--- end
- end
- -- state.start can already be set and will be set when an entry is added or flushed
- return state
-end
-
-local function getstate(tag)
- local state = notestates[tag]
- return state and state.kind or "unknown"
-end
-
-notes.setstate = setstate
-notes.getstate = getstate
-
-commands.setnotestate = setstate
-
-function commands.getnotestate(tag)
- context(getstate(tag))
-end
-
-function notes.define(tag,kind,number)
- local state = setstate(tag,kind)
- state.number = number
-end
-
-commands.definenote = notes.define
-
-function notes.save(tag,newkind)
- local state = notestates[tag]
- if state and not state.saved then
- if trace_notes then
- report_notes("saving state of %a, old: %a, new %a",tag,state.kind,newkind or state.kind)
- end
- state.saveddata = notedata[tag]
- state.savedkind = state.kind
- state.kind = newkind or state.kind
- state.saved = true
- notedata[tag] = { }
- end
-end
-
-function notes.restore(tag,forcedstate)
- local state = notestates[tag]
- if state and state.saved then
- if trace_notes then
- report_notes("restoring state of %a, old: %a, new: %a",tag,state.kind,state.savedkind)
- end
- notedata[tag] = state.saveddata
- state.kind = forcedstate or state.savedkind
- state.saveddata = nil
- state.saved = false
- end
-end
-
-commands.savenote = notes.save
-commands.restorenote = notes.restore
-
-local function hascontent(tag)
- local ok = notestates[tag]
- if ok then
- if ok.kind == "insert" then
- ok = tex.box[ok.number]
- if ok then
- ok = tbs.list
- ok = lst and lst.next
- end
- else
- ok = ok.start
- end
- end
- return ok and true or false
-end
-
-notes.hascontent = hascontent
-
-function commands.doifnotecontent(tag)
- commands.doif(hascontent(tag))
-end
-
-local function internal(tag,n)
- local nd = get(tag,n)
- if nd then
- local r = nd.references
- if r then
- local i = r.internal
- return i and references.internals[i] -- dependency on references
- end
- end
- return nil
-end
-
-local function ordered(kind,name,n)
- local o = lists.ordered[kind]
- o = o and o[name]
- return o and o[n]
-end
-
-notes.internal = internal
-notes.ordered = ordered
-
-local function onsamepageasprevious(tag)
- local same = false
- local n = getn(tag,n)
- local current, previous = get(tag,n), get(tag,n-1)
- if current and previous then
- local cr, pr = current.references, previous.references
- same = cr and pr and cr.realpage == pr.realpage
- end
- return same and true or false
-end
-
-notes.doifonsamepageasprevious = onsamepageasprevious
-
-function commands.doifnoteonsamepageasprevious(tag)
- commands.doifelse(onsamepageasprevious(tag))
-end
-
-function notes.checkpagechange(tag) -- called before increment !
- local nd = notedata[tag] -- can be unset at first entry
- if nd then
- local current = ordered("note",tag,#nd)
- local nextone = ordered("note",tag,#nd+1)
- if nextone then
- -- we can use data from the previous pass
- if nextone.pagenumber.number > current.pagenumber.number then
- counters.reset(tag)
- end
- elseif current then
- -- we need to locate the next one, best guess
- if texcount.realpageno > current.pagenumber.number then
- counters.reset(tag)
- end
- end
- end
-end
-
-function notes.postpone()
- if trace_notes then
- report_notes("postponing all insert notes")
- end
- for tag, state in next, notestates do
- if state.kind ~= "store" then
- setstate(tag,"postpone")
- end
- end
-end
-
-commands.postponenotes = notes.postpone
-
-function notes.setsymbolpage(tag,n,l)
- local l = l or listindex(tag,n)
- if l then
- local p = texcount.realpageno
- if trace_notes or trace_references then
- report_notes("note %a of %a with list index %a gets symbol page %a",n,tag,l,p)
- end
- local entry = lists.cached[l]
- if entry then
- entry.references.symbolpage = p
- else
- report_notes("internal error: note %a of %a is not flushed",n,tag)
- end
- else
- report_notes("internal error: note %a of %a is not initialized",n,tag)
- end
-end
-
-commands.setnotesymbolpage = notes.setsymbolpage
-
-local function getsymbolpage(tag,n)
- local li = internal(tag,n)
- li = li and li.references
- li = li and (li.symbolpage or li.realpage) or 0
- if trace_notes or trace_references then
- report_notes("page number of note symbol %a of %a is %a",n,tag,li)
- end
- return li
-end
-
-local function getnumberpage(tag,n)
- local li = internal(tag,n)
- li = li and li.references
- li = li and li.realpage or 0
- if trace_notes or trace_references then
- report_notes("page number of note number %s of %a is %a",n,tag,li)
- end
- return li
-end
-
-local function getdeltapage(tag,n)
- -- 0:unknown 1:textbefore, 2:textafter, 3:samepage
- local what = 0
- -- references.internals[lists.tobesaved[nd].internal]
- local li = internal(tag,n)
- if li then
- local references = li.references
- if references then
- local symbolpage = references.symbolpage or 0
- local notepage = references.realpage or 0
- if trace_references then
- report_notes("note number %a of %a points from page %a to page %a",n,tag,symbolpage,notepage)
- end
- if notepage < symbolpage then
- what = 3 -- after
- elseif notepage > symbolpage then
- what = 2 -- before
- elseif notepage > 0 then
- what = 1 -- same
- end
- else
- -- might be a note that is not flushed due to to deep
- -- nesting in a vbox
- end
- end
- return what
-end
-
-notes.getsymbolpage = getsymbolpage
-notes.getnumberpage = getnumberpage
-notes.getdeltapage = getdeltapage
-
-function commands.notesymbolpage(tag,n) context(getsymbolpage(tag,n)) end
-function commands.notenumberpage(tag,n) context(getnumberpage(tag,n)) end
-function commands.notedeltapage (tag,n) context(getdeltapage (tag,n)) end
-
-function commands.flushnotes(tag,whatkind,how) -- store and postpone
- local state = notestates[tag]
- local kind = state.kind
- if kind == whatkind then
- local nd = notedata[tag]
- local ns = state.start -- first index
- if kind == "postpone" then
- if nd and ns then
- if trace_notes then
- report_notes("flushing state %a of %a from %a to %a",whatkind,tag,ns,#nd)
- end
- for i=ns,#nd do
- context.handlenoteinsert(tag,i)
- end
- end
- state.start = nil
- state.kind = "insert"
- elseif kind == "store" then
- if nd and ns then
- if trace_notes then
- report_notes("flushing state %a of %a from %a to %a",whatkind,tag,ns,#nd)
- end
- -- todo: as registers: start, stop, inbetween
- for i=ns,#nd do
- -- tricky : trialtypesetting
- if how == variables.page then
- local rp = get(tag,i)
- rp = rp and rp.references
- rp = rp and rp.symbolpage or 0
- if rp > texcount.realpageno then
- state.start = i
- return
- end
- end
- if i > ns then
- context.betweennoteitself(tag)
- end
- context.handlenoteitself(tag,i)
- end
- end
- state.start = nil
- elseif kind == "reset" then
- if nd and ns then
- if trace_notes then
- report_notes("flushing state %a of %a from %a to %a",whatkind,tag,ns,#nd)
- end
- end
- state.start = nil
- elseif trace_notes then
- report_notes("not flushing state %a of %a",whatkind,tag)
- end
- elseif trace_notes then
- report_notes("not flushing state %a of %a",whatkind,tag)
- end
-end
-
-function commands.flushpostponednotes()
- if trace_notes then
- report_notes("flushing all postponed notes")
- end
- for tag, _ in next, notestates do
- commands.flushnotes(tag,"postpone")
- end
-end
-
-function notes.resetpostponed()
- if trace_notes then
- report_notes("resetting all postponed notes")
- end
- for tag, state in next, notestates do
- if state.kind == "postpone" then
- state.start = nil
- state.kind = "insert"
- end
- end
-end
-
-function commands.notetitle(tag,n)
- command.savedlisttitle(tag,notedata[tag][n])
-end
-
-function commands.noteprefixednumber(tag,n,spec)
- commands.savedlistprefixednumber(tag,notedata[tag][n])
-end
-
-function notes.internalid(tag,n)
- local nd = get(tag,n)
- if nd then
- local r = nd.references
- return r.internal
- end
-end
+if not modules then modules = { } end modules ['strc-not'] = {
+ version = 1.001,
+ comment = "companion to strc-not.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 next = next
+local texcount = tex.count
+
+local trace_notes = false trackers.register("structures.notes", function(v) trace_notes = v end)
+local trace_references = false trackers.register("structures.notes.references", function(v) trace_references = v end)
+
+local report_notes = logs.reporter("structure","notes")
+
+local structures = structures
+local helpers = structures.helpers
+local lists = structures.lists
+local sections = structures.sections
+local counters = structures.counters
+local notes = structures.notes
+local references = structures.references
+local counterspecials = counters.specials
+
+notes.states = notes.states or { }
+lists.enhancers = lists.enhancers or { }
+
+storage.register("structures/notes/states", notes.states, "structures.notes.states")
+
+local notestates = notes.states
+local notedata = { }
+
+local variables = interfaces.variables
+local context = context
+local commands = commands
+
+-- state: store, insert, postpone
+
+local function store(tag,n)
+ -- somewhat weird but this is a cheap hook spot
+ if not counterspecials[tag] then
+ counterspecials[tag] = function(tag)
+ context.doresetlinenotecompression(tag) -- maybe flag that controls it
+ end
+ end
+ --
+ local nd = notedata[tag]
+ if not nd then
+ nd = { }
+ notedata[tag] = nd
+ end
+ local nnd = #nd + 1
+ nd[nnd] = n
+ local state = notestates[tag]
+ if not state then
+ report_notes("unknown state for %a",tag)
+ elseif state.kind ~= "insert" then
+ if trace_notes then
+ report_notes("storing %a with state %a as %a",tag,state.kind,nnd)
+ end
+ state.start = state.start or nnd
+ end
+ return #nd
+end
+
+notes.store = store
+
+function commands.storenote(tag,n)
+ context(store(tag,n))
+end
+
+local function get(tag,n) -- tricky ... only works when defined
+ local nd = notedata[tag]
+ if nd then
+ n = n or #nd
+ nd = nd[n]
+ if nd then
+ if trace_notes then
+ report_notes("getting note %a of %a with listindex %a",n,tag,nd)
+ end
+ -- is this right?
+-- local newdata = lists.collected[nd]
+ local newdata = lists.cached[nd]
+-- local newdata = lists.tobesaved[nd]
+ return newdata
+ end
+ end
+end
+
+local function getn(tag)
+ local nd = notedata[tag]
+ return (nd and #nd) or 0
+end
+
+notes.get = get
+notes.getn = getn
+
+-- we could make a special enhancer
+
+local function listindex(tag,n)
+ local ndt = notedata[tag]
+ return ndt and ndt[n]
+end
+
+notes.listindex = listindex
+
+function commands.notelistindex(tag,n)
+ context(listindex(tag,n))
+end
+
+local function setstate(tag,newkind)
+ local state = notestates[tag]
+ if trace_notes then
+ report_notes("setting state of %a from %s to %s",tag,(state and state.kind) or "unset",newkind)
+ end
+ if not state then
+ state = {
+ kind = newkind
+ }
+ notestates[tag] = state
+ elseif newkind == "insert" then
+ if not state.start then
+ state.kind = newkind
+ end
+ else
+-- if newkind == "postpone" and state.kind == "store" then
+-- else
+ state.kind = newkind
+-- end
+ end
+ -- state.start can already be set and will be set when an entry is added or flushed
+ return state
+end
+
+local function getstate(tag)
+ local state = notestates[tag]
+ return state and state.kind or "unknown"
+end
+
+notes.setstate = setstate
+notes.getstate = getstate
+
+commands.setnotestate = setstate
+
+function commands.getnotestate(tag)
+ context(getstate(tag))
+end
+
+function notes.define(tag,kind,number)
+ local state = setstate(tag,kind)
+ state.number = number
+end
+
+commands.definenote = notes.define
+
+function notes.save(tag,newkind)
+ local state = notestates[tag]
+ if state and not state.saved then
+ if trace_notes then
+ report_notes("saving state of %a, old: %a, new %a",tag,state.kind,newkind or state.kind)
+ end
+ state.saveddata = notedata[tag]
+ state.savedkind = state.kind
+ state.kind = newkind or state.kind
+ state.saved = true
+ notedata[tag] = { }
+ end
+end
+
+function notes.restore(tag,forcedstate)
+ local state = notestates[tag]
+ if state and state.saved then
+ if trace_notes then
+ report_notes("restoring state of %a, old: %a, new: %a",tag,state.kind,state.savedkind)
+ end
+ notedata[tag] = state.saveddata
+ state.kind = forcedstate or state.savedkind
+ state.saveddata = nil
+ state.saved = false
+ end
+end
+
+commands.savenote = notes.save
+commands.restorenote = notes.restore
+
+local function hascontent(tag)
+ local ok = notestates[tag]
+ if ok then
+ if ok.kind == "insert" then
+ ok = tex.box[ok.number]
+ if ok then
+ ok = tbs.list
+ ok = lst and lst.next
+ end
+ else
+ ok = ok.start
+ end
+ end
+ return ok and true or false
+end
+
+notes.hascontent = hascontent
+
+function commands.doifnotecontent(tag)
+ commands.doif(hascontent(tag))
+end
+
+local function internal(tag,n)
+ local nd = get(tag,n)
+ if nd then
+ local r = nd.references
+ if r then
+ local i = r.internal
+ return i and references.internals[i] -- dependency on references
+ end
+ end
+ return nil
+end
+
+local function ordered(kind,name,n)
+ local o = lists.ordered[kind]
+ o = o and o[name]
+ return o and o[n]
+end
+
+notes.internal = internal
+notes.ordered = ordered
+
+local function onsamepageasprevious(tag)
+ local same = false
+ local n = getn(tag,n)
+ local current, previous = get(tag,n), get(tag,n-1)
+ if current and previous then
+ local cr, pr = current.references, previous.references
+ same = cr and pr and cr.realpage == pr.realpage
+ end
+ return same and true or false
+end
+
+notes.doifonsamepageasprevious = onsamepageasprevious
+
+function commands.doifnoteonsamepageasprevious(tag)
+ commands.doifelse(onsamepageasprevious(tag))
+end
+
+function notes.checkpagechange(tag) -- called before increment !
+ local nd = notedata[tag] -- can be unset at first entry
+ if nd then
+ local current = ordered("note",tag,#nd)
+ local nextone = ordered("note",tag,#nd+1)
+ if nextone then
+ -- we can use data from the previous pass
+ if nextone.pagenumber.number > current.pagenumber.number then
+ counters.reset(tag)
+ end
+ elseif current then
+ -- we need to locate the next one, best guess
+ if texcount.realpageno > current.pagenumber.number then
+ counters.reset(tag)
+ end
+ end
+ end
+end
+
+function notes.postpone()
+ if trace_notes then
+ report_notes("postponing all insert notes")
+ end
+ for tag, state in next, notestates do
+ if state.kind ~= "store" then
+ setstate(tag,"postpone")
+ end
+ end
+end
+
+commands.postponenotes = notes.postpone
+
+function notes.setsymbolpage(tag,n,l)
+ local l = l or listindex(tag,n)
+ if l then
+ local p = texcount.realpageno
+ if trace_notes or trace_references then
+ report_notes("note %a of %a with list index %a gets symbol page %a",n,tag,l,p)
+ end
+ local entry = lists.cached[l]
+ if entry then
+ entry.references.symbolpage = p
+ else
+ report_notes("internal error: note %a of %a is not flushed",n,tag)
+ end
+ else
+ report_notes("internal error: note %a of %a is not initialized",n,tag)
+ end
+end
+
+commands.setnotesymbolpage = notes.setsymbolpage
+
+local function getsymbolpage(tag,n)
+ local li = internal(tag,n)
+ li = li and li.references
+ li = li and (li.symbolpage or li.realpage) or 0
+ if trace_notes or trace_references then
+ report_notes("page number of note symbol %a of %a is %a",n,tag,li)
+ end
+ return li
+end
+
+local function getnumberpage(tag,n)
+ local li = internal(tag,n)
+ li = li and li.references
+ li = li and li.realpage or 0
+ if trace_notes or trace_references then
+ report_notes("page number of note number %s of %a is %a",n,tag,li)
+ end
+ return li
+end
+
+local function getdeltapage(tag,n)
+ -- 0:unknown 1:textbefore, 2:textafter, 3:samepage
+ local what = 0
+ -- references.internals[lists.tobesaved[nd].internal]
+ local li = internal(tag,n)
+ if li then
+ local references = li.references
+ if references then
+ local symbolpage = references.symbolpage or 0
+ local notepage = references.realpage or 0
+ if trace_references then
+ report_notes("note number %a of %a points from page %a to page %a",n,tag,symbolpage,notepage)
+ end
+ if notepage < symbolpage then
+ what = 3 -- after
+ elseif notepage > symbolpage then
+ what = 2 -- before
+ elseif notepage > 0 then
+ what = 1 -- same
+ end
+ else
+ -- might be a note that is not flushed due to to deep
+ -- nesting in a vbox
+ end
+ end
+ return what
+end
+
+notes.getsymbolpage = getsymbolpage
+notes.getnumberpage = getnumberpage
+notes.getdeltapage = getdeltapage
+
+function commands.notesymbolpage(tag,n) context(getsymbolpage(tag,n)) end
+function commands.notenumberpage(tag,n) context(getnumberpage(tag,n)) end
+function commands.notedeltapage (tag,n) context(getdeltapage (tag,n)) end
+
+function commands.flushnotes(tag,whatkind,how) -- store and postpone
+ local state = notestates[tag]
+ local kind = state.kind
+ if kind == whatkind then
+ local nd = notedata[tag]
+ local ns = state.start -- first index
+ if kind == "postpone" then
+ if nd and ns then
+ if trace_notes then
+ report_notes("flushing state %a of %a from %a to %a",whatkind,tag,ns,#nd)
+ end
+ for i=ns,#nd do
+ context.handlenoteinsert(tag,i)
+ end
+ end
+ state.start = nil
+ state.kind = "insert"
+ elseif kind == "store" then
+ if nd and ns then
+ if trace_notes then
+ report_notes("flushing state %a of %a from %a to %a",whatkind,tag,ns,#nd)
+ end
+ -- todo: as registers: start, stop, inbetween
+ for i=ns,#nd do
+ -- tricky : trialtypesetting
+ if how == variables.page then
+ local rp = get(tag,i)
+ rp = rp and rp.references
+ rp = rp and rp.symbolpage or 0
+ if rp > texcount.realpageno then
+ state.start = i
+ return
+ end
+ end
+ if i > ns then
+ context.betweennoteitself(tag)
+ end
+ context.handlenoteitself(tag,i)
+ end
+ end
+ state.start = nil
+ elseif kind == "reset" then
+ if nd and ns then
+ if trace_notes then
+ report_notes("flushing state %a of %a from %a to %a",whatkind,tag,ns,#nd)
+ end
+ end
+ state.start = nil
+ elseif trace_notes then
+ report_notes("not flushing state %a of %a",whatkind,tag)
+ end
+ elseif trace_notes then
+ report_notes("not flushing state %a of %a",whatkind,tag)
+ end
+end
+
+function commands.flushpostponednotes()
+ if trace_notes then
+ report_notes("flushing all postponed notes")
+ end
+ for tag, _ in next, notestates do
+ commands.flushnotes(tag,"postpone")
+ end
+end
+
+function notes.resetpostponed()
+ if trace_notes then
+ report_notes("resetting all postponed notes")
+ end
+ for tag, state in next, notestates do
+ if state.kind == "postpone" then
+ state.start = nil
+ state.kind = "insert"
+ end
+ end
+end
+
+function commands.notetitle(tag,n)
+ command.savedlisttitle(tag,notedata[tag][n])
+end
+
+function commands.noteprefixednumber(tag,n,spec)
+ commands.savedlistprefixednumber(tag,notedata[tag][n])
+end
+
+function notes.internalid(tag,n)
+ local nd = get(tag,n)
+ if nd then
+ local r = nd.references
+ return r.internal
+ end
+end
diff --git a/tex/context/base/strc-num.lua b/tex/context/base/strc-num.lua
index 6245a537e..b0eae6b78 100644
--- a/tex/context/base/strc-num.lua
+++ b/tex/context/base/strc-num.lua
@@ -1,649 +1,649 @@
-if not modules then modules = { } end modules ['strc-num'] = {
- version = 1.001,
- comment = "companion to strc-num.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 next, type = next, type
-local min, max = math.min, math.max
-local texcount, texsetcount = tex.count, tex.setcount
-
--- Counters are managed here. They can have multiple levels which makes it easier to synchronize
--- them. Synchronization is sort of special anyway, as it relates to document structuring.
-
-local allocate = utilities.storage.allocate
-local setmetatableindex = table.setmetatableindex
-
-local trace_counters = false trackers.register("structures.counters", function(v) trace_counters = v end)
-local report_counters = logs.reporter("structure","counters")
-
-local structures = structures
-local helpers = structures.helpers
-local sections = structures.sections
-local counters = structures.counters
-local documents = structures.documents
-
-local variables = interfaces.variables
-local v_start = variables.start
-local v_page = variables.page
-local v_reverse = variables.reverse
-local v_first = variables.first
-local v_next = variables.next
-local v_previous = variables.previous
-local v_prev = variables.prev
-local v_last = variables.last
------ v_no = variables.no
-local v_backward = variables.backward
-local v_forward = variables.forward
------ v_subs = variables.subs or "subs"
-
--- states: start stop none reset
-
--- specials are used for counters that are set and incremented in special ways, like
--- pagecounters that get this treatment in the page builder
-
-counters.specials = counters.specials or { }
-local counterspecials = counters.specials
-
-local counterranges, tbs = { }, 0
-
-counters.collected = allocate()
-counters.tobesaved = counters.tobesaved or { }
-counters.data = counters.data or { }
-
-storage.register("structures/counters/data", counters.data, "structures.counters.data")
-storage.register("structures/counters/tobesaved", counters.tobesaved, "structures.counters.tobesaved")
-
-local collected = counters.collected
-local tobesaved = counters.tobesaved
-local counterdata = counters.data
-
-local function initializer() -- not really needed
- collected = counters.collected
- tobesaved = counters.tobesaved
- counterdata = counters.data
-end
-
-local function finalizer()
- for name, cd in next, counterdata do
- local cs = tobesaved[name]
- local data = cd.data
- for i=1,#data do
- local d = data[i]
- local r = d.range
- cs[i][r] = d.number
- d.range = r + 1
- end
- end
-end
-
-job.register('structures.counters.collected', tobesaved, initializer, finalizer)
-
-local constructor = { -- maybe some day we will provide an installer for more variants
-
- last = function(t,name,i)
- local cc = collected[name]
- local stop = (cc and cc[i] and cc[i][t.range]) or 0 -- stop is available for diagnostics purposes only
- t.stop = stop
- if t.offset then
- return stop - t.step
- else
- return stop
- end
- end,
-
- first = function(t,name,i)
- local start = t.start
- if start > 0 then
- return start -- brrr
- elseif t.offset then
- return start + t.step + 1
- else
- return start + 1
- end
- end,
-
- prev = function(t,name,i)
- return max(t.first,t.number-1) -- todo: step
- end,
-
- previous = function(t,name,i)
- return max(t.first,t.number-1) -- todo: step
- end,
-
- next = function(t,name,i)
- return min(t.last,t.number+1) -- todo: step
- end,
-
- backward =function(t,name,i)
- if t.number - 1 < t.first then
- return t.last
- else
- return t.previous
- end
- end,
-
- forward = function(t,name,i)
- if t.number + 1 > t.last then
- return t.first
- else
- return t.next
- end
- end,
-
- subs = function(t,name,i)
- local cc = collected[name]
- t.subs = (cc and cc[i+1] and cc[i+1][t.range]) or 0
- return t.subs
- end,
-
-}
-
-local function dummyconstructor(t,name,i)
- return nil -- was 0, but that is fuzzy in testing for e.g. own
-end
-
-setmetatableindex(constructor,function(t,k)
- if trace_counters then
- report_counters("unknown constructor %a",k)
- end
- return dummyconstructor
-end)
-
-local function enhance()
- for name, cd in next, counterdata do
- local data = cd.data
- for i=1,#data do
- local ci = data[i]
- setmetatableindex(ci, function(t,s) return constructor[s](t,name,i) end)
- end
- end
- enhance = nil
-end
-
-local function allocate(name,i) -- can be metatable
- local cd = counterdata[name]
- if not cd then
- cd = {
- level = 1,
- -- block = "", -- todo
- numbers = nil,
- state = v_start, -- true
- data = { },
- saved = { },
- }
- tobesaved[name] = { }
- counterdata[name] = cd
- end
- cd = cd.data
- local ci = cd[i]
- if not ci then
- ci = {
- number = 0,
- start = 0,
- saved = 0,
- step = 1,
- range = 1,
- offset = false,
- stop = 0, -- via metatable: last, first, stop only for tracing
- }
- setmetatableindex(ci, function(t,s) return constructor[s](t,name,i) end)
- cd[i] = ci
- tobesaved[name][i] = { }
- else
- if enhance then enhance() end -- not stored in bytecode
- end
- return ci
-end
-
-function counters.record(name,i)
- return allocate(name,i or 1)
-end
-
-local function savevalue(name,i)
- if name then
- local cd = counterdata[name].data[i]
- local cs = tobesaved[name][i]
- local cc = collected[name]
- if trace_counters then
- report_counters("action %a, counter %s, value %s","save",name,cd.number)
- end
- local cr = cd.range
- local old = (cc and cc[i] and cc[i][cr]) or 0
- local number = cd.number
- if cd.method == v_page then
- -- we can be one page ahead
- number = number - 1
- end
- cs[cr] = (number >= 0) and number or 0
- cd.range = cr + 1
- return old
- else
- return 0
- end
-end
-
-function counters.define(specification)
- local name = specification.name
- if name and name ~= "" then
- -- todo: step
- local d = allocate(name,1)
- d.start = tonumber(specification.start) or 0
- d.state = v_state or ""
- local counter = specification.counter
- if counter and counter ~= "" then
- d.counter = counter -- only for special purposes, cannot be false
- d.method = specification.method -- frozen at define time
- end
- end
-end
-
-function counters.raw(name)
- return counterdata[name]
-end
-
-function counters.compact(name,level,onlynumbers)
- local cd = counterdata[name]
- if cd then
- local data = cd.data
- local compact = { }
- for i=1,level or #data do
- local d = data[i]
- if d.number ~= 0 then
- compact[i] = (onlynumbers and d.number) or d
- end
- end
- return compact
- end
-end
-
--- depends on when incremented, before or after (driven by d.offset)
-
-function counters.previous(name,n)
- return allocate(name,n).previous
-end
-
-function counters.next(name,n)
- return allocate(name,n).next
-end
-
-counters.prev = counters.previous
-
-function counters.currentvalue(name,n)
- return allocate(name,n).number
-end
-
-function counters.first(name,n)
- return allocate(name,n).first
-end
-
-function counters.last(name,n)
- return allocate(name,n).last
-end
-
-function counters.subs(name,n)
- return counterdata[name].data[n].subs or 0
-end
-
-local function setvalue(name,tag,value)
- local cd = counterdata[name]
- if cd then
- cd[tag] = value
- end
-end
-
-counters.setvalue = setvalue
-
-function counters.setstate(name,value) -- true/false
- value = variables[value]
- if value then
- setvalue(name,"state",value)
- end
-end
-
-function counters.setlevel(name,value)
- setvalue(name,"level",value)
-end
-
-function counters.setoffset(name,value)
- setvalue(name,"offset",value)
-end
-
-local function synchronize(name,d)
- local dc = d.counter
- if dc then
- if trace_counters then
- report_counters("action %a, name %a, counter %a, value %a","synchronize",name,dc,d.number)
- end
- texsetcount("global",dc,d.number)
- end
- local cs = counterspecials[name]
- if cs then
- if trace_counters then
- report_counters("action %a, name %a, counter %a","synccommand",name,dc)
- end
- cs(name)
- end
-end
-
-local function reset(name,n)
- local cd = counterdata[name]
- if cd then
- for i=n or 1,#cd.data do
- local d = cd.data[i]
- savevalue(name,i)
- local number = d.start or 0
- d.number = number
- d.own = nil
- if trace_counters then
- report_counters("action %a, name %a, sub %a, value %a","reset",name,i,number)
- end
- synchronize(name,d)
- end
- cd.numbers = nil
- else
- end
-end
-
-local function set(name,n,value)
- local cd = counterdata[name]
- if cd then
- local d = allocate(name,n)
- local number = value or 0
- d.number = number
- d.own = nil
- if trace_counters then
- report_counters("action %a, name %a, sub %a, value %a","set",name,"no",number)
- end
- synchronize(name,d)
- end
-end
-
-local function check(name,data,start,stop)
- for i=start or 1,stop or #data do
- local d = data[i]
- savevalue(name,i)
- local number = d.start or 0
- d.number = number
- d.own = nil
- if trace_counters then
- report_counters("action %a, name %a, sub %a, value %a","check",name,i,number)
- end
- synchronize(name,d)
- end
-end
-
-counters.reset = reset
-counters.set = set
-
-function counters.setown(name,n,value)
- local cd = counterdata[name]
- if cd then
- local d = allocate(name,n)
- d.own = value
- d.number = (d.number or d.start or 0) + (d.step or 0)
- local level = cd.level
- if not level or level == -1 then
- -- -1 is signal that we reset manually
- elseif level > 0 or level == -3 then
- check(name,d,n+1)
- elseif level == 0 then
- -- happens elsewhere, check this for block
- end
- synchronize(name,d)
- end
-end
-
-function counters.restart(name,n,newstart,noreset)
- local cd = counterdata[name]
- if cd then
- newstart = tonumber(newstart)
- if newstart then
- local d = allocate(name,n)
- d.start = newstart
- if not noreset then
- reset(name,n) -- hm
- end
- end
- end
-end
-
-function counters.save(name) -- or just number
- local cd = counterdata[name]
- if cd then
- table.insert(cd.saved,table.copy(cd.data))
- end
-end
-
-function counters.restore(name)
- local cd = counterdata[name]
- if cd and cd.saved then
- cd.data = table.remove(cd.saved)
- end
-end
-
-function counters.add(name,n,delta)
- local cd = counterdata[name]
- if cd and (cd.state == v_start or cd.state == "") then
- local data = cd.data
- local d = allocate(name,n)
- d.number = (d.number or d.start or 0) + delta*(d.step or 0)
- -- d.own = nil
- local level = cd.level
- if not level or level == -1 then
- -- -1 is signal that we reset manually
- if trace_counters then
- report_counters("action %a, name %a, sub %a, how %a","add",name,"no","no checking")
- end
- elseif level == -2 then
- -- -2 is signal that we work per text
- if trace_counters then
- report_counters("action %a, name %a, sub %a, how %a","add",name,"text","checking")
- end
- check(name,data,n+1)
- elseif level > 0 or level == -3 then
- -- within countergroup
- if trace_counters then
- report_counters("action %a, name %a, sub %a, how %a","add",name,level,"checking within group")
- end
- check(name,data,n+1)
- elseif level == 0 then
- -- happens elsewhere
- if trace_counters then
- report_counters("action %a, name %a, sub %a, how %a","add",name,level,"no checking")
- end
- else
- if trace_counters then
- report_counters("action %a, name %a, sub %a, how %a","add",name,"unknown","no checking")
- end
- end
- synchronize(name,d)
- return d.number -- not needed
- end
- return 0
-end
-
-function counters.check(level)
- for name, cd in next, counterdata do
- if level > 0 and cd.level == -3 then -- could become an option
- if trace_counters then
- report_counters("action %a, name %a, sub %a, detail %a","reset",name,level,"head")
- end
- reset(name)
- elseif cd.level == level then
- if trace_counters then
- report_counters("action %a, name %a, sub %a, detail %a","reset",name,level,"normal")
- end
- reset(name)
- end
- end
-end
-
-local function get(name,n,key)
- local d = allocate(name,n)
- d = d and d[key]
- if not d then
- return 0
- elseif type(d) == "function" then
- return d()
- else
- return d
- end
-end
-
-counters.get = get
-
-function counters.value(name,n) -- what to do with own
- return get(name,n or 1,'number') or 0
-end
-
-function counters.converted(name,spec) -- name can be number and reference to storage
- local cd
- if type(name) == "number" then
- cd = specials.retrieve("counter",name)
- cd = cd and cd.counter
- else
- cd = counterdata[name]
- end
- if cd then
- local spec = spec or { }
- local numbers, ownnumbers = { }, { }
- local reverse = spec.order == v_reverse
- local kind = spec.type or "number"
- local data = cd.data
- for k=1,#data do
- local v = data[k]
- -- somewhat messy, what if subnr? only last must honour kind?
- local vn
- if v.own then
- numbers[k], ownnumbers[k] = v.number, v.own
- else
- if kind == v_first then
- vn = v.first
- elseif kind == v_next then
- vn = v.next
- elseif kind == v_prev or kind == v_previous then
- vn = v.prev
- elseif kind == v_last then
- vn = v.last
- else
- vn = v.number
- if reverse then
- local vf = v.first
- local vl = v.last
- if vl > 0 then
- -- vn = vl - vn + 1 + vf
- vn = vl - vn + vf -- see testbed for test
- end
- end
- end
- numbers[k], ownnumbers[k] = vn or v.number, nil
- end
- end
- cd.numbers = numbers
- cd.ownnumbers = ownnumbers
- sections.typesetnumber(cd,'number',spec)
- cd.numbers = nil
- cd.ownnumbers = nil
- end
-end
-
--- interfacing
-
-commands.definecounter = counters.define
-commands.setcounter = counters.set
-commands.setowncounter = counters.setown
-commands.resetcounter = counters.reset
-commands.restartcounter = counters.restart
-commands.savecounter = counters.save
-commands.restorecounter = counters.restore
-commands.addcounter = counters.add
-
-commands.rawcountervalue = function(...) context(counters.raw (...)) end
-commands.countervalue = function(...) context(counters.value (...)) end
-commands.lastcountervalue = function(...) context(counters.last (...)) end
-commands.firstcountervalue = function(...) context(counters.first (...)) end
-commands.nextcountervalue = function(...) context(counters.next (...)) end
-commands.prevcountervalue = function(...) context(counters.previous(...)) end
-commands.subcountervalues = function(...) context(counters.subs (...)) end
-
-function commands.showcounter(name)
- local cd = counterdata[name]
- if cd then
- context("[%s:",name)
- local data = cd.data
- for i=1,#data do
- local d = data[i]
- context(" (%s: %s,%s,%s s:%s r:%s)",i,d.start or 0,d.number or 0,d.last,d.step or 0,d.range or 0)
- end
- context("]")
- end
-end
-
-function commands.doifelsecounter(name) commands.doifelse(counterdata[name]) end
-function commands.doifcounter (name) commands.doif (counterdata[name]) end
-function commands.doifnotcounter (name) commands.doifnot (counterdata[name]) end
-
-function commands.incrementedcounter(...) context(counters.add(...)) end
-
-function commands.checkcountersetup(name,level,start,state)
- counters.restart(name,1,start,true) -- no reset
- counters.setstate(name,state)
- counters.setlevel(name,level)
- sections.setchecker(name,level,counters.reset)
-end
-
--- -- move to strc-pag.lua
---
--- function counters.analyze(name,counterspecification)
--- local cd = counterdata[name]
--- -- safeguard
--- if not cd then
--- return false, false, "no counter data"
--- end
--- -- section data
--- local sectiondata = sections.current()
--- if not sectiondata then
--- return cd, false, "not in section"
--- end
--- local references = sectiondata.references
--- if not references then
--- return cd, false, "no references"
--- end
--- local section = references.section
--- if not section then
--- return cd, false, "no section"
--- end
--- sectiondata = sections.collected[references.section]
--- if not sectiondata then
--- return cd, false, "no section data"
--- end
--- -- local preferences
--- local no = v_no
--- if counterspecification and counterspecification.prefix == no then
--- return cd, false, "current spec blocks prefix"
--- end
--- -- stored preferences (not used)
--- if cd.prefix == no then
--- return cd, false, "entry blocks prefix"
--- end
--- -- sectioning
--- -- if sectiondata.prefix == no then
--- -- return false, false, "sectiondata blocks prefix"
--- -- end
--- -- final verdict
--- return cd, sectiondata, "okay"
--- end
---
--- function counters.prefixedconverted(name,prefixspec,numberspec)
--- local cd, prefixdata, result = counters.analyze(name,prefixspec)
--- if cd then
--- if prefixdata then
--- sections.typesetnumber(prefixdata,"prefix",prefixspec or false,cd or false)
--- end
--- counters.converted(name,numberspec)
--- end
--- end
+if not modules then modules = { } end modules ['strc-num'] = {
+ version = 1.001,
+ comment = "companion to strc-num.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 next, type = next, type
+local min, max = math.min, math.max
+local texcount, texsetcount = tex.count, tex.setcount
+
+-- Counters are managed here. They can have multiple levels which makes it easier to synchronize
+-- them. Synchronization is sort of special anyway, as it relates to document structuring.
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+
+local trace_counters = false trackers.register("structures.counters", function(v) trace_counters = v end)
+local report_counters = logs.reporter("structure","counters")
+
+local structures = structures
+local helpers = structures.helpers
+local sections = structures.sections
+local counters = structures.counters
+local documents = structures.documents
+
+local variables = interfaces.variables
+local v_start = variables.start
+local v_page = variables.page
+local v_reverse = variables.reverse
+local v_first = variables.first
+local v_next = variables.next
+local v_previous = variables.previous
+local v_prev = variables.prev
+local v_last = variables.last
+----- v_no = variables.no
+local v_backward = variables.backward
+local v_forward = variables.forward
+----- v_subs = variables.subs or "subs"
+
+-- states: start stop none reset
+
+-- specials are used for counters that are set and incremented in special ways, like
+-- pagecounters that get this treatment in the page builder
+
+counters.specials = counters.specials or { }
+local counterspecials = counters.specials
+
+local counterranges, tbs = { }, 0
+
+counters.collected = allocate()
+counters.tobesaved = counters.tobesaved or { }
+counters.data = counters.data or { }
+
+storage.register("structures/counters/data", counters.data, "structures.counters.data")
+storage.register("structures/counters/tobesaved", counters.tobesaved, "structures.counters.tobesaved")
+
+local collected = counters.collected
+local tobesaved = counters.tobesaved
+local counterdata = counters.data
+
+local function initializer() -- not really needed
+ collected = counters.collected
+ tobesaved = counters.tobesaved
+ counterdata = counters.data
+end
+
+local function finalizer()
+ for name, cd in next, counterdata do
+ local cs = tobesaved[name]
+ local data = cd.data
+ for i=1,#data do
+ local d = data[i]
+ local r = d.range
+ cs[i][r] = d.number
+ d.range = r + 1
+ end
+ end
+end
+
+job.register('structures.counters.collected', tobesaved, initializer, finalizer)
+
+local constructor = { -- maybe some day we will provide an installer for more variants
+
+ last = function(t,name,i)
+ local cc = collected[name]
+ local stop = (cc and cc[i] and cc[i][t.range]) or 0 -- stop is available for diagnostics purposes only
+ t.stop = stop
+ if t.offset then
+ return stop - t.step
+ else
+ return stop
+ end
+ end,
+
+ first = function(t,name,i)
+ local start = t.start
+ if start > 0 then
+ return start -- brrr
+ elseif t.offset then
+ return start + t.step + 1
+ else
+ return start + 1
+ end
+ end,
+
+ prev = function(t,name,i)
+ return max(t.first,t.number-1) -- todo: step
+ end,
+
+ previous = function(t,name,i)
+ return max(t.first,t.number-1) -- todo: step
+ end,
+
+ next = function(t,name,i)
+ return min(t.last,t.number+1) -- todo: step
+ end,
+
+ backward =function(t,name,i)
+ if t.number - 1 < t.first then
+ return t.last
+ else
+ return t.previous
+ end
+ end,
+
+ forward = function(t,name,i)
+ if t.number + 1 > t.last then
+ return t.first
+ else
+ return t.next
+ end
+ end,
+
+ subs = function(t,name,i)
+ local cc = collected[name]
+ t.subs = (cc and cc[i+1] and cc[i+1][t.range]) or 0
+ return t.subs
+ end,
+
+}
+
+local function dummyconstructor(t,name,i)
+ return nil -- was 0, but that is fuzzy in testing for e.g. own
+end
+
+setmetatableindex(constructor,function(t,k)
+ if trace_counters then
+ report_counters("unknown constructor %a",k)
+ end
+ return dummyconstructor
+end)
+
+local function enhance()
+ for name, cd in next, counterdata do
+ local data = cd.data
+ for i=1,#data do
+ local ci = data[i]
+ setmetatableindex(ci, function(t,s) return constructor[s](t,name,i) end)
+ end
+ end
+ enhance = nil
+end
+
+local function allocate(name,i) -- can be metatable
+ local cd = counterdata[name]
+ if not cd then
+ cd = {
+ level = 1,
+ -- block = "", -- todo
+ numbers = nil,
+ state = v_start, -- true
+ data = { },
+ saved = { },
+ }
+ tobesaved[name] = { }
+ counterdata[name] = cd
+ end
+ cd = cd.data
+ local ci = cd[i]
+ if not ci then
+ ci = {
+ number = 0,
+ start = 0,
+ saved = 0,
+ step = 1,
+ range = 1,
+ offset = false,
+ stop = 0, -- via metatable: last, first, stop only for tracing
+ }
+ setmetatableindex(ci, function(t,s) return constructor[s](t,name,i) end)
+ cd[i] = ci
+ tobesaved[name][i] = { }
+ else
+ if enhance then enhance() end -- not stored in bytecode
+ end
+ return ci
+end
+
+function counters.record(name,i)
+ return allocate(name,i or 1)
+end
+
+local function savevalue(name,i)
+ if name then
+ local cd = counterdata[name].data[i]
+ local cs = tobesaved[name][i]
+ local cc = collected[name]
+ if trace_counters then
+ report_counters("action %a, counter %s, value %s","save",name,cd.number)
+ end
+ local cr = cd.range
+ local old = (cc and cc[i] and cc[i][cr]) or 0
+ local number = cd.number
+ if cd.method == v_page then
+ -- we can be one page ahead
+ number = number - 1
+ end
+ cs[cr] = (number >= 0) and number or 0
+ cd.range = cr + 1
+ return old
+ else
+ return 0
+ end
+end
+
+function counters.define(specification)
+ local name = specification.name
+ if name and name ~= "" then
+ -- todo: step
+ local d = allocate(name,1)
+ d.start = tonumber(specification.start) or 0
+ d.state = v_state or ""
+ local counter = specification.counter
+ if counter and counter ~= "" then
+ d.counter = counter -- only for special purposes, cannot be false
+ d.method = specification.method -- frozen at define time
+ end
+ end
+end
+
+function counters.raw(name)
+ return counterdata[name]
+end
+
+function counters.compact(name,level,onlynumbers)
+ local cd = counterdata[name]
+ if cd then
+ local data = cd.data
+ local compact = { }
+ for i=1,level or #data do
+ local d = data[i]
+ if d.number ~= 0 then
+ compact[i] = (onlynumbers and d.number) or d
+ end
+ end
+ return compact
+ end
+end
+
+-- depends on when incremented, before or after (driven by d.offset)
+
+function counters.previous(name,n)
+ return allocate(name,n).previous
+end
+
+function counters.next(name,n)
+ return allocate(name,n).next
+end
+
+counters.prev = counters.previous
+
+function counters.currentvalue(name,n)
+ return allocate(name,n).number
+end
+
+function counters.first(name,n)
+ return allocate(name,n).first
+end
+
+function counters.last(name,n)
+ return allocate(name,n).last
+end
+
+function counters.subs(name,n)
+ return counterdata[name].data[n].subs or 0
+end
+
+local function setvalue(name,tag,value)
+ local cd = counterdata[name]
+ if cd then
+ cd[tag] = value
+ end
+end
+
+counters.setvalue = setvalue
+
+function counters.setstate(name,value) -- true/false
+ value = variables[value]
+ if value then
+ setvalue(name,"state",value)
+ end
+end
+
+function counters.setlevel(name,value)
+ setvalue(name,"level",value)
+end
+
+function counters.setoffset(name,value)
+ setvalue(name,"offset",value)
+end
+
+local function synchronize(name,d)
+ local dc = d.counter
+ if dc then
+ if trace_counters then
+ report_counters("action %a, name %a, counter %a, value %a","synchronize",name,dc,d.number)
+ end
+ texsetcount("global",dc,d.number)
+ end
+ local cs = counterspecials[name]
+ if cs then
+ if trace_counters then
+ report_counters("action %a, name %a, counter %a","synccommand",name,dc)
+ end
+ cs(name)
+ end
+end
+
+local function reset(name,n)
+ local cd = counterdata[name]
+ if cd then
+ for i=n or 1,#cd.data do
+ local d = cd.data[i]
+ savevalue(name,i)
+ local number = d.start or 0
+ d.number = number
+ d.own = nil
+ if trace_counters then
+ report_counters("action %a, name %a, sub %a, value %a","reset",name,i,number)
+ end
+ synchronize(name,d)
+ end
+ cd.numbers = nil
+ else
+ end
+end
+
+local function set(name,n,value)
+ local cd = counterdata[name]
+ if cd then
+ local d = allocate(name,n)
+ local number = value or 0
+ d.number = number
+ d.own = nil
+ if trace_counters then
+ report_counters("action %a, name %a, sub %a, value %a","set",name,"no",number)
+ end
+ synchronize(name,d)
+ end
+end
+
+local function check(name,data,start,stop)
+ for i=start or 1,stop or #data do
+ local d = data[i]
+ savevalue(name,i)
+ local number = d.start or 0
+ d.number = number
+ d.own = nil
+ if trace_counters then
+ report_counters("action %a, name %a, sub %a, value %a","check",name,i,number)
+ end
+ synchronize(name,d)
+ end
+end
+
+counters.reset = reset
+counters.set = set
+
+function counters.setown(name,n,value)
+ local cd = counterdata[name]
+ if cd then
+ local d = allocate(name,n)
+ d.own = value
+ d.number = (d.number or d.start or 0) + (d.step or 0)
+ local level = cd.level
+ if not level or level == -1 then
+ -- -1 is signal that we reset manually
+ elseif level > 0 or level == -3 then
+ check(name,d,n+1)
+ elseif level == 0 then
+ -- happens elsewhere, check this for block
+ end
+ synchronize(name,d)
+ end
+end
+
+function counters.restart(name,n,newstart,noreset)
+ local cd = counterdata[name]
+ if cd then
+ newstart = tonumber(newstart)
+ if newstart then
+ local d = allocate(name,n)
+ d.start = newstart
+ if not noreset then
+ reset(name,n) -- hm
+ end
+ end
+ end
+end
+
+function counters.save(name) -- or just number
+ local cd = counterdata[name]
+ if cd then
+ table.insert(cd.saved,table.copy(cd.data))
+ end
+end
+
+function counters.restore(name)
+ local cd = counterdata[name]
+ if cd and cd.saved then
+ cd.data = table.remove(cd.saved)
+ end
+end
+
+function counters.add(name,n,delta)
+ local cd = counterdata[name]
+ if cd and (cd.state == v_start or cd.state == "") then
+ local data = cd.data
+ local d = allocate(name,n)
+ d.number = (d.number or d.start or 0) + delta*(d.step or 0)
+ -- d.own = nil
+ local level = cd.level
+ if not level or level == -1 then
+ -- -1 is signal that we reset manually
+ if trace_counters then
+ report_counters("action %a, name %a, sub %a, how %a","add",name,"no","no checking")
+ end
+ elseif level == -2 then
+ -- -2 is signal that we work per text
+ if trace_counters then
+ report_counters("action %a, name %a, sub %a, how %a","add",name,"text","checking")
+ end
+ check(name,data,n+1)
+ elseif level > 0 or level == -3 then
+ -- within countergroup
+ if trace_counters then
+ report_counters("action %a, name %a, sub %a, how %a","add",name,level,"checking within group")
+ end
+ check(name,data,n+1)
+ elseif level == 0 then
+ -- happens elsewhere
+ if trace_counters then
+ report_counters("action %a, name %a, sub %a, how %a","add",name,level,"no checking")
+ end
+ else
+ if trace_counters then
+ report_counters("action %a, name %a, sub %a, how %a","add",name,"unknown","no checking")
+ end
+ end
+ synchronize(name,d)
+ return d.number -- not needed
+ end
+ return 0
+end
+
+function counters.check(level)
+ for name, cd in next, counterdata do
+ if level > 0 and cd.level == -3 then -- could become an option
+ if trace_counters then
+ report_counters("action %a, name %a, sub %a, detail %a","reset",name,level,"head")
+ end
+ reset(name)
+ elseif cd.level == level then
+ if trace_counters then
+ report_counters("action %a, name %a, sub %a, detail %a","reset",name,level,"normal")
+ end
+ reset(name)
+ end
+ end
+end
+
+local function get(name,n,key)
+ local d = allocate(name,n)
+ d = d and d[key]
+ if not d then
+ return 0
+ elseif type(d) == "function" then
+ return d()
+ else
+ return d
+ end
+end
+
+counters.get = get
+
+function counters.value(name,n) -- what to do with own
+ return get(name,n or 1,'number') or 0
+end
+
+function counters.converted(name,spec) -- name can be number and reference to storage
+ local cd
+ if type(name) == "number" then
+ cd = specials.retrieve("counter",name)
+ cd = cd and cd.counter
+ else
+ cd = counterdata[name]
+ end
+ if cd then
+ local spec = spec or { }
+ local numbers, ownnumbers = { }, { }
+ local reverse = spec.order == v_reverse
+ local kind = spec.type or "number"
+ local data = cd.data
+ for k=1,#data do
+ local v = data[k]
+ -- somewhat messy, what if subnr? only last must honour kind?
+ local vn
+ if v.own then
+ numbers[k], ownnumbers[k] = v.number, v.own
+ else
+ if kind == v_first then
+ vn = v.first
+ elseif kind == v_next then
+ vn = v.next
+ elseif kind == v_prev or kind == v_previous then
+ vn = v.prev
+ elseif kind == v_last then
+ vn = v.last
+ else
+ vn = v.number
+ if reverse then
+ local vf = v.first
+ local vl = v.last
+ if vl > 0 then
+ -- vn = vl - vn + 1 + vf
+ vn = vl - vn + vf -- see testbed for test
+ end
+ end
+ end
+ numbers[k], ownnumbers[k] = vn or v.number, nil
+ end
+ end
+ cd.numbers = numbers
+ cd.ownnumbers = ownnumbers
+ sections.typesetnumber(cd,'number',spec)
+ cd.numbers = nil
+ cd.ownnumbers = nil
+ end
+end
+
+-- interfacing
+
+commands.definecounter = counters.define
+commands.setcounter = counters.set
+commands.setowncounter = counters.setown
+commands.resetcounter = counters.reset
+commands.restartcounter = counters.restart
+commands.savecounter = counters.save
+commands.restorecounter = counters.restore
+commands.addcounter = counters.add
+
+commands.rawcountervalue = function(...) context(counters.raw (...)) end
+commands.countervalue = function(...) context(counters.value (...)) end
+commands.lastcountervalue = function(...) context(counters.last (...)) end
+commands.firstcountervalue = function(...) context(counters.first (...)) end
+commands.nextcountervalue = function(...) context(counters.next (...)) end
+commands.prevcountervalue = function(...) context(counters.previous(...)) end
+commands.subcountervalues = function(...) context(counters.subs (...)) end
+
+function commands.showcounter(name)
+ local cd = counterdata[name]
+ if cd then
+ context("[%s:",name)
+ local data = cd.data
+ for i=1,#data do
+ local d = data[i]
+ context(" (%s: %s,%s,%s s:%s r:%s)",i,d.start or 0,d.number or 0,d.last,d.step or 0,d.range or 0)
+ end
+ context("]")
+ end
+end
+
+function commands.doifelsecounter(name) commands.doifelse(counterdata[name]) end
+function commands.doifcounter (name) commands.doif (counterdata[name]) end
+function commands.doifnotcounter (name) commands.doifnot (counterdata[name]) end
+
+function commands.incrementedcounter(...) context(counters.add(...)) end
+
+function commands.checkcountersetup(name,level,start,state)
+ counters.restart(name,1,start,true) -- no reset
+ counters.setstate(name,state)
+ counters.setlevel(name,level)
+ sections.setchecker(name,level,counters.reset)
+end
+
+-- -- move to strc-pag.lua
+--
+-- function counters.analyze(name,counterspecification)
+-- local cd = counterdata[name]
+-- -- safeguard
+-- if not cd then
+-- return false, false, "no counter data"
+-- end
+-- -- section data
+-- local sectiondata = sections.current()
+-- if not sectiondata then
+-- return cd, false, "not in section"
+-- end
+-- local references = sectiondata.references
+-- if not references then
+-- return cd, false, "no references"
+-- end
+-- local section = references.section
+-- if not section then
+-- return cd, false, "no section"
+-- end
+-- sectiondata = sections.collected[references.section]
+-- if not sectiondata then
+-- return cd, false, "no section data"
+-- end
+-- -- local preferences
+-- local no = v_no
+-- if counterspecification and counterspecification.prefix == no then
+-- return cd, false, "current spec blocks prefix"
+-- end
+-- -- stored preferences (not used)
+-- if cd.prefix == no then
+-- return cd, false, "entry blocks prefix"
+-- end
+-- -- sectioning
+-- -- if sectiondata.prefix == no then
+-- -- return false, false, "sectiondata blocks prefix"
+-- -- end
+-- -- final verdict
+-- return cd, sectiondata, "okay"
+-- end
+--
+-- function counters.prefixedconverted(name,prefixspec,numberspec)
+-- local cd, prefixdata, result = counters.analyze(name,prefixspec)
+-- if cd then
+-- if prefixdata then
+-- sections.typesetnumber(prefixdata,"prefix",prefixspec or false,cd or false)
+-- end
+-- counters.converted(name,numberspec)
+-- end
+-- end
diff --git a/tex/context/base/strc-pag.lua b/tex/context/base/strc-pag.lua
index 63c77c1e6..f70d37d63 100644
--- a/tex/context/base/strc-pag.lua
+++ b/tex/context/base/strc-pag.lua
@@ -1,313 +1,313 @@
-if not modules then modules = { } end modules ['strc-pag'] = {
- version = 1.001,
- comment = "companion to strc-pag.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local texcount = tex.count
-
-local allocate, mark = utilities.storage.allocate, utilities.storage.mark
-
-local trace_pages = false trackers.register("structures.pages", function(v) trace_pages = v end)
-
-local report_pages = logs.reporter("structure","pages")
-
-local structures = structures
-
-local helpers = structures.helpers
-local sections = structures.sections
-local pages = structures.pages
-local sets = structures.sets
-local counters = structures.counters
-
-local counterdata = counters.data
-
-local variables = interfaces.variables
-local context = context
-
-local processors = typesetters.processors
-local applyprocessor = processors.apply
-local startapplyprocessor = processors.startapply
-local stopapplyprocessor = processors.stopapply
-
--- storage
-
-local collected, tobesaved = allocate(), allocate()
-
-pages.collected = collected
-pages.tobesaved = tobesaved
-
-local function initializer()
- collected = pages.collected
- tobesaved = pages.tobesaved
-end
-
-job.register('structures.pages.collected', tobesaved, initializer)
-
-local specification = { } -- to be checked
-
-function pages.save(prefixdata,numberdata)
- local realpage, userpage = texcount.realpageno, texcount.userpageno
- if realpage > 0 then
- if trace_pages then
- report_pages("saving page %s.%s",realpage,userpage)
- end
- local data = {
- number = userpage,
- block = sections.currentblock(),
- prefixdata = prefixdata and helpers.simplify(prefixdata),
- numberdata = numberdata and helpers.simplify(numberdata),
- }
- tobesaved[realpage] = data
- if not collected[realpage] then
- collected[realpage] = data
- end
- elseif trace_pages then
- report_pages("not saving page %s.%s",realpage,userpage)
- end
-end
-
--- We can set the pagenumber but as it only get incremented in the page
--- builder we have to make sure it starts at least at 1.
-
-function counters.specials.userpage()
- local r = texcount.realpageno
- if r > 0 then
- local t = tobesaved[r]
- if t then
- t.number = texcount.userpageno
- if trace_pages then
- report_pages("forcing pagenumber of realpage %s to %s",r,t.number)
- end
- return
- end
- end
- local u = texcount.userpageno
- if u == 0 then
- if trace_pages then
- report_pages("forcing pagenumber of realpage %s to %s (probably a bug)",r,1)
- end
- counters.setvalue("userpage",1)
- texcount.userpageno = 1
- end
-end
-
-local f_convert = string.formatters["\\convertnumber{%s}{%s}"]
-
-local function convertnumber(str,n)
- return f_convert(str or "numbers",n)
-end
-
-function pages.number(realdata,pagespec)
- local userpage, block = realdata.number, realdata.block or "" -- sections.currentblock()
- local numberspec = realdata.numberdata
- local conversionset = (pagespec and pagespec.conversionset ~= "" and pagespec.conversionset) or (numberspec and numberspec.conversionset ~= "" and numberspec.conversionset) or ""
- local conversion = (pagespec and pagespec.conversion ~= "" and pagespec.conversion ) or (numberspec and numberspec.conversion ~= "" and numberspec.conversion ) or ""
- local starter = (pagespec and pagespec.starter ~= "" and pagespec.starter ) or (numberspec and numberspec.starter ~= "" and numberspec.starter ) or ""
- local stopper = (pagespec and pagespec.stopper ~= "" and pagespec.stopper ) or (numberspec and numberspec.stopper ~= "" and numberspec.stopper ) or ""
- if starter ~= "" then
- applyprocessor(starter)
- end
- if conversion ~= "" then
- context.convertnumber(conversion,userpage)
- else
- if conversionset == "" then conversionset = "default" end
- local theconversion = sets.get("structure:conversions",block,conversionset,1,"numbers") -- to be checked: 1
- local data = startapplyprocessor(theconversion)
- context.convertnumber(data or "number",userpage)
- stopapplyprocessor()
- end
- if stopper ~= "" then
- applyprocessors(stopper)
- end
-end
-
--- (pagespec.prefix == yes|unset) and (pages.prefix == yes) => prefix
-
-function pages.analyze(entry,pagespecification)
- -- safeguard
- if not entry then
- return false, false, "no entry"
- end
- local references = entry.references
- if not references then
- return false, false, "no references"
- end
- local pagedata = references.pagedata -- sometimes resolved (external)
- if not pagedata then
- local realpage = references.realpage
- if realpage then
- pagedata = collected[realpage]
- else
- return false, false, "no realpage"
- end
- end
- if not pagedata then
- return false, false, "no pagedata"
- end
- local sectiondata = references.sectiondata -- sometimes resolved (external)
- if not sectiondata then
- local section = references.section
- if section then
- sectiondata = sections.collected[section]
- else
- return pagedata, false, "no section"
- end
- end
- if not sectiondata then
- return pagedata, false, "no sectiondata"
- end
- local no = variables.no
- -- local preferences
- if pagespecification and pagespecification.prefix == no then
- return pagedata, false, "current spec blocks prefix"
- end
- -- stored preferences
- -- if entry.prefix == no then
- -- return pagedata, false, "entry blocks prefix"
- -- end
- -- stored page state
- pagespecification = pagedata.prefixdata
- if pagespecification and pagespecification.prefix == no then
- return pagedata, false, "pagedata blocks prefix"
- end
- -- final verdict
- return pagedata, sectiondata, "okay"
-end
-
-function helpers.page(data,pagespec)
- if data then
- local pagedata = pages.analyze(data,pagespec)
- if pagedata then
- pages.number(pagedata,pagespec)
- end
- end
-end
-
-function helpers.prefixpage(data,prefixspec,pagespec)
- if data then
- local pagedata, prefixdata, e = pages.analyze(data,pagespec)
- if pagedata then
- if prefixdata then
- sections.typesetnumber(prefixdata,"prefix",prefixspec or false,prefixdata or false,pagedata.prefixdata or false)
- end
- pages.number(pagedata,pagespec)
- end
- end
-end
-
-function helpers.prefixlastpage(data,prefixspec,pagespec)
- if data then
- local r = data.references
- local ls, lr = r.section, r.realpage
- r.section, r.realpage = r.lastsection or r.section, r.lastrealpage or r.realpage
- helpers.prefixpage(data,prefixspec,pagespec)
- r.section, r.realpage = ls, lr
- end
-end
-
---
-
-function helpers.analyze(entry,specification)
- -- safeguard
- if not entry then
- return false, false, "no entry"
- end
- local yes, no = variables.yes, variables.no
- -- section data
- local references = entry.references
- if not references then
- return entry, false, "no references"
- end
- local section = references.section
- if not section then
- return entry, false, "no section"
- end
- local sectiondata = sections.collected[references.section]
- if not sectiondata then
- return entry, false, "no section data"
- end
- -- local preferences
- if specification and specification.prefix == no then
- return entry, false, "current spec blocks prefix"
- end
- -- stored preferences (not used)
- local prefixdata = entry.prefixdata
- if prefixdata and prefixdata.prefix == no then
- return entry, false, "entry blocks prefix"
- end
- -- final verdict
- return entry, sectiondata, "okay"
-end
-
-function helpers.prefix(data,prefixspec)
- if data then
- local _, prefixdata, status = helpers.analyze(data,prefixspec)
- if prefixdata then
- sections.typesetnumber(prefixdata,"prefix",prefixspec or false,data.prefixdata or false,prefixdata or false)
- end
- end
-end
-
-function pages.is_odd(n)
- n = n or texcount.realpageno
- if texcount.pagenoshift % 2 == 0 then
- return n % 2 == 0
- else
- return n % 2 ~= 0
- end
-end
-
--- move to strc-pag.lua
-
-function counters.analyze(name,counterspecification)
- local cd = counterdata[name]
- -- safeguard
- if not cd then
- return false, false, "no counter data"
- end
- -- section data
- local sectiondata = sections.current()
- if not sectiondata then
- return cd, false, "not in section"
- end
- local references = sectiondata.references
- if not references then
- return cd, false, "no references"
- end
- local section = references.section
- if not section then
- return cd, false, "no section"
- end
- sectiondata = sections.collected[references.section]
- if not sectiondata then
- return cd, false, "no section data"
- end
- -- local preferences
- local no = variables.no
- if counterspecification and counterspecification.prefix == no then
- return cd, false, "current spec blocks prefix"
- end
- -- stored preferences (not used)
- if cd.prefix == no then
- return cd, false, "entry blocks prefix"
- end
- -- sectioning
- -- if sectiondata.prefix == no then
- -- return false, false, "sectiondata blocks prefix"
- -- end
- -- final verdict
- return cd, sectiondata, "okay"
-end
-
-function sections.prefixedconverted(name,prefixspec,numberspec)
- local cd, prefixdata, result = counters.analyze(name,prefixspec)
- if cd then
- if prefixdata then
- sections.typesetnumber(prefixdata,"prefix",prefixspec or false,cd or false)
- end
- counters.converted(name,numberspec)
- end
-end
+if not modules then modules = { } end modules ['strc-pag'] = {
+ version = 1.001,
+ comment = "companion to strc-pag.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local texcount = tex.count
+
+local allocate, mark = utilities.storage.allocate, utilities.storage.mark
+
+local trace_pages = false trackers.register("structures.pages", function(v) trace_pages = v end)
+
+local report_pages = logs.reporter("structure","pages")
+
+local structures = structures
+
+local helpers = structures.helpers
+local sections = structures.sections
+local pages = structures.pages
+local sets = structures.sets
+local counters = structures.counters
+
+local counterdata = counters.data
+
+local variables = interfaces.variables
+local context = context
+
+local processors = typesetters.processors
+local applyprocessor = processors.apply
+local startapplyprocessor = processors.startapply
+local stopapplyprocessor = processors.stopapply
+
+-- storage
+
+local collected, tobesaved = allocate(), allocate()
+
+pages.collected = collected
+pages.tobesaved = tobesaved
+
+local function initializer()
+ collected = pages.collected
+ tobesaved = pages.tobesaved
+end
+
+job.register('structures.pages.collected', tobesaved, initializer)
+
+local specification = { } -- to be checked
+
+function pages.save(prefixdata,numberdata)
+ local realpage, userpage = texcount.realpageno, texcount.userpageno
+ if realpage > 0 then
+ if trace_pages then
+ report_pages("saving page %s.%s",realpage,userpage)
+ end
+ local data = {
+ number = userpage,
+ block = sections.currentblock(),
+ prefixdata = prefixdata and helpers.simplify(prefixdata),
+ numberdata = numberdata and helpers.simplify(numberdata),
+ }
+ tobesaved[realpage] = data
+ if not collected[realpage] then
+ collected[realpage] = data
+ end
+ elseif trace_pages then
+ report_pages("not saving page %s.%s",realpage,userpage)
+ end
+end
+
+-- We can set the pagenumber but as it only get incremented in the page
+-- builder we have to make sure it starts at least at 1.
+
+function counters.specials.userpage()
+ local r = texcount.realpageno
+ if r > 0 then
+ local t = tobesaved[r]
+ if t then
+ t.number = texcount.userpageno
+ if trace_pages then
+ report_pages("forcing pagenumber of realpage %s to %s",r,t.number)
+ end
+ return
+ end
+ end
+ local u = texcount.userpageno
+ if u == 0 then
+ if trace_pages then
+ report_pages("forcing pagenumber of realpage %s to %s (probably a bug)",r,1)
+ end
+ counters.setvalue("userpage",1)
+ texcount.userpageno = 1
+ end
+end
+
+local f_convert = string.formatters["\\convertnumber{%s}{%s}"]
+
+local function convertnumber(str,n)
+ return f_convert(str or "numbers",n)
+end
+
+function pages.number(realdata,pagespec)
+ local userpage, block = realdata.number, realdata.block or "" -- sections.currentblock()
+ local numberspec = realdata.numberdata
+ local conversionset = (pagespec and pagespec.conversionset ~= "" and pagespec.conversionset) or (numberspec and numberspec.conversionset ~= "" and numberspec.conversionset) or ""
+ local conversion = (pagespec and pagespec.conversion ~= "" and pagespec.conversion ) or (numberspec and numberspec.conversion ~= "" and numberspec.conversion ) or ""
+ local starter = (pagespec and pagespec.starter ~= "" and pagespec.starter ) or (numberspec and numberspec.starter ~= "" and numberspec.starter ) or ""
+ local stopper = (pagespec and pagespec.stopper ~= "" and pagespec.stopper ) or (numberspec and numberspec.stopper ~= "" and numberspec.stopper ) or ""
+ if starter ~= "" then
+ applyprocessor(starter)
+ end
+ if conversion ~= "" then
+ context.convertnumber(conversion,userpage)
+ else
+ if conversionset == "" then conversionset = "default" end
+ local theconversion = sets.get("structure:conversions",block,conversionset,1,"numbers") -- to be checked: 1
+ local data = startapplyprocessor(theconversion)
+ context.convertnumber(data or "number",userpage)
+ stopapplyprocessor()
+ end
+ if stopper ~= "" then
+ applyprocessors(stopper)
+ end
+end
+
+-- (pagespec.prefix == yes|unset) and (pages.prefix == yes) => prefix
+
+function pages.analyze(entry,pagespecification)
+ -- safeguard
+ if not entry then
+ return false, false, "no entry"
+ end
+ local references = entry.references
+ if not references then
+ return false, false, "no references"
+ end
+ local pagedata = references.pagedata -- sometimes resolved (external)
+ if not pagedata then
+ local realpage = references.realpage
+ if realpage then
+ pagedata = collected[realpage]
+ else
+ return false, false, "no realpage"
+ end
+ end
+ if not pagedata then
+ return false, false, "no pagedata"
+ end
+ local sectiondata = references.sectiondata -- sometimes resolved (external)
+ if not sectiondata then
+ local section = references.section
+ if section then
+ sectiondata = sections.collected[section]
+ else
+ return pagedata, false, "no section"
+ end
+ end
+ if not sectiondata then
+ return pagedata, false, "no sectiondata"
+ end
+ local no = variables.no
+ -- local preferences
+ if pagespecification and pagespecification.prefix == no then
+ return pagedata, false, "current spec blocks prefix"
+ end
+ -- stored preferences
+ -- if entry.prefix == no then
+ -- return pagedata, false, "entry blocks prefix"
+ -- end
+ -- stored page state
+ pagespecification = pagedata.prefixdata
+ if pagespecification and pagespecification.prefix == no then
+ return pagedata, false, "pagedata blocks prefix"
+ end
+ -- final verdict
+ return pagedata, sectiondata, "okay"
+end
+
+function helpers.page(data,pagespec)
+ if data then
+ local pagedata = pages.analyze(data,pagespec)
+ if pagedata then
+ pages.number(pagedata,pagespec)
+ end
+ end
+end
+
+function helpers.prefixpage(data,prefixspec,pagespec)
+ if data then
+ local pagedata, prefixdata, e = pages.analyze(data,pagespec)
+ if pagedata then
+ if prefixdata then
+ sections.typesetnumber(prefixdata,"prefix",prefixspec or false,prefixdata or false,pagedata.prefixdata or false)
+ end
+ pages.number(pagedata,pagespec)
+ end
+ end
+end
+
+function helpers.prefixlastpage(data,prefixspec,pagespec)
+ if data then
+ local r = data.references
+ local ls, lr = r.section, r.realpage
+ r.section, r.realpage = r.lastsection or r.section, r.lastrealpage or r.realpage
+ helpers.prefixpage(data,prefixspec,pagespec)
+ r.section, r.realpage = ls, lr
+ end
+end
+
+--
+
+function helpers.analyze(entry,specification)
+ -- safeguard
+ if not entry then
+ return false, false, "no entry"
+ end
+ local yes, no = variables.yes, variables.no
+ -- section data
+ local references = entry.references
+ if not references then
+ return entry, false, "no references"
+ end
+ local section = references.section
+ if not section then
+ return entry, false, "no section"
+ end
+ local sectiondata = sections.collected[references.section]
+ if not sectiondata then
+ return entry, false, "no section data"
+ end
+ -- local preferences
+ if specification and specification.prefix == no then
+ return entry, false, "current spec blocks prefix"
+ end
+ -- stored preferences (not used)
+ local prefixdata = entry.prefixdata
+ if prefixdata and prefixdata.prefix == no then
+ return entry, false, "entry blocks prefix"
+ end
+ -- final verdict
+ return entry, sectiondata, "okay"
+end
+
+function helpers.prefix(data,prefixspec)
+ if data then
+ local _, prefixdata, status = helpers.analyze(data,prefixspec)
+ if prefixdata then
+ sections.typesetnumber(prefixdata,"prefix",prefixspec or false,data.prefixdata or false,prefixdata or false)
+ end
+ end
+end
+
+function pages.is_odd(n)
+ n = n or texcount.realpageno
+ if texcount.pagenoshift % 2 == 0 then
+ return n % 2 == 0
+ else
+ return n % 2 ~= 0
+ end
+end
+
+-- move to strc-pag.lua
+
+function counters.analyze(name,counterspecification)
+ local cd = counterdata[name]
+ -- safeguard
+ if not cd then
+ return false, false, "no counter data"
+ end
+ -- section data
+ local sectiondata = sections.current()
+ if not sectiondata then
+ return cd, false, "not in section"
+ end
+ local references = sectiondata.references
+ if not references then
+ return cd, false, "no references"
+ end
+ local section = references.section
+ if not section then
+ return cd, false, "no section"
+ end
+ sectiondata = sections.collected[references.section]
+ if not sectiondata then
+ return cd, false, "no section data"
+ end
+ -- local preferences
+ local no = variables.no
+ if counterspecification and counterspecification.prefix == no then
+ return cd, false, "current spec blocks prefix"
+ end
+ -- stored preferences (not used)
+ if cd.prefix == no then
+ return cd, false, "entry blocks prefix"
+ end
+ -- sectioning
+ -- if sectiondata.prefix == no then
+ -- return false, false, "sectiondata blocks prefix"
+ -- end
+ -- final verdict
+ return cd, sectiondata, "okay"
+end
+
+function sections.prefixedconverted(name,prefixspec,numberspec)
+ local cd, prefixdata, result = counters.analyze(name,prefixspec)
+ if cd then
+ if prefixdata then
+ sections.typesetnumber(prefixdata,"prefix",prefixspec or false,cd or false)
+ end
+ counters.converted(name,numberspec)
+ end
+end
diff --git a/tex/context/base/strc-ref.lua b/tex/context/base/strc-ref.lua
index 54484fabe..284418c48 100644
--- a/tex/context/base/strc-ref.lua
+++ b/tex/context/base/strc-ref.lua
@@ -1,2158 +1,2158 @@
-if not modules then modules = { } end modules ['strc-ref'] = {
- version = 1.001,
- comment = "companion to strc-ref.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- beware, this is a first step in the rewrite (just getting rid of
--- the tuo file); later all access and parsing will also move to lua
-
--- the useddata and pagedata names might change
--- todo: pack exported data
-
--- todo: autoload components when :::
-
-local format, find, gmatch, match, concat = string.format, string.find, string.gmatch, string.match, table.concat
-local texcount, texsetcount = tex.count, tex.setcount
-local rawget, tonumber = rawget, tonumber
-local lpegmatch = lpeg.match
-local copytable = table.copy
-local formatters = string.formatters
-
-local allocate = utilities.storage.allocate
-local mark = utilities.storage.mark
-local setmetatableindex = table.setmetatableindex
-
-local trace_referencing = false trackers.register("structures.referencing", function(v) trace_referencing = v end)
-local trace_analyzing = false trackers.register("structures.referencing.analyzing", function(v) trace_analyzing = v end)
-local trace_identifying = false trackers.register("structures.referencing.identifying", function(v) trace_identifying = v end)
-local trace_importing = false trackers.register("structures.referencing.importing", function(v) trace_importing = v end)
-local trace_empty = false trackers.register("structures.referencing.empty", function(v) trace_empty = v end)
-
-local check_duplicates = true
-
-directives.register("structures.referencing.checkduplicates", function(v)
- check_duplicates = v
-end)
-
-local report_references = logs.reporter("references")
-local report_unknown = logs.reporter("references","unknown")
-local report_identifying = logs.reporter("references","identifying")
-local report_importing = logs.reporter("references","importing")
-local report_empty = logs.reporter("references","empty")
-
-local variables = interfaces.variables
-local constants = interfaces.constants
-local context = context
-
-local v_default = variables.default
-local v_url = variables.url
-local v_file = variables.file
-local v_unknown = variables.unknown
-local v_yes = variables.yes
-
-local texcount = tex.count
-local texconditionals = tex.conditionals
-
-local productcomponent = resolvers.jobs.productcomponent
-local justacomponent = resolvers.jobs.justacomponent
-
-local logsnewline = logs.newline
-local logspushtarget = logs.pushtarget
-local logspoptarget = logs.poptarget
-
-local settings_to_array = utilities.parsers.settings_to_array
-local unsetvalue = attributes.unsetvalue
-
-local structures = structures
-local helpers = structures.helpers
-local sections = structures.sections
-local references = structures.references
-local lists = structures.lists
-local counters = structures.counters
-
--- some might become local
-
-references.defined = references.defined or allocate()
-
-local defined = references.defined
-local derived = allocate()
-local specials = allocate()
-local runners = allocate()
-local internals = allocate()
-local filters = allocate()
-local executers = allocate()
-local handlers = allocate()
-local tobesaved = allocate()
-local collected = allocate()
-local tobereferred = allocate()
-local referred = allocate()
-
-references.derived = derived
-references.specials = specials
-references.runners = runners
-references.internals = internals
-references.filters = filters
-references.executers = executers
-references.handlers = handlers
-references.tobesaved = tobesaved
-references.collected = collected
-references.tobereferred = tobereferred
-references.referred = referred
-
-local splitreference = references.splitreference
-local splitprefix = references.splitcomponent -- replaces: references.splitprefix
-local prefixsplitter = references.prefixsplitter
-local componentsplitter = references.componentsplitter
-
-local currentreference = nil
-
-storage.register("structures/references/defined", references.defined, "structures.references.defined")
-
-local initializers = { }
-local finalizers = { }
-
-function references.registerinitializer(func) -- we could use a token register instead
- initializers[#initializers+1] = func
-end
-function references.registerfinalizer(func) -- we could use a token register instead
- finalizers[#finalizers+1] = func
-end
-
-local function initializer() -- can we use a tobesaved as metatable for collected?
- tobesaved = references.tobesaved
- collected = references.collected
- for i=1,#initializers do
- initializers[i](tobesaved,collected)
- end
-end
-
-local function finalizer()
- for i=1,#finalizers do
- finalizers[i](tobesaved)
- end
-end
-
-job.register('structures.references.collected', tobesaved, initializer, finalizer)
-
-local maxreferred = 1
-local nofreferred = 0
-
--- local function initializer() -- can we use a tobesaved as metatable for collected?
--- tobereferred = references.tobereferred
--- referred = references.referred
--- nofreferred = #referred
--- end
-
-local function initializer() -- can we use a tobesaved as metatable for collected?
- tobereferred = references.tobereferred
- referred = references.referred
- setmetatableindex(referred,get) -- hm, what is get ?
-end
-
--- We make the array sparse (maybe a finalizer should optionally return a table) because
--- there can be quite some page links involved. We only store one action number per page
--- which is normally good enough for what we want (e.g. see above/below) and we do
--- a combination of a binary search and traverse backwards. A previous implementation
--- always did a traverse and was pretty slow on a large number of links (given that this
--- methods was used). It took me about a day to locate this as a bottleneck in processing
--- a 2500 page interactive document with 60 links per page. In that case, traversing
--- thousands of slots per link then brings processing to a grinding halt (especially when
--- there are no slots at all, which is the case in a first run).
-
-local sparsetobereferred = { }
-
-local function finalizer()
- local lastr, lasti
- local n = 0
- for i=1,maxreferred do
- local r = tobereferred[i]
- if not lastr then
- lastr = r
- lasti = i
- elseif r ~= lastr then
- n = n + 1
- sparsetobereferred[n] = { lastr, lasti }
- lastr = r
- lasti = i
- end
- end
- if lastr then
- n = n + 1
- sparsetobereferred[n] = { lastr, lasti }
- end
-end
-
-job.register('structures.references.referred', sparsetobereferred, initializer, finalizer)
-
-local function referredpage(n)
- local max = nofreferred
- if max > 0 then
- -- find match
- local min = 1
- while true do
- local mid = floor((min+max)/2)
- local r = referred[mid]
- local m = r[2]
- if n == m then
- return r[1]
- elseif n > m then
- min = mid + 1
- else
- max = mid - 1
- end
- if min > max then
- break
- end
- end
- -- find first previous
- for i=min,1,-1 do
- local r = referred[i]
- if r and r[2] < n then
- return r[1]
- end
- end
- end
- -- fallback
- return texcount.realpageno
-end
-
-references.referredpage = referredpage
-
-function references.registerpage(n) -- called in the backend code
- if not tobereferred[n] then
- if n > maxreferred then
- maxreferred = n
- end
- tobereferred[n] = texcount.realpageno
- end
-end
-
--- todo: delay split till later as in destinations we split anyway
-
-local orders, lastorder = { }, 0
-
-local function setnextorder(kind,name)
- lastorder = 0
- if kind and name then
- local ok = orders[kind]
- if not ok then
- ok = { }
- orders[kind] = ok
- end
- lastorder = (ok[name] or 0) + 1
- ok[name] = lastorder
- end
- texsetcount("global","locationorder",lastorder)
-end
-
-references.setnextorder = setnextorder
-
-function references.setnextinternal(kind,name)
- setnextorder(kind,name) -- always incremented with internal
- local n = texcount.locationcount + 1
- texsetcount("global","locationcount",n)
- return n
-end
-
-function references.currentorder(kind,name)
- return orders[kind] and orders[kind][name] or lastorder
-end
-
-local function setcomponent(data)
- -- we might consider doing this at the tex end, just like prefix
- local component = productcomponent()
- if component then
- local references = data and data.references
- if references then
- references.component = component
- end
- return component
- end
- -- but for the moment we do it here (experiment)
-end
-
-commands.setnextinternalreference = references.setnextinternal
-
-function commands.currentreferenceorder(kind,name)
- context(references.currentorder(kind,name))
-end
-
-references.setcomponent = setcomponent
-
-function references.set(kind,prefix,tag,data)
--- setcomponent(data)
- local pd = tobesaved[prefix] -- nicer is a metatable
- if not pd then
- pd = { }
- tobesaved[prefix] = pd
- end
- local n = 0
- for ref in gmatch(tag,"[^,]+") do
- if ref ~= "" then
- if check_duplicates and pd[ref] then
- if prefix and prefix ~= "" then
- report_references("redundant reference %a in namespace %a",ref,prefix)
- else
- report_references("redundant reference %a",ref)
- end
- else
- n = n + 1
- pd[ref] = data
- context.dofinishsomereference(kind,prefix,ref)
- end
- end
- end
- return n > 0
-end
-
-function references.enhance(prefix,tag)
- local l = tobesaved[prefix][tag]
- if l then
- l.references.realpage = texcount.realpageno
- end
-end
-
-commands.enhancereference = references.enhance
-
--- -- -- related to strc-ini.lua -- -- --
-
-references.resolvers = references.resolvers or { }
-local resolvers = references.resolvers
-
-local function getfromlist(var)
- local vi = var.i
- if vi then
- vi = vi[3] or lists.collected[vi[2]]
- if vi then
- local r = vi.references and vi.references
- if r then
- r = r.realpage
- end
- if not r then
- r = vi.pagedata and vi.pagedata
- if r then
- r = r.realpage
- end
- end
- var.i = vi
- var.r = r or 1
- else
- var.i = nil
- var.r = 1
- end
- else
- var.i = nil
- var.r = 1
- end
-end
-
--- resolvers.section = getfromlist
--- resolvers.float = getfromlist
--- resolvers.description = getfromlist
--- resolvers.formula = getfromlist
--- resolvers.note = getfromlist
-
-setmetatableindex(resolvers,function(t,k)
- local v = getfromlist
- resolvers[k] = v
- return v
-end)
-
-function resolvers.reference(var)
- local vi = var.i[2] -- check
- if vi then
- var.i = vi
- var.r = (vi.references and vi.references.realpage) or (vi.pagedata and vi.pagedata.realpage) or 1
- else
- var.i = nil
- var.r = 1
- end
-end
-
-local function register_from_lists(collected,derived,pages,sections)
- local g = derived[""] if not g then g = { } derived[""] = g end -- global
- for i=1,#collected do
- local entry = collected[i]
- local m, r = entry.metadata, entry.references
- if m and r then
- local reference = r.reference or ""
- local prefix = r.referenceprefix or ""
- local component = r.component and r.component or ""
- if reference ~= "" then
- local kind, realpage = m.kind, r.realpage
- if kind and realpage then
- local d = derived[prefix]
- if not d then
- d = { }
- derived[prefix] = d
- end
- local c = derived[component]
- if not c then
- c = { }
- derived[component] = c
- end
- local t = { kind, i, entry }
- for s in gmatch(reference,"%s*([^,]+)") do
- if trace_referencing then
- report_references("list entry %a provides %a reference %a on realpage %a",i,kind,s,realpage)
- end
- c[s] = c[s] or t -- share them
- d[s] = d[s] or t -- share them
- g[s] = g[s] or t -- first wins
- end
- end
- end
- end
- end
--- inspect(derived)
-end
-
-references.registerinitializer(function() register_from_lists(lists.collected,derived) end)
-
--- urls
-
-references.urls = references.urls or { }
-references.urls.data = references.urls.data or { }
-
-local urls = references.urls.data
-
-function references.urls.define(name,url,file,description)
- if name and name ~= "" then
- urls[name] = { url or "", file or "", description or url or file or ""}
- end
-end
-
-local pushcatcodes = context.pushcatcodes
-local popcatcodes = context.popcatcodes
-local txtcatcodes = catcodes.numbers.txtcatcodes -- or just use "txtcatcodes"
-
-function references.urls.get(name)
- local u = urls[name]
- if u then
- local url, file = u[1], u[2]
- if file and file ~= "" then
- return formatters["%s/%s"](url,file)
- else
- return url
- end
- end
-end
-
-function commands.geturl(name)
- local url = references.urls.get(name)
- if url and url ~= "" then
- pushcatcodes(txtcatcodes)
- context(url)
- popcatcodes()
- end
-end
-
--- function commands.gethyphenatedurl(name,...)
--- local url = references.urls.get(name)
--- if url and url ~= "" then
--- hyphenatedurl(url,...)
--- end
--- end
-
-function commands.doifurldefinedelse(name)
- commands.doifelse(urls[name])
-end
-
-commands.useurl= references.urls.define
-
--- files
-
-references.files = references.files or { }
-references.files.data = references.files.data or { }
-
-local files = references.files.data
-
-function references.files.define(name,file,description)
- if name and name ~= "" then
- files[name] = { file or "", description or file or "" }
- end
-end
-
-function references.files.get(name,method,space) -- method: none, before, after, both, space: yes/no
- local f = files[name]
- if f then
- context(f[1])
- end
-end
-
-function commands.doiffiledefinedelse(name)
- commands.doifelse(files[name])
-end
-
-commands.usefile= references.files.define
-
--- helpers
-
-function references.checkedfile(whatever) -- return whatever if not resolved
- if whatever then
- local w = files[whatever]
- if w then
- return w[1]
- else
- return whatever
- end
- end
-end
-
-function references.checkedurl(whatever) -- return whatever if not resolved
- if whatever then
- local w = urls[whatever]
- if w then
- local u, f = w[1], w[2]
- if f and f ~= "" then
- return u .. "/" .. f
- else
- return u
- end
- else
- return whatever
- end
- end
-end
-
-function references.checkedfileorurl(whatever,default) -- return nil, nil if not resolved
- if whatever then
- local w = files[whatever]
- if w then
- return w[1], nil
- else
- local w = urls[whatever]
- if w then
- local u, f = w[1], w[2]
- if f and f ~= "" then
- return nil, u .. "/" .. f
- else
- return nil, u
- end
- end
- end
- end
- return default
-end
-
--- programs
-
-references.programs = references.programs or { }
-references.programs.data = references.programs.data or { }
-
-local programs = references.programs.data
-
-function references.programs.define(name,file,description)
- if name and name ~= "" then
- programs[name] = { file or "", description or file or ""}
- end
-end
-
-function references.programs.get(name)
- local f = programs[name]
- return f and f[1]
-end
-
-function references.checkedprogram(whatever) -- return whatever if not resolved
- if whatever then
- local w = programs[whatever]
- if w then
- return w[1]
- else
- return whatever
- end
- end
-end
-
-commands.defineprogram = references.programs.define
-
-function commands.getprogram(name)
- local f = programs[name]
- if f then
- context(f[1])
- end
-end
-
--- shared by urls and files
-
-function references.whatfrom(name)
- context((urls[name] and v_url) or (files[name] and v_file) or v_unknown)
-end
-
-function references.from(name)
- local u = urls[name]
- if u then
- local url, file, description = u[1], u[2], u[3]
- if description ~= "" then
- return description
- -- ok
- elseif file and file ~= "" then
- return url .. "/" .. file
- else
- return url
- end
- else
- local f = files[name]
- if f then
- local file, description = f[1], f[2]
- if description ~= "" then
- return description
- else
- return file
- end
- end
- end
-end
-
-function commands.from(name)
- local u = urls[name]
- if u then
- local url, file, description = u[1], u[2], u[3]
- if description ~= "" then
- context.dofromurldescription(description)
- -- ok
- elseif file and file ~= "" then
- context.dofromurlliteral(url .. "/" .. file)
- else
- context.dofromurlliteral(url)
- end
- else
- local f = files[name]
- if f then
- local file, description = f[1], f[2]
- if description ~= "" then
- context.dofromfiledescription(description)
- else
- context.dofromfileliteral(file)
- end
- end
- end
-end
-
-function references.define(prefix,reference,list)
- local d = defined[prefix] if not d then d = { } defined[prefix] = d end
- d[reference] = { "defined", list }
-end
-
-function references.reset(prefix,reference)
- local d = defined[prefix]
- if d then
- d[reference] = nil
- end
-end
-
-commands.definereference = references.define
-commands.resetreference = references.reset
-
--- \primaryreferencefoundaction
--- \secondaryreferencefoundaction
--- \referenceunknownaction
-
--- t.special t.operation t.arguments t.outer t.inner
-
--- to what extend do we check the non prefixed variant
-
-local strict = false
-
-local function resolve(prefix,reference,args,set) -- we start with prefix,reference
- if reference and reference ~= "" then
- if not set then
- set = { prefix = prefix, reference = reference }
- else
- set.reference = set.reference or reference
- set.prefix = set.prefix or prefix
- end
- local r = settings_to_array(reference)
- for i=1,#r do
- local ri = r[i]
- local d
- if strict then
- d = defined[prefix] or defined[""]
- d = d and d[ri]
- else
- d = defined[prefix]
- d = d and d[ri]
- if not d then
- d = defined[""]
- d = d and d[ri]
- end
- end
- if d then
- resolve(prefix,d[2],nil,set)
- else
- local var = splitreference(ri)
- if var then
- var.reference = ri
- local vo, vi = var.outer, var.inner
- if not vo and vi then
- -- to be checked
- if strict then
- d = defined[prefix] or defined[""]
- d = d and d[vi]
- else
- d = defined[prefix]
- d = d and d[vi]
- if not d then
- d = defined[""]
- d = d and d[vi]
- end
- end
- --
- if d then
- resolve(prefix,d[2],var.arguments,set) -- args can be nil
- else
- if args then var.arguments = args end
- set[#set+1] = var
- end
- else
- if args then var.arguments = args end
- set[#set+1] = var
- end
- if var.has_tex then
- set.has_tex = true
- end
- else
- -- report_references("funny pattern %a",ri)
- end
- end
- end
- return set
- else
- return { }
- end
-end
-
--- prefix == "" is valid prefix which saves multistep lookup
-
-references.currentset = nil
-
-function commands.setreferenceoperation(k,v)
- references.currentset[k].operation = v
-end
-
-function commands.setreferencearguments(k,v)
- references.currentset[k].arguments = v
-end
-
-local expandreferenceoperation = context.expandreferenceoperation
-local expandreferencearguments = context.expandreferencearguments
-
-function references.expandcurrent() -- todo: two booleans: o_has_tex& a_has_tex
- local currentset = references.currentset
- if currentset and currentset.has_tex then
- for i=1,#currentset do
- local ci = currentset[i]
- local operation = ci.operation
- if operation and find(operation,"\\") then -- if o_has_tex then
- expandreferenceoperation(i,operation)
- end
- local arguments = ci.arguments
- if arguments and find(arguments,"\\") then -- if a_has_tex then
- expandreferencearguments(i,arguments)
- end
- end
- end
-end
-
-commands.expandcurrentreference = references.expandcurrent -- for the moment the same
-
-local externals = { }
-
--- we have prefixes but also components:
---
--- : prefix
--- :: always external
--- ::: internal (for products) or external (for components)
-
-local function loadexternalreferences(name,utilitydata)
- local struc = utilitydata.structures
- if struc then
- local external = struc.references.collected -- direct references
- local lists = struc.lists.collected -- indirect references (derived)
- local pages = struc.pages.collected -- pagenumber data
- -- a bit weird one, as we don't have the externals in the collected
- for prefix, set in next, external do
- for reference, data in next, set do
- if trace_importing then
- report_importing("registering %a reference, kind %a, name %a, prefix %a, reference %a",
- "external","regular",name,prefix,reference)
- end
- local section = reference.section
- local realpage = reference.realpage
- if section then
- reference.sectiondata = lists[section]
- end
- if realpage then
- reference.pagedata = pages[realpage]
- end
- end
- end
- for i=1,#lists do
- local entry = lists[i]
- local metadata = entry.metadata
- local references = entry.references
- if metadata and references then
- local reference = references.reference
- if reference and reference ~= "" then
- local kind = metadata.kind
- local realpage = references.realpage
- if kind and realpage then
- references.pagedata = pages[realpage]
- local prefix = references.referenceprefix or ""
- local target = external[prefix]
- if not target then
- target = { }
- external[prefix] = target
- end
- for s in gmatch(reference,"%s*([^,]+)") do
- if trace_importing then
- report_importing("registering %s reference, kind %a, name %a, prefix %a, reference %a",
- "external",kind,name,prefix,s)
- end
- target[s] = target[s] or entry
- end
- end
- end
- end
- end
- externals[name] = external
- return external
- end
-end
-
-local externalfiles = { }
-
-table.setmetatableindex(externalfiles, function(t,k)
- local v = files[k]
- if not v then
- v = { k, k }
- end
- externalfiles[k] = v
- return v
-end)
-
-table.setmetatableindex(externals,function(t,k) -- either or not automatically
- local filename = externalfiles[k][1] -- filename
- local fullname = file.replacesuffix(filename,"tuc")
- if lfs.isfile(fullname) then -- todo: use other locator
- local utilitydata = job.loadother(fullname)
- if utilitydata then
- local external = loadexternalreferences(k,utilitydata)
- t[k] = external or false
- return external
- end
- end
- t[k] = false
- return false
-end)
-
-local productdata = allocate {
- productreferences = { },
- componentreferences = { },
- components = { },
-}
-
-references.productdata = productdata
-
-local function loadproductreferences(productname,componentname,utilitydata)
- local struc = utilitydata.structures
- if struc then
- local productreferences = struc.references.collected -- direct references
- local lists = struc.lists.collected -- indirect references (derived)
- local pages = struc.pages.collected -- pagenumber data
- -- we use indirect tables to save room but as they are eventually
- -- just references we resolve them to data here (the mechanisms
- -- that use this data check for indirectness)
- for prefix, set in next, productreferences do
- for reference, data in next, set do
- if trace_importing then
- report_importing("registering %s reference, kind %a, name %a, prefix %a, reference %a",
- "product","regular",productname,prefix,reference)
- end
- local section = reference.section
- local realpage = reference.realpage
- if section then
- reference.sectiondata = lists[section]
- end
- if realpage then
- reference.pagedata = pages[realpage]
- end
- end
- end
- --
- local componentreferences = { }
- for i=1,#lists do
- local entry = lists[i]
- local metadata = entry.metadata
- local references = entry.references
- if metadata and references then
- local reference = references.reference
- if reference and reference ~= "" then
- local kind = metadata.kind
- local realpage = references.realpage
- if kind and realpage then
- references.pagedata = pages[realpage]
- local prefix = references.referenceprefix or ""
- local component = references.component
- local ctarget, ptarget
- if not component or component == componentname then
- -- skip
- else
- -- one level up
- local external = componentreferences[component]
- if not external then
- external = { }
- componentreferences[component] = external
- end
- if component == prefix then
- prefix = ""
- end
- ctarget = external[prefix]
- if not ctarget then
- ctarget = { }
- external[prefix] = ctarget
- end
- end
- ptarget = productreferences[prefix]
- if not ptarget then
- ptarget = { }
- productreferences[prefix] = ptarget
- end
- for s in gmatch(reference,"%s*([^,]+)") do
- if ptarget then
- if trace_importing then
- report_importing("registering %s reference, kind %a, name %a, prefix %a, reference %a",
- "product",kind,productname,prefix,s)
- end
- ptarget[s] = ptarget[s] or entry
- end
- if ctarget then
- if trace_importing then
- report_importing("registering %s reference, kind %a, name %a, prefix %a, referenc %a",
- "component",kind,productname,prefix,s)
- end
- ctarget[s] = ctarget[s] or entry
- end
- end
- end
- end
- end
- end
- productdata.productreferences = productreferences -- not yet used
- productdata.componentreferences = componentreferences
- end
-end
-
-local function loadproductvariables(product,component,utilitydata)
- local struc = utilitydata.structures
- if struc then
- local lists = struc.lists and struc.lists.collected
- if lists then
- local pages = struc.pages and struc.pages.collected
- for i=1,#lists do
- local li = lists[i]
- if li.metadata.kind == "section" and li.references.component == component then
- local firstsection = li
- if firstsection.numberdata then
- local numbers = firstsection.numberdata.numbers
- if numbers then
- if trace_importing then
- report_importing("initializing section number to %:t",numbers)
- end
- productdata.firstsection = firstsection
- structures.documents.preset(numbers)
- end
- end
- if pages and firstsection.references then
- local firstpage = pages[firstsection.references.realpage]
- local number = firstpage and firstpage.number
- if number then
- if trace_importing then
- report_importing("initializing page number to %a",number)
- end
- productdata.firstpage = firstpage
- counters.set("userpage",1,number)
- end
- end
- break
- end
- end
- end
- end
-end
-
-local function componentlist(tree,target)
- local branches = tree and tree.branches
- if branches then
- for i=1,#branches do
- local branch = branches[i]
- local type = branch.type
- if type == "component" then
- if target then
- target[#target+1] = branch.name
- else
- target = { branch.name }
- end
- elseif type == "product" or type == "component" then
- target = componentlist(branch,target)
- end
- end
- end
- return target
-end
-
-local function loadproductcomponents(product,component,utilitydata)
- local job = utilitydata.job
- productdata.components = componentlist(job and job.structure and job.structure.collected) or { }
-end
-
-references.registerinitializer(function(tobesaved,collected)
- -- not that much related to tobesaved or collected
- productdata.components = componentlist(job.structure.collected) or { }
-end)
-
-function structures.references.loadpresets(product,component) -- we can consider a special components hash
- if product and component and product~= "" and component ~= "" and not productdata.product then -- maybe: productdata.filename ~= filename
- productdata.product = product
- productdata.component = component
- local fullname = file.replacesuffix(product,"tuc")
- if lfs.isfile(fullname) then -- todo: use other locator
- local utilitydata = job.loadother(fullname)
- if utilitydata then
- if trace_importing then
- report_importing("loading references for component %a of product %a from %a",component,product,fullname)
- end
- loadproductvariables (product,component,utilitydata)
- loadproductreferences(product,component,utilitydata)
- loadproductcomponents(product,component,utilitydata)
- -- inspect(productdata)
- end
- end
- end
-end
-
-structures.references.productdata = productdata
-
-local useproduct = commands.useproduct
-
-if useproduct then
-
- function commands.useproduct(product)
- useproduct(product)
- if texconditionals.autocrossfilereferences then
- local component = justacomponent()
- if component then
- if trace_referencing or trace_importing then
- report_references("loading presets for component %a of product %a",component,product)
- end
- structures.references.loadpresets(product,component)
- end
- end
- end
-
-end
-
--- productdata.firstsection.numberdata.numbers
--- productdata.firstpage.number
-
-local function report_identify_special(set,var,i,type)
- local reference = set.reference
- local prefix = set.prefix or ""
- local special = var.special
- local error = var.error
- local kind = var.kind
- if error then
- report_identifying("type %a, reference %a, index %a, prefix %a, special %a, error %a",type,reference,i,prefix,special,error)
- else
- report_identifying("type %a, reference %a, index %a, prefix %a, special %a, kind %a",type,reference,i,prefix,special,kind)
- end
-end
-
-local function report_identify_arguments(set,var,i,type)
- local reference = set.reference
- local prefix = set.prefix or ""
- local arguments = var.arguments
- local error = var.error
- local kind = var.kind
- if error then
- report_identifying("type %a, reference %a, index %a, prefix %a, arguments %a, error %a",type,reference,i,prefix,arguments,error)
- else
- report_identifying("type %a, reference %a, index %a, prefix %a, arguments %a, kind %a",type,reference,i,prefix,arguments,kind)
- end
-end
-
-local function report_identify_outer(set,var,i,type)
- local reference = set.reference
- local prefix = set.prefix or ""
- local outer = var.outer
- local error = var.error
- local kind = var.kind
- if outer then
- if error then
- report_identifying("type %a, reference %a, index %a, prefix %a, outer %a, error %a",type,reference,i,prefix,outer,error)
- else
- report_identifying("type %a, reference %a, index %a, prefix %a, outer %a, kind %a",type,reference,i,prefix,outer,kind)
- end
- else
- if error then
- report_identifying("type %a, reference %a, index %a, prefix %a, error %a",type,reference,i,prefix,error)
- else
- report_identifying("type %a, reference %a, index %a, prefix %a, kind %a",type,reference,i,prefix,kind)
- end
- end
-end
-
-local function identify_special(set,var,i)
- local special = var.special
- local s = specials[special]
- if s then
- local outer = var.outer
- local operation = var.operation
- local arguments = var.arguments
- if outer then
- if operation then
- -- special(outer::operation)
- var.kind = "special outer with operation"
- else
- -- special()
- var.kind = "special outer"
- end
- var.f = outer
- elseif operation then
- if arguments then
- -- special(operation{argument,argument})
- var.kind = "special operation with arguments"
- else
- -- special(operation)
- var.kind = "special operation"
- end
- else
- -- special()
- var.kind = "special"
- end
- if trace_identifying then
- report_identify_special(set,var,i,"1a")
- end
- else
- var.error = "unknown special"
- end
- return var
-end
-
-local function identify_arguments(set,var,i)
- local s = specials[var.inner]
- if s then
- -- inner{argument}
- var.kind = "special with arguments"
- else
- var.error = "unknown inner or special"
- end
- if trace_identifying then
- report_identify_arguments(set,var,i,"3a")
- end
- return var
-end
-
-local function identify_inner(set,var,prefix,collected,derived,tobesaved)
- local inner = var.inner
- local outer = var.outer
- -- inner ... we could move the prefix logic into the parser so that we have 'm for each entry
- -- foo:bar -> foo == prefix (first we try the global one)
- -- -:bar -> ignore prefix
- local p, i = prefix, nil
- local splitprefix, splitinner
- -- the next test is a safeguard when references are auto loaded from outer
- if inner then
- splitprefix, splitinner = lpegmatch(prefixsplitter,inner)
- end
- -- these are taken from other anonymous references
- if splitprefix and splitinner then
- if splitprefix == "-" then
- i = collected[""]
- i = i and i[splitinner]
- if i then
- p = ""
- end
- else
- i = collected[splitprefix]
- i = i and i[splitinner]
- if i then
- p = splitprefix
- end
- end
- end
- -- todo: strict here
- if not i then
- i = collected[prefix]
- i = i and i[inner]
- if i then
- p = prefix
- end
- end
- if not i and prefix ~= "" then
- i = collected[""]
- i = i and i[inner]
- if i then
- p = ""
- end
- end
- if i then
- var.i = { "reference", i }
- resolvers.reference(var)
- var.kind = "inner"
- var.p = p
- elseif derived then
- -- these are taken from other data structures (like lists)
- if splitprefix and splitinner then
- if splitprefix == "-" then
- i = derived[""]
- i = i and i[splitinner]
- if i then
- p = ""
- end
- else
- i = derived[splitprefix]
- i = i and i[splitinner]
- if i then
- p = splitprefix
- end
- end
- end
- if not i then
- i = derived[prefix]
- i = i and i[inner]
- if i then
- p = prefix
- end
- end
- if not i and prefix ~= "" then
- i = derived[""]
- i = i and i[inner]
- if i then
- p = ""
- end
- end
- if i then
- var.kind = "inner"
- var.i = i
- var.p = p
- local ri = resolvers[i[1]]
- if ri then
- ri(var)
- else
- -- can't happen as we catch it with a metatable now
- report_references("unknown inner resolver for %a",i[1])
- end
- else
- -- no prefixes here
- local s = specials[inner]
- if s then
- var.kind = "special"
- else
- i = (collected and collected[""] and collected[""][inner]) or
- (derived and derived [""] and derived [""][inner]) or
- (tobesaved and tobesaved[""] and tobesaved[""][inner])
- if i then
- var.kind = "inner"
- var.i = { "reference", i }
- resolvers.reference(var)
- var.p = ""
- else
- var.error = "unknown inner or special"
- end
- end
- end
- end
- return var
-end
-
-local function identify_outer(set,var,i)
- local outer = var.outer
- local inner = var.inner
- local external = externals[outer]
- if external then
- local v = copytable(var)
- v = identify_inner(set,v,nil,external)
- if v.i and not v.error then
- v.kind = "outer with inner"
- set.external = true
- if trace_identifying then
- report_identify_outer(set,v,i,"2a")
- end
- return v
- end
- v = copytable(var)
- local v = identify_inner(set,v,v.outer,external)
- if v.i and not v.error then
- v.kind = "outer with inner"
- set.external = true
- if trace_identifying then
- report_identify_outer(set,v,i,"2b")
- end
- return v
- end
- end
- local external = productdata.componentreferences[outer]
- if external then
- local v = identify_inner(set,copytable(var),nil,external)
- if v.i and not v.error then
- v.kind = "outer with inner"
- set.external = true
- if trace_identifying then
- report_identify_outer(set,v,i,"2c")
- end
- return v
- end
- end
- local external = productdata.productreferences[outer]
- if external then
- local vi = external[inner]
- if vi then
- var.kind = "outer with inner"
- var.i = vi
- set.external = true
- if trace_identifying then
- report_identify_outer(set,var,i,"2d")
- end
- return var
- end
- end
- -- the rest
- local special = var.special
- local arguments = var.arguments
- local operation = var.operation
- if inner then
- if arguments then
- -- outer::inner{argument}
- var.kind = "outer with inner with arguments"
- else
- -- outer::inner
- var.kind = "outer with inner"
- end
- var.i = { "reference", inner }
- resolvers.reference(var)
- var.f = outer
- if trace_identifying then
- report_identify_outer(set,var,i,"2e")
- end
- elseif special then
- local s = specials[special]
- if s then
- if operation then
- if arguments then
- -- outer::special(operation{argument,argument})
- var.kind = "outer with special and operation and arguments"
- else
- -- outer::special(operation)
- var.kind = "outer with special and operation"
- end
- else
- -- outer::special()
- var.kind = "outer with special"
- end
- var.f = outer
- else
- var.error = "unknown outer with special"
- end
- if trace_identifying then
- report_identify_outer(set,var,i,"2f")
- end
- else
- -- outer::
- var.kind = "outer"
- var.f = outer
- if trace_identifying then
- report_identify_outer(set,var,i,"2g")
- end
- end
- return var
-end
-
-local function identify_inner_or_outer(set,var,i)
- -- here we fall back on product data
- local inner = var.inner
- if inner and inner ~= "" then
- local v = identify_inner(set,copytable(var),set.prefix,collected,derived,tobesaved)
- if v.i and not v.error then
- v.kind = "inner" -- check this
- if trace_identifying then
- report_identify_outer(set,v,i,"4a")
- end
- return v
- end
-
-local components = job.structure.components
-
-if components then
- for i=1,#components do
- local component = components[i]
- local data = collected[component]
- local vi = data and data[inner]
- if vi then
- var.outer = component
- var.i = vi
- var.kind = "outer with inner"
- set.external = true
- if trace_identifying then
- report_identify_outer(set,var,i,"4x")
- end
- return var
- end
- end
-end
-
- local componentreferences = productdata.componentreferences
- local productreferences = productdata.productreferences
- local components = productdata.components
- if components and componentreferences then
- -- for component, data in next, productdata.componentreferences do -- better do this in order of processing:
- for i=1,#components do
- local component = components[i]
- local data = componentreferences[component]
- if data then
- local d = data[""]
- local vi = d and d[inner]
- if vi then
- var.outer = component
- var.i = vi
- var.kind = "outer with inner"
- set.external = true
- if trace_identifying then
- report_identify_outer(set,var,i,"4b")
- end
- return var
- end
- end
- end
- end
- local component, inner = lpegmatch(componentsplitter,inner)
- if component then
- local data = componentreferences and componentreferences[component]
- if data then
- local d = data[""]
- local vi = d and d[inner]
- if vi then
- var.inner = inner
- var.outer = component
- var.i = vi
- var.kind = "outer with inner"
- set.external = true
- if trace_identifying then
- report_identify_outer(set,var,i,"4c")
- end
- return var
- end
- end
- local data = productreferences and productreferences[component]
- if data then
- local vi = data[inner]
- if vi then
- var.inner = inner
- var.outer = component
- var.i = vi
- var.kind = "outer with inner"
- set.external = true
- if trace_identifying then
- report_identify_outer(set,var,i,"4d")
- end
- return var
- end
- end
- end
- var.error = "unknown inner"
- else
- var.error = "no inner"
- end
- if trace_identifying then
- report_identify_outer(set,var,i,"4e")
- end
- return var
-end
-
--- local function identify_inner_or_outer(set,var,i)
--- -- we might consider first checking with a prefix prepended and then without
--- -- which is better for fig:oeps
--- local var = do_identify_inner_or_outer(set,var,i)
--- if var.error then
--- local prefix = set.prefix
--- if prefix and prefix ~= "" then
--- var.inner = prefix .. ':' .. var.inner
--- var.error = nil
--- return do_identify_inner_or_outer(set,var,i)
--- end
--- end
--- return var
--- end
-
-local function identify_inner_component(set,var,i)
- -- we're in a product (maybe ignore when same as component)
- local component = var.component
- identify_inner(set,var,component,collected,derived,tobesaved)
- if trace_identifying then
- report_identify_outer(set,var,i,"5a")
- end
- return var
-end
-
-local function identify_outer_component(set,var,i)
- local component = var.component
- local inner = var.inner
- local data = productdata.componentreferences[component]
- if data then
- local d = data[""]
- local vi = d and d[inner]
- if vi then
- var.inner = inner
- var.outer = component
- var.i = vi
- var.kind = "outer with inner"
- set.external = true
- if trace_identifying then
- report_identify_outer(set,var,i,"6a")
- end
- return var
- end
- end
- local data = productdata.productreferences[component]
- if data then
- local vi = data[inner]
- if vi then
- var.inner = inner
- var.outer = component
- var.i = vi
- var.kind = "outer with inner"
- set.external = true
- if trace_identifying then
- report_identify_outer(set,var,i,"6b")
- end
- return var
- end
- end
- var.error = "unknown component"
- if trace_identifying then
- report_identify_outer(set,var,i,"6c")
- end
- return var
-end
-
-local nofidentified = 0
-
-local function identify(prefix,reference)
- if not reference then
- prefix = ""
- reference = prefix
- end
- local set = resolve(prefix,reference)
- local bug = false
- texcount.referencehastexstate = set.has_tex and 1 or 0
- nofidentified = nofidentified + 1
- set.n = nofidentified
- for i=1,#set do
- local var = set[i]
- if var.special then
- var = identify_special(set,var,i)
- elseif var.outer then
- var = identify_outer(set,var,i)
- elseif var.arguments then
- var = identify_arguments(set,var,i)
- elseif not var.component then
- var = identify_inner_or_outer(set,var,i)
- elseif productcomponent() then
- var = identify_inner_component(set,var,i)
- else
- var = identify_outer_component(set,var,i)
- end
- set[i] = var
- bug = bug or var.error
- end
- references.currentset = mark(set) -- mark, else in api doc
- if trace_analyzing then
- report_references(table.serialize(set,reference))
- end
- return set, bug
-end
-
-references.identify = identify
-
-local unknowns, nofunknowns, f_valid = { }, 0, formatters["[%s][%s]"]
-
-function references.valid(prefix,reference,highlight,newwindow,layer)
- local set, bug = identify(prefix,reference)
- local unknown = bug or #set == 0
- if unknown then
- currentreference = nil -- will go away
- local str = f_valid(prefix,reference)
- local u = unknowns[str]
- if not u then
- interfaces.showmessage("references",1,str) -- 1 = unknown, 4 = illegal
- unknowns[str] = 1
- nofunknowns = nofunknowns + 1
- else
- unknowns[str] = u + 1
- end
- else
- set.highlight, set.newwindow, set.layer = highlight, newwindow, layer
- currentreference = set[1]
- end
- -- we can do the expansion here which saves a call
- return not unknown
-end
-
-function commands.doifelsereference(prefix,reference,highlight,newwindow,layer)
- commands.doifelse(references.valid(prefix,reference,highlight,newwindow,layer))
-end
-
-function references.reportproblems() -- might become local
- if nofunknowns > 0 then
- statistics.register("cross referencing", function()
- return format("%s identified, %s unknown",nofidentified,nofunknowns)
- end)
- logspushtarget("logfile")
- logsnewline()
- report_references("start problematic references")
- logsnewline()
- for k, v in table.sortedpairs(unknowns) do
- report_unknown("%4i: %s",v,k)
- end
- logsnewline()
- report_references("stop problematic references")
- logsnewline()
- logspoptarget()
- end
-end
-
-luatex.registerstopactions(references.reportproblems)
-
-local innermethod = "names"
-
-function references.setinnermethod(m)
- if m then
- if m == "page" or m == "mixed" or m == "names" then
- innermethod = m
- elseif m == true or m == v_yes then
- innermethod = "page"
- end
- end
- function references.setinnermethod()
- report_references("inner method is already set and frozen to %a",innermethod)
- end
-end
-
-function references.getinnermethod()
- return innermethod or "names"
-end
-
-directives.register("references.linkmethod", function(v) -- page mixed names
- references.setinnermethod(v)
-end)
-
--- this is inconsistent
-
-function references.setinternalreference(prefix,tag,internal,view) -- needs checking
- if innermethod == "page" then
- return unsetvalue
- else
- local t, tn = { }, 0 -- maybe add to current
- if tag then
- if prefix and prefix ~= "" then
- prefix = prefix .. ":" -- watch out, : here
- for ref in gmatch(tag,"[^,]+") do
- tn = tn + 1
- t[tn] = prefix .. ref
- end
- else
- for ref in gmatch(tag,"[^,]+") do
- tn = tn + 1
- t[tn] = ref
- end
- end
- end
- if internal and innermethod == "names" then -- mixed or page
- tn = tn + 1
- t[tn] = "aut:" .. internal
- end
- local destination = references.mark(t,nil,nil,view) -- returns an attribute
- texcount.lastdestinationattribute = destination
- return destination
- end
-end
-
-function references.setandgetattribute(kind,prefix,tag,data,view) -- maybe do internal automatically here
- local attr = references.set(kind,prefix,tag,data) and references.setinternalreference(prefix,tag,nil,view) or unsetvalue
- texcount.lastdestinationattribute = attr
- return attr
-end
-
-commands.setreferenceattribute = references.setandgetattribute
-
-function references.getinternalreference(n) -- n points into list (todo: registers)
- local l = lists.collected[n]
- return l and l.references.internal or n
-end
-
-function commands.setinternalreference(prefix,tag,internal,view) -- needs checking
- context(references.setinternalreference(prefix,tag,internal,view))
-end
-
-function commands.getinternalreference(n) -- this will also be a texcount
- local l = lists.collected[n]
- context(l and l.references.internal or n)
-end
-
---
-
-function references.getcurrentmetadata(tag)
- local data = currentreference and currentreference.i
- return data and data.metadata and data.metadata[tag]
-end
-
-function commands.getcurrentreferencemetadata(tag)
- local data = references.getcurrentmetadata(tag)
- if data then
- context(data)
- end
-end
-
-local function currentmetadata(tag)
- local data = currentreference and currentreference.i
- return data and data.metadata and data.metadata[tag]
-end
-
-references.currentmetadata = currentmetadata
-
-local function getcurrentprefixspec(default)
- -- todo: message
- return currentmetadata("kind") or "?", currentmetadata("name") or "?", default or "?"
-end
-
-references.getcurrentprefixspec = getcurrentprefixspec
-
-function commands.getcurrentprefixspec(default)
- context.getreferencestructureprefix(getcurrentprefixspec(default))
-end
-
-function references.filter(name,...) -- number page title ...
- local data = currentreference and currentreference.i -- maybe we should take realpage from here
- if data then
- if name == "realpage" then
- local cs = references.analyze() -- normally already analyzed but also sets state
- context(tonumber(cs.realpage) or 0) -- todo, return and in command namespace
- else -- assumes data is table
- local kind = type(data) == "table" and data.metadata and data.metadata.kind
- if kind then
- local filter = filters[kind] or filters.generic
- filter = filter and (filter[name] or filter.unknown or filters.generic[name] or filters.generic.unknown)
- if filter then
- if trace_referencing then
- report_references("name %a, kind %a, using dedicated filter",name,kind)
- end
- filter(data,name,...)
- elseif trace_referencing then
- report_references("name %a, kind %a, using generic filter",name,kind)
- end
- elseif trace_referencing then
- report_references("name %a, unknown kind",name)
- end
- end
- elseif name == "realpage" then
- context(0)
- elseif trace_referencing then
- report_references("name %a, no reference",name)
- end
-end
-
-function references.filterdefault()
- return references.filter("default",getcurrentprefixspec(v_default))
-end
-
-function commands.currentreferencedefault(tag)
- if not tag then tag = "default" end
- references.filter(tag,context.delayed(getcurrentprefixspec(tag)))
-end
-
-filters.generic = { }
-
-function filters.generic.title(data)
- if data then
- local titledata = data.titledata or data.useddata
- if titledata then
- helpers.title(titledata.title or "?",data.metadata)
- end
- end
-end
-
-function filters.generic.text(data)
- if data then
- local entries = data.entries or data.useddata
- if entries then
- helpers.title(entries.text or "?",data.metadata)
- end
- end
-end
-
-function filters.generic.number(data,what,prefixspec) -- todo: spec and then no stopper
- if data then
- numberdata = lists.reordered(data) -- data.numberdata
- if numberdata then
- helpers.prefix(data,prefixspec)
- sections.typesetnumber(numberdata,"number",numberdata)
- else
- local useddata = data.useddata
- if useddata and useddsta.number then
- context(useddata.number)
- end
- end
- end
-end
-
-filters.generic.default = filters.generic.text
-
-function filters.generic.page(data,prefixspec,pagespec)
- local pagedata = data.pagedata
- if pagedata then
- local number, conversion = pagedata.number, pagedata.conversion
- if not number then
- -- error
- elseif conversion then
- context.convertnumber(conversion,number)
- else
- context(number)
- end
- else
- helpers.prefixpage(data,prefixspec,pagespec)
- end
-end
-
-filters.user = { }
-
-function filters.user.unknown(data,name)
- if data then
- local userdata = data.userdata
- local userkind = userdata and userdata.kind
- if userkind then
- local filter = filters[userkind] or filters.generic
- filter = filter and (filter[name] or filter.unknown)
- if filter then
- filter(data,name)
- return
- end
- end
- local namedata = userdata and userdata[name]
- if namedata then
- context(namedata)
- end
- end
-end
-
-filters.text = { }
-
-function filters.text.title(data)
- helpers.title(data.entries.text or "?",data.metadata)
-end
-
--- no longer considered useful:
---
--- function filters.text.number(data)
--- helpers.title(data.entries.text or "?",data.metadata)
--- end
-
-function filters.text.page(data,prefixspec,pagespec)
- helpers.prefixpage(data,prefixspec,pagespec)
-end
-
-filters.full = { }
-
-filters.full.title = filters.text.title
-filters.full.page = filters.text.page
-
-filters.section = { }
-
-function filters.section.number(data,what,prefixspec)
- if data then
- local numberdata = data.numberdata
- if not numberdata then
- local useddata = data.useddata
- if useddata and useddata.number then
- context(useddata.number)
- end
- elseif numberdata.hidenumber then
- local references = data.references
- if trace_empty then
- report_empty("reference %a has a hidden number",references.reference)
- context.emptyreference() -- maybe an option
- end
- else
- sections.typesetnumber(numberdata,"number",prefixspec,numberdata)
- end
- end
-end
-
-filters.section.title = filters.generic.title
-filters.section.page = filters.generic.page
-filters.section.default = filters.section.number
-
--- filters.note = { default = filters.generic.number }
--- filters.formula = { default = filters.generic.number }
--- filters.float = { default = filters.generic.number }
--- filters.description = { default = filters.generic.number }
--- filters.item = { default = filters.generic.number }
-
-setmetatableindex(filters, function(t,k) -- beware, test with rawget
- local v = { default = filters.generic.number } -- not copy as it might be extended differently
- t[k] = v
- return v
-end)
-
--- function references.sectiontitle(n)
--- helpers.sectiontitle(lists.collected[tonumber(n) or 0])
--- end
-
--- function references.sectionnumber(n)
--- helpers.sectionnumber(lists.collected[tonumber(n) or 0])
--- end
-
--- function references.sectionpage(n,prefixspec,pagespec)
--- helpers.prefixedpage(lists.collected[tonumber(n) or 0],prefixspec,pagespec)
--- end
-
--- analyze
-
-references.testrunners = references.testrunners or { }
-references.testspecials = references.testspecials or { }
-
-local runners = references.testrunners
-local specials = references.testspecials
-
--- We need to prevent ending up in the 'relative location' analyzer as it is
--- pretty slow (progressively). In the pagebody one can best check the reference
--- real page to determine if we need contrastlocation as that is more lightweight.
-
-local function checkedpagestate(n,page)
- local r, p = referredpage(n), tonumber(page)
- if not p then
- return 0
- elseif p > r then
- return 3 -- after
- elseif p < r then
- return 2 -- before
- else
- return 1 -- same
- end
-end
-
-local function setreferencerealpage(actions)
- actions = actions or references.currentset
- if not actions then
- return 0
- else
- local realpage = actions.realpage
- if realpage then
- return realpage
- end
- local nofactions = #actions
- if nofactions > 0 then
- for i=1,nofactions do
- local a = actions[i]
- local what = runners[a.kind]
- if what then
- what = what(a,actions) -- needs documentation
- end
- end
- realpage = actions.realpage
- if realpage then
- return realpage
- end
- end
- actions.realpage = 0
- return 0
- end
-end
-
--- we store some analysis data alongside the indexed array
--- at this moment only the real reference page is analyzed
--- normally such an analysis happens in the backend code
-
-function references.analyze(actions)
- actions = actions or references.currentset
- if not actions then
- actions = { realpage = 0, pagestate = 0 }
- elseif actions.pagestate then
- -- already done
- else
- local realpage = actions.realpage or setreferencerealpage(actions)
- if realpage == 0 then
- actions.pagestate = 0
- elseif actions.external then
- actions.pagestate = 0
- else
- actions.pagestate = checkedpagestate(actions.n,realpage)
- end
- end
- return actions
-end
-
-function commands.referencepagestate(actions)
- actions = actions or references.currentset
- if not actions then
- context(0)
- else
- if not actions.pagestate then
- references.analyze(actions) -- delayed unless explicitly asked for
- end
- context(actions.pagestate)
- end
-end
-
-function commands.referencerealpage(actions)
- actions = actions or references.currentset
- context(not actions and 0 or actions.realpage or setreferencerealpage(actions))
-end
-
-local plist, nofrealpages
-
-local function realpageofpage(p) -- the last one counts !
- if not plist then
- local pages = structures.pages.collected
- nofrealpages = #pages
- plist = { }
- for rp=1,nofrealpages do
- plist[pages[rp].number] = rp
- end
- references.nofrealpages = nofrealpages
- end
- return plist[p]
-end
-
-references.realpageofpage = realpageofpage
-
-function references.checkedrealpage(r)
- if not plist then
- realpageofpage(r) -- just initialize
- end
- if not r then
- return texcount.realpageno
- elseif r < 1 then
- return 1
- elseif r > nofrealpages then
- return nofrealpages
- else
- return r
- end
-end
-
--- use local ?
-
-local pages = allocate {
- [variables.firstpage] = function() return counters.record("realpage")["first"] end,
- [variables.previouspage] = function() return counters.record("realpage")["previous"] end,
- [variables.nextpage] = function() return counters.record("realpage")["next"] end,
- [variables.lastpage] = function() return counters.record("realpage")["last"] end,
-
- [variables.firstsubpage] = function() return counters.record("subpage" )["first"] end,
- [variables.previoussubpage] = function() return counters.record("subpage" )["previous"] end,
- [variables.nextsubpage] = function() return counters.record("subpage" )["next"] end,
- [variables.lastsubpage] = function() return counters.record("subpage" )["last"] end,
-
- [variables.forward] = function() return counters.record("realpage")["forward"] end,
- [variables.backward] = function() return counters.record("realpage")["backward"] end,
-}
-
-references.pages = pages
-
--- maybe some day i will merge this in the backend code with a testmode (so each
--- runner then implements a branch)
-
-runners["inner"] = function(var,actions)
- local r = var.r
- if r then
- actions.realpage = r
- end
-end
-
-runners["special"] = function(var,actions)
- local handler = specials[var.special]
- return handler and handler(var,actions)
-end
-
-runners["special operation"] = runners["special"]
-runners["special operation with arguments"] = runners["special"]
-
--- These are the testspecials not the real ones. They are used to
--- check the validity.
-
-function specials.internal(var,actions)
- local v = references.internals[tonumber(var.operation)]
- local r = v and v.references.realpage
- if r then
- actions.realpage = r
- end
-end
-
-specials.i = specials.internal
-
-function specials.page(var,actions)
- local o = var.operation
- local p = pages[o]
- if type(p) == "function" then
- p = p()
- else
- p = tonumber(realpageofpage(tonumber(o)))
- end
- if p then
- var.r = p
- actions.realpage = actions.realpage or p -- first wins
- end
-end
-
-function specials.realpage(var,actions)
- local p = tonumber(var.operation)
- if p then
- var.r = p
- actions.realpage = actions.realpage or p -- first wins
- end
-end
-
-function specials.userpage(var,actions)
- local p = tonumber(realpageofpage(var.operation))
- if p then
- var.r = p
- actions.realpage = actions.realpage or p -- first wins
- end
-end
-
-function specials.deltapage(var,actions)
- local p = tonumber(var.operation)
- if p then
- p = references.checkedrealpage(p + texcount.realpageno)
- var.r = p
- actions.realpage = actions.realpage or p -- first wins
- end
-end
-
-function specials.section(var,actions)
- 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
- specials.internal(var,actions)
- end
-end
-
--- needs a better split ^^^
-
-commands.filterreference = references.filter
-commands.filterdefaultreference = references.filterdefault
-
--- done differently now:
-
-function references.export(usedname) end
-function references.import(usedname) end
-function references.load (usedname) end
-
-commands.exportreferences = references.export
+if not modules then modules = { } end modules ['strc-ref'] = {
+ version = 1.001,
+ comment = "companion to strc-ref.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- beware, this is a first step in the rewrite (just getting rid of
+-- the tuo file); later all access and parsing will also move to lua
+
+-- the useddata and pagedata names might change
+-- todo: pack exported data
+
+-- todo: autoload components when :::
+
+local format, find, gmatch, match, concat = string.format, string.find, string.gmatch, string.match, table.concat
+local texcount, texsetcount = tex.count, tex.setcount
+local rawget, tonumber = rawget, tonumber
+local lpegmatch = lpeg.match
+local copytable = table.copy
+local formatters = string.formatters
+
+local allocate = utilities.storage.allocate
+local mark = utilities.storage.mark
+local setmetatableindex = table.setmetatableindex
+
+local trace_referencing = false trackers.register("structures.referencing", function(v) trace_referencing = v end)
+local trace_analyzing = false trackers.register("structures.referencing.analyzing", function(v) trace_analyzing = v end)
+local trace_identifying = false trackers.register("structures.referencing.identifying", function(v) trace_identifying = v end)
+local trace_importing = false trackers.register("structures.referencing.importing", function(v) trace_importing = v end)
+local trace_empty = false trackers.register("structures.referencing.empty", function(v) trace_empty = v end)
+
+local check_duplicates = true
+
+directives.register("structures.referencing.checkduplicates", function(v)
+ check_duplicates = v
+end)
+
+local report_references = logs.reporter("references")
+local report_unknown = logs.reporter("references","unknown")
+local report_identifying = logs.reporter("references","identifying")
+local report_importing = logs.reporter("references","importing")
+local report_empty = logs.reporter("references","empty")
+
+local variables = interfaces.variables
+local constants = interfaces.constants
+local context = context
+
+local v_default = variables.default
+local v_url = variables.url
+local v_file = variables.file
+local v_unknown = variables.unknown
+local v_yes = variables.yes
+
+local texcount = tex.count
+local texconditionals = tex.conditionals
+
+local productcomponent = resolvers.jobs.productcomponent
+local justacomponent = resolvers.jobs.justacomponent
+
+local logsnewline = logs.newline
+local logspushtarget = logs.pushtarget
+local logspoptarget = logs.poptarget
+
+local settings_to_array = utilities.parsers.settings_to_array
+local unsetvalue = attributes.unsetvalue
+
+local structures = structures
+local helpers = structures.helpers
+local sections = structures.sections
+local references = structures.references
+local lists = structures.lists
+local counters = structures.counters
+
+-- some might become local
+
+references.defined = references.defined or allocate()
+
+local defined = references.defined
+local derived = allocate()
+local specials = allocate()
+local runners = allocate()
+local internals = allocate()
+local filters = allocate()
+local executers = allocate()
+local handlers = allocate()
+local tobesaved = allocate()
+local collected = allocate()
+local tobereferred = allocate()
+local referred = allocate()
+
+references.derived = derived
+references.specials = specials
+references.runners = runners
+references.internals = internals
+references.filters = filters
+references.executers = executers
+references.handlers = handlers
+references.tobesaved = tobesaved
+references.collected = collected
+references.tobereferred = tobereferred
+references.referred = referred
+
+local splitreference = references.splitreference
+local splitprefix = references.splitcomponent -- replaces: references.splitprefix
+local prefixsplitter = references.prefixsplitter
+local componentsplitter = references.componentsplitter
+
+local currentreference = nil
+
+storage.register("structures/references/defined", references.defined, "structures.references.defined")
+
+local initializers = { }
+local finalizers = { }
+
+function references.registerinitializer(func) -- we could use a token register instead
+ initializers[#initializers+1] = func
+end
+function references.registerfinalizer(func) -- we could use a token register instead
+ finalizers[#finalizers+1] = func
+end
+
+local function initializer() -- can we use a tobesaved as metatable for collected?
+ tobesaved = references.tobesaved
+ collected = references.collected
+ for i=1,#initializers do
+ initializers[i](tobesaved,collected)
+ end
+end
+
+local function finalizer()
+ for i=1,#finalizers do
+ finalizers[i](tobesaved)
+ end
+end
+
+job.register('structures.references.collected', tobesaved, initializer, finalizer)
+
+local maxreferred = 1
+local nofreferred = 0
+
+-- local function initializer() -- can we use a tobesaved as metatable for collected?
+-- tobereferred = references.tobereferred
+-- referred = references.referred
+-- nofreferred = #referred
+-- end
+
+local function initializer() -- can we use a tobesaved as metatable for collected?
+ tobereferred = references.tobereferred
+ referred = references.referred
+ setmetatableindex(referred,get) -- hm, what is get ?
+end
+
+-- We make the array sparse (maybe a finalizer should optionally return a table) because
+-- there can be quite some page links involved. We only store one action number per page
+-- which is normally good enough for what we want (e.g. see above/below) and we do
+-- a combination of a binary search and traverse backwards. A previous implementation
+-- always did a traverse and was pretty slow on a large number of links (given that this
+-- methods was used). It took me about a day to locate this as a bottleneck in processing
+-- a 2500 page interactive document with 60 links per page. In that case, traversing
+-- thousands of slots per link then brings processing to a grinding halt (especially when
+-- there are no slots at all, which is the case in a first run).
+
+local sparsetobereferred = { }
+
+local function finalizer()
+ local lastr, lasti
+ local n = 0
+ for i=1,maxreferred do
+ local r = tobereferred[i]
+ if not lastr then
+ lastr = r
+ lasti = i
+ elseif r ~= lastr then
+ n = n + 1
+ sparsetobereferred[n] = { lastr, lasti }
+ lastr = r
+ lasti = i
+ end
+ end
+ if lastr then
+ n = n + 1
+ sparsetobereferred[n] = { lastr, lasti }
+ end
+end
+
+job.register('structures.references.referred', sparsetobereferred, initializer, finalizer)
+
+local function referredpage(n)
+ local max = nofreferred
+ if max > 0 then
+ -- find match
+ local min = 1
+ while true do
+ local mid = floor((min+max)/2)
+ local r = referred[mid]
+ local m = r[2]
+ if n == m then
+ return r[1]
+ elseif n > m then
+ min = mid + 1
+ else
+ max = mid - 1
+ end
+ if min > max then
+ break
+ end
+ end
+ -- find first previous
+ for i=min,1,-1 do
+ local r = referred[i]
+ if r and r[2] < n then
+ return r[1]
+ end
+ end
+ end
+ -- fallback
+ return texcount.realpageno
+end
+
+references.referredpage = referredpage
+
+function references.registerpage(n) -- called in the backend code
+ if not tobereferred[n] then
+ if n > maxreferred then
+ maxreferred = n
+ end
+ tobereferred[n] = texcount.realpageno
+ end
+end
+
+-- todo: delay split till later as in destinations we split anyway
+
+local orders, lastorder = { }, 0
+
+local function setnextorder(kind,name)
+ lastorder = 0
+ if kind and name then
+ local ok = orders[kind]
+ if not ok then
+ ok = { }
+ orders[kind] = ok
+ end
+ lastorder = (ok[name] or 0) + 1
+ ok[name] = lastorder
+ end
+ texsetcount("global","locationorder",lastorder)
+end
+
+references.setnextorder = setnextorder
+
+function references.setnextinternal(kind,name)
+ setnextorder(kind,name) -- always incremented with internal
+ local n = texcount.locationcount + 1
+ texsetcount("global","locationcount",n)
+ return n
+end
+
+function references.currentorder(kind,name)
+ return orders[kind] and orders[kind][name] or lastorder
+end
+
+local function setcomponent(data)
+ -- we might consider doing this at the tex end, just like prefix
+ local component = productcomponent()
+ if component then
+ local references = data and data.references
+ if references then
+ references.component = component
+ end
+ return component
+ end
+ -- but for the moment we do it here (experiment)
+end
+
+commands.setnextinternalreference = references.setnextinternal
+
+function commands.currentreferenceorder(kind,name)
+ context(references.currentorder(kind,name))
+end
+
+references.setcomponent = setcomponent
+
+function references.set(kind,prefix,tag,data)
+-- setcomponent(data)
+ local pd = tobesaved[prefix] -- nicer is a metatable
+ if not pd then
+ pd = { }
+ tobesaved[prefix] = pd
+ end
+ local n = 0
+ for ref in gmatch(tag,"[^,]+") do
+ if ref ~= "" then
+ if check_duplicates and pd[ref] then
+ if prefix and prefix ~= "" then
+ report_references("redundant reference %a in namespace %a",ref,prefix)
+ else
+ report_references("redundant reference %a",ref)
+ end
+ else
+ n = n + 1
+ pd[ref] = data
+ context.dofinishsomereference(kind,prefix,ref)
+ end
+ end
+ end
+ return n > 0
+end
+
+function references.enhance(prefix,tag)
+ local l = tobesaved[prefix][tag]
+ if l then
+ l.references.realpage = texcount.realpageno
+ end
+end
+
+commands.enhancereference = references.enhance
+
+-- -- -- related to strc-ini.lua -- -- --
+
+references.resolvers = references.resolvers or { }
+local resolvers = references.resolvers
+
+local function getfromlist(var)
+ local vi = var.i
+ if vi then
+ vi = vi[3] or lists.collected[vi[2]]
+ if vi then
+ local r = vi.references and vi.references
+ if r then
+ r = r.realpage
+ end
+ if not r then
+ r = vi.pagedata and vi.pagedata
+ if r then
+ r = r.realpage
+ end
+ end
+ var.i = vi
+ var.r = r or 1
+ else
+ var.i = nil
+ var.r = 1
+ end
+ else
+ var.i = nil
+ var.r = 1
+ end
+end
+
+-- resolvers.section = getfromlist
+-- resolvers.float = getfromlist
+-- resolvers.description = getfromlist
+-- resolvers.formula = getfromlist
+-- resolvers.note = getfromlist
+
+setmetatableindex(resolvers,function(t,k)
+ local v = getfromlist
+ resolvers[k] = v
+ return v
+end)
+
+function resolvers.reference(var)
+ local vi = var.i[2] -- check
+ if vi then
+ var.i = vi
+ var.r = (vi.references and vi.references.realpage) or (vi.pagedata and vi.pagedata.realpage) or 1
+ else
+ var.i = nil
+ var.r = 1
+ end
+end
+
+local function register_from_lists(collected,derived,pages,sections)
+ local g = derived[""] if not g then g = { } derived[""] = g end -- global
+ for i=1,#collected do
+ local entry = collected[i]
+ local m, r = entry.metadata, entry.references
+ if m and r then
+ local reference = r.reference or ""
+ local prefix = r.referenceprefix or ""
+ local component = r.component and r.component or ""
+ if reference ~= "" then
+ local kind, realpage = m.kind, r.realpage
+ if kind and realpage then
+ local d = derived[prefix]
+ if not d then
+ d = { }
+ derived[prefix] = d
+ end
+ local c = derived[component]
+ if not c then
+ c = { }
+ derived[component] = c
+ end
+ local t = { kind, i, entry }
+ for s in gmatch(reference,"%s*([^,]+)") do
+ if trace_referencing then
+ report_references("list entry %a provides %a reference %a on realpage %a",i,kind,s,realpage)
+ end
+ c[s] = c[s] or t -- share them
+ d[s] = d[s] or t -- share them
+ g[s] = g[s] or t -- first wins
+ end
+ end
+ end
+ end
+ end
+-- inspect(derived)
+end
+
+references.registerinitializer(function() register_from_lists(lists.collected,derived) end)
+
+-- urls
+
+references.urls = references.urls or { }
+references.urls.data = references.urls.data or { }
+
+local urls = references.urls.data
+
+function references.urls.define(name,url,file,description)
+ if name and name ~= "" then
+ urls[name] = { url or "", file or "", description or url or file or ""}
+ end
+end
+
+local pushcatcodes = context.pushcatcodes
+local popcatcodes = context.popcatcodes
+local txtcatcodes = catcodes.numbers.txtcatcodes -- or just use "txtcatcodes"
+
+function references.urls.get(name)
+ local u = urls[name]
+ if u then
+ local url, file = u[1], u[2]
+ if file and file ~= "" then
+ return formatters["%s/%s"](url,file)
+ else
+ return url
+ end
+ end
+end
+
+function commands.geturl(name)
+ local url = references.urls.get(name)
+ if url and url ~= "" then
+ pushcatcodes(txtcatcodes)
+ context(url)
+ popcatcodes()
+ end
+end
+
+-- function commands.gethyphenatedurl(name,...)
+-- local url = references.urls.get(name)
+-- if url and url ~= "" then
+-- hyphenatedurl(url,...)
+-- end
+-- end
+
+function commands.doifurldefinedelse(name)
+ commands.doifelse(urls[name])
+end
+
+commands.useurl= references.urls.define
+
+-- files
+
+references.files = references.files or { }
+references.files.data = references.files.data or { }
+
+local files = references.files.data
+
+function references.files.define(name,file,description)
+ if name and name ~= "" then
+ files[name] = { file or "", description or file or "" }
+ end
+end
+
+function references.files.get(name,method,space) -- method: none, before, after, both, space: yes/no
+ local f = files[name]
+ if f then
+ context(f[1])
+ end
+end
+
+function commands.doiffiledefinedelse(name)
+ commands.doifelse(files[name])
+end
+
+commands.usefile= references.files.define
+
+-- helpers
+
+function references.checkedfile(whatever) -- return whatever if not resolved
+ if whatever then
+ local w = files[whatever]
+ if w then
+ return w[1]
+ else
+ return whatever
+ end
+ end
+end
+
+function references.checkedurl(whatever) -- return whatever if not resolved
+ if whatever then
+ local w = urls[whatever]
+ if w then
+ local u, f = w[1], w[2]
+ if f and f ~= "" then
+ return u .. "/" .. f
+ else
+ return u
+ end
+ else
+ return whatever
+ end
+ end
+end
+
+function references.checkedfileorurl(whatever,default) -- return nil, nil if not resolved
+ if whatever then
+ local w = files[whatever]
+ if w then
+ return w[1], nil
+ else
+ local w = urls[whatever]
+ if w then
+ local u, f = w[1], w[2]
+ if f and f ~= "" then
+ return nil, u .. "/" .. f
+ else
+ return nil, u
+ end
+ end
+ end
+ end
+ return default
+end
+
+-- programs
+
+references.programs = references.programs or { }
+references.programs.data = references.programs.data or { }
+
+local programs = references.programs.data
+
+function references.programs.define(name,file,description)
+ if name and name ~= "" then
+ programs[name] = { file or "", description or file or ""}
+ end
+end
+
+function references.programs.get(name)
+ local f = programs[name]
+ return f and f[1]
+end
+
+function references.checkedprogram(whatever) -- return whatever if not resolved
+ if whatever then
+ local w = programs[whatever]
+ if w then
+ return w[1]
+ else
+ return whatever
+ end
+ end
+end
+
+commands.defineprogram = references.programs.define
+
+function commands.getprogram(name)
+ local f = programs[name]
+ if f then
+ context(f[1])
+ end
+end
+
+-- shared by urls and files
+
+function references.whatfrom(name)
+ context((urls[name] and v_url) or (files[name] and v_file) or v_unknown)
+end
+
+function references.from(name)
+ local u = urls[name]
+ if u then
+ local url, file, description = u[1], u[2], u[3]
+ if description ~= "" then
+ return description
+ -- ok
+ elseif file and file ~= "" then
+ return url .. "/" .. file
+ else
+ return url
+ end
+ else
+ local f = files[name]
+ if f then
+ local file, description = f[1], f[2]
+ if description ~= "" then
+ return description
+ else
+ return file
+ end
+ end
+ end
+end
+
+function commands.from(name)
+ local u = urls[name]
+ if u then
+ local url, file, description = u[1], u[2], u[3]
+ if description ~= "" then
+ context.dofromurldescription(description)
+ -- ok
+ elseif file and file ~= "" then
+ context.dofromurlliteral(url .. "/" .. file)
+ else
+ context.dofromurlliteral(url)
+ end
+ else
+ local f = files[name]
+ if f then
+ local file, description = f[1], f[2]
+ if description ~= "" then
+ context.dofromfiledescription(description)
+ else
+ context.dofromfileliteral(file)
+ end
+ end
+ end
+end
+
+function references.define(prefix,reference,list)
+ local d = defined[prefix] if not d then d = { } defined[prefix] = d end
+ d[reference] = { "defined", list }
+end
+
+function references.reset(prefix,reference)
+ local d = defined[prefix]
+ if d then
+ d[reference] = nil
+ end
+end
+
+commands.definereference = references.define
+commands.resetreference = references.reset
+
+-- \primaryreferencefoundaction
+-- \secondaryreferencefoundaction
+-- \referenceunknownaction
+
+-- t.special t.operation t.arguments t.outer t.inner
+
+-- to what extend do we check the non prefixed variant
+
+local strict = false
+
+local function resolve(prefix,reference,args,set) -- we start with prefix,reference
+ if reference and reference ~= "" then
+ if not set then
+ set = { prefix = prefix, reference = reference }
+ else
+ set.reference = set.reference or reference
+ set.prefix = set.prefix or prefix
+ end
+ local r = settings_to_array(reference)
+ for i=1,#r do
+ local ri = r[i]
+ local d
+ if strict then
+ d = defined[prefix] or defined[""]
+ d = d and d[ri]
+ else
+ d = defined[prefix]
+ d = d and d[ri]
+ if not d then
+ d = defined[""]
+ d = d and d[ri]
+ end
+ end
+ if d then
+ resolve(prefix,d[2],nil,set)
+ else
+ local var = splitreference(ri)
+ if var then
+ var.reference = ri
+ local vo, vi = var.outer, var.inner
+ if not vo and vi then
+ -- to be checked
+ if strict then
+ d = defined[prefix] or defined[""]
+ d = d and d[vi]
+ else
+ d = defined[prefix]
+ d = d and d[vi]
+ if not d then
+ d = defined[""]
+ d = d and d[vi]
+ end
+ end
+ --
+ if d then
+ resolve(prefix,d[2],var.arguments,set) -- args can be nil
+ else
+ if args then var.arguments = args end
+ set[#set+1] = var
+ end
+ else
+ if args then var.arguments = args end
+ set[#set+1] = var
+ end
+ if var.has_tex then
+ set.has_tex = true
+ end
+ else
+ -- report_references("funny pattern %a",ri)
+ end
+ end
+ end
+ return set
+ else
+ return { }
+ end
+end
+
+-- prefix == "" is valid prefix which saves multistep lookup
+
+references.currentset = nil
+
+function commands.setreferenceoperation(k,v)
+ references.currentset[k].operation = v
+end
+
+function commands.setreferencearguments(k,v)
+ references.currentset[k].arguments = v
+end
+
+local expandreferenceoperation = context.expandreferenceoperation
+local expandreferencearguments = context.expandreferencearguments
+
+function references.expandcurrent() -- todo: two booleans: o_has_tex& a_has_tex
+ local currentset = references.currentset
+ if currentset and currentset.has_tex then
+ for i=1,#currentset do
+ local ci = currentset[i]
+ local operation = ci.operation
+ if operation and find(operation,"\\") then -- if o_has_tex then
+ expandreferenceoperation(i,operation)
+ end
+ local arguments = ci.arguments
+ if arguments and find(arguments,"\\") then -- if a_has_tex then
+ expandreferencearguments(i,arguments)
+ end
+ end
+ end
+end
+
+commands.expandcurrentreference = references.expandcurrent -- for the moment the same
+
+local externals = { }
+
+-- we have prefixes but also components:
+--
+-- : prefix
+-- :: always external
+-- ::: internal (for products) or external (for components)
+
+local function loadexternalreferences(name,utilitydata)
+ local struc = utilitydata.structures
+ if struc then
+ local external = struc.references.collected -- direct references
+ local lists = struc.lists.collected -- indirect references (derived)
+ local pages = struc.pages.collected -- pagenumber data
+ -- a bit weird one, as we don't have the externals in the collected
+ for prefix, set in next, external do
+ for reference, data in next, set do
+ if trace_importing then
+ report_importing("registering %a reference, kind %a, name %a, prefix %a, reference %a",
+ "external","regular",name,prefix,reference)
+ end
+ local section = reference.section
+ local realpage = reference.realpage
+ if section then
+ reference.sectiondata = lists[section]
+ end
+ if realpage then
+ reference.pagedata = pages[realpage]
+ end
+ end
+ end
+ for i=1,#lists do
+ local entry = lists[i]
+ local metadata = entry.metadata
+ local references = entry.references
+ if metadata and references then
+ local reference = references.reference
+ if reference and reference ~= "" then
+ local kind = metadata.kind
+ local realpage = references.realpage
+ if kind and realpage then
+ references.pagedata = pages[realpage]
+ local prefix = references.referenceprefix or ""
+ local target = external[prefix]
+ if not target then
+ target = { }
+ external[prefix] = target
+ end
+ for s in gmatch(reference,"%s*([^,]+)") do
+ if trace_importing then
+ report_importing("registering %s reference, kind %a, name %a, prefix %a, reference %a",
+ "external",kind,name,prefix,s)
+ end
+ target[s] = target[s] or entry
+ end
+ end
+ end
+ end
+ end
+ externals[name] = external
+ return external
+ end
+end
+
+local externalfiles = { }
+
+table.setmetatableindex(externalfiles, function(t,k)
+ local v = files[k]
+ if not v then
+ v = { k, k }
+ end
+ externalfiles[k] = v
+ return v
+end)
+
+table.setmetatableindex(externals,function(t,k) -- either or not automatically
+ local filename = externalfiles[k][1] -- filename
+ local fullname = file.replacesuffix(filename,"tuc")
+ if lfs.isfile(fullname) then -- todo: use other locator
+ local utilitydata = job.loadother(fullname)
+ if utilitydata then
+ local external = loadexternalreferences(k,utilitydata)
+ t[k] = external or false
+ return external
+ end
+ end
+ t[k] = false
+ return false
+end)
+
+local productdata = allocate {
+ productreferences = { },
+ componentreferences = { },
+ components = { },
+}
+
+references.productdata = productdata
+
+local function loadproductreferences(productname,componentname,utilitydata)
+ local struc = utilitydata.structures
+ if struc then
+ local productreferences = struc.references.collected -- direct references
+ local lists = struc.lists.collected -- indirect references (derived)
+ local pages = struc.pages.collected -- pagenumber data
+ -- we use indirect tables to save room but as they are eventually
+ -- just references we resolve them to data here (the mechanisms
+ -- that use this data check for indirectness)
+ for prefix, set in next, productreferences do
+ for reference, data in next, set do
+ if trace_importing then
+ report_importing("registering %s reference, kind %a, name %a, prefix %a, reference %a",
+ "product","regular",productname,prefix,reference)
+ end
+ local section = reference.section
+ local realpage = reference.realpage
+ if section then
+ reference.sectiondata = lists[section]
+ end
+ if realpage then
+ reference.pagedata = pages[realpage]
+ end
+ end
+ end
+ --
+ local componentreferences = { }
+ for i=1,#lists do
+ local entry = lists[i]
+ local metadata = entry.metadata
+ local references = entry.references
+ if metadata and references then
+ local reference = references.reference
+ if reference and reference ~= "" then
+ local kind = metadata.kind
+ local realpage = references.realpage
+ if kind and realpage then
+ references.pagedata = pages[realpage]
+ local prefix = references.referenceprefix or ""
+ local component = references.component
+ local ctarget, ptarget
+ if not component or component == componentname then
+ -- skip
+ else
+ -- one level up
+ local external = componentreferences[component]
+ if not external then
+ external = { }
+ componentreferences[component] = external
+ end
+ if component == prefix then
+ prefix = ""
+ end
+ ctarget = external[prefix]
+ if not ctarget then
+ ctarget = { }
+ external[prefix] = ctarget
+ end
+ end
+ ptarget = productreferences[prefix]
+ if not ptarget then
+ ptarget = { }
+ productreferences[prefix] = ptarget
+ end
+ for s in gmatch(reference,"%s*([^,]+)") do
+ if ptarget then
+ if trace_importing then
+ report_importing("registering %s reference, kind %a, name %a, prefix %a, reference %a",
+ "product",kind,productname,prefix,s)
+ end
+ ptarget[s] = ptarget[s] or entry
+ end
+ if ctarget then
+ if trace_importing then
+ report_importing("registering %s reference, kind %a, name %a, prefix %a, referenc %a",
+ "component",kind,productname,prefix,s)
+ end
+ ctarget[s] = ctarget[s] or entry
+ end
+ end
+ end
+ end
+ end
+ end
+ productdata.productreferences = productreferences -- not yet used
+ productdata.componentreferences = componentreferences
+ end
+end
+
+local function loadproductvariables(product,component,utilitydata)
+ local struc = utilitydata.structures
+ if struc then
+ local lists = struc.lists and struc.lists.collected
+ if lists then
+ local pages = struc.pages and struc.pages.collected
+ for i=1,#lists do
+ local li = lists[i]
+ if li.metadata.kind == "section" and li.references.component == component then
+ local firstsection = li
+ if firstsection.numberdata then
+ local numbers = firstsection.numberdata.numbers
+ if numbers then
+ if trace_importing then
+ report_importing("initializing section number to %:t",numbers)
+ end
+ productdata.firstsection = firstsection
+ structures.documents.preset(numbers)
+ end
+ end
+ if pages and firstsection.references then
+ local firstpage = pages[firstsection.references.realpage]
+ local number = firstpage and firstpage.number
+ if number then
+ if trace_importing then
+ report_importing("initializing page number to %a",number)
+ end
+ productdata.firstpage = firstpage
+ counters.set("userpage",1,number)
+ end
+ end
+ break
+ end
+ end
+ end
+ end
+end
+
+local function componentlist(tree,target)
+ local branches = tree and tree.branches
+ if branches then
+ for i=1,#branches do
+ local branch = branches[i]
+ local type = branch.type
+ if type == "component" then
+ if target then
+ target[#target+1] = branch.name
+ else
+ target = { branch.name }
+ end
+ elseif type == "product" or type == "component" then
+ target = componentlist(branch,target)
+ end
+ end
+ end
+ return target
+end
+
+local function loadproductcomponents(product,component,utilitydata)
+ local job = utilitydata.job
+ productdata.components = componentlist(job and job.structure and job.structure.collected) or { }
+end
+
+references.registerinitializer(function(tobesaved,collected)
+ -- not that much related to tobesaved or collected
+ productdata.components = componentlist(job.structure.collected) or { }
+end)
+
+function structures.references.loadpresets(product,component) -- we can consider a special components hash
+ if product and component and product~= "" and component ~= "" and not productdata.product then -- maybe: productdata.filename ~= filename
+ productdata.product = product
+ productdata.component = component
+ local fullname = file.replacesuffix(product,"tuc")
+ if lfs.isfile(fullname) then -- todo: use other locator
+ local utilitydata = job.loadother(fullname)
+ if utilitydata then
+ if trace_importing then
+ report_importing("loading references for component %a of product %a from %a",component,product,fullname)
+ end
+ loadproductvariables (product,component,utilitydata)
+ loadproductreferences(product,component,utilitydata)
+ loadproductcomponents(product,component,utilitydata)
+ -- inspect(productdata)
+ end
+ end
+ end
+end
+
+structures.references.productdata = productdata
+
+local useproduct = commands.useproduct
+
+if useproduct then
+
+ function commands.useproduct(product)
+ useproduct(product)
+ if texconditionals.autocrossfilereferences then
+ local component = justacomponent()
+ if component then
+ if trace_referencing or trace_importing then
+ report_references("loading presets for component %a of product %a",component,product)
+ end
+ structures.references.loadpresets(product,component)
+ end
+ end
+ end
+
+end
+
+-- productdata.firstsection.numberdata.numbers
+-- productdata.firstpage.number
+
+local function report_identify_special(set,var,i,type)
+ local reference = set.reference
+ local prefix = set.prefix or ""
+ local special = var.special
+ local error = var.error
+ local kind = var.kind
+ if error then
+ report_identifying("type %a, reference %a, index %a, prefix %a, special %a, error %a",type,reference,i,prefix,special,error)
+ else
+ report_identifying("type %a, reference %a, index %a, prefix %a, special %a, kind %a",type,reference,i,prefix,special,kind)
+ end
+end
+
+local function report_identify_arguments(set,var,i,type)
+ local reference = set.reference
+ local prefix = set.prefix or ""
+ local arguments = var.arguments
+ local error = var.error
+ local kind = var.kind
+ if error then
+ report_identifying("type %a, reference %a, index %a, prefix %a, arguments %a, error %a",type,reference,i,prefix,arguments,error)
+ else
+ report_identifying("type %a, reference %a, index %a, prefix %a, arguments %a, kind %a",type,reference,i,prefix,arguments,kind)
+ end
+end
+
+local function report_identify_outer(set,var,i,type)
+ local reference = set.reference
+ local prefix = set.prefix or ""
+ local outer = var.outer
+ local error = var.error
+ local kind = var.kind
+ if outer then
+ if error then
+ report_identifying("type %a, reference %a, index %a, prefix %a, outer %a, error %a",type,reference,i,prefix,outer,error)
+ else
+ report_identifying("type %a, reference %a, index %a, prefix %a, outer %a, kind %a",type,reference,i,prefix,outer,kind)
+ end
+ else
+ if error then
+ report_identifying("type %a, reference %a, index %a, prefix %a, error %a",type,reference,i,prefix,error)
+ else
+ report_identifying("type %a, reference %a, index %a, prefix %a, kind %a",type,reference,i,prefix,kind)
+ end
+ end
+end
+
+local function identify_special(set,var,i)
+ local special = var.special
+ local s = specials[special]
+ if s then
+ local outer = var.outer
+ local operation = var.operation
+ local arguments = var.arguments
+ if outer then
+ if operation then
+ -- special(outer::operation)
+ var.kind = "special outer with operation"
+ else
+ -- special()
+ var.kind = "special outer"
+ end
+ var.f = outer
+ elseif operation then
+ if arguments then
+ -- special(operation{argument,argument})
+ var.kind = "special operation with arguments"
+ else
+ -- special(operation)
+ var.kind = "special operation"
+ end
+ else
+ -- special()
+ var.kind = "special"
+ end
+ if trace_identifying then
+ report_identify_special(set,var,i,"1a")
+ end
+ else
+ var.error = "unknown special"
+ end
+ return var
+end
+
+local function identify_arguments(set,var,i)
+ local s = specials[var.inner]
+ if s then
+ -- inner{argument}
+ var.kind = "special with arguments"
+ else
+ var.error = "unknown inner or special"
+ end
+ if trace_identifying then
+ report_identify_arguments(set,var,i,"3a")
+ end
+ return var
+end
+
+local function identify_inner(set,var,prefix,collected,derived,tobesaved)
+ local inner = var.inner
+ local outer = var.outer
+ -- inner ... we could move the prefix logic into the parser so that we have 'm for each entry
+ -- foo:bar -> foo == prefix (first we try the global one)
+ -- -:bar -> ignore prefix
+ local p, i = prefix, nil
+ local splitprefix, splitinner
+ -- the next test is a safeguard when references are auto loaded from outer
+ if inner then
+ splitprefix, splitinner = lpegmatch(prefixsplitter,inner)
+ end
+ -- these are taken from other anonymous references
+ if splitprefix and splitinner then
+ if splitprefix == "-" then
+ i = collected[""]
+ i = i and i[splitinner]
+ if i then
+ p = ""
+ end
+ else
+ i = collected[splitprefix]
+ i = i and i[splitinner]
+ if i then
+ p = splitprefix
+ end
+ end
+ end
+ -- todo: strict here
+ if not i then
+ i = collected[prefix]
+ i = i and i[inner]
+ if i then
+ p = prefix
+ end
+ end
+ if not i and prefix ~= "" then
+ i = collected[""]
+ i = i and i[inner]
+ if i then
+ p = ""
+ end
+ end
+ if i then
+ var.i = { "reference", i }
+ resolvers.reference(var)
+ var.kind = "inner"
+ var.p = p
+ elseif derived then
+ -- these are taken from other data structures (like lists)
+ if splitprefix and splitinner then
+ if splitprefix == "-" then
+ i = derived[""]
+ i = i and i[splitinner]
+ if i then
+ p = ""
+ end
+ else
+ i = derived[splitprefix]
+ i = i and i[splitinner]
+ if i then
+ p = splitprefix
+ end
+ end
+ end
+ if not i then
+ i = derived[prefix]
+ i = i and i[inner]
+ if i then
+ p = prefix
+ end
+ end
+ if not i and prefix ~= "" then
+ i = derived[""]
+ i = i and i[inner]
+ if i then
+ p = ""
+ end
+ end
+ if i then
+ var.kind = "inner"
+ var.i = i
+ var.p = p
+ local ri = resolvers[i[1]]
+ if ri then
+ ri(var)
+ else
+ -- can't happen as we catch it with a metatable now
+ report_references("unknown inner resolver for %a",i[1])
+ end
+ else
+ -- no prefixes here
+ local s = specials[inner]
+ if s then
+ var.kind = "special"
+ else
+ i = (collected and collected[""] and collected[""][inner]) or
+ (derived and derived [""] and derived [""][inner]) or
+ (tobesaved and tobesaved[""] and tobesaved[""][inner])
+ if i then
+ var.kind = "inner"
+ var.i = { "reference", i }
+ resolvers.reference(var)
+ var.p = ""
+ else
+ var.error = "unknown inner or special"
+ end
+ end
+ end
+ end
+ return var
+end
+
+local function identify_outer(set,var,i)
+ local outer = var.outer
+ local inner = var.inner
+ local external = externals[outer]
+ if external then
+ local v = copytable(var)
+ v = identify_inner(set,v,nil,external)
+ if v.i and not v.error then
+ v.kind = "outer with inner"
+ set.external = true
+ if trace_identifying then
+ report_identify_outer(set,v,i,"2a")
+ end
+ return v
+ end
+ v = copytable(var)
+ local v = identify_inner(set,v,v.outer,external)
+ if v.i and not v.error then
+ v.kind = "outer with inner"
+ set.external = true
+ if trace_identifying then
+ report_identify_outer(set,v,i,"2b")
+ end
+ return v
+ end
+ end
+ local external = productdata.componentreferences[outer]
+ if external then
+ local v = identify_inner(set,copytable(var),nil,external)
+ if v.i and not v.error then
+ v.kind = "outer with inner"
+ set.external = true
+ if trace_identifying then
+ report_identify_outer(set,v,i,"2c")
+ end
+ return v
+ end
+ end
+ local external = productdata.productreferences[outer]
+ if external then
+ local vi = external[inner]
+ if vi then
+ var.kind = "outer with inner"
+ var.i = vi
+ set.external = true
+ if trace_identifying then
+ report_identify_outer(set,var,i,"2d")
+ end
+ return var
+ end
+ end
+ -- the rest
+ local special = var.special
+ local arguments = var.arguments
+ local operation = var.operation
+ if inner then
+ if arguments then
+ -- outer::inner{argument}
+ var.kind = "outer with inner with arguments"
+ else
+ -- outer::inner
+ var.kind = "outer with inner"
+ end
+ var.i = { "reference", inner }
+ resolvers.reference(var)
+ var.f = outer
+ if trace_identifying then
+ report_identify_outer(set,var,i,"2e")
+ end
+ elseif special then
+ local s = specials[special]
+ if s then
+ if operation then
+ if arguments then
+ -- outer::special(operation{argument,argument})
+ var.kind = "outer with special and operation and arguments"
+ else
+ -- outer::special(operation)
+ var.kind = "outer with special and operation"
+ end
+ else
+ -- outer::special()
+ var.kind = "outer with special"
+ end
+ var.f = outer
+ else
+ var.error = "unknown outer with special"
+ end
+ if trace_identifying then
+ report_identify_outer(set,var,i,"2f")
+ end
+ else
+ -- outer::
+ var.kind = "outer"
+ var.f = outer
+ if trace_identifying then
+ report_identify_outer(set,var,i,"2g")
+ end
+ end
+ return var
+end
+
+local function identify_inner_or_outer(set,var,i)
+ -- here we fall back on product data
+ local inner = var.inner
+ if inner and inner ~= "" then
+ local v = identify_inner(set,copytable(var),set.prefix,collected,derived,tobesaved)
+ if v.i and not v.error then
+ v.kind = "inner" -- check this
+ if trace_identifying then
+ report_identify_outer(set,v,i,"4a")
+ end
+ return v
+ end
+
+local components = job.structure.components
+
+if components then
+ for i=1,#components do
+ local component = components[i]
+ local data = collected[component]
+ local vi = data and data[inner]
+ if vi then
+ var.outer = component
+ var.i = vi
+ var.kind = "outer with inner"
+ set.external = true
+ if trace_identifying then
+ report_identify_outer(set,var,i,"4x")
+ end
+ return var
+ end
+ end
+end
+
+ local componentreferences = productdata.componentreferences
+ local productreferences = productdata.productreferences
+ local components = productdata.components
+ if components and componentreferences then
+ -- for component, data in next, productdata.componentreferences do -- better do this in order of processing:
+ for i=1,#components do
+ local component = components[i]
+ local data = componentreferences[component]
+ if data then
+ local d = data[""]
+ local vi = d and d[inner]
+ if vi then
+ var.outer = component
+ var.i = vi
+ var.kind = "outer with inner"
+ set.external = true
+ if trace_identifying then
+ report_identify_outer(set,var,i,"4b")
+ end
+ return var
+ end
+ end
+ end
+ end
+ local component, inner = lpegmatch(componentsplitter,inner)
+ if component then
+ local data = componentreferences and componentreferences[component]
+ if data then
+ local d = data[""]
+ local vi = d and d[inner]
+ if vi then
+ var.inner = inner
+ var.outer = component
+ var.i = vi
+ var.kind = "outer with inner"
+ set.external = true
+ if trace_identifying then
+ report_identify_outer(set,var,i,"4c")
+ end
+ return var
+ end
+ end
+ local data = productreferences and productreferences[component]
+ if data then
+ local vi = data[inner]
+ if vi then
+ var.inner = inner
+ var.outer = component
+ var.i = vi
+ var.kind = "outer with inner"
+ set.external = true
+ if trace_identifying then
+ report_identify_outer(set,var,i,"4d")
+ end
+ return var
+ end
+ end
+ end
+ var.error = "unknown inner"
+ else
+ var.error = "no inner"
+ end
+ if trace_identifying then
+ report_identify_outer(set,var,i,"4e")
+ end
+ return var
+end
+
+-- local function identify_inner_or_outer(set,var,i)
+-- -- we might consider first checking with a prefix prepended and then without
+-- -- which is better for fig:oeps
+-- local var = do_identify_inner_or_outer(set,var,i)
+-- if var.error then
+-- local prefix = set.prefix
+-- if prefix and prefix ~= "" then
+-- var.inner = prefix .. ':' .. var.inner
+-- var.error = nil
+-- return do_identify_inner_or_outer(set,var,i)
+-- end
+-- end
+-- return var
+-- end
+
+local function identify_inner_component(set,var,i)
+ -- we're in a product (maybe ignore when same as component)
+ local component = var.component
+ identify_inner(set,var,component,collected,derived,tobesaved)
+ if trace_identifying then
+ report_identify_outer(set,var,i,"5a")
+ end
+ return var
+end
+
+local function identify_outer_component(set,var,i)
+ local component = var.component
+ local inner = var.inner
+ local data = productdata.componentreferences[component]
+ if data then
+ local d = data[""]
+ local vi = d and d[inner]
+ if vi then
+ var.inner = inner
+ var.outer = component
+ var.i = vi
+ var.kind = "outer with inner"
+ set.external = true
+ if trace_identifying then
+ report_identify_outer(set,var,i,"6a")
+ end
+ return var
+ end
+ end
+ local data = productdata.productreferences[component]
+ if data then
+ local vi = data[inner]
+ if vi then
+ var.inner = inner
+ var.outer = component
+ var.i = vi
+ var.kind = "outer with inner"
+ set.external = true
+ if trace_identifying then
+ report_identify_outer(set,var,i,"6b")
+ end
+ return var
+ end
+ end
+ var.error = "unknown component"
+ if trace_identifying then
+ report_identify_outer(set,var,i,"6c")
+ end
+ return var
+end
+
+local nofidentified = 0
+
+local function identify(prefix,reference)
+ if not reference then
+ prefix = ""
+ reference = prefix
+ end
+ local set = resolve(prefix,reference)
+ local bug = false
+ texcount.referencehastexstate = set.has_tex and 1 or 0
+ nofidentified = nofidentified + 1
+ set.n = nofidentified
+ for i=1,#set do
+ local var = set[i]
+ if var.special then
+ var = identify_special(set,var,i)
+ elseif var.outer then
+ var = identify_outer(set,var,i)
+ elseif var.arguments then
+ var = identify_arguments(set,var,i)
+ elseif not var.component then
+ var = identify_inner_or_outer(set,var,i)
+ elseif productcomponent() then
+ var = identify_inner_component(set,var,i)
+ else
+ var = identify_outer_component(set,var,i)
+ end
+ set[i] = var
+ bug = bug or var.error
+ end
+ references.currentset = mark(set) -- mark, else in api doc
+ if trace_analyzing then
+ report_references(table.serialize(set,reference))
+ end
+ return set, bug
+end
+
+references.identify = identify
+
+local unknowns, nofunknowns, f_valid = { }, 0, formatters["[%s][%s]"]
+
+function references.valid(prefix,reference,highlight,newwindow,layer)
+ local set, bug = identify(prefix,reference)
+ local unknown = bug or #set == 0
+ if unknown then
+ currentreference = nil -- will go away
+ local str = f_valid(prefix,reference)
+ local u = unknowns[str]
+ if not u then
+ interfaces.showmessage("references",1,str) -- 1 = unknown, 4 = illegal
+ unknowns[str] = 1
+ nofunknowns = nofunknowns + 1
+ else
+ unknowns[str] = u + 1
+ end
+ else
+ set.highlight, set.newwindow, set.layer = highlight, newwindow, layer
+ currentreference = set[1]
+ end
+ -- we can do the expansion here which saves a call
+ return not unknown
+end
+
+function commands.doifelsereference(prefix,reference,highlight,newwindow,layer)
+ commands.doifelse(references.valid(prefix,reference,highlight,newwindow,layer))
+end
+
+function references.reportproblems() -- might become local
+ if nofunknowns > 0 then
+ statistics.register("cross referencing", function()
+ return format("%s identified, %s unknown",nofidentified,nofunknowns)
+ end)
+ logspushtarget("logfile")
+ logsnewline()
+ report_references("start problematic references")
+ logsnewline()
+ for k, v in table.sortedpairs(unknowns) do
+ report_unknown("%4i: %s",v,k)
+ end
+ logsnewline()
+ report_references("stop problematic references")
+ logsnewline()
+ logspoptarget()
+ end
+end
+
+luatex.registerstopactions(references.reportproblems)
+
+local innermethod = "names"
+
+function references.setinnermethod(m)
+ if m then
+ if m == "page" or m == "mixed" or m == "names" then
+ innermethod = m
+ elseif m == true or m == v_yes then
+ innermethod = "page"
+ end
+ end
+ function references.setinnermethod()
+ report_references("inner method is already set and frozen to %a",innermethod)
+ end
+end
+
+function references.getinnermethod()
+ return innermethod or "names"
+end
+
+directives.register("references.linkmethod", function(v) -- page mixed names
+ references.setinnermethod(v)
+end)
+
+-- this is inconsistent
+
+function references.setinternalreference(prefix,tag,internal,view) -- needs checking
+ if innermethod == "page" then
+ return unsetvalue
+ else
+ local t, tn = { }, 0 -- maybe add to current
+ if tag then
+ if prefix and prefix ~= "" then
+ prefix = prefix .. ":" -- watch out, : here
+ for ref in gmatch(tag,"[^,]+") do
+ tn = tn + 1
+ t[tn] = prefix .. ref
+ end
+ else
+ for ref in gmatch(tag,"[^,]+") do
+ tn = tn + 1
+ t[tn] = ref
+ end
+ end
+ end
+ if internal and innermethod == "names" then -- mixed or page
+ tn = tn + 1
+ t[tn] = "aut:" .. internal
+ end
+ local destination = references.mark(t,nil,nil,view) -- returns an attribute
+ texcount.lastdestinationattribute = destination
+ return destination
+ end
+end
+
+function references.setandgetattribute(kind,prefix,tag,data,view) -- maybe do internal automatically here
+ local attr = references.set(kind,prefix,tag,data) and references.setinternalreference(prefix,tag,nil,view) or unsetvalue
+ texcount.lastdestinationattribute = attr
+ return attr
+end
+
+commands.setreferenceattribute = references.setandgetattribute
+
+function references.getinternalreference(n) -- n points into list (todo: registers)
+ local l = lists.collected[n]
+ return l and l.references.internal or n
+end
+
+function commands.setinternalreference(prefix,tag,internal,view) -- needs checking
+ context(references.setinternalreference(prefix,tag,internal,view))
+end
+
+function commands.getinternalreference(n) -- this will also be a texcount
+ local l = lists.collected[n]
+ context(l and l.references.internal or n)
+end
+
+--
+
+function references.getcurrentmetadata(tag)
+ local data = currentreference and currentreference.i
+ return data and data.metadata and data.metadata[tag]
+end
+
+function commands.getcurrentreferencemetadata(tag)
+ local data = references.getcurrentmetadata(tag)
+ if data then
+ context(data)
+ end
+end
+
+local function currentmetadata(tag)
+ local data = currentreference and currentreference.i
+ return data and data.metadata and data.metadata[tag]
+end
+
+references.currentmetadata = currentmetadata
+
+local function getcurrentprefixspec(default)
+ -- todo: message
+ return currentmetadata("kind") or "?", currentmetadata("name") or "?", default or "?"
+end
+
+references.getcurrentprefixspec = getcurrentprefixspec
+
+function commands.getcurrentprefixspec(default)
+ context.getreferencestructureprefix(getcurrentprefixspec(default))
+end
+
+function references.filter(name,...) -- number page title ...
+ local data = currentreference and currentreference.i -- maybe we should take realpage from here
+ if data then
+ if name == "realpage" then
+ local cs = references.analyze() -- normally already analyzed but also sets state
+ context(tonumber(cs.realpage) or 0) -- todo, return and in command namespace
+ else -- assumes data is table
+ local kind = type(data) == "table" and data.metadata and data.metadata.kind
+ if kind then
+ local filter = filters[kind] or filters.generic
+ filter = filter and (filter[name] or filter.unknown or filters.generic[name] or filters.generic.unknown)
+ if filter then
+ if trace_referencing then
+ report_references("name %a, kind %a, using dedicated filter",name,kind)
+ end
+ filter(data,name,...)
+ elseif trace_referencing then
+ report_references("name %a, kind %a, using generic filter",name,kind)
+ end
+ elseif trace_referencing then
+ report_references("name %a, unknown kind",name)
+ end
+ end
+ elseif name == "realpage" then
+ context(0)
+ elseif trace_referencing then
+ report_references("name %a, no reference",name)
+ end
+end
+
+function references.filterdefault()
+ return references.filter("default",getcurrentprefixspec(v_default))
+end
+
+function commands.currentreferencedefault(tag)
+ if not tag then tag = "default" end
+ references.filter(tag,context.delayed(getcurrentprefixspec(tag)))
+end
+
+filters.generic = { }
+
+function filters.generic.title(data)
+ if data then
+ local titledata = data.titledata or data.useddata
+ if titledata then
+ helpers.title(titledata.title or "?",data.metadata)
+ end
+ end
+end
+
+function filters.generic.text(data)
+ if data then
+ local entries = data.entries or data.useddata
+ if entries then
+ helpers.title(entries.text or "?",data.metadata)
+ end
+ end
+end
+
+function filters.generic.number(data,what,prefixspec) -- todo: spec and then no stopper
+ if data then
+ numberdata = lists.reordered(data) -- data.numberdata
+ if numberdata then
+ helpers.prefix(data,prefixspec)
+ sections.typesetnumber(numberdata,"number",numberdata)
+ else
+ local useddata = data.useddata
+ if useddata and useddsta.number then
+ context(useddata.number)
+ end
+ end
+ end
+end
+
+filters.generic.default = filters.generic.text
+
+function filters.generic.page(data,prefixspec,pagespec)
+ local pagedata = data.pagedata
+ if pagedata then
+ local number, conversion = pagedata.number, pagedata.conversion
+ if not number then
+ -- error
+ elseif conversion then
+ context.convertnumber(conversion,number)
+ else
+ context(number)
+ end
+ else
+ helpers.prefixpage(data,prefixspec,pagespec)
+ end
+end
+
+filters.user = { }
+
+function filters.user.unknown(data,name)
+ if data then
+ local userdata = data.userdata
+ local userkind = userdata and userdata.kind
+ if userkind then
+ local filter = filters[userkind] or filters.generic
+ filter = filter and (filter[name] or filter.unknown)
+ if filter then
+ filter(data,name)
+ return
+ end
+ end
+ local namedata = userdata and userdata[name]
+ if namedata then
+ context(namedata)
+ end
+ end
+end
+
+filters.text = { }
+
+function filters.text.title(data)
+ helpers.title(data.entries.text or "?",data.metadata)
+end
+
+-- no longer considered useful:
+--
+-- function filters.text.number(data)
+-- helpers.title(data.entries.text or "?",data.metadata)
+-- end
+
+function filters.text.page(data,prefixspec,pagespec)
+ helpers.prefixpage(data,prefixspec,pagespec)
+end
+
+filters.full = { }
+
+filters.full.title = filters.text.title
+filters.full.page = filters.text.page
+
+filters.section = { }
+
+function filters.section.number(data,what,prefixspec)
+ if data then
+ local numberdata = data.numberdata
+ if not numberdata then
+ local useddata = data.useddata
+ if useddata and useddata.number then
+ context(useddata.number)
+ end
+ elseif numberdata.hidenumber then
+ local references = data.references
+ if trace_empty then
+ report_empty("reference %a has a hidden number",references.reference)
+ context.emptyreference() -- maybe an option
+ end
+ else
+ sections.typesetnumber(numberdata,"number",prefixspec,numberdata)
+ end
+ end
+end
+
+filters.section.title = filters.generic.title
+filters.section.page = filters.generic.page
+filters.section.default = filters.section.number
+
+-- filters.note = { default = filters.generic.number }
+-- filters.formula = { default = filters.generic.number }
+-- filters.float = { default = filters.generic.number }
+-- filters.description = { default = filters.generic.number }
+-- filters.item = { default = filters.generic.number }
+
+setmetatableindex(filters, function(t,k) -- beware, test with rawget
+ local v = { default = filters.generic.number } -- not copy as it might be extended differently
+ t[k] = v
+ return v
+end)
+
+-- function references.sectiontitle(n)
+-- helpers.sectiontitle(lists.collected[tonumber(n) or 0])
+-- end
+
+-- function references.sectionnumber(n)
+-- helpers.sectionnumber(lists.collected[tonumber(n) or 0])
+-- end
+
+-- function references.sectionpage(n,prefixspec,pagespec)
+-- helpers.prefixedpage(lists.collected[tonumber(n) or 0],prefixspec,pagespec)
+-- end
+
+-- analyze
+
+references.testrunners = references.testrunners or { }
+references.testspecials = references.testspecials or { }
+
+local runners = references.testrunners
+local specials = references.testspecials
+
+-- We need to prevent ending up in the 'relative location' analyzer as it is
+-- pretty slow (progressively). In the pagebody one can best check the reference
+-- real page to determine if we need contrastlocation as that is more lightweight.
+
+local function checkedpagestate(n,page)
+ local r, p = referredpage(n), tonumber(page)
+ if not p then
+ return 0
+ elseif p > r then
+ return 3 -- after
+ elseif p < r then
+ return 2 -- before
+ else
+ return 1 -- same
+ end
+end
+
+local function setreferencerealpage(actions)
+ actions = actions or references.currentset
+ if not actions then
+ return 0
+ else
+ local realpage = actions.realpage
+ if realpage then
+ return realpage
+ end
+ local nofactions = #actions
+ if nofactions > 0 then
+ for i=1,nofactions do
+ local a = actions[i]
+ local what = runners[a.kind]
+ if what then
+ what = what(a,actions) -- needs documentation
+ end
+ end
+ realpage = actions.realpage
+ if realpage then
+ return realpage
+ end
+ end
+ actions.realpage = 0
+ return 0
+ end
+end
+
+-- we store some analysis data alongside the indexed array
+-- at this moment only the real reference page is analyzed
+-- normally such an analysis happens in the backend code
+
+function references.analyze(actions)
+ actions = actions or references.currentset
+ if not actions then
+ actions = { realpage = 0, pagestate = 0 }
+ elseif actions.pagestate then
+ -- already done
+ else
+ local realpage = actions.realpage or setreferencerealpage(actions)
+ if realpage == 0 then
+ actions.pagestate = 0
+ elseif actions.external then
+ actions.pagestate = 0
+ else
+ actions.pagestate = checkedpagestate(actions.n,realpage)
+ end
+ end
+ return actions
+end
+
+function commands.referencepagestate(actions)
+ actions = actions or references.currentset
+ if not actions then
+ context(0)
+ else
+ if not actions.pagestate then
+ references.analyze(actions) -- delayed unless explicitly asked for
+ end
+ context(actions.pagestate)
+ end
+end
+
+function commands.referencerealpage(actions)
+ actions = actions or references.currentset
+ context(not actions and 0 or actions.realpage or setreferencerealpage(actions))
+end
+
+local plist, nofrealpages
+
+local function realpageofpage(p) -- the last one counts !
+ if not plist then
+ local pages = structures.pages.collected
+ nofrealpages = #pages
+ plist = { }
+ for rp=1,nofrealpages do
+ plist[pages[rp].number] = rp
+ end
+ references.nofrealpages = nofrealpages
+ end
+ return plist[p]
+end
+
+references.realpageofpage = realpageofpage
+
+function references.checkedrealpage(r)
+ if not plist then
+ realpageofpage(r) -- just initialize
+ end
+ if not r then
+ return texcount.realpageno
+ elseif r < 1 then
+ return 1
+ elseif r > nofrealpages then
+ return nofrealpages
+ else
+ return r
+ end
+end
+
+-- use local ?
+
+local pages = allocate {
+ [variables.firstpage] = function() return counters.record("realpage")["first"] end,
+ [variables.previouspage] = function() return counters.record("realpage")["previous"] end,
+ [variables.nextpage] = function() return counters.record("realpage")["next"] end,
+ [variables.lastpage] = function() return counters.record("realpage")["last"] end,
+
+ [variables.firstsubpage] = function() return counters.record("subpage" )["first"] end,
+ [variables.previoussubpage] = function() return counters.record("subpage" )["previous"] end,
+ [variables.nextsubpage] = function() return counters.record("subpage" )["next"] end,
+ [variables.lastsubpage] = function() return counters.record("subpage" )["last"] end,
+
+ [variables.forward] = function() return counters.record("realpage")["forward"] end,
+ [variables.backward] = function() return counters.record("realpage")["backward"] end,
+}
+
+references.pages = pages
+
+-- maybe some day i will merge this in the backend code with a testmode (so each
+-- runner then implements a branch)
+
+runners["inner"] = function(var,actions)
+ local r = var.r
+ if r then
+ actions.realpage = r
+ end
+end
+
+runners["special"] = function(var,actions)
+ local handler = specials[var.special]
+ return handler and handler(var,actions)
+end
+
+runners["special operation"] = runners["special"]
+runners["special operation with arguments"] = runners["special"]
+
+-- These are the testspecials not the real ones. They are used to
+-- check the validity.
+
+function specials.internal(var,actions)
+ local v = references.internals[tonumber(var.operation)]
+ local r = v and v.references.realpage
+ if r then
+ actions.realpage = r
+ end
+end
+
+specials.i = specials.internal
+
+function specials.page(var,actions)
+ local o = var.operation
+ local p = pages[o]
+ if type(p) == "function" then
+ p = p()
+ else
+ p = tonumber(realpageofpage(tonumber(o)))
+ end
+ if p then
+ var.r = p
+ actions.realpage = actions.realpage or p -- first wins
+ end
+end
+
+function specials.realpage(var,actions)
+ local p = tonumber(var.operation)
+ if p then
+ var.r = p
+ actions.realpage = actions.realpage or p -- first wins
+ end
+end
+
+function specials.userpage(var,actions)
+ local p = tonumber(realpageofpage(var.operation))
+ if p then
+ var.r = p
+ actions.realpage = actions.realpage or p -- first wins
+ end
+end
+
+function specials.deltapage(var,actions)
+ local p = tonumber(var.operation)
+ if p then
+ p = references.checkedrealpage(p + texcount.realpageno)
+ var.r = p
+ actions.realpage = actions.realpage or p -- first wins
+ end
+end
+
+function specials.section(var,actions)
+ 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
+ specials.internal(var,actions)
+ end
+end
+
+-- needs a better split ^^^
+
+commands.filterreference = references.filter
+commands.filterdefaultreference = references.filterdefault
+
+-- done differently now:
+
+function references.export(usedname) end
+function references.import(usedname) end
+function references.load (usedname) end
+
+commands.exportreferences = references.export
diff --git a/tex/context/base/strc-reg.lua b/tex/context/base/strc-reg.lua
index 61d18a5d5..40cd3455b 100644
--- a/tex/context/base/strc-reg.lua
+++ b/tex/context/base/strc-reg.lua
@@ -1,862 +1,862 @@
-if not modules then modules = { } end modules ['strc-reg'] = {
- version = 1.001,
- comment = "companion to strc-reg.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 texcount = tex.count
-local format, gmatch = string.format, string.gmatch
-local equal, concat, remove = table.are_equal, table.concat, table.remove
-local utfchar = utf.char
-local lpegmatch = lpeg.match
-local allocate = utilities.storage.allocate
-
-local trace_registers = false trackers.register("structures.registers", function(v) trace_registers = v end)
-
-local report_registers = logs.reporter("structure","registers")
-
-local structures = structures
-local registers = structures.registers
-local helpers = structures.helpers
-local sections = structures.sections
-local documents = structures.documents
-local pages = structures.pages
-local references = structures.references
-
-local mappings = sorters.mappings
-local entries = sorters.entries
-local replacements = sorters.replacements
-
-local processors = typesetters.processors
-local splitprocessor = processors.split
-
-local variables = interfaces.variables
-local context = context
-
-local matchingtilldepth, numberatdepth = sections.matchingtilldepth, sections.numberatdepth
-
--- some day we will share registers and lists (although there are some conceptual
--- differences in the application of keywords)
-
-local function filtercollected(names,criterium,number,collected,prevmode)
- if not criterium or criterium == "" then criterium = variables.all end
- local data = documents.data
- local numbers, depth = data.numbers, data.depth
- local hash, result, nofresult, all, detail = { }, { }, 0, not names or names == "" or names == variables.all, nil
- if not all then
- for s in gmatch(names,"[^, ]+") do
- hash[s] = true
- end
- end
- if criterium == variables.all or criterium == variables.text then
- for i=1,#collected do
- local v = collected[i]
- if all then
- nofresult = nofresult + 1
- result[nofresult] = v
- else
- local vmn = v.metadata and v.metadata.name
- if hash[vmn] then
- nofresult = nofresult + 1
- result[nofresult] = v
- end
- end
- end
- elseif criterium == variables.current then
- for i=1,#collected do
- local v = collected[i]
- local sectionnumber = sections.collected[v.references.section]
- if sectionnumber then
- local cnumbers = sectionnumber.numbers
- if prevmode then
- if (all or hash[v.metadata.name]) and #cnumbers >= depth then -- is the = ok for lists as well?
- local ok = true
- for d=1,depth do
- if not (cnumbers[d] == numbers[d]) then -- no zero test
- ok = false
- break
- end
- end
- if ok then
- nofresult = nofresult + 1
- result[nofresult] = v
- end
- end
- else
- if (all or hash[v.metadata.name]) and #cnumbers > depth then
- local ok = true
- for d=1,depth do
- local cnd = cnumbers[d]
- if not (cnd == 0 or cnd == numbers[d]) then
- ok = false
- break
- end
- end
- if ok then
- nofresult = nofresult + 1
- result[nofresult] = v
- end
- end
- end
- end
- end
- elseif criterium == variables.previous then
- for i=1,#collected do
- local v = collected[i]
- local sectionnumber = sections.collected[v.references.section]
- if sectionnumber then
- local cnumbers = sectionnumber.numbers
- if (all or hash[v.metadata.name]) and #cnumbers >= depth then
- local ok = true
- if prevmode then
- for d=1,depth do
- if not (cnumbers[d] == numbers[d]) then
- ok = false
- break
- end
- end
- else
- for d=1,depth do
- local cnd = cnumbers[d]
- if not (cnd == 0 or cnd == numbers[d]) then
- ok = false
- break
- end
- end
- end
- if ok then
- nofresult = nofresult + 1
- result[nofresult] = v
- end
- end
- end
- end
- elseif criterium == variables["local"] then
- if sections.autodepth(data.numbers) == 0 then
- return filtercollected(names,variables.all,number,collected,prevmode)
- else
- return filtercollected(names,variables.current,number,collected,prevmode)
- end
- else -- sectionname, number
- -- beware, this works ok for registers
- local depth = sections.getlevel(criterium)
- local number = tonumber(number) or numberatdepth(depth) or 0
- if trace_registers then
- detail = format("depth: %s, number: %s, numbers: %s, startset: %s",depth,number,concat(sections.numbers(),".",1,depth),#collected)
- end
- if number > 0 then
- for i=1,#collected do
- local v = collected[i]
- local r = v.references
- if r then
- local sectionnumber = sections.collected[r.section]
- if sectionnumber then
- local metadata = v.metadata
- local cnumbers = sectionnumber.numbers
- if cnumbers then
- if (all or hash[metadata.name or false]) and #cnumbers >= depth and matchingtilldepth(depth,cnumbers) then
- nofresult = nofresult + 1
- result[nofresult] = v
- end
- end
- end
- end
- end
- end
- end
- if trace_registers then
- if detail then
- report_registers("criterium %a, detail %a, found %a",criterium,detail,#result)
- else
- report_registers("criterium %a, detail %a, found %a",criterium,nil,#result)
- end
- end
- return result
-end
-
-local tobesaved = allocate()
-local collected = allocate()
-
-registers.collected = collected
-registers.tobesaved = tobesaved
-registers.filtercollected = filtercollected
-
--- we follow a different strategy than by lists, where we have a global
--- result table; we might do that here as well but since sorting code is
--- older we delay that decision
-
-local function initializer()
- tobesaved = registers.tobesaved
- collected = registers.collected
- local internals = references.internals
- for name, list in next, collected do
- local entries = list.entries
- for e=1,#entries do
- local entry = entries[e]
- local r = entry.references
- if r then
- local internal = r and r.internal
- if internal then
- internals[internal] = entry
- end
- end
- end
- end
-end
-
-job.register('structures.registers.collected', tobesaved, initializer)
-
-local function allocate(class)
- local d = tobesaved[class]
- if not d then
- d = {
- metadata = {
- language = 'en',
- sorted = false,
- class = class
- },
- entries = { },
- }
- tobesaved[class] = d
- end
- return d
-end
-
-registers.define = allocate
-
-local entrysplitter = lpeg.tsplitat('+') -- & obsolete in mkiv
-
-local tagged = { }
-
-local function preprocessentries(rawdata)
- local entries = rawdata.entries
- if entries then
---~ table.print(rawdata)
- local e, k = entries[1] or "", entries[2] or ""
- local et, kt, entryproc, pageproc
- if type(e) == "table" then
- et = e
- else
- entryproc, e = splitprocessor(e)
- et = lpegmatch(entrysplitter,e)
- end
- if type(k) == "table" then
- kt = k
- else
- pageproc, k = splitprocessor(k)
- kt = lpegmatch(entrysplitter,k)
- end
- entries = { }
- for k=1,#et do
- entries[k] = { et[k] or "", kt[k] or "" }
- end
- for k=#et,1,-1 do
- if entries[k][1] ~= "" then
- break
- else
- entries[k] = nil
- end
- end
- rawdata.list = entries
- if pageproc or entryproc then
- rawdata.processors = { entryproc, pageproc }
- end
- rawdata.entries = nil
- end
- local seeword = rawdata.seeword
- if seeword then
- seeword.processor, seeword.text = splitprocessor(seeword.text or "")
- end
-end
-
-function registers.store(rawdata) -- metadata, references, entries
- local data = allocate(rawdata.metadata.name).entries
- local references = rawdata.references
- references.realpage = references.realpage or 0 -- just to be sure as it can be refered to
- preprocessentries(rawdata)
- data[#data+1] = rawdata
- local label = references.label
- if label and label ~= "" then tagged[label] = #data end
- context(#data)
-end
-
-function registers.enhance(name,n)
- local r = tobesaved[name].entries[n]
- if r then
- r.references.realpage = texcount.realpageno
- end
-end
-
-function registers.extend(name,tag,rawdata) -- maybe do lastsection internally
- if type(tag) == "string" then
- tag = tagged[tag]
- end
- if tag then
- local r = tobesaved[name].entries[tag]
- if r then
- local rr = r.references
- rr.lastrealpage = texcount.realpageno
- rr.lastsection = sections.currentid()
- if rawdata then
- if rawdata.entries then
- preprocessentries(rawdata)
- end
- for k,v in next, rawdata do
- if not r[k] then
- r[k] = v
- else
- local rk = r[k]
- for kk,vv in next, v do
- if type(vv) == "table" then
- if next(vv) then
- rk[kk] = vv
- end
- elseif vv ~= "" then
- rk[kk] = vv
- end
- end
- end
- end
- end
- end
- end
-end
-
--- sorting and rendering
-
-local compare = sorters.comparers.basic
-
-function registers.compare(a,b)
- local result = compare(a,b)
- if result ~= 0 then
- return result
- else
- local ka, kb = a.metadata.kind, b.metadata.kind
- if ka == kb then
- local page_a, page_b = a.references.realpage, b.references.realpage
- if not page_a or not page_b then
- return 0
- elseif page_a < page_b then
- return -1
- elseif page_a > page_b then
- return 1
- end
- elseif ka == "see" then
- return 1
- elseif kb == "see" then
- return -1
- end
- end
- return 0
-end
-
-function registers.filter(data,options)
- data.result = registers.filtercollected(nil,options.criterium,options.number,data.entries,true)
-end
-
-local seeindex = 0
-
--- meerdere loops, seewords, dan words, an seewords
-
-local function crosslinkseewords(result) -- all words
- -- collect all seewords
- local seewords = { }
- for i=1,#result do
- local data = result[i]
- local seeword = data.seeword
- if seeword then
- local seetext = seeword.text
- if seetext and not seewords[seetext] then
- seeindex = seeindex + 1
- seewords[seetext] = seeindex
- if trace_registers then
- report_registers("see word %03i: %s",seeindex,seetext)
- end
- end
- end
- end
- -- mark seeparents
- local seeparents = { }
- for i=1,#result do
- local data = result[i]
- local word = data.list[1]
- word = word and word[1]
- if word then
- local seeindex = seewords[word]
- if seeindex then
- seeparents[word] = data
- data.references.seeparent = seeindex
- if trace_registers then
- report_registers("see parent %03i: %s",seeindex,word)
- end
- end
- end
- end
- -- mark seewords and extend sort list
- for i=1,#result do
- local data = result[i]
- local seeword = data.seeword
- if seeword then
- local text = seeword.text
- if text then
- local seeparent = seeparents[text]
- if seeparent then
- local seeindex = seewords[text]
- local s, ns, d, w, l = { }, 0, data.split, seeparent.split, data.list
- -- trick: we influence sorting by adding fake subentries
- for i=1,#d do
- ns = ns + 1
- s[ns] = d[i] -- parent
- end
- for i=1,#w do
- ns = ns + 1
- s[ns] = w[i] -- see
- end
- data.split = s
- -- we also register a fake extra list entry so that the
- -- collapser works okay
- l[#l+1] = { text, "" }
- data.references.seeindex = seeindex
- if trace_registers then
- report_registers("see crosslink %03i: %s",seeindex,text)
- end
- end
- end
- end
- end
-end
-
-local function removeemptyentries(result)
- local i, n, m = 1, #result, 0
- while i <= n do
- local entry = result[i]
- if #entry.list == 0 or #entry.split == 0 then
- remove(result,i)
- n = n - 1
- m = m + 1
- else
- i = i + 1
- end
- end
- if m > 0 then
- report_registers("%s empty entries removed in register",m)
- end
-end
-
-function registers.prepare(data)
- -- data has 'list' table
- local strip = sorters.strip
- local splitter = sorters.splitters.utf
- local result = data.result
- if result then
- for i=1, #result do
- local entry, split = result[i], { }
- local list = entry.list
- if list then
- for l=1,#list do
- local ll = list[l]
- local word, key = ll[1], ll[2]
- if not key or key == "" then
- key = word
- end
- split[l] = splitter(strip(key))
- end
- end
- entry.split = split
- end
- removeemptyentries(result)
- crosslinkseewords(result)
- end
-end
-
-function registers.sort(data,options)
- sorters.sort(data.result,registers.compare)
-end
-
-function registers.unique(data,options)
- local result, nofresult, prev = { }, 0, nil
- local dataresult = data.result
- for k=1,#dataresult do
- local v = dataresult[k]
- if prev then
- local pr, vr = prev.references, v.references
- if not equal(prev.list,v.list) then
- -- ok
- elseif pr.realpage ~= vr.realpage then
- -- ok
- else
- local pl, vl = pr.lastrealpage, vr.lastrealpage
- if pl or vl then
- if not vl then
- -- ok
- elseif not pl then
- -- ok
- elseif pl ~= vl then
- -- ok
- else
- v = nil
- end
- else
- v = nil
- end
- end
- end
- if v then
- nofresult = nofresult + 1
- result[nofresult] = v
- prev = v
- end
- end
- data.result = result
-end
-
-function registers.finalize(data,options) -- maps character to index (order)
- local result = data.result
- data.metadata.nofsorted = #result
- local split, nofsplit, lasttag, done, nofdone = { }, 0, nil, nil, 0
- local firstofsplit = sorters.firstofsplit
- for k=1,#result do
- local v = result[k]
- local entry, tag = firstofsplit(v)
- if tag ~= lasttag then
- if trace_registers then
- report_registers("splitting at %a",tag)
- end
- done, nofdone = { }, 0
- nofsplit = nofsplit + 1
- split[nofsplit] = { tag = tag, data = done }
- lasttag = tag
- end
- nofdone = nofdone + 1
- done[nofdone] = v
- end
- data.result = split
-end
-
-function registers.analyzed(class,options)
- local data = collected[class]
- if data and data.entries then
- options = options or { }
- sorters.setlanguage(options.language,options.method,options.numberorder)
- registers.filter(data,options) -- filter entries into results (criteria)
- registers.prepare(data,options) -- adds split table parallel to list table
- registers.sort(data,options) -- sorts results
- registers.unique(data,options) -- get rid of duplicates
- registers.finalize(data,options) -- split result in ranges
- data.metadata.sorted = true
- return data.metadata.nofsorted or 0
- else
- return 0
- end
-end
-
--- todo take conversion from index
-
-function registers.userdata(index,name)
- local data = references.internals[tonumber(index)]
- data = data and data.userdata and data.userdata[name]
- if data then
- context(data)
- end
-end
-
--- todo: ownnumber
-
-local function pagerange(f_entry,t_entry,is_last,prefixspec,pagespec)
- local fer, ter = f_entry.references, t_entry.references
- context.registerpagerange(
- f_entry.processors and f_entry.processors[2] or "",
- fer.internal or 0,
- fer.realpage or 0,
- function()
- helpers.prefixpage(f_entry,prefixspec,pagespec)
- end,
- ter.internal or 0,
- ter.lastrealpage or ter.realpage or 0,
- function()
- if is_last then
- helpers.prefixlastpage(t_entry,prefixspec,pagespec) -- swaps page and realpage keys
- else
- helpers.prefixpage (t_entry,prefixspec,pagespec)
- end
- end
- )
-end
-
-local function pagenumber(entry,prefixspec,pagespec)
- local er = entry.references
- context.registeronepage(
- entry.processors and entry.processors[2] or "",
- er.internal or 0,
- er.realpage or 0,
- function() helpers.prefixpage(entry,prefixspec,pagespec) end
- )
-end
-
-local function collapsedpage(pages)
- for i=2,#pages do
- local first, second = pages[i-1], pages[i]
- local first_first, first_last, second_first, second_last = first[1], first[2], second[1], second[2]
- local first_last_pn = first_last .references.realpage
- local second_first_pn = second_first.references.realpage
- local second_last_pn = second_last .references.realpage
- local first_last_last = first_last .references.lastrealpage
- local second_first_last = second_first.references.lastrealpage
- if first_last_last then
- first_last_pn = first_last_last
- if second_first == second_last and second_first_pn <= first_last_pn then
- -- 2=8, 5 -> 12=8
- remove(pages,i)
- return true
- elseif second_first == second_last and second_first_pn > first_last_pn then
- -- 2=8, 9 -> 2-9
- pages[i-1] = { first_first, second_last }
- remove(pages,i)
- return true
- elseif second_last_pn < first_last_pn then
- -- 2=8, 3-4 -> 2=8
- remove(pages,i)
- return true
- elseif first_last_pn < second_last_pn then
- -- 2=8, 3-9 -> 2-9
- pages[i-1] = { first_first, second_last }
- remove(pages,i)
- return true
- elseif first_last_pn + 1 == second_first_pn and second_last_pn > first_last_pn then
- -- 2=8, 9-11 -> 2-11
- pages[i-1] = { first_first, second_last }
- remove(pages,i)
- return true
- elseif second_first.references.lastrealpage then
- -- 2=8, 9=11 -> 2-11
- pages[i-1] = { first_first, second_last }
- remove(pages,i)
- return true
- end
- elseif second_first_last then
- second_first_pn = second_first_last
- if first_last_pn == second_first_pn then
- -- 2-4, 5=9 -> 2-9
- pages[i-1] = { first_first, second_last }
- remove(pages,i)
- return true
- end
- elseif first_last_pn == second_first_pn then
- -- 2-3, 3-4 -> 2-4
- pages[i-1] = { first_last, second_last }
- remove(pages,i)
- return true
- end
- end
- return false
-end
-
-local function collapsepages(pages)
- while collapsedpage(pages) do end
- return #pages
-end
-
-function registers.flush(data,options,prefixspec,pagespec)
- local collapse_singles = options.compress == variables.yes
- local collapse_ranges = options.compress == variables.all
- local result = data.result
- context.startregisteroutput()
- for i=1,#result do
- -- ranges need checking !
- local sublist = result[i]
- local done = { false, false, false, false }
- local data = sublist.data
- local d, n = 0, 0
- context.startregistersection(sublist.tag)
- for d=1,#data do
- local entry = data[d]
- if entry.metadata.kind == "see" then
- local list = entry.list
- if #list > 1 then
- list[#list] = nil
- else
- -- we have an \seeindex{Foo}{Bar} without Foo being defined anywhere
- report_registers("invalid see entry in register %a, reference %a",entry.metadata.name,list[1][1])
- end
- end
- end
- while d < #data do
- d = d + 1
- local entry = data[d]
- local e = { false, false, false, false }
- local metadata = entry.metadata
- local kind = metadata.kind
- local list = entry.list
- for i=1,4 do -- max 4
- if list[i] then
- e[i] = list[i][1]
- end
- if e[i] ~= done[i] then
- if e[i] and e[i] ~= "" then
- done[i] = e[i]
- if n == i then
- context.stopregisterentries()
- context.startregisterentries(n)
- else
- while n > i do
- n = n - 1
- context.stopregisterentries()
- end
- while n < i do
- n = n + 1
- context.startregisterentries(n)
- end
- end
- local internal = entry.references.internal or 0
- local seeparent = entry.references.seeparent or ""
- local processor = entry.processors and entry.processors[1] or ""
- if metadata then
- context.registerentry(processor,internal,seeparent,function() helpers.title(e[i],metadata) end)
- else -- ?
- context.registerentry(processor,internal,seeindex,e[i])
- end
- else
- done[i] = false
- end
- end
- end
- if kind == 'entry' then
- context.startregisterpages()
- if collapse_singles or collapse_ranges then
- -- we collapse ranges and keep existing ranges as they are
- -- so we get prebuilt as well as built ranges
- local first, last, prev, pages, dd, nofpages = entry, nil, entry, { }, d, 0
- while dd < #data do
- dd = dd + 1
- local next = data[dd]
- if next and next.metadata.kind == "see" then
- dd = dd - 1
- break
- else
- local el, nl = entry.list, next.list
- if not equal(el,nl) then
- dd = dd - 1
- --~ first = nil
- break
- elseif next.references.lastrealpage then
- nofpages = nofpages + 1
- pages[nofpages] = first and { first, last or first } or { entry, entry }
- nofpages = nofpages + 1
- pages[nofpages] = { next, next }
- first, last, prev = nil, nil, nil
- elseif not first then
- first, prev = next, next
- elseif next.references.realpage - prev.references.realpage == 1 then -- 1 ?
- last, prev = next, next
- else
- nofpages = nofpages + 1
- pages[nofpages] = { first, last or first }
- first, last, prev = next, nil, next
- end
- end
- end
- if first then
- nofpages = nofpages + 1
- pages[nofpages] = { first, last or first }
- end
- if collapse_ranges and nofpages > 1 then
- nofpages = collapsepages(pages)
- end
- if nofpages > 0 then -- or 0
- d = dd
- for p=1,nofpages do
- local first, last = pages[p][1], pages[p][2]
- if first == last then
- if first.references.lastrealpage then
- pagerange(first,first,true,prefixspec,pagespec)
- else
- pagenumber(first,prefixspec,pagespec)
- end
- elseif last.references.lastrealpage then
- pagerange(first,last,true,prefixspec,pagespec)
- else
- pagerange(first,last,false,prefixspec,pagespec)
- end
- end
- elseif entry.references.lastrealpage then
- pagerange(entry,entry,true,prefixspec,pagespec)
- else
- pagenumber(entry,prefixspec,pagespec)
- end
- else
- while true do
- if entry.references.lastrealpage then
- pagerange(entry,entry,true,prefixspec,pagespec)
- else
- pagenumber(entry,prefixspec,pagespec)
- end
- if d == #data then
- break
- else
- d = d + 1
- local next = data[d]
- if next.metadata.kind == "see" or not equal(entry.list,next.list) then
- d = d - 1
- break
- else
- entry = next
- end
- end
- end
- end
- context.stopregisterpages()
- elseif kind == 'see' then
- local t, nt = { }, 0
- while true do
- nt = nt + 1
- t[nt] = entry
- if d == #data then
- break
- else
- d = d + 1
- local next = data[d]
- if next.metadata.kind ~= "see" or not equal(entry.list,next.list) then
- d = d - 1
- break
- else
- entry = next
- end
- end
- end
- context.startregisterseewords()
- for i=1,nt do
- local entry = t[i]
- local seeword = entry.seeword
- local seetext = seeword.text or ""
- local processor = seeword.processor or (entry.processors and entry.processors[1]) or ""
- local seeindex = entry.references.seeindex or ""
- context.registerseeword(i,n,processor,0,seeindex,seetext)
- end
- context.stopregisterseewords()
- end
- end
- while n > 0 do
- context.stopregisterentries()
- n = n - 1
- end
- context.stopregistersection()
- end
- context.stopregisteroutput()
- -- for now, maybe at some point we will do a multipass or so
- data.result = nil
- data.metadata.sorted = false
-end
-
-function registers.analyze(class,options)
- context(registers.analyzed(class,options))
-end
-
-function registers.process(class,...)
- if registers.analyzed(class,...) > 0 then
- registers.flush(collected[class],...)
- end
-end
-
+if not modules then modules = { } end modules ['strc-reg'] = {
+ version = 1.001,
+ comment = "companion to strc-reg.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 texcount = tex.count
+local format, gmatch = string.format, string.gmatch
+local equal, concat, remove = table.are_equal, table.concat, table.remove
+local utfchar = utf.char
+local lpegmatch = lpeg.match
+local allocate = utilities.storage.allocate
+
+local trace_registers = false trackers.register("structures.registers", function(v) trace_registers = v end)
+
+local report_registers = logs.reporter("structure","registers")
+
+local structures = structures
+local registers = structures.registers
+local helpers = structures.helpers
+local sections = structures.sections
+local documents = structures.documents
+local pages = structures.pages
+local references = structures.references
+
+local mappings = sorters.mappings
+local entries = sorters.entries
+local replacements = sorters.replacements
+
+local processors = typesetters.processors
+local splitprocessor = processors.split
+
+local variables = interfaces.variables
+local context = context
+
+local matchingtilldepth, numberatdepth = sections.matchingtilldepth, sections.numberatdepth
+
+-- some day we will share registers and lists (although there are some conceptual
+-- differences in the application of keywords)
+
+local function filtercollected(names,criterium,number,collected,prevmode)
+ if not criterium or criterium == "" then criterium = variables.all end
+ local data = documents.data
+ local numbers, depth = data.numbers, data.depth
+ local hash, result, nofresult, all, detail = { }, { }, 0, not names or names == "" or names == variables.all, nil
+ if not all then
+ for s in gmatch(names,"[^, ]+") do
+ hash[s] = true
+ end
+ end
+ if criterium == variables.all or criterium == variables.text then
+ for i=1,#collected do
+ local v = collected[i]
+ if all then
+ nofresult = nofresult + 1
+ result[nofresult] = v
+ else
+ local vmn = v.metadata and v.metadata.name
+ if hash[vmn] then
+ nofresult = nofresult + 1
+ result[nofresult] = v
+ end
+ end
+ end
+ elseif criterium == variables.current then
+ for i=1,#collected do
+ local v = collected[i]
+ local sectionnumber = sections.collected[v.references.section]
+ if sectionnumber then
+ local cnumbers = sectionnumber.numbers
+ if prevmode then
+ if (all or hash[v.metadata.name]) and #cnumbers >= depth then -- is the = ok for lists as well?
+ local ok = true
+ for d=1,depth do
+ if not (cnumbers[d] == numbers[d]) then -- no zero test
+ ok = false
+ break
+ end
+ end
+ if ok then
+ nofresult = nofresult + 1
+ result[nofresult] = v
+ end
+ end
+ else
+ if (all or hash[v.metadata.name]) and #cnumbers > depth then
+ local ok = true
+ for d=1,depth do
+ local cnd = cnumbers[d]
+ if not (cnd == 0 or cnd == numbers[d]) then
+ ok = false
+ break
+ end
+ end
+ if ok then
+ nofresult = nofresult + 1
+ result[nofresult] = v
+ end
+ end
+ end
+ end
+ end
+ elseif criterium == variables.previous then
+ for i=1,#collected do
+ local v = collected[i]
+ local sectionnumber = sections.collected[v.references.section]
+ if sectionnumber then
+ local cnumbers = sectionnumber.numbers
+ if (all or hash[v.metadata.name]) and #cnumbers >= depth then
+ local ok = true
+ if prevmode then
+ for d=1,depth do
+ if not (cnumbers[d] == numbers[d]) then
+ ok = false
+ break
+ end
+ end
+ else
+ for d=1,depth do
+ local cnd = cnumbers[d]
+ if not (cnd == 0 or cnd == numbers[d]) then
+ ok = false
+ break
+ end
+ end
+ end
+ if ok then
+ nofresult = nofresult + 1
+ result[nofresult] = v
+ end
+ end
+ end
+ end
+ elseif criterium == variables["local"] then
+ if sections.autodepth(data.numbers) == 0 then
+ return filtercollected(names,variables.all,number,collected,prevmode)
+ else
+ return filtercollected(names,variables.current,number,collected,prevmode)
+ end
+ else -- sectionname, number
+ -- beware, this works ok for registers
+ local depth = sections.getlevel(criterium)
+ local number = tonumber(number) or numberatdepth(depth) or 0
+ if trace_registers then
+ detail = format("depth: %s, number: %s, numbers: %s, startset: %s",depth,number,concat(sections.numbers(),".",1,depth),#collected)
+ end
+ if number > 0 then
+ for i=1,#collected do
+ local v = collected[i]
+ local r = v.references
+ if r then
+ local sectionnumber = sections.collected[r.section]
+ if sectionnumber then
+ local metadata = v.metadata
+ local cnumbers = sectionnumber.numbers
+ if cnumbers then
+ if (all or hash[metadata.name or false]) and #cnumbers >= depth and matchingtilldepth(depth,cnumbers) then
+ nofresult = nofresult + 1
+ result[nofresult] = v
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ if trace_registers then
+ if detail then
+ report_registers("criterium %a, detail %a, found %a",criterium,detail,#result)
+ else
+ report_registers("criterium %a, detail %a, found %a",criterium,nil,#result)
+ end
+ end
+ return result
+end
+
+local tobesaved = allocate()
+local collected = allocate()
+
+registers.collected = collected
+registers.tobesaved = tobesaved
+registers.filtercollected = filtercollected
+
+-- we follow a different strategy than by lists, where we have a global
+-- result table; we might do that here as well but since sorting code is
+-- older we delay that decision
+
+local function initializer()
+ tobesaved = registers.tobesaved
+ collected = registers.collected
+ local internals = references.internals
+ for name, list in next, collected do
+ local entries = list.entries
+ for e=1,#entries do
+ local entry = entries[e]
+ local r = entry.references
+ if r then
+ local internal = r and r.internal
+ if internal then
+ internals[internal] = entry
+ end
+ end
+ end
+ end
+end
+
+job.register('structures.registers.collected', tobesaved, initializer)
+
+local function allocate(class)
+ local d = tobesaved[class]
+ if not d then
+ d = {
+ metadata = {
+ language = 'en',
+ sorted = false,
+ class = class
+ },
+ entries = { },
+ }
+ tobesaved[class] = d
+ end
+ return d
+end
+
+registers.define = allocate
+
+local entrysplitter = lpeg.tsplitat('+') -- & obsolete in mkiv
+
+local tagged = { }
+
+local function preprocessentries(rawdata)
+ local entries = rawdata.entries
+ if entries then
+--~ table.print(rawdata)
+ local e, k = entries[1] or "", entries[2] or ""
+ local et, kt, entryproc, pageproc
+ if type(e) == "table" then
+ et = e
+ else
+ entryproc, e = splitprocessor(e)
+ et = lpegmatch(entrysplitter,e)
+ end
+ if type(k) == "table" then
+ kt = k
+ else
+ pageproc, k = splitprocessor(k)
+ kt = lpegmatch(entrysplitter,k)
+ end
+ entries = { }
+ for k=1,#et do
+ entries[k] = { et[k] or "", kt[k] or "" }
+ end
+ for k=#et,1,-1 do
+ if entries[k][1] ~= "" then
+ break
+ else
+ entries[k] = nil
+ end
+ end
+ rawdata.list = entries
+ if pageproc or entryproc then
+ rawdata.processors = { entryproc, pageproc }
+ end
+ rawdata.entries = nil
+ end
+ local seeword = rawdata.seeword
+ if seeword then
+ seeword.processor, seeword.text = splitprocessor(seeword.text or "")
+ end
+end
+
+function registers.store(rawdata) -- metadata, references, entries
+ local data = allocate(rawdata.metadata.name).entries
+ local references = rawdata.references
+ references.realpage = references.realpage or 0 -- just to be sure as it can be refered to
+ preprocessentries(rawdata)
+ data[#data+1] = rawdata
+ local label = references.label
+ if label and label ~= "" then tagged[label] = #data end
+ context(#data)
+end
+
+function registers.enhance(name,n)
+ local r = tobesaved[name].entries[n]
+ if r then
+ r.references.realpage = texcount.realpageno
+ end
+end
+
+function registers.extend(name,tag,rawdata) -- maybe do lastsection internally
+ if type(tag) == "string" then
+ tag = tagged[tag]
+ end
+ if tag then
+ local r = tobesaved[name].entries[tag]
+ if r then
+ local rr = r.references
+ rr.lastrealpage = texcount.realpageno
+ rr.lastsection = sections.currentid()
+ if rawdata then
+ if rawdata.entries then
+ preprocessentries(rawdata)
+ end
+ for k,v in next, rawdata do
+ if not r[k] then
+ r[k] = v
+ else
+ local rk = r[k]
+ for kk,vv in next, v do
+ if type(vv) == "table" then
+ if next(vv) then
+ rk[kk] = vv
+ end
+ elseif vv ~= "" then
+ rk[kk] = vv
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+-- sorting and rendering
+
+local compare = sorters.comparers.basic
+
+function registers.compare(a,b)
+ local result = compare(a,b)
+ if result ~= 0 then
+ return result
+ else
+ local ka, kb = a.metadata.kind, b.metadata.kind
+ if ka == kb then
+ local page_a, page_b = a.references.realpage, b.references.realpage
+ if not page_a or not page_b then
+ return 0
+ elseif page_a < page_b then
+ return -1
+ elseif page_a > page_b then
+ return 1
+ end
+ elseif ka == "see" then
+ return 1
+ elseif kb == "see" then
+ return -1
+ end
+ end
+ return 0
+end
+
+function registers.filter(data,options)
+ data.result = registers.filtercollected(nil,options.criterium,options.number,data.entries,true)
+end
+
+local seeindex = 0
+
+-- meerdere loops, seewords, dan words, an seewords
+
+local function crosslinkseewords(result) -- all words
+ -- collect all seewords
+ local seewords = { }
+ for i=1,#result do
+ local data = result[i]
+ local seeword = data.seeword
+ if seeword then
+ local seetext = seeword.text
+ if seetext and not seewords[seetext] then
+ seeindex = seeindex + 1
+ seewords[seetext] = seeindex
+ if trace_registers then
+ report_registers("see word %03i: %s",seeindex,seetext)
+ end
+ end
+ end
+ end
+ -- mark seeparents
+ local seeparents = { }
+ for i=1,#result do
+ local data = result[i]
+ local word = data.list[1]
+ word = word and word[1]
+ if word then
+ local seeindex = seewords[word]
+ if seeindex then
+ seeparents[word] = data
+ data.references.seeparent = seeindex
+ if trace_registers then
+ report_registers("see parent %03i: %s",seeindex,word)
+ end
+ end
+ end
+ end
+ -- mark seewords and extend sort list
+ for i=1,#result do
+ local data = result[i]
+ local seeword = data.seeword
+ if seeword then
+ local text = seeword.text
+ if text then
+ local seeparent = seeparents[text]
+ if seeparent then
+ local seeindex = seewords[text]
+ local s, ns, d, w, l = { }, 0, data.split, seeparent.split, data.list
+ -- trick: we influence sorting by adding fake subentries
+ for i=1,#d do
+ ns = ns + 1
+ s[ns] = d[i] -- parent
+ end
+ for i=1,#w do
+ ns = ns + 1
+ s[ns] = w[i] -- see
+ end
+ data.split = s
+ -- we also register a fake extra list entry so that the
+ -- collapser works okay
+ l[#l+1] = { text, "" }
+ data.references.seeindex = seeindex
+ if trace_registers then
+ report_registers("see crosslink %03i: %s",seeindex,text)
+ end
+ end
+ end
+ end
+ end
+end
+
+local function removeemptyentries(result)
+ local i, n, m = 1, #result, 0
+ while i <= n do
+ local entry = result[i]
+ if #entry.list == 0 or #entry.split == 0 then
+ remove(result,i)
+ n = n - 1
+ m = m + 1
+ else
+ i = i + 1
+ end
+ end
+ if m > 0 then
+ report_registers("%s empty entries removed in register",m)
+ end
+end
+
+function registers.prepare(data)
+ -- data has 'list' table
+ local strip = sorters.strip
+ local splitter = sorters.splitters.utf
+ local result = data.result
+ if result then
+ for i=1, #result do
+ local entry, split = result[i], { }
+ local list = entry.list
+ if list then
+ for l=1,#list do
+ local ll = list[l]
+ local word, key = ll[1], ll[2]
+ if not key or key == "" then
+ key = word
+ end
+ split[l] = splitter(strip(key))
+ end
+ end
+ entry.split = split
+ end
+ removeemptyentries(result)
+ crosslinkseewords(result)
+ end
+end
+
+function registers.sort(data,options)
+ sorters.sort(data.result,registers.compare)
+end
+
+function registers.unique(data,options)
+ local result, nofresult, prev = { }, 0, nil
+ local dataresult = data.result
+ for k=1,#dataresult do
+ local v = dataresult[k]
+ if prev then
+ local pr, vr = prev.references, v.references
+ if not equal(prev.list,v.list) then
+ -- ok
+ elseif pr.realpage ~= vr.realpage then
+ -- ok
+ else
+ local pl, vl = pr.lastrealpage, vr.lastrealpage
+ if pl or vl then
+ if not vl then
+ -- ok
+ elseif not pl then
+ -- ok
+ elseif pl ~= vl then
+ -- ok
+ else
+ v = nil
+ end
+ else
+ v = nil
+ end
+ end
+ end
+ if v then
+ nofresult = nofresult + 1
+ result[nofresult] = v
+ prev = v
+ end
+ end
+ data.result = result
+end
+
+function registers.finalize(data,options) -- maps character to index (order)
+ local result = data.result
+ data.metadata.nofsorted = #result
+ local split, nofsplit, lasttag, done, nofdone = { }, 0, nil, nil, 0
+ local firstofsplit = sorters.firstofsplit
+ for k=1,#result do
+ local v = result[k]
+ local entry, tag = firstofsplit(v)
+ if tag ~= lasttag then
+ if trace_registers then
+ report_registers("splitting at %a",tag)
+ end
+ done, nofdone = { }, 0
+ nofsplit = nofsplit + 1
+ split[nofsplit] = { tag = tag, data = done }
+ lasttag = tag
+ end
+ nofdone = nofdone + 1
+ done[nofdone] = v
+ end
+ data.result = split
+end
+
+function registers.analyzed(class,options)
+ local data = collected[class]
+ if data and data.entries then
+ options = options or { }
+ sorters.setlanguage(options.language,options.method,options.numberorder)
+ registers.filter(data,options) -- filter entries into results (criteria)
+ registers.prepare(data,options) -- adds split table parallel to list table
+ registers.sort(data,options) -- sorts results
+ registers.unique(data,options) -- get rid of duplicates
+ registers.finalize(data,options) -- split result in ranges
+ data.metadata.sorted = true
+ return data.metadata.nofsorted or 0
+ else
+ return 0
+ end
+end
+
+-- todo take conversion from index
+
+function registers.userdata(index,name)
+ local data = references.internals[tonumber(index)]
+ data = data and data.userdata and data.userdata[name]
+ if data then
+ context(data)
+ end
+end
+
+-- todo: ownnumber
+
+local function pagerange(f_entry,t_entry,is_last,prefixspec,pagespec)
+ local fer, ter = f_entry.references, t_entry.references
+ context.registerpagerange(
+ f_entry.processors and f_entry.processors[2] or "",
+ fer.internal or 0,
+ fer.realpage or 0,
+ function()
+ helpers.prefixpage(f_entry,prefixspec,pagespec)
+ end,
+ ter.internal or 0,
+ ter.lastrealpage or ter.realpage or 0,
+ function()
+ if is_last then
+ helpers.prefixlastpage(t_entry,prefixspec,pagespec) -- swaps page and realpage keys
+ else
+ helpers.prefixpage (t_entry,prefixspec,pagespec)
+ end
+ end
+ )
+end
+
+local function pagenumber(entry,prefixspec,pagespec)
+ local er = entry.references
+ context.registeronepage(
+ entry.processors and entry.processors[2] or "",
+ er.internal or 0,
+ er.realpage or 0,
+ function() helpers.prefixpage(entry,prefixspec,pagespec) end
+ )
+end
+
+local function collapsedpage(pages)
+ for i=2,#pages do
+ local first, second = pages[i-1], pages[i]
+ local first_first, first_last, second_first, second_last = first[1], first[2], second[1], second[2]
+ local first_last_pn = first_last .references.realpage
+ local second_first_pn = second_first.references.realpage
+ local second_last_pn = second_last .references.realpage
+ local first_last_last = first_last .references.lastrealpage
+ local second_first_last = second_first.references.lastrealpage
+ if first_last_last then
+ first_last_pn = first_last_last
+ if second_first == second_last and second_first_pn <= first_last_pn then
+ -- 2=8, 5 -> 12=8
+ remove(pages,i)
+ return true
+ elseif second_first == second_last and second_first_pn > first_last_pn then
+ -- 2=8, 9 -> 2-9
+ pages[i-1] = { first_first, second_last }
+ remove(pages,i)
+ return true
+ elseif second_last_pn < first_last_pn then
+ -- 2=8, 3-4 -> 2=8
+ remove(pages,i)
+ return true
+ elseif first_last_pn < second_last_pn then
+ -- 2=8, 3-9 -> 2-9
+ pages[i-1] = { first_first, second_last }
+ remove(pages,i)
+ return true
+ elseif first_last_pn + 1 == second_first_pn and second_last_pn > first_last_pn then
+ -- 2=8, 9-11 -> 2-11
+ pages[i-1] = { first_first, second_last }
+ remove(pages,i)
+ return true
+ elseif second_first.references.lastrealpage then
+ -- 2=8, 9=11 -> 2-11
+ pages[i-1] = { first_first, second_last }
+ remove(pages,i)
+ return true
+ end
+ elseif second_first_last then
+ second_first_pn = second_first_last
+ if first_last_pn == second_first_pn then
+ -- 2-4, 5=9 -> 2-9
+ pages[i-1] = { first_first, second_last }
+ remove(pages,i)
+ return true
+ end
+ elseif first_last_pn == second_first_pn then
+ -- 2-3, 3-4 -> 2-4
+ pages[i-1] = { first_last, second_last }
+ remove(pages,i)
+ return true
+ end
+ end
+ return false
+end
+
+local function collapsepages(pages)
+ while collapsedpage(pages) do end
+ return #pages
+end
+
+function registers.flush(data,options,prefixspec,pagespec)
+ local collapse_singles = options.compress == variables.yes
+ local collapse_ranges = options.compress == variables.all
+ local result = data.result
+ context.startregisteroutput()
+ for i=1,#result do
+ -- ranges need checking !
+ local sublist = result[i]
+ local done = { false, false, false, false }
+ local data = sublist.data
+ local d, n = 0, 0
+ context.startregistersection(sublist.tag)
+ for d=1,#data do
+ local entry = data[d]
+ if entry.metadata.kind == "see" then
+ local list = entry.list
+ if #list > 1 then
+ list[#list] = nil
+ else
+ -- we have an \seeindex{Foo}{Bar} without Foo being defined anywhere
+ report_registers("invalid see entry in register %a, reference %a",entry.metadata.name,list[1][1])
+ end
+ end
+ end
+ while d < #data do
+ d = d + 1
+ local entry = data[d]
+ local e = { false, false, false, false }
+ local metadata = entry.metadata
+ local kind = metadata.kind
+ local list = entry.list
+ for i=1,4 do -- max 4
+ if list[i] then
+ e[i] = list[i][1]
+ end
+ if e[i] ~= done[i] then
+ if e[i] and e[i] ~= "" then
+ done[i] = e[i]
+ if n == i then
+ context.stopregisterentries()
+ context.startregisterentries(n)
+ else
+ while n > i do
+ n = n - 1
+ context.stopregisterentries()
+ end
+ while n < i do
+ n = n + 1
+ context.startregisterentries(n)
+ end
+ end
+ local internal = entry.references.internal or 0
+ local seeparent = entry.references.seeparent or ""
+ local processor = entry.processors and entry.processors[1] or ""
+ if metadata then
+ context.registerentry(processor,internal,seeparent,function() helpers.title(e[i],metadata) end)
+ else -- ?
+ context.registerentry(processor,internal,seeindex,e[i])
+ end
+ else
+ done[i] = false
+ end
+ end
+ end
+ if kind == 'entry' then
+ context.startregisterpages()
+ if collapse_singles or collapse_ranges then
+ -- we collapse ranges and keep existing ranges as they are
+ -- so we get prebuilt as well as built ranges
+ local first, last, prev, pages, dd, nofpages = entry, nil, entry, { }, d, 0
+ while dd < #data do
+ dd = dd + 1
+ local next = data[dd]
+ if next and next.metadata.kind == "see" then
+ dd = dd - 1
+ break
+ else
+ local el, nl = entry.list, next.list
+ if not equal(el,nl) then
+ dd = dd - 1
+ --~ first = nil
+ break
+ elseif next.references.lastrealpage then
+ nofpages = nofpages + 1
+ pages[nofpages] = first and { first, last or first } or { entry, entry }
+ nofpages = nofpages + 1
+ pages[nofpages] = { next, next }
+ first, last, prev = nil, nil, nil
+ elseif not first then
+ first, prev = next, next
+ elseif next.references.realpage - prev.references.realpage == 1 then -- 1 ?
+ last, prev = next, next
+ else
+ nofpages = nofpages + 1
+ pages[nofpages] = { first, last or first }
+ first, last, prev = next, nil, next
+ end
+ end
+ end
+ if first then
+ nofpages = nofpages + 1
+ pages[nofpages] = { first, last or first }
+ end
+ if collapse_ranges and nofpages > 1 then
+ nofpages = collapsepages(pages)
+ end
+ if nofpages > 0 then -- or 0
+ d = dd
+ for p=1,nofpages do
+ local first, last = pages[p][1], pages[p][2]
+ if first == last then
+ if first.references.lastrealpage then
+ pagerange(first,first,true,prefixspec,pagespec)
+ else
+ pagenumber(first,prefixspec,pagespec)
+ end
+ elseif last.references.lastrealpage then
+ pagerange(first,last,true,prefixspec,pagespec)
+ else
+ pagerange(first,last,false,prefixspec,pagespec)
+ end
+ end
+ elseif entry.references.lastrealpage then
+ pagerange(entry,entry,true,prefixspec,pagespec)
+ else
+ pagenumber(entry,prefixspec,pagespec)
+ end
+ else
+ while true do
+ if entry.references.lastrealpage then
+ pagerange(entry,entry,true,prefixspec,pagespec)
+ else
+ pagenumber(entry,prefixspec,pagespec)
+ end
+ if d == #data then
+ break
+ else
+ d = d + 1
+ local next = data[d]
+ if next.metadata.kind == "see" or not equal(entry.list,next.list) then
+ d = d - 1
+ break
+ else
+ entry = next
+ end
+ end
+ end
+ end
+ context.stopregisterpages()
+ elseif kind == 'see' then
+ local t, nt = { }, 0
+ while true do
+ nt = nt + 1
+ t[nt] = entry
+ if d == #data then
+ break
+ else
+ d = d + 1
+ local next = data[d]
+ if next.metadata.kind ~= "see" or not equal(entry.list,next.list) then
+ d = d - 1
+ break
+ else
+ entry = next
+ end
+ end
+ end
+ context.startregisterseewords()
+ for i=1,nt do
+ local entry = t[i]
+ local seeword = entry.seeword
+ local seetext = seeword.text or ""
+ local processor = seeword.processor or (entry.processors and entry.processors[1]) or ""
+ local seeindex = entry.references.seeindex or ""
+ context.registerseeword(i,n,processor,0,seeindex,seetext)
+ end
+ context.stopregisterseewords()
+ end
+ end
+ while n > 0 do
+ context.stopregisterentries()
+ n = n - 1
+ end
+ context.stopregistersection()
+ end
+ context.stopregisteroutput()
+ -- for now, maybe at some point we will do a multipass or so
+ data.result = nil
+ data.metadata.sorted = false
+end
+
+function registers.analyze(class,options)
+ context(registers.analyzed(class,options))
+end
+
+function registers.process(class,...)
+ if registers.analyzed(class,...) > 0 then
+ registers.flush(collected[class],...)
+ end
+end
+
diff --git a/tex/context/base/strc-rsc.lua b/tex/context/base/strc-rsc.lua
index 34a532928..a90f577e3 100644
--- a/tex/context/base/strc-rsc.lua
+++ b/tex/context/base/strc-rsc.lua
@@ -1,154 +1,154 @@
-if not modules then modules = { } end modules ['strc-rsc'] = {
- version = 1.001,
- comment = "companion to strc-ref.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- The scanner is in a separate module so that we can test without too
--- many dependencies.
-
--- The scanner accepts nested outer, but we don't care too much, maybe
--- some day we will have both but currently the innermost wins.
-
-local lpegmatch, lpegP, lpegS, lpegCs, lpegCt, lpegCf, lpegCc, lpegC, lpegCg = lpeg.match, lpeg.P, lpeg.S, lpeg.Cs, lpeg.Ct, lpeg.Cf, lpeg.Cc, lpeg.C, lpeg.Cg
-local find = string.find
-
-local spaces = lpegP(" ")^0
-local lparent = lpegP("(")
-local rparent = lpegP(")")
-local lbrace = lpegP("{")
-local rbrace = lpegP("}")
-local tcolon = lpegP(":::") -- component or outer
-local dcolon = lpegP("::") -- outer
-local scolon = lpegP(":") -- prefix
-local backslash = lpegP("\\")
-
- lparent = spaces * lparent * spaces
- rparent = spaces * rparent * spaces
- lbrace = spaces * lbrace * spaces
- rbrace = spaces * rbrace * spaces
- tcolon = spaces * tcolon * spaces
- dcolon = spaces * dcolon * spaces
-
-local endofall = spaces * lpegP(-1)
-
-local o_token = 1 - rparent - rbrace - lparent - lbrace -- can be made more efficient
-local a_token = 1 - rbrace
-local s_token = 1 - lparent - lbrace
-local i_token = 1 - lparent - lbrace - endofall
-local f_token = 1 - lparent - lbrace - dcolon
-local c_token = 1 - lparent - lbrace - tcolon
-
-local hastexcode = lpegCg(lpegCc("has_tex") * lpegCc(true)) -- cannot be made to work
-local component = lpegCg(lpegCc("component") * lpegCs(c_token^1))
-local outer = lpegCg(lpegCc("outer") * lpegCs(f_token^1))
-local operation = lpegCg(lpegCc("operation") * lpegCs(o_token^1))
-local arguments = lpegCg(lpegCc("arguments") * lpegCs(a_token^0))
-local special = lpegCg(lpegCc("special") * lpegCs(s_token^1))
-local inner = lpegCg(lpegCc("inner") * lpegCs(i_token^1))
-
- arguments = (lbrace * arguments * rbrace)^-1
- component = component * tcolon
- outer = outer * dcolon
- operation = outer^-1 * operation -- special case: page(file::1) and file::page(1)
- inner = inner * arguments
- special = special * lparent * (operation * arguments)^-1 * rparent
-
-local referencesplitter = spaces * lpegCf (lpegCt("") * (component + outer)^-1 * (special + inner)^-1 * endofall, rawset)
-local prefixsplitter = lpegCs(lpegP((1-scolon)^1 * scolon)) * #-scolon * lpegCs(lpegP(1)^1)
-local componentsplitter = lpegCs(lpegP((1-scolon)^1)) * scolon * #-scolon * lpegCs(lpegP(1)^1)
-
-prefixsplitter = componentsplitter
-
-local function splitreference(str)
- if str and str ~= "" then
- local t = lpegmatch(referencesplitter,str)
- if t then
- local a = t.arguments
- if a and find(a,"\\") then
- t.has_tex = true
- else
- local o = t.arguments
- if o and find(o,"\\") then
- t.has_tex = true
- end
- end
- return t
- end
- end
-end
-
-local function splitprefix(str)
- return lpegmatch(prefixsplitter,str)
-end
-
-local function splitcomponent(str)
- return lpegmatch(componentsplitter,str)
-end
-
--- register in the right namespace
-
-structures = structures or { }
-structures.references = structures.references or { }
-local references = structures.references
-
-references.referencesplitter = referencesplitter
-references.splitreference = splitreference
-references.prefixsplitter = prefixsplitter
-references.splitprefix = splitprefix
-references.componentsplitter = componentsplitter
-references.splitcomponent = splitcomponent
-
--- test code:
-
--- inspect(splitreference([[component:::inner]]))
--- inspect(splitprefix([[component:::inner]]))
--- inspect(splitprefix([[component:inner]]))
-
--- inspect(splitreference([[ ]]))
--- inspect(splitreference([[ inner ]]))
--- inspect(splitreference([[ special ( operation { argument, argument } ) ]]))
--- inspect(splitreference([[ special ( operation { argument } ) ]]))
--- inspect(splitreference([[ special ( operation { argument, \argument } ) ]]))
--- inspect(splitreference([[ special ( operation { \argument } ) ]]))
--- inspect(splitreference([[ special ( operation ) ]]))
--- inspect(splitreference([[ special ( \operation ) ]]))
--- inspect(splitreference([[ special ( o\peration ) ]]))
--- inspect(splitreference([[ special ( ) ]]))
--- inspect(splitreference([[ inner { argument } ]]))
--- inspect(splitreference([[ inner { \argument } ]]))
--- inspect(splitreference([[ inner { ar\gument } ]]))
--- inspect(splitreference([[inner{a\rgument}]]))
--- inspect(splitreference([[ inner { argument, argument } ]]))
--- inspect(splitreference([[ inner { argument, \argument } ]])) -- fails: bug in lpeg?
--- inspect(splitreference([[ inner { \argument, \argument } ]]))
--- inspect(splitreference([[ outer :: ]]))
--- inspect(splitreference([[ outer :: inner]]))
--- inspect(splitreference([[ outer :: special (operation { argument,argument } ) ]]))
--- inspect(splitreference([[ outer :: special (operation { } )]]))
--- inspect(splitreference([[ outer :: special ( operation { argument, \argument } ) ]]))
--- inspect(splitreference([[ outer :: special ( operation ) ]]))
--- inspect(splitreference([[ outer :: special ( \operation ) ]]))
--- inspect(splitreference([[ outer :: special ( ) ]]))
--- inspect(splitreference([[ outer :: inner { argument } ]]))
--- inspect(splitreference([[ special ( outer :: operation ) ]]))
-
--- inspect(splitreference([[]]))
--- inspect(splitreference([[inner]]))
--- inspect(splitreference([[special(operation{argument,argument})]]))
--- inspect(splitreference([[special(operation)]]))
--- inspect(splitreference([[special(\operation)]]))
--- inspect(splitreference([[special()]]))
--- inspect(splitreference([[inner{argument}]]))
--- inspect(splitreference([[inner{\argument}]]))
--- inspect(splitreference([[outer::]]))
--- inspect(splitreference([[outer::inner]]))
--- inspect(splitreference([[outer::special(operation{argument,argument})]]))
--- inspect(splitreference([[outer::special(operation{argument,\argument})]]))
--- inspect(splitreference([[outer::special(operation)]]))
--- inspect(splitreference([[outer::special(\operation)]]))
--- inspect(splitreference([[outer::special()]]))
--- inspect(splitreference([[outer::inner{argument}]]))
--- inspect(splitreference([[special(outer::operation)]]))
+if not modules then modules = { } end modules ['strc-rsc'] = {
+ version = 1.001,
+ comment = "companion to strc-ref.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- The scanner is in a separate module so that we can test without too
+-- many dependencies.
+
+-- The scanner accepts nested outer, but we don't care too much, maybe
+-- some day we will have both but currently the innermost wins.
+
+local lpegmatch, lpegP, lpegS, lpegCs, lpegCt, lpegCf, lpegCc, lpegC, lpegCg = lpeg.match, lpeg.P, lpeg.S, lpeg.Cs, lpeg.Ct, lpeg.Cf, lpeg.Cc, lpeg.C, lpeg.Cg
+local find = string.find
+
+local spaces = lpegP(" ")^0
+local lparent = lpegP("(")
+local rparent = lpegP(")")
+local lbrace = lpegP("{")
+local rbrace = lpegP("}")
+local tcolon = lpegP(":::") -- component or outer
+local dcolon = lpegP("::") -- outer
+local scolon = lpegP(":") -- prefix
+local backslash = lpegP("\\")
+
+ lparent = spaces * lparent * spaces
+ rparent = spaces * rparent * spaces
+ lbrace = spaces * lbrace * spaces
+ rbrace = spaces * rbrace * spaces
+ tcolon = spaces * tcolon * spaces
+ dcolon = spaces * dcolon * spaces
+
+local endofall = spaces * lpegP(-1)
+
+local o_token = 1 - rparent - rbrace - lparent - lbrace -- can be made more efficient
+local a_token = 1 - rbrace
+local s_token = 1 - lparent - lbrace
+local i_token = 1 - lparent - lbrace - endofall
+local f_token = 1 - lparent - lbrace - dcolon
+local c_token = 1 - lparent - lbrace - tcolon
+
+local hastexcode = lpegCg(lpegCc("has_tex") * lpegCc(true)) -- cannot be made to work
+local component = lpegCg(lpegCc("component") * lpegCs(c_token^1))
+local outer = lpegCg(lpegCc("outer") * lpegCs(f_token^1))
+local operation = lpegCg(lpegCc("operation") * lpegCs(o_token^1))
+local arguments = lpegCg(lpegCc("arguments") * lpegCs(a_token^0))
+local special = lpegCg(lpegCc("special") * lpegCs(s_token^1))
+local inner = lpegCg(lpegCc("inner") * lpegCs(i_token^1))
+
+ arguments = (lbrace * arguments * rbrace)^-1
+ component = component * tcolon
+ outer = outer * dcolon
+ operation = outer^-1 * operation -- special case: page(file::1) and file::page(1)
+ inner = inner * arguments
+ special = special * lparent * (operation * arguments)^-1 * rparent
+
+local referencesplitter = spaces * lpegCf (lpegCt("") * (component + outer)^-1 * (special + inner)^-1 * endofall, rawset)
+local prefixsplitter = lpegCs(lpegP((1-scolon)^1 * scolon)) * #-scolon * lpegCs(lpegP(1)^1)
+local componentsplitter = lpegCs(lpegP((1-scolon)^1)) * scolon * #-scolon * lpegCs(lpegP(1)^1)
+
+prefixsplitter = componentsplitter
+
+local function splitreference(str)
+ if str and str ~= "" then
+ local t = lpegmatch(referencesplitter,str)
+ if t then
+ local a = t.arguments
+ if a and find(a,"\\") then
+ t.has_tex = true
+ else
+ local o = t.arguments
+ if o and find(o,"\\") then
+ t.has_tex = true
+ end
+ end
+ return t
+ end
+ end
+end
+
+local function splitprefix(str)
+ return lpegmatch(prefixsplitter,str)
+end
+
+local function splitcomponent(str)
+ return lpegmatch(componentsplitter,str)
+end
+
+-- register in the right namespace
+
+structures = structures or { }
+structures.references = structures.references or { }
+local references = structures.references
+
+references.referencesplitter = referencesplitter
+references.splitreference = splitreference
+references.prefixsplitter = prefixsplitter
+references.splitprefix = splitprefix
+references.componentsplitter = componentsplitter
+references.splitcomponent = splitcomponent
+
+-- test code:
+
+-- inspect(splitreference([[component:::inner]]))
+-- inspect(splitprefix([[component:::inner]]))
+-- inspect(splitprefix([[component:inner]]))
+
+-- inspect(splitreference([[ ]]))
+-- inspect(splitreference([[ inner ]]))
+-- inspect(splitreference([[ special ( operation { argument, argument } ) ]]))
+-- inspect(splitreference([[ special ( operation { argument } ) ]]))
+-- inspect(splitreference([[ special ( operation { argument, \argument } ) ]]))
+-- inspect(splitreference([[ special ( operation { \argument } ) ]]))
+-- inspect(splitreference([[ special ( operation ) ]]))
+-- inspect(splitreference([[ special ( \operation ) ]]))
+-- inspect(splitreference([[ special ( o\peration ) ]]))
+-- inspect(splitreference([[ special ( ) ]]))
+-- inspect(splitreference([[ inner { argument } ]]))
+-- inspect(splitreference([[ inner { \argument } ]]))
+-- inspect(splitreference([[ inner { ar\gument } ]]))
+-- inspect(splitreference([[inner{a\rgument}]]))
+-- inspect(splitreference([[ inner { argument, argument } ]]))
+-- inspect(splitreference([[ inner { argument, \argument } ]])) -- fails: bug in lpeg?
+-- inspect(splitreference([[ inner { \argument, \argument } ]]))
+-- inspect(splitreference([[ outer :: ]]))
+-- inspect(splitreference([[ outer :: inner]]))
+-- inspect(splitreference([[ outer :: special (operation { argument,argument } ) ]]))
+-- inspect(splitreference([[ outer :: special (operation { } )]]))
+-- inspect(splitreference([[ outer :: special ( operation { argument, \argument } ) ]]))
+-- inspect(splitreference([[ outer :: special ( operation ) ]]))
+-- inspect(splitreference([[ outer :: special ( \operation ) ]]))
+-- inspect(splitreference([[ outer :: special ( ) ]]))
+-- inspect(splitreference([[ outer :: inner { argument } ]]))
+-- inspect(splitreference([[ special ( outer :: operation ) ]]))
+
+-- inspect(splitreference([[]]))
+-- inspect(splitreference([[inner]]))
+-- inspect(splitreference([[special(operation{argument,argument})]]))
+-- inspect(splitreference([[special(operation)]]))
+-- inspect(splitreference([[special(\operation)]]))
+-- inspect(splitreference([[special()]]))
+-- inspect(splitreference([[inner{argument}]]))
+-- inspect(splitreference([[inner{\argument}]]))
+-- inspect(splitreference([[outer::]]))
+-- inspect(splitreference([[outer::inner]]))
+-- inspect(splitreference([[outer::special(operation{argument,argument})]]))
+-- inspect(splitreference([[outer::special(operation{argument,\argument})]]))
+-- inspect(splitreference([[outer::special(operation)]]))
+-- inspect(splitreference([[outer::special(\operation)]]))
+-- inspect(splitreference([[outer::special()]]))
+-- inspect(splitreference([[outer::inner{argument}]]))
+-- inspect(splitreference([[special(outer::operation)]]))
diff --git a/tex/context/base/strc-syn.lua b/tex/context/base/strc-syn.lua
index a6d49715a..ca4b3ac18 100644
--- a/tex/context/base/strc-syn.lua
+++ b/tex/context/base/strc-syn.lua
@@ -1,198 +1,198 @@
-if not modules then modules = { } end modules ['strc-syn'] = {
- version = 1.001,
- comment = "companion to str-syn.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 allocate = utilities.storage.allocate
-
--- interface to tex end
-
-local structures = structures
-local synonyms = structures.synonyms
-local tags = structures.tags
-
-local collected = allocate()
-local tobesaved = allocate()
-
-synonyms.collected = collected
-synonyms.tobesaved = tobesaved
-
-local function initializer()
- collected = synonyms.collected
- tobesaved = synonyms.tobesaved
-end
-
-local function finalizer()
- for entry, data in next, tobesaved do
- data.hash = nil
- end
-end
-
-job.register('structures.synonyms.collected', tobesaved, initializer, finalizer)
-
--- todo: allocate becomes metatable
-
-local function allocate(class)
- local d = tobesaved[class]
- if not d then
- d = {
- metadata = {
- language = 'en',
- sorted = false,
- class = class
- },
- entries = {
- },
- hash = {
- }
- }
- tobesaved[class] = d
- end
- return d
-end
-
-function synonyms.define(class,kind)
- local data = allocate(class)
- data.metadata.kind = kind
-end
-
-function synonyms.register(class,kind,spec)
- local data = allocate(class)
- data.metadata.kind = kind -- runtime, not saved in format (yet)
- if not data.hash[spec.definition.tag or ""] then
- data.entries[#data.entries+1] = spec
- data.hash[spec.definition.tag or ""] = spec
- end
-end
-
-function synonyms.registerused(class,tag)
- local data = allocate(class)
- local dht = data.hash[tag]
- if dht then
- dht.definition.used = true
- end
-end
-
-function synonyms.synonym(class,tag)
- local data = allocate(class).hash
- local d = data[tag]
- if d then
- local de = d.definition
- de.used = true
- context(de.synonym)
- end
-end
-
-function synonyms.meaning(class,tag)
- local data = allocate(class).hash
- local d = data[tag]
- if d then
- local de = d.definition
- de.used = true
- context(de.meaning)
- end
-end
-
-synonyms.compare = sorters.comparers.basic -- (a,b)
-
-function synonyms.filter(data,options)
- local result = { }
- local entries = data.entries
- local all = options and options.criterium == interfaces.variables.all
- for i=1,#entries do
- local entry = entries[i]
- if all or entry.definition.used then
- result[#result+1] = entry
- end
- end
- data.result = result
-end
-
-function synonyms.prepare(data)
- local strip = sorters.strip
- local splitter = sorters.splitters.utf
- local result = data.result
- if result then
- for i=1, #result do
- local r = result[i]
- local rd = r.definition
- if rd then
- local rt = rd.tag
- local sortkey = (rt and rt ~= "" and rt) or rd.synonym
- r.split = splitter(strip(sortkey))
- end
- end
- end
-end
-
-function synonyms.sort(data,options)
- sorters.sort(data.result,synonyms.compare)
-end
-
-function synonyms.finalize(data,options)
- local result = data.result
- data.metadata.nofsorted = #result
- local split = { }
- for k=1,#result do
- local v = result[k]
- local entry, tag = sorters.firstofsplit(v)
- local s = split[entry] -- keeps track of change
- if not s then
- s = { tag = tag, data = { } }
- split[entry] = s
- end
- s.data[#s.data+1] = v
- end
- data.result = split
-end
-
--- for now, maybe at some point we will do a multipass or so
--- maybe pass the settings differently
-
-function synonyms.flush(data,options)
- local kind = data.metadata.kind -- hack, will be done better
- -- context[format("\\start%soutput",kind)]()
- local result = data.result
- local sorted = table.sortedkeys(result)
- for k=1,#sorted do
- local letter = sorted[k]
- local sublist = result[letter]
- local data = sublist.data
- -- context[format("\\start%ssection",kind)](sublist.tag)
- for d=1,#data do
- local entry = data[d].definition
- -- context[format("\\%sentry",kind)](d,entry.tag,entry.synonym,entry.meaning or "")
- context("\\%sentry{%s}{%s}{%s}{%s}",kind,d,entry.tag,entry.synonym,entry.meaning or "")
- end
- -- context[format("\\stop%ssection",kind)]()
- end
- -- context[format("\\stop%soutput",kind)]()
- data.result = nil
- data.metadata.sorted = false
-end
-
-function synonyms.analyzed(class,options)
- local data = synonyms.collected[class]
- if data and data.entries then
- options = options or { }
- sorters.setlanguage(options.language)
- synonyms.filter(data,options) -- filters entries to result
- synonyms.prepare(data,options) -- adds split table parallel to list table
- synonyms.sort(data,options) -- sorts entries in result
- synonyms.finalize(data,options) -- do things with data.entries
- data.metadata.sorted = true
- end
- return data and data.metadata.sorted and data.result and next(data.result)
-end
-
-function synonyms.process(class,options)
- if synonyms.analyzed(class,options) then
- synonyms.flush(synonyms.collected[class],options)
- end
-end
-
+if not modules then modules = { } end modules ['strc-syn'] = {
+ version = 1.001,
+ comment = "companion to str-syn.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 allocate = utilities.storage.allocate
+
+-- interface to tex end
+
+local structures = structures
+local synonyms = structures.synonyms
+local tags = structures.tags
+
+local collected = allocate()
+local tobesaved = allocate()
+
+synonyms.collected = collected
+synonyms.tobesaved = tobesaved
+
+local function initializer()
+ collected = synonyms.collected
+ tobesaved = synonyms.tobesaved
+end
+
+local function finalizer()
+ for entry, data in next, tobesaved do
+ data.hash = nil
+ end
+end
+
+job.register('structures.synonyms.collected', tobesaved, initializer, finalizer)
+
+-- todo: allocate becomes metatable
+
+local function allocate(class)
+ local d = tobesaved[class]
+ if not d then
+ d = {
+ metadata = {
+ language = 'en',
+ sorted = false,
+ class = class
+ },
+ entries = {
+ },
+ hash = {
+ }
+ }
+ tobesaved[class] = d
+ end
+ return d
+end
+
+function synonyms.define(class,kind)
+ local data = allocate(class)
+ data.metadata.kind = kind
+end
+
+function synonyms.register(class,kind,spec)
+ local data = allocate(class)
+ data.metadata.kind = kind -- runtime, not saved in format (yet)
+ if not data.hash[spec.definition.tag or ""] then
+ data.entries[#data.entries+1] = spec
+ data.hash[spec.definition.tag or ""] = spec
+ end
+end
+
+function synonyms.registerused(class,tag)
+ local data = allocate(class)
+ local dht = data.hash[tag]
+ if dht then
+ dht.definition.used = true
+ end
+end
+
+function synonyms.synonym(class,tag)
+ local data = allocate(class).hash
+ local d = data[tag]
+ if d then
+ local de = d.definition
+ de.used = true
+ context(de.synonym)
+ end
+end
+
+function synonyms.meaning(class,tag)
+ local data = allocate(class).hash
+ local d = data[tag]
+ if d then
+ local de = d.definition
+ de.used = true
+ context(de.meaning)
+ end
+end
+
+synonyms.compare = sorters.comparers.basic -- (a,b)
+
+function synonyms.filter(data,options)
+ local result = { }
+ local entries = data.entries
+ local all = options and options.criterium == interfaces.variables.all
+ for i=1,#entries do
+ local entry = entries[i]
+ if all or entry.definition.used then
+ result[#result+1] = entry
+ end
+ end
+ data.result = result
+end
+
+function synonyms.prepare(data)
+ local strip = sorters.strip
+ local splitter = sorters.splitters.utf
+ local result = data.result
+ if result then
+ for i=1, #result do
+ local r = result[i]
+ local rd = r.definition
+ if rd then
+ local rt = rd.tag
+ local sortkey = (rt and rt ~= "" and rt) or rd.synonym
+ r.split = splitter(strip(sortkey))
+ end
+ end
+ end
+end
+
+function synonyms.sort(data,options)
+ sorters.sort(data.result,synonyms.compare)
+end
+
+function synonyms.finalize(data,options)
+ local result = data.result
+ data.metadata.nofsorted = #result
+ local split = { }
+ for k=1,#result do
+ local v = result[k]
+ local entry, tag = sorters.firstofsplit(v)
+ local s = split[entry] -- keeps track of change
+ if not s then
+ s = { tag = tag, data = { } }
+ split[entry] = s
+ end
+ s.data[#s.data+1] = v
+ end
+ data.result = split
+end
+
+-- for now, maybe at some point we will do a multipass or so
+-- maybe pass the settings differently
+
+function synonyms.flush(data,options)
+ local kind = data.metadata.kind -- hack, will be done better
+ -- context[format("\\start%soutput",kind)]()
+ local result = data.result
+ local sorted = table.sortedkeys(result)
+ for k=1,#sorted do
+ local letter = sorted[k]
+ local sublist = result[letter]
+ local data = sublist.data
+ -- context[format("\\start%ssection",kind)](sublist.tag)
+ for d=1,#data do
+ local entry = data[d].definition
+ -- context[format("\\%sentry",kind)](d,entry.tag,entry.synonym,entry.meaning or "")
+ context("\\%sentry{%s}{%s}{%s}{%s}",kind,d,entry.tag,entry.synonym,entry.meaning or "")
+ end
+ -- context[format("\\stop%ssection",kind)]()
+ end
+ -- context[format("\\stop%soutput",kind)]()
+ data.result = nil
+ data.metadata.sorted = false
+end
+
+function synonyms.analyzed(class,options)
+ local data = synonyms.collected[class]
+ if data and data.entries then
+ options = options or { }
+ sorters.setlanguage(options.language)
+ synonyms.filter(data,options) -- filters entries to result
+ synonyms.prepare(data,options) -- adds split table parallel to list table
+ synonyms.sort(data,options) -- sorts entries in result
+ synonyms.finalize(data,options) -- do things with data.entries
+ data.metadata.sorted = true
+ end
+ return data and data.metadata.sorted and data.result and next(data.result)
+end
+
+function synonyms.process(class,options)
+ if synonyms.analyzed(class,options) then
+ synonyms.flush(synonyms.collected[class],options)
+ end
+end
+
diff --git a/tex/context/base/strc-tag.lua b/tex/context/base/strc-tag.lua
index bb083786b..7e5c6f993 100644
--- a/tex/context/base/strc-tag.lua
+++ b/tex/context/base/strc-tag.lua
@@ -1,354 +1,354 @@
-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.
-
-local insert, remove, unpack, concat = table.insert, table.remove, table.unpack, table.concat
-local gsub, find, topattern, format = string.gsub, string.find, string.topattern, string.format
-local lpegmatch = lpeg.match
-local texattribute = tex.attribute
-local allocate = utilities.storage.allocate
-local settings_to_hash = utilities.parsers.settings_to_hash
-
-local trace_tags = false trackers.register("structures.tags", function(v) trace_tags = v end)
-
-local report_tags = logs.reporter("structure","tags")
-
-local attributes, structures = attributes, structures
-
-local a_tagged = attributes.private('tagged')
-
-local unsetvalue = attributes.unsetvalue
-local codeinjections = backends.codeinjections
-
-local taglist = allocate()
-local properties = allocate()
-local labels = allocate()
-local stack = { }
-local chain = { }
-local ids = { }
-local enabled = false
-local tagdata = { } -- used in export
-local tagmetadata = { } -- used in export
-
-local tags = structures.tags
-tags.taglist = taglist -- can best be hidden
-tags.labels = labels
-tags.data = tagdata
-tags.metadata = tagmetadata
-
-local properties = allocate {
-
- 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" },
- 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" },
-
- 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" },
-
- synonym = { pdf = "Span", nature = "inline" },
- sorting = { pdf = "Span", nature = "inline" },
-
- register = { pdf = "Div", nature = "display" },
- registersection = { pdf = "Div", nature = "display" },
- registertag = { pdf = "Span", nature = "mixed" },
- registerentries = { pdf = "Div", nature = "display" },
- registerentry = { pdf = "Span", nature = "mixed" },
- registersee = { pdf = "Span", nature = "mixed" },
- registerpages = { pdf = "Span", nature = "mixed" },
- registerpage = { pdf = "Span", nature = "inline" },
- registerpagerange = { pdf = "Span", nature = "mixed" },
-
- table = { pdf = "Table", nature = "display" },
- tablerow = { pdf = "TR", nature = "display" },
- tablecell = { pdf = "TD", nature = "mixed" },
-
- tabulate = { pdf = "Table", nature = "display" },
- tabulaterow = { pdf = "TR", nature = "display" },
- tabulatecell = { pdf = "TD", nature = "mixed" },
-
- 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" },
-
- delimitedblock = { pdf = "BlockQuote", nature = "display" },
- delimited = { pdf = "Quote", nature = "inline" },
- subsentence = { 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" },
-
- margintextblock = { pdf = "Span", nature = "inline" },
- margintext = { 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" },
-
- 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" },
- 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" },
-}
-
-function tags.detailedtag(tag,detail,attribute)
- if not attribute then
- attribute = texattribute[a_tagged]
- end
- if attribute >= 0 then
- local tl = taglist[attribute]
- if tl then
- local pattern
- if detail and detail ~= "" then
- pattern = "^" .. tag .. ":".. detail .. "%-"
- else
- pattern = "^" .. tag .. "%-"
- end
- for i=#tl,1,-1 do
- local tli = tl[i]
- if find(tli,pattern) then
- return tli
- end
- end
- end
- else
- -- enabled but not auto
- end
- return false -- handy as bogus index
-end
-
-tags.properties = properties
-
-local lasttags = { }
-local userdata = { }
-
-tags.userdata = userdata
-
-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.registerdata(data)
- local fulltag = chain[nstack]
- if fulltag then
- tagdata[fulltag] = data
- end
-end
-
-local metadata
-
-function tags.registermetadata(data)
- local d = settings_to_hash(data)
- if metadata then
- table.merge(metadata,d)
- else
- metadata = d
- end
-end
-
-local nstack = 0
-
-function tags.start(tag,specification)
- local label, detail, user
- if specification then
- label, detail, user = specification.label, specification.detail, specification.userdata
- end
- if not enabled then
- codeinjections.enabletags()
- enabled = true
- end
- --
---~ labels[tag] = label ~= "" and label or tag
---~ local fulltag
---~ if detail and detail ~= "" then
---~ fulltag = tag .. ":" .. detail
---~ else
---~ fulltag = tag
---~ end
- --
- local fulltag = label ~= "" and label or tag
- labels[tag] = fulltag
- if detail and detail ~= "" then
- fulltag = fulltag .. ":" .. detail
- end
- --
- local t = #taglist + 1
- local n = (ids[fulltag] or 0) + 1
- ids[fulltag] = n
- lasttags[tag] = n
- local completetag = fulltag .. "-" .. n
- nstack = nstack + 1
- chain[nstack] = completetag
- stack[nstack] = t
- -- a copy as we can add key values for alt and actualtext if needed:
- taglist[t] = { unpack(chain,1,nstack) }
- --
- if user and user ~= "" then
- -- maybe we should merge this into taglist or whatever ... anyway there is room to optimize
- -- taglist.userdata = settings_to_hash(user)
- userdata[completetag] = settings_to_hash(user)
- end
- if metadata then
- tagmetadata[completetag] = metadata
- metadata = nil
- end
- texattribute[a_tagged] = t
- return t
-end
-
-function tags.restart(completetag)
- local t = #taglist + 1
- nstack = nstack + 1
- chain[nstack] = completetag
- stack[nstack] = t
- taglist[t] = { unpack(chain,1,nstack) }
- texattribute[a_tagged] = t
- return t
-end
-
-function tags.stop()
- if nstack > 0 then
- nstack = nstack -1
- end
- local t = stack[nstack]
- if not t then
- if trace_tags then
- report_tags("ignoring end tag, previous chain: %s",nstack > 0 and concat(chain[nstack],"",1,nstack) or "none")
- end
- t = unsetvalue
- end
- texattribute[a_tagged] = t
- return t
-end
-
-function tags.getid(tag,detail)
- if detail and detail ~= "" then
- return ids[tag .. ":" .. detail] or "?"
- else
- return ids[tag] or "?"
- end
-end
-
-function tags.last(tag)
- return lasttags[tag] -- or false
-end
-
-function tags.lastinchain()
- return chain[nstack]
-end
-
-function structures.atlocation(str)
- local location = gsub(concat(taglist[texattribute[a_tagged]],"-"),"%-%d+","")
- return find(location,topattern(str)) ~= nil
-end
-
-function tags.handler(head) -- we need a dummy
- return head, false
-end
-
-statistics.register("structure elements", function()
- if enabled then
- if nstack > 0 then
- return format("%s element chains identified, open chain: %s ",#taglist,concat(chain," => ",1,nstack))
- 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)
-
-commands.starttag = tags.start
-commands.stoptag = tags.stop
-commands.settagproperty = tags.setproperty
+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.
+
+local insert, remove, unpack, concat = table.insert, table.remove, table.unpack, table.concat
+local gsub, find, topattern, format = string.gsub, string.find, string.topattern, string.format
+local lpegmatch = lpeg.match
+local texattribute = tex.attribute
+local allocate = utilities.storage.allocate
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+local trace_tags = false trackers.register("structures.tags", function(v) trace_tags = v end)
+
+local report_tags = logs.reporter("structure","tags")
+
+local attributes, structures = attributes, structures
+
+local a_tagged = attributes.private('tagged')
+
+local unsetvalue = attributes.unsetvalue
+local codeinjections = backends.codeinjections
+
+local taglist = allocate()
+local properties = allocate()
+local labels = allocate()
+local stack = { }
+local chain = { }
+local ids = { }
+local enabled = false
+local tagdata = { } -- used in export
+local tagmetadata = { } -- used in export
+
+local tags = structures.tags
+tags.taglist = taglist -- can best be hidden
+tags.labels = labels
+tags.data = tagdata
+tags.metadata = tagmetadata
+
+local properties = allocate {
+
+ 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" },
+ 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" },
+
+ 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" },
+
+ synonym = { pdf = "Span", nature = "inline" },
+ sorting = { pdf = "Span", nature = "inline" },
+
+ register = { pdf = "Div", nature = "display" },
+ registersection = { pdf = "Div", nature = "display" },
+ registertag = { pdf = "Span", nature = "mixed" },
+ registerentries = { pdf = "Div", nature = "display" },
+ registerentry = { pdf = "Span", nature = "mixed" },
+ registersee = { pdf = "Span", nature = "mixed" },
+ registerpages = { pdf = "Span", nature = "mixed" },
+ registerpage = { pdf = "Span", nature = "inline" },
+ registerpagerange = { pdf = "Span", nature = "mixed" },
+
+ table = { pdf = "Table", nature = "display" },
+ tablerow = { pdf = "TR", nature = "display" },
+ tablecell = { pdf = "TD", nature = "mixed" },
+
+ tabulate = { pdf = "Table", nature = "display" },
+ tabulaterow = { pdf = "TR", nature = "display" },
+ tabulatecell = { pdf = "TD", nature = "mixed" },
+
+ 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" },
+
+ delimitedblock = { pdf = "BlockQuote", nature = "display" },
+ delimited = { pdf = "Quote", nature = "inline" },
+ subsentence = { 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" },
+
+ margintextblock = { pdf = "Span", nature = "inline" },
+ margintext = { 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" },
+
+ 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" },
+ 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" },
+}
+
+function tags.detailedtag(tag,detail,attribute)
+ if not attribute then
+ attribute = texattribute[a_tagged]
+ end
+ if attribute >= 0 then
+ local tl = taglist[attribute]
+ if tl then
+ local pattern
+ if detail and detail ~= "" then
+ pattern = "^" .. tag .. ":".. detail .. "%-"
+ else
+ pattern = "^" .. tag .. "%-"
+ end
+ for i=#tl,1,-1 do
+ local tli = tl[i]
+ if find(tli,pattern) then
+ return tli
+ end
+ end
+ end
+ else
+ -- enabled but not auto
+ end
+ return false -- handy as bogus index
+end
+
+tags.properties = properties
+
+local lasttags = { }
+local userdata = { }
+
+tags.userdata = userdata
+
+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.registerdata(data)
+ local fulltag = chain[nstack]
+ if fulltag then
+ tagdata[fulltag] = data
+ end
+end
+
+local metadata
+
+function tags.registermetadata(data)
+ local d = settings_to_hash(data)
+ if metadata then
+ table.merge(metadata,d)
+ else
+ metadata = d
+ end
+end
+
+local nstack = 0
+
+function tags.start(tag,specification)
+ local label, detail, user
+ if specification then
+ label, detail, user = specification.label, specification.detail, specification.userdata
+ end
+ if not enabled then
+ codeinjections.enabletags()
+ enabled = true
+ end
+ --
+--~ labels[tag] = label ~= "" and label or tag
+--~ local fulltag
+--~ if detail and detail ~= "" then
+--~ fulltag = tag .. ":" .. detail
+--~ else
+--~ fulltag = tag
+--~ end
+ --
+ local fulltag = label ~= "" and label or tag
+ labels[tag] = fulltag
+ if detail and detail ~= "" then
+ fulltag = fulltag .. ":" .. detail
+ end
+ --
+ local t = #taglist + 1
+ local n = (ids[fulltag] or 0) + 1
+ ids[fulltag] = n
+ lasttags[tag] = n
+ local completetag = fulltag .. "-" .. n
+ nstack = nstack + 1
+ chain[nstack] = completetag
+ stack[nstack] = t
+ -- a copy as we can add key values for alt and actualtext if needed:
+ taglist[t] = { unpack(chain,1,nstack) }
+ --
+ if user and user ~= "" then
+ -- maybe we should merge this into taglist or whatever ... anyway there is room to optimize
+ -- taglist.userdata = settings_to_hash(user)
+ userdata[completetag] = settings_to_hash(user)
+ end
+ if metadata then
+ tagmetadata[completetag] = metadata
+ metadata = nil
+ end
+ texattribute[a_tagged] = t
+ return t
+end
+
+function tags.restart(completetag)
+ local t = #taglist + 1
+ nstack = nstack + 1
+ chain[nstack] = completetag
+ stack[nstack] = t
+ taglist[t] = { unpack(chain,1,nstack) }
+ texattribute[a_tagged] = t
+ return t
+end
+
+function tags.stop()
+ if nstack > 0 then
+ nstack = nstack -1
+ end
+ local t = stack[nstack]
+ if not t then
+ if trace_tags then
+ report_tags("ignoring end tag, previous chain: %s",nstack > 0 and concat(chain[nstack],"",1,nstack) or "none")
+ end
+ t = unsetvalue
+ end
+ texattribute[a_tagged] = t
+ return t
+end
+
+function tags.getid(tag,detail)
+ if detail and detail ~= "" then
+ return ids[tag .. ":" .. detail] or "?"
+ else
+ return ids[tag] or "?"
+ end
+end
+
+function tags.last(tag)
+ return lasttags[tag] -- or false
+end
+
+function tags.lastinchain()
+ return chain[nstack]
+end
+
+function structures.atlocation(str)
+ local location = gsub(concat(taglist[texattribute[a_tagged]],"-"),"%-%d+","")
+ return find(location,topattern(str)) ~= nil
+end
+
+function tags.handler(head) -- we need a dummy
+ return head, false
+end
+
+statistics.register("structure elements", function()
+ if enabled then
+ if nstack > 0 then
+ return format("%s element chains identified, open chain: %s ",#taglist,concat(chain," => ",1,nstack))
+ 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)
+
+commands.starttag = tags.start
+commands.stoptag = tags.stop
+commands.settagproperty = tags.setproperty
diff --git a/tex/context/base/supp-box.lua b/tex/context/base/supp-box.lua
index f564723ed..c7382834a 100644
--- a/tex/context/base/supp-box.lua
+++ b/tex/context/base/supp-box.lua
@@ -1,112 +1,112 @@
-if not modules then modules = { } end modules ['supp-box'] = {
- 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"
-}
-
--- this is preliminary code
-
-local report_hyphenation = logs.reporter("languages","hyphenation")
-
-local tex, node = tex, node
-local context, commands, nodes = context, commands, nodes
-
-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 glyph_code = nodecodes.glyph
-
-local new_penalty = nodes.pool.penalty
-
-local free_node = node.free
-local copynodelist = node.copy_list
-local copynode = node.copy
-local texbox = tex.box
-
-local function hyphenatedlist(list)
- while list do
- local id, next, prev = list.id, list.next, list.prev
- if id == disc_code then
- local hyphen = list.pre
- if hyphen then
- local penalty = new_penalty(-500)
- hyphen.next, penalty.prev = penalty, hyphen
- prev.next, next.prev = hyphen, penalty
- penalty.next, hyphen.prev = next, prev
- list.pre = nil
- free_node(list)
- end
- elseif id == vlist_code or id == hlist_code then
- hyphenatedlist(list.list)
- end
- list = next
- end
-end
-
-commands.hyphenatedlist = hyphenatedlist
-
-function commands.showhyphenatedinlist(list)
- report_hyphenation("show: %s",nodes.listtoutf(list,false,true))
-end
-
-local function checkedlist(list)
- if type(list) == "number" then
- return texbox[list].list
- else
- return list
- end
-end
-
-local function applytochars(list,what,nested)
- local doaction = context[what or "ruledhbox"]
- local noaction = context
- local current = checkedlist(list)
- while current do
- local id = current.id
- if nested and (id == hlist_code or id == vlist_code) then
- context.beginhbox()
- applytochars(current.list,what,nested)
- context.endhbox()
- elseif id ~= glyph_code then
- noaction(copynode(current))
- else
- doaction(copynode(current))
- end
- current = current.next
- end
-end
-
-local function applytowords(list,what,nested)
- local doaction = context[what or "ruledhbox"]
- local noaction = context
- local current = checkedlist(list)
- local start
- while current do
- local id = current.id
- if id == glue_code then
- if start then
- doaction(copynodelist(start,current))
- start = nil
- end
- noaction(copynode(current))
- elseif nested and (id == hlist_code or id == vlist_code) then
- context.beginhbox()
- applytowords(current.list,what,nested)
- context.egroup()
- elseif not start then
- start = current
- end
- current = current.next
- end
- if start then
- doaction(copynodelist(start))
- end
-end
-
-commands.applytochars = applytochars
-commands.applytowords = applytowords
+if not modules then modules = { } end modules ['supp-box'] = {
+ 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"
+}
+
+-- this is preliminary code
+
+local report_hyphenation = logs.reporter("languages","hyphenation")
+
+local tex, node = tex, node
+local context, commands, nodes = context, commands, nodes
+
+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 glyph_code = nodecodes.glyph
+
+local new_penalty = nodes.pool.penalty
+
+local free_node = node.free
+local copynodelist = node.copy_list
+local copynode = node.copy
+local texbox = tex.box
+
+local function hyphenatedlist(list)
+ while list do
+ local id, next, prev = list.id, list.next, list.prev
+ if id == disc_code then
+ local hyphen = list.pre
+ if hyphen then
+ local penalty = new_penalty(-500)
+ hyphen.next, penalty.prev = penalty, hyphen
+ prev.next, next.prev = hyphen, penalty
+ penalty.next, hyphen.prev = next, prev
+ list.pre = nil
+ free_node(list)
+ end
+ elseif id == vlist_code or id == hlist_code then
+ hyphenatedlist(list.list)
+ end
+ list = next
+ end
+end
+
+commands.hyphenatedlist = hyphenatedlist
+
+function commands.showhyphenatedinlist(list)
+ report_hyphenation("show: %s",nodes.listtoutf(list,false,true))
+end
+
+local function checkedlist(list)
+ if type(list) == "number" then
+ return texbox[list].list
+ else
+ return list
+ end
+end
+
+local function applytochars(list,what,nested)
+ local doaction = context[what or "ruledhbox"]
+ local noaction = context
+ local current = checkedlist(list)
+ while current do
+ local id = current.id
+ if nested and (id == hlist_code or id == vlist_code) then
+ context.beginhbox()
+ applytochars(current.list,what,nested)
+ context.endhbox()
+ elseif id ~= glyph_code then
+ noaction(copynode(current))
+ else
+ doaction(copynode(current))
+ end
+ current = current.next
+ end
+end
+
+local function applytowords(list,what,nested)
+ local doaction = context[what or "ruledhbox"]
+ local noaction = context
+ local current = checkedlist(list)
+ local start
+ while current do
+ local id = current.id
+ if id == glue_code then
+ if start then
+ doaction(copynodelist(start,current))
+ start = nil
+ end
+ noaction(copynode(current))
+ elseif nested and (id == hlist_code or id == vlist_code) then
+ context.beginhbox()
+ applytowords(current.list,what,nested)
+ context.egroup()
+ elseif not start then
+ start = current
+ end
+ current = current.next
+ end
+ if start then
+ doaction(copynodelist(start))
+ end
+end
+
+commands.applytochars = applytochars
+commands.applytowords = applytowords
diff --git a/tex/context/base/supp-ran.lua b/tex/context/base/supp-ran.lua
index 57f041c69..7997db8f6 100644
--- a/tex/context/base/supp-ran.lua
+++ b/tex/context/base/supp-ran.lua
@@ -1,73 +1,73 @@
-if not modules then modules = { } end modules ['supp-ran'] = {
- version = 1.001,
- comment = "companion to supp-ran.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- We cannot ask for the current seed, so we need some messy hack here.
-
-local report_system = logs.reporter("system","randomizer")
-
-local math = math
-local context, commands = context, commands
-
-local random, randomseed, round, seed, last = math.random, math.randomseed, math.round, false, 1
-
-local maxcount = 2^30-1 -- 1073741823
-
-local function setrandomseedi(n,comment)
- if not n then
- -- n = 0.5 -- hack
- end
- if n <= 1 then
- n = n * maxcount
- end
- n = round(n)
- if false then
- report_system("setting seed to %s (%s)",n,comment or "normal")
- end
- randomseed(n)
- last = random(0,maxcount) -- we need an initial value
-end
-
-math.setrandomseedi = setrandomseedi
-
-function commands.getrandomcounta(min,max)
- last = random(min,max)
- context(last)
-end
-
-function commands.getrandomcountb(min,max)
- last = random(min,max)/65536
- context(last)
-end
-
-function commands.setrandomseed(n)
- last = n
- setrandomseedi(n)
-end
-
-function commands.getrandomseed(n)
- context(last)
-end
-
--- maybe stack
-
-function commands.freezerandomseed(n)
- if seed == false or seed == nil then
- seed = last
- setrandomseedi(seed,"freeze",seed)
- end
- if n then
- randomseed(n)
- end
-end
-
-function commands.defrostrandomseed()
- if seed ~= false then
- setrandomseedi(seed,"defrost",seed) -- was last (bug)
- seed = false
- end
-end
+if not modules then modules = { } end modules ['supp-ran'] = {
+ version = 1.001,
+ comment = "companion to supp-ran.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- We cannot ask for the current seed, so we need some messy hack here.
+
+local report_system = logs.reporter("system","randomizer")
+
+local math = math
+local context, commands = context, commands
+
+local random, randomseed, round, seed, last = math.random, math.randomseed, math.round, false, 1
+
+local maxcount = 2^30-1 -- 1073741823
+
+local function setrandomseedi(n,comment)
+ if not n then
+ -- n = 0.5 -- hack
+ end
+ if n <= 1 then
+ n = n * maxcount
+ end
+ n = round(n)
+ if false then
+ report_system("setting seed to %s (%s)",n,comment or "normal")
+ end
+ randomseed(n)
+ last = random(0,maxcount) -- we need an initial value
+end
+
+math.setrandomseedi = setrandomseedi
+
+function commands.getrandomcounta(min,max)
+ last = random(min,max)
+ context(last)
+end
+
+function commands.getrandomcountb(min,max)
+ last = random(min,max)/65536
+ context(last)
+end
+
+function commands.setrandomseed(n)
+ last = n
+ setrandomseedi(n)
+end
+
+function commands.getrandomseed(n)
+ context(last)
+end
+
+-- maybe stack
+
+function commands.freezerandomseed(n)
+ if seed == false or seed == nil then
+ seed = last
+ setrandomseedi(seed,"freeze",seed)
+ end
+ if n then
+ randomseed(n)
+ end
+end
+
+function commands.defrostrandomseed()
+ if seed ~= false then
+ setrandomseedi(seed,"defrost",seed) -- was last (bug)
+ seed = false
+ end
+end
diff --git a/tex/context/base/symb-ini.lua b/tex/context/base/symb-ini.lua
index 1be423b92..deeef667a 100644
--- a/tex/context/base/symb-ini.lua
+++ b/tex/context/base/symb-ini.lua
@@ -1,50 +1,50 @@
-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 variables = interfaces.variables
-
-fonts = fonts or { } -- brrrr
-
-local symbols = fonts.symbols or { }
-fonts.symbols = symbols
-
-local report_symbols = logs.reporter ("fonts","symbols")
-local status_symbols = logs.messenger("fonts","symbols")
-
-local patterns = { "symb-imp-%s.mkiv", "symb-imp-%s.tex", "symb-%s.mkiv", "symb-%s.tex" }
-local listitem = utilities.parsers.listitem
-
-local function action(name,foundname)
- -- context.startnointerference()
- context.startreadingfile()
- context.input(foundname)
- status_symbols("library %a loaded",name)
- context.stopreadingfile()
- -- context.stopnointerference()
-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
- commands.uselibrary {
- name = name,
- patterns = patterns,
- action = action,
- failure = failure,
- onlyonce = true,
- }
- end
- end
-end
-
-commands.usesymbols = symbols.uselibrary
+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 variables = interfaces.variables
+
+fonts = fonts or { } -- brrrr
+
+local symbols = fonts.symbols or { }
+fonts.symbols = symbols
+
+local report_symbols = logs.reporter ("fonts","symbols")
+local status_symbols = logs.messenger("fonts","symbols")
+
+local patterns = { "symb-imp-%s.mkiv", "symb-imp-%s.tex", "symb-%s.mkiv", "symb-%s.tex" }
+local listitem = utilities.parsers.listitem
+
+local function action(name,foundname)
+ -- context.startnointerference()
+ context.startreadingfile()
+ context.input(foundname)
+ status_symbols("library %a loaded",name)
+ context.stopreadingfile()
+ -- context.stopnointerference()
+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
+ commands.uselibrary {
+ name = name,
+ patterns = patterns,
+ action = action,
+ failure = failure,
+ onlyonce = true,
+ }
+ end
+ end
+end
+
+commands.usesymbols = symbols.uselibrary
diff --git a/tex/context/base/syst-aux.lua b/tex/context/base/syst-aux.lua
index d7250d239..b0fb8483b 100644
--- a/tex/context/base/syst-aux.lua
+++ b/tex/context/base/syst-aux.lua
@@ -1,80 +1,80 @@
-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 commands, context = commands, context
-
-local settings_to_array = utilities.parsers.settings_to_array
-local format = string.format
-local utfsub = utf.sub
-local P, C, Carg, lpegmatch, utf8char = lpeg.P, lpeg.C, lpeg.Carg, lpeg.match, lpeg.patterns.utf8char
-
-local setvalue = context.setvalue
-
-local pattern = C(utf8char^-1) * C(P(1)^0)
-
-function commands.getfirstcharacter(str)
- local first, rest = lpegmatch(pattern,str)
- setvalue("firstcharacter",first)
- setvalue("remainingcharacters",rest)
-end
-
-local pattern = C(utf8char^-1)
-
-function commands.doiffirstcharelse(chr,str)
- commands.doifelse(lpegmatch(pattern,str) == chr)
-end
-
-function commands.getsubstring(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 format("%.0fsp",d * tonumber(n)/100) end * P("%") * P(-1)
-
--- commands.percentageof("10%",65536*10)
-
-function commands.percentageof(str,dim)
- context(lpegmatch(pattern,str,1,dim) or str)
-end
-
--- \gdef\setpercentdimen#1#2%
--- {#1=\ctxcommand{percentageof("#2",\number#1)}\relax}
+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 commands, context = commands, context
+
+local settings_to_array = utilities.parsers.settings_to_array
+local format = string.format
+local utfsub = utf.sub
+local P, C, Carg, lpegmatch, utf8char = lpeg.P, lpeg.C, lpeg.Carg, lpeg.match, lpeg.patterns.utf8char
+
+local setvalue = context.setvalue
+
+local pattern = C(utf8char^-1) * C(P(1)^0)
+
+function commands.getfirstcharacter(str)
+ local first, rest = lpegmatch(pattern,str)
+ setvalue("firstcharacter",first)
+ setvalue("remainingcharacters",rest)
+end
+
+local pattern = C(utf8char^-1)
+
+function commands.doiffirstcharelse(chr,str)
+ commands.doifelse(lpegmatch(pattern,str) == chr)
+end
+
+function commands.getsubstring(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 format("%.0fsp",d * tonumber(n)/100) end * P("%") * P(-1)
+
+-- commands.percentageof("10%",65536*10)
+
+function commands.percentageof(str,dim)
+ context(lpegmatch(pattern,str,1,dim) or str)
+end
+
+-- \gdef\setpercentdimen#1#2%
+-- {#1=\ctxcommand{percentageof("#2",\number#1)}\relax}
diff --git a/tex/context/base/syst-con.lua b/tex/context/base/syst-con.lua
index 103aea2d4..48f02da3a 100644
--- a/tex/context/base/syst-con.lua
+++ b/tex/context/base/syst-con.lua
@@ -1,62 +1,62 @@
-if not modules then modules = { } end modules ['syst-con'] = {
- version = 1.001,
- comment = "companion to syst-con.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-converters = converters or { }
-
---[[ldx--
-<p>For raw 8 bit characters, the offset is 0x110000 (bottom of plane 18) at
-the top of <l n='luatex'/>'s char range but outside the unicode range.</p>
---ldx]]--
-
-local tonumber = tonumber
-local utfchar = utf.char
-local gsub, format = string.gsub, string.format
-
-function converters.hexstringtonumber(n) tonumber(n,16) end
-function converters.octstringtonumber(n) tonumber(n, 8) end
-function converters.rawcharacter (n) utfchar(0x110000+n) end
-function converters.lchexnumber (n) format("%x" ,n) end
-function converters.uchexnumber (n) format("%X" ,n) end
-function converters.lchexnumbers (n) format("%02x",n) end
-function converters.uchexnumbers (n) format("%02X",n) end
-function converters.octnumber (n) format("%03o",n) end
-
-function commands.hexstringtonumber(n) context(tonumber(n,16)) end
-function commands.octstringtonumber(n) context(tonumber(n, 8)) end
-function commands.rawcharacter (n) context(utfchar(0x110000+n)) end
-function commands.lchexnumber (n) context("%x" ,n) end
-function commands.uchexnumber (n) context("%X" ,n) end
-function commands.lchexnumbers (n) context("%02x",n) end
-function commands.uchexnumbers (n) context("%02X",n) end
-function commands.octnumber (n) context("%03o",n) end
-
-function commands.format(fmt,...) -- used ?
- fmt = gsub(fmt,"@","%%")
- context(fmt,...)
-end
-
-local cosd, sind, tand = math.cosd, math.sind, math.tand
-local cos, sin, tan = math.cos, math.sin, math.tan
-
--- unfortunately %s spits out: 6.1230317691119e-017
---
--- function commands.sind(n) context(sind(n)) end
--- function commands.cosd(n) context(cosd(n)) end
--- function commands.tand(n) context(tand(n)) end
---
--- function commands.sin (n) context(sin (n)) end
--- function commands.cos (n) context(cos (n)) end
--- function commands.tan (n) context(tan (n)) end
-
-function commands.sind(n) context("%0.6f",sind(n)) end
-function commands.cosd(n) context("%0.6f",cosd(n)) end
-function commands.tand(n) context("%0.6f",tand(n)) end
-
-function commands.sin (n) context("%0.6f",sin (n)) end
-function commands.cos (n) context("%0.6f",cos (n)) end
-function commands.tan (n) context("%0.6f",tan (n)) end
+if not modules then modules = { } end modules ['syst-con'] = {
+ version = 1.001,
+ comment = "companion to syst-con.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+converters = converters or { }
+
+--[[ldx--
+<p>For raw 8 bit characters, the offset is 0x110000 (bottom of plane 18) at
+the top of <l n='luatex'/>'s char range but outside the unicode range.</p>
+--ldx]]--
+
+local tonumber = tonumber
+local utfchar = utf.char
+local gsub, format = string.gsub, string.format
+
+function converters.hexstringtonumber(n) tonumber(n,16) end
+function converters.octstringtonumber(n) tonumber(n, 8) end
+function converters.rawcharacter (n) utfchar(0x110000+n) end
+function converters.lchexnumber (n) format("%x" ,n) end
+function converters.uchexnumber (n) format("%X" ,n) end
+function converters.lchexnumbers (n) format("%02x",n) end
+function converters.uchexnumbers (n) format("%02X",n) end
+function converters.octnumber (n) format("%03o",n) end
+
+function commands.hexstringtonumber(n) context(tonumber(n,16)) end
+function commands.octstringtonumber(n) context(tonumber(n, 8)) end
+function commands.rawcharacter (n) context(utfchar(0x110000+n)) end
+function commands.lchexnumber (n) context("%x" ,n) end
+function commands.uchexnumber (n) context("%X" ,n) end
+function commands.lchexnumbers (n) context("%02x",n) end
+function commands.uchexnumbers (n) context("%02X",n) end
+function commands.octnumber (n) context("%03o",n) end
+
+function commands.format(fmt,...) -- used ?
+ fmt = gsub(fmt,"@","%%")
+ context(fmt,...)
+end
+
+local cosd, sind, tand = math.cosd, math.sind, math.tand
+local cos, sin, tan = math.cos, math.sin, math.tan
+
+-- unfortunately %s spits out: 6.1230317691119e-017
+--
+-- function commands.sind(n) context(sind(n)) end
+-- function commands.cosd(n) context(cosd(n)) end
+-- function commands.tand(n) context(tand(n)) end
+--
+-- function commands.sin (n) context(sin (n)) end
+-- function commands.cos (n) context(cos (n)) end
+-- function commands.tan (n) context(tan (n)) end
+
+function commands.sind(n) context("%0.6f",sind(n)) end
+function commands.cosd(n) context("%0.6f",cosd(n)) end
+function commands.tand(n) context("%0.6f",tand(n)) end
+
+function commands.sin (n) context("%0.6f",sin (n)) end
+function commands.cos (n) context("%0.6f",cos (n)) end
+function commands.tan (n) context("%0.6f",tan (n)) end
diff --git a/tex/context/base/syst-lua.lua b/tex/context/base/syst-lua.lua
index 4795efe68..ef524c339 100644
--- a/tex/context/base/syst-lua.lua
+++ b/tex/context/base/syst-lua.lua
@@ -1,123 +1,123 @@
-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 format, find, match, rep = string.format, string.find, string.match, string.rep
-local tonumber = tonumber
-local S, lpegmatch, lpegtsplitat = lpeg.S, lpeg.match, lpeg.tsplitat
-
-local context = context
-
-commands = commands or { }
-
-function commands.writestatus(...) logs.status(...) end -- overloaded later
-
-local firstoftwoarguments = context.firstoftwoarguments -- context.constructcsonly("firstoftwoarguments" )
-local secondoftwoarguments = context.secondoftwoarguments -- context.constructcsonly("secondoftwoarguments")
-local firstofoneargument = context.firstofoneargument -- context.constructcsonly("firstofoneargument" )
-local gobbleoneargument = context.gobbleoneargument -- context.constructcsonly("gobbleoneargument" )
-
--- contextsprint(prtcatcodes,[[\ui_fo]]) -- firstofonearguments
--- contextsprint(prtcatcodes,[[\ui_go]]) -- gobbleonearguments
--- contextsprint(prtcatcodes,[[\ui_ft]]) -- firstoftwoarguments
--- contextsprint(prtcatcodes,[[\ui_st]]) -- secondoftwoarguments
-
-function commands.doifelse(b)
- if b then
- firstoftwoarguments()
- else
- secondoftwoarguments()
- end
-end
-
-function commands.doif(b)
- if b then
- firstofoneargument()
- else
- gobbleoneargument()
- end
-end
-
-function commands.doifnot(b)
- if b then
- gobbleoneargument()
- else
- 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
- firstoftwoarguments()
- else
- secondoftwoarguments()
- end
-end
-
-local s = lpegtsplitat(",")
-local h = { }
-
-function commands.doifcommonelse(a,b) -- often the same test
- local ha = h[a]
- local hb = h[b]
- if not ha then
- ha = lpegmatch(s,a)
- h[a] = ha
- end
- if not hb then
- hb = lpegmatch(s,b)
- h[b] = hb
- end
- local na = #ha
- local nb = #hb
- for i=1,na do
- for j=1,nb do
- if ha[i] == hb[j] then
- firstoftwoarguments()
- return
- end
- end
- end
- secondoftwoarguments()
-end
-
-function commands.doifinsetelse(a,b)
- local hb = h[b]
- if not hb then hb = lpegmatch(s,b) h[b] = hb end
- for i=1,#hb do
- if a == hb[i] then
- firstoftwoarguments()
- return
- end
- end
- secondoftwoarguments()
-end
-
-local pattern = lpeg.patterns.validdimen
-
-function commands.doifdimenstringelse(str)
- if lpegmatch(pattern,str) then
- firstoftwoarguments()
- else
- secondoftwoarguments()
- end
-end
-
-function commands.firstinset(str)
- local first = match(str,"^([^,]+),")
- context(first or str)
-end
-
-function commands.ntimes(str,n)
- context(rep(str,n or 1))
-end
+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 format, find, match, rep = string.format, string.find, string.match, string.rep
+local tonumber = tonumber
+local S, lpegmatch, lpegtsplitat = lpeg.S, lpeg.match, lpeg.tsplitat
+
+local context = context
+
+commands = commands or { }
+
+function commands.writestatus(...) logs.status(...) end -- overloaded later
+
+local firstoftwoarguments = context.firstoftwoarguments -- context.constructcsonly("firstoftwoarguments" )
+local secondoftwoarguments = context.secondoftwoarguments -- context.constructcsonly("secondoftwoarguments")
+local firstofoneargument = context.firstofoneargument -- context.constructcsonly("firstofoneargument" )
+local gobbleoneargument = context.gobbleoneargument -- context.constructcsonly("gobbleoneargument" )
+
+-- contextsprint(prtcatcodes,[[\ui_fo]]) -- firstofonearguments
+-- contextsprint(prtcatcodes,[[\ui_go]]) -- gobbleonearguments
+-- contextsprint(prtcatcodes,[[\ui_ft]]) -- firstoftwoarguments
+-- contextsprint(prtcatcodes,[[\ui_st]]) -- secondoftwoarguments
+
+function commands.doifelse(b)
+ if b then
+ firstoftwoarguments()
+ else
+ secondoftwoarguments()
+ end
+end
+
+function commands.doif(b)
+ if b then
+ firstofoneargument()
+ else
+ gobbleoneargument()
+ end
+end
+
+function commands.doifnot(b)
+ if b then
+ gobbleoneargument()
+ else
+ 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
+ firstoftwoarguments()
+ else
+ secondoftwoarguments()
+ end
+end
+
+local s = lpegtsplitat(",")
+local h = { }
+
+function commands.doifcommonelse(a,b) -- often the same test
+ local ha = h[a]
+ local hb = h[b]
+ if not ha then
+ ha = lpegmatch(s,a)
+ h[a] = ha
+ end
+ if not hb then
+ hb = lpegmatch(s,b)
+ h[b] = hb
+ end
+ local na = #ha
+ local nb = #hb
+ for i=1,na do
+ for j=1,nb do
+ if ha[i] == hb[j] then
+ firstoftwoarguments()
+ return
+ end
+ end
+ end
+ secondoftwoarguments()
+end
+
+function commands.doifinsetelse(a,b)
+ local hb = h[b]
+ if not hb then hb = lpegmatch(s,b) h[b] = hb end
+ for i=1,#hb do
+ if a == hb[i] then
+ firstoftwoarguments()
+ return
+ end
+ end
+ secondoftwoarguments()
+end
+
+local pattern = lpeg.patterns.validdimen
+
+function commands.doifdimenstringelse(str)
+ if lpegmatch(pattern,str) then
+ firstoftwoarguments()
+ else
+ secondoftwoarguments()
+ end
+end
+
+function commands.firstinset(str)
+ local first = match(str,"^([^,]+),")
+ context(first or str)
+end
+
+function commands.ntimes(str,n)
+ context(rep(str,n or 1))
+end
diff --git a/tex/context/base/tabl-tbl.lua b/tex/context/base/tabl-tbl.lua
index 224b2fb99..19548e7b3 100644
--- a/tex/context/base/tabl-tbl.lua
+++ b/tex/context/base/tabl-tbl.lua
@@ -1,41 +1,41 @@
-if not modules then modules = { } end modules ['tabl-tbl'] = {
- version = 1.001,
- comment = "companion to tabl-tbl.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- A couple of hacks ... easier to do in Lua than in regular TeX. More will
--- follow.
-
-local context, commands = context, commands
-
-local tonumber = tonumber
-local gsub, rep, sub, find = string.gsub, string.rep, string.sub, string.find
-local P, C, Cc, Ct, lpegmatch = lpeg.P, lpeg.C, lpeg.Cc, lpeg.Ct, lpeg.match
-
-local settexcount = tex.setcount
-
-local separator = P("|")
-local nested = lpeg.patterns.nested
-local pattern = Ct((separator * (C(nested) + Cc("")) * C((1-separator)^0))^0)
-
-function commands.presettabulate(preamble)
- preamble = gsub(preamble,"~","d") -- let's get rid of ~ mess here
- if find(preamble,"%*") then
- -- todo: lpeg but not now
- preamble = gsub(preamble, "%*(%b{})(%b{})", function(n,p)
- return rep(sub(p,2,-2),tonumber(sub(n,2,-2)) or 1)
- end)
- end
- local t = lpegmatch(pattern,preamble)
- local m = #t - 2
- settexcount("global","c_tabl_tabulate_nofcolumns", m/2)
- settexcount("global","c_tabl_tabulate_has_rule_spec_first", t[1] == "" and 0 or 1)
- settexcount("global","c_tabl_tabulate_has_rule_spec_last", t[m+1] == "" and 0 or 1)
- for i=1,m,2 do
- context.settabulateentry(t[i],t[i+1])
- end
- context.settabulatelastentry(t[m+1])
-end
+if not modules then modules = { } end modules ['tabl-tbl'] = {
+ version = 1.001,
+ comment = "companion to tabl-tbl.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- A couple of hacks ... easier to do in Lua than in regular TeX. More will
+-- follow.
+
+local context, commands = context, commands
+
+local tonumber = tonumber
+local gsub, rep, sub, find = string.gsub, string.rep, string.sub, string.find
+local P, C, Cc, Ct, lpegmatch = lpeg.P, lpeg.C, lpeg.Cc, lpeg.Ct, lpeg.match
+
+local settexcount = tex.setcount
+
+local separator = P("|")
+local nested = lpeg.patterns.nested
+local pattern = Ct((separator * (C(nested) + Cc("")) * C((1-separator)^0))^0)
+
+function commands.presettabulate(preamble)
+ preamble = gsub(preamble,"~","d") -- let's get rid of ~ mess here
+ if find(preamble,"%*") then
+ -- todo: lpeg but not now
+ preamble = gsub(preamble, "%*(%b{})(%b{})", function(n,p)
+ return rep(sub(p,2,-2),tonumber(sub(n,2,-2)) or 1)
+ end)
+ end
+ local t = lpegmatch(pattern,preamble)
+ local m = #t - 2
+ settexcount("global","c_tabl_tabulate_nofcolumns", m/2)
+ settexcount("global","c_tabl_tabulate_has_rule_spec_first", t[1] == "" and 0 or 1)
+ settexcount("global","c_tabl_tabulate_has_rule_spec_last", t[m+1] == "" and 0 or 1)
+ for i=1,m,2 do
+ context.settabulateentry(t[i],t[i+1])
+ end
+ context.settabulatelastentry(t[m+1])
+end
diff --git a/tex/context/base/tabl-xtb.lua b/tex/context/base/tabl-xtb.lua
index 5b47bf705..3ffe8a219 100644
--- a/tex/context/base/tabl-xtb.lua
+++ b/tex/context/base/tabl-xtb.lua
@@ -1,988 +1,988 @@
-if not modules then modules = { } end modules ['tabl-xtb'] = {
- version = 1.001,
- comment = "companion to tabl-xtb.mkvi",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
---[[
-
-This table mechanism is a combination between TeX and Lua. We do process
-cells at the TeX end and inspect them at the Lua end. After some analysis
-we have a second pass using the calculated widths, and if needed cells
-will go through a third pass to get the heights right. This last pass is
-avoided when possible which is why some code below looks a bit more
-complex than needed. The reason for such optimizations is that each cells
-is actually a framed instance and because tables like this can be hundreds
-of pages we want to keep processing time reasonable.
-
-To a large extend the behaviour is comparable with the way bTABLE/eTABLE
-works and there is a module that maps that one onto this one. Eventually
-this mechamism will be improved so that it can replace its older cousin.
-
-]]--
-
--- todo: use linked list instead of r/c array
-
-local commands, context, tex, node = commands, context, tex, node
-
-local texdimen = tex.dimen
-local texcount = tex.count
-local texbox = tex.box
-local texsetcount = tex.setcount
-local texsetdimen = tex.setdimen
-
-local format = string.format
-local concat = table.concat
-local points = number.points
-
-local context = context
-local context_beginvbox = context.beginvbox
-local context_endvbox = context.endvbox
-local context_blank = context.blank
-local context_nointerlineskip = context.nointerlineskip
-
-local variables = interfaces.variables
-
-local setmetatableindex = table.setmetatableindex
-local settings_to_hash = utilities.parsers.settings_to_hash
-
-local copy_node_list = node.copy_list
-local hpack_node_list = node.hpack
-local vpack_node_list = node.vpack
-local slide_node_list = node.slide
-local flush_node_list = node.flush_list
-
-local nodepool = nodes.pool
-
-local new_glue = nodepool.glue
-local new_kern = nodepool.kern
-local new_penalty = nodepool.penalty
-local new_hlist = nodepool.hlist
-
-local v_stretch = variables.stretch
-local v_normal = variables.normal
-local v_width = variables.width
-local v_height = variables.height
-local v_repeat = variables["repeat"]
-local v_max = variables.max
-local v_fixed = variables.fixed
-
-local xtables = { }
-typesetters.xtables = xtables
-
-local trace_xtable = false
-local report_xtable = logs.reporter("xtable")
-
-trackers.register("xtable.construct", function(v) trace_xtable = v end)
-
-local null_mode = 0
-local head_mode = 1
-local foot_mode = 2
-local more_mode = 3
-local body_mode = 4
-
-local namedmodes = { [0] =
- "null",
- "head",
- "foot",
- "next",
- "body",
-}
-
-local stack, data = { }, nil
-
-function xtables.create(settings)
- table.insert(stack,data)
- local rows = { }
- local widths = { }
- local heights = { }
- local depths = { }
- local spans = { }
- local distances = { }
- local autowidths = { }
- local modes = { }
- local fixedrows = { }
- local fixedcolumns = { }
- local frozencolumns = { }
- local options = { }
- data = {
- rows = rows,
- widths = widths,
- heights = heights,
- depths = depths,
- spans = spans,
- distances = distances,
- modes = modes,
- autowidths = autowidths,
- fixedrows = fixedrows,
- fixedcolumns = fixedcolumns,
- frozencolumns = frozencolumns,
- options = options,
- nofrows = 0,
- nofcolumns = 0,
- currentrow = 0,
- currentcolumn = 0,
- settings = settings or { },
- }
- local function add_zero(t,k)
- t[k] = 0
- return 0
- end
- local function add_table(t,k)
- local v = { }
- t[k] = v
- return v
- end
- local function add_cell(row,c)
- local cell = {
- nx = 0,
- ny = 0,
- list = false,
- }
- row[c] = cell
- if c > data.nofcolumns then
- data.nofcolumns = c
- end
- return cell
- end
- local function add_row(rows,r)
- local row = { }
- setmetatableindex(row,add_cell)
- rows[r] = row
- if r > data.nofrows then
- data.nofrows = r
- end
- return row
- end
- setmetatableindex(rows,add_row)
- setmetatableindex(widths,add_zero)
- setmetatableindex(heights,add_zero)
- setmetatableindex(depths,add_zero)
- setmetatableindex(distances,add_zero)
- setmetatableindex(modes,add_zero)
- setmetatableindex(fixedrows,add_zero)
- setmetatableindex(fixedcolumns,add_zero)
- setmetatableindex(options,add_table)
- --
- settings.columndistance = tonumber(settings.columndistance) or 0
- settings.rowdistance = tonumber(settings.rowdistance) or 0
- settings.leftmargindistance = tonumber(settings.leftmargindistance) or 0
- settings.rightmargindistance = tonumber(settings.rightmargindistance) or 0
- settings.options = settings_to_hash(settings.option)
- settings.textwidth = tonumber(settings.textwidth) or tex.hsize
- settings.lineheight = tonumber(settings.lineheight) or texdimen.lineheight
- settings.maxwidth = tonumber(settings.maxwidth) or settings.textwidth/8
- -- if #stack > 0 then
- -- settings.textwidth = tex.hsize
- -- end
- data.criterium_v = 2 * data.settings.lineheight
- data.criterium_h = .75 * data.settings.textwidth
-
-end
-
-function xtables.initialize_reflow_width(option)
- local r = data.currentrow
- local c = data.currentcolumn + 1
- local drc = data.rows[r][c]
- drc.nx = texcount.c_tabl_x_nx
- drc.ny = texcount.c_tabl_x_ny
- local distances = data.distances
- local distance = texdimen.d_tabl_x_distance
- if distance > distances[c] then
- distances[c] = distance
- end
- if option and option ~= "" then
- local options = settings_to_hash(option)
- data.options[r][c] = options
- if options[v_fixed] then
- data.frozencolumns[c] = true
- end
- end
- data.currentcolumn = c
-end
-
--- local function rather_fixed(n)
--- for n in node.
-
-function xtables.set_reflow_width()
- local r = data.currentrow
- local c = data.currentcolumn
- local rows = data.rows
- local row = rows[r]
- while row[c].span do -- can also be previous row ones
- c = c + 1
- end
- local tb = texbox.b_tabl_x
- local drc = row[c]
- --
- drc.list = true -- we don't need to keep the content around as we're in trial mode (no: copy_node_list(tb))
- --
- local widths, width = data.widths, tb.width
- if width > widths[c] then
- widths[c] = width
- end
- local heights, height = data.heights, tb.height
- if height > heights[r] then
- heights[r] = height
- end
- local depths, depth = data.depths, tb.depth
- if depth > depths[r] then
- depths[r] = depth
- end
- --
- local dimensionstate = texcount.frameddimensionstate
- local fixedcolumns = data.fixedcolumns
- local fixedrows = data.fixedrows
- if dimensionstate == 1 then
- if width > fixedcolumns[c] then -- how about a span here?
- fixedcolumns[c] = width
- end
- elseif dimensionstate == 2 then
- fixedrows[r] = height
- elseif dimensionstate == 3 then
- fixedrows[r] = height -- width
- fixedcolumns[c] = width -- height
- else -- probably something frozen, like an image -- we could parse the list
- if width <= data.criterium_h and height >= data.criterium_v then
- if width > fixedcolumns[c] then -- how about a span here?
- fixedcolumns[c] = width
- end
- end
- end
- drc.dimensionstate = dimensionstate
- --
- local nx, ny = drc.nx, drc.ny
- if nx > 1 or ny > 1 then
- local spans = data.spans
- local self = true
- for y=1,ny do
- for x=1,nx do
- if self then
- self = false
- else
- local ry = r + y - 1
- local cx = c + x - 1
- if y > 1 then
- spans[ry] = true
- end
- rows[ry][cx].span = true
- end
- end
- end
- c = c + nx - 1
- end
- if c > data.nofcolumns then
- data.nofcolumns = c
- end
- data.currentcolumn = c
-end
-
-function xtables.initialize_reflow_height()
- local r = data.currentrow
- local c = data.currentcolumn + 1
- local rows = data.rows
- local row = rows[r]
- while row[c].span do -- can also be previous row ones
- c = c + 1
- end
- data.currentcolumn = c
- local widths = data.widths
- local w = widths[c]
- local drc = row[c]
- for x=1,drc.nx-1 do
- w = w + widths[c+x]
- end
- texdimen.d_tabl_x_width = w
- local dimensionstate = drc.dimensionstate or 0
- if dimensionstate == 1 or dimensionstate == 3 then
- -- width was fixed so height is known
- texcount.c_tabl_x_skip_mode = 1
- elseif dimensionstate == 2 then
- -- height is enforced
- texcount.c_tabl_x_skip_mode = 1
- elseif data.autowidths[c] then
- -- width has changed so we need to recalculate the height
- texcount.c_tabl_x_skip_mode = 0
- else
- texcount.c_tabl_x_skip_mode = 1
- end
-end
-
-function xtables.set_reflow_height()
- local r = data.currentrow
- local c = data.currentcolumn
- local rows = data.rows
- local row = rows[r]
--- while row[c].span do -- we could adapt drc.nx instead
--- c = c + 1
--- end
- local tb = texbox.b_tabl_x
- local drc = row[c]
- if data.fixedrows[r] == 0 then -- and drc.dimensionstate < 2
- local heights, height = data.heights, tb.height
- if height > heights[r] then
- heights[r] = height
- end
- local depths, depth = data.depths, tb.depth
- if depth > depths[r] then
- depths[r] = depth
- end
- end
--- c = c + drc.nx - 1
--- data.currentcolumn = c
-end
-
-function xtables.initialize_construct()
- local r = data.currentrow
- local c = data.currentcolumn + 1
- local rows = data.rows
- local row = rows[r]
- while row[c].span do -- can also be previous row ones
- c = c + 1
- end
- data.currentcolumn = c
- local widths = data.widths
- local heights = data.heights
- local depths = data.depths
- local w = widths[c]
- local h = heights[r]
- local d = depths[r]
- local drc = row[c]
- for x=1,drc.nx-1 do
- w = w + widths[c+x]
- end
- for y=1,drc.ny-1 do
- h = h + heights[r+y]
- d = d + depths[r+y]
- end
- texdimen.d_tabl_x_width = w
- texdimen.d_tabl_x_height = h + d
- texdimen.d_tabl_x_depth = 0
-end
-
-function xtables.set_construct()
- local r = data.currentrow
- local c = data.currentcolumn
- local rows = data.rows
- local row = rows[r]
--- while row[c].span do -- can also be previous row ones
--- c = c + 1
--- end
- local drc = row[c]
- -- this will change as soon as in luatex we can reset a box list without freeing
- drc.list = copy_node_list(texbox.b_tabl_x)
--- c = c + drc.nx - 1
--- data.currentcolumn = c
-end
-
-local function showwidths(where,widths,autowidths)
- local result = { }
- for i=1,#widths do
- result[#result+1] = format("%12s%s",points(widths[i]),autowidths[i] and "*" or " ")
- end
- return report_xtable("%s : %s",where,concat(result," "))
-end
-
-function xtables.reflow_width()
- local nofrows = data.nofrows
- local nofcolumns = data.nofcolumns
- local rows = data.rows
- for r=1,nofrows do
- local row = rows[r]
- for c=1,nofcolumns do
- local drc = row[c]
- if drc.list then
- -- flush_node_list(drc.list)
- drc.list = false
- end
- end
- end
- -- spread
- local settings = data.settings
- local options = settings.options
- local maxwidth = settings.maxwidth
- -- calculate width
- local widths = data.widths
- local distances = data.distances
- local autowidths = data.autowidths
- local fixedcolumns = data.fixedcolumns
- local frozencolumns = data.frozencolumns
- local width = 0
- local distance = 0
- local nofwide = 0
- local widetotal = 0
- local available = settings.textwidth - settings.leftmargindistance - settings.rightmargindistance
- if trace_xtable then
- showwidths("stage 1",widths,autowidths)
- end
- local noffrozen = 0
- if options[v_max] then
- for c=1,nofcolumns do
- width = width + widths[c]
- if width > maxwidth then
- autowidths[c] = true
- nofwide = nofwide + 1
- widetotal = widetotal + widths[c]
- end
- if c < nofcolumns then
- distance = distance + distances[c]
- end
- if frozencolumns[c] then
- noffrozen = noffrozen + 1 -- brr, should be nx or so
- end
- end
- else
- for c=1,nofcolumns do -- also keep track of forced
- local fixedwidth = fixedcolumns[c]
- if fixedwidth > 0 then
- widths[c] = fixedwidth
- width = width + fixedwidth
- else
- width = width + widths[c]
- if width > maxwidth then
- autowidths[c] = true
- nofwide = nofwide + 1
- widetotal = widetotal + widths[c]
- end
- end
- if c < nofcolumns then
- distance = distance + distances[c]
- end
- if frozencolumns[c] then
- noffrozen = noffrozen + 1 -- brr, should be nx or so
- end
- end
- end
- if trace_xtable then
- showwidths("stage 2",widths,autowidths)
- end
- local delta = available - width - distance - (nofcolumns-1) * settings.columndistance
- if delta == 0 then
- -- nothing to be done
- if trace_xtable then
- report_xtable("perfect fit")
- end
- elseif delta > 0 then
- -- we can distribute some
- if not options[v_stretch] then
- -- not needed
- if trace_xtable then
- report_xtable("too wide but no stretch, delta %p",delta)
- end
- elseif options[v_width] then
- local factor = delta / width
- if trace_xtable then
- report_xtable("proportional stretch, delta %p, width %p, factor %a",delta,width,factor)
- end
- for c=1,nofcolumns do
- widths[c] = widths[c] + factor * widths[c]
- end
- else
- -- frozen -> a column with option=fixed will not stretch
- local extra = delta / (nofcolumns - noffrozen)
- if trace_xtable then
- report_xtable("normal stretch, delta %p, extra %p",delta,extra)
- end
- for c=1,nofcolumns do
- if not frozencolumns[c] then
- widths[c] = widths[c] + extra
- end
- end
- end
- elseif nofwide > 0 then
- while true do
- done = false
- local available = (widetotal + delta) / nofwide
- if trace_xtable then
- report_xtable("shrink check, total %p, delta %p, columns %s, fixed %p",widetotal,delta,nofwide,available)
- end
- for c=1,nofcolumns do
- if autowidths[c] and available >= widths[c] then
- autowidths[c] = nil
- nofwide = nofwide - 1
- widetotal = widetotal - widths[c]
- done = true
- end
- end
- if not done then
- break
- end
- end
- -- maybe also options[v_width] here but tricky as width does not say
- -- much about amount
- if options[v_width] then -- not that much (we could have a clever vpack loop balancing .. no fun)
- local factor = (widetotal + delta) / width
- if trace_xtable then
- report_xtable("proportional shrink used, total %p, delta %p, columns %s, factor %s",widetotal,delta,nofwide,factor)
- end
- for c=1,nofcolumns do
- if autowidths[c] then
- widths[c] = factor * widths[c]
- end
- end
- else
- local available = (widetotal + delta) / nofwide
- if trace_xtable then
- report_xtable("normal shrink used, total %p, delta %p, columns %s, fixed %p",widetotal,delta,nofwide,available)
- end
- for c=1,nofcolumns do
- if autowidths[c] then
- widths[c] = available
- end
- end
- end
- end
- if trace_xtable then
- showwidths("stage 3",widths,autowidths)
- end
- --
- data.currentrow = 0
- data.currentcolumn = 0
-end
-
-function xtables.reflow_height()
- data.currentrow = 0
- data.currentcolumn = 0
- local settings = data.settings
- if settings.options[v_height] then
- local heights = data.heights
- local depths = data.depths
- local nofrows = data.nofrows
- local totalheight = 0
- local totaldepth = 0
- for i=1,nofrows do
- totalheight = totalheight + heights[i]
- totalheight = totalheight + depths [i]
- end
- local total = totalheight + totaldepth
- local leftover = settings.textheight - total
- if leftover > 0 then
- local leftheight = (totalheight / total ) * leftover / #heights
- local leftdepth = (totaldepth / total ) * leftover / #depths
- for i=1,nofrows do
- heights[i] = heights[i] + leftheight
- depths [i] = depths [i] + leftdepth
- end
- end
- end
-end
-
-local function showspans(data)
- local rows = data.rows
- local modes = data.modes
- local nofcolumns = data.nofcolumns
- local nofrows = data.nofrows
- for r=1,nofrows do
- local line = { }
- local row = rows[r]
- for c=1,nofcolumns do
- local cell =row[c]
- if cell.list then
- line[#line+1] = "list"
- elseif cell.span then
- line[#line+1] = "span"
- else
- line[#line+1] = "none"
- end
- end
- report_xtable("%3d : %s : % t",r,namedmodes[modes[r]] or "----",line)
- end
-end
-
-function xtables.construct()
- local rows = data.rows
- local heights = data.heights
- local depths = data.depths
- local widths = data.widths
- local spans = data.spans
- local distances = data.distances
- local modes = data.modes
- local settings = data.settings
- local nofcolumns = data.nofcolumns
- local nofrows = data.nofrows
- local columndistance = settings.columndistance
- local rowdistance = settings.rowdistance
- local leftmargindistance = settings.leftmargindistance
- local rightmargindistance = settings.rightmargindistance
- -- ranges can be mixes so we collect
-
- if trace_xtable then
- showspans(data)
- end
-
- local ranges = {
- [head_mode] = { },
- [foot_mode] = { },
- [more_mode] = { },
- [body_mode] = { },
- }
- for r=1,nofrows do
- local m = modes[r]
- if m == 0 then
- m = body_mode
- end
- local range = ranges[m]
- range[#range+1] = r
- end
- -- todo: hook in the splitter ... the splitter can ask for a chunk of
- -- a certain size ... no longer a split memory issue then and header
- -- footer then has to happen here too .. target height
- local function packaged_column(r)
- local row = rows[r]
- local start = nil
- local stop = nil
- if leftmargindistance > 0 then
- start = new_kern(leftmargindistance)
- stop = start
- end
- local hasspan = false
- for c=1,nofcolumns do
- local drc = row[c]
- if not hasspan then
- hasspan = drc.span
- end
- local list = drc.list
- if list then
- list.shift = list.height + list.depth
- -- list = hpack_node_list(list) -- is somehow needed
- -- list.width = 0
- -- list.height = 0
- -- list.depth = 0
- -- faster:
- local h = new_hlist()
- h.list = list
- list = h
- --
- if start then
- stop.next = list
- list.prev = stop
- else
- start = list
- end
- stop = list -- one node anyway, so not needed: slide_node_list(list)
- end
- local step = widths[c]
- if c < nofcolumns then
- step = step + columndistance + distances[c]
- end
- local kern = new_kern(step)
- if stop then
- stop.prev = kern
- stop.next = kern
- else -- can be first spanning next row (ny=...)
- start = kern
- end
- stop = kern
- end
- if start then
- if rightmargindistance > 0 then
- local kern = new_kern(rightmargindistance)
- stop.next = kern
- kern.prev = stop
- -- stop = kern
- end
- return start, heights[r] + depths[r], hasspan
- end
- end
- local function collect_range(range)
- local result, nofr = { }, 0
- local nofrange = #range
- for i=1,#range do
- local r = range[i]
- -- local row = rows[r]
- local list, size, hasspan = packaged_column(r)
- if list then
- if hasspan and nofr > 0 then
- result[nofr][4] = true
- end
- nofr = nofr + 1
- result[nofr] = {
- hpack_node_list(list),
- size,
- i < nofrange and rowdistance > 0 and rowdistance or false, -- might move
- false
- }
- end
- end
- return result
- end
- local body = collect_range(ranges[body_mode])
- data.results = {
- [head_mode] = collect_range(ranges[head_mode]),
- [foot_mode] = collect_range(ranges[foot_mode]),
- [more_mode] = collect_range(ranges[more_mode]),
- [body_mode] = body,
- }
- if #body == 0 then
- texsetcount("global","c_tabl_x_state",0)
- texsetdimen("global","d_tabl_x_final_width",0)
- else
- texsetcount("global","c_tabl_x_state",1)
- texsetdimen("global","d_tabl_x_final_width",body[1][1].width)
- end
-end
-
-local function inject(row,copy,package)
- local list = row[1]
- if copy then
- row[1] = copy_node_list(list)
- end
- if package then
- context_beginvbox()
- context(list)
- context(new_kern(row[2]))
- context_endvbox()
- context_nointerlineskip() -- figure out a better way
- if row[4] then
- -- nothing as we have a span
- elseif row[3] then
- context_blank(row[3] .. "sp") -- why blank ?
- else
- context(new_glue(0))
- end
- else
- context(list)
- context(new_kern(row[2]))
- if row[3] then
- context(new_glue(row[3]))
- end
- end
-end
-
-local function total(row,distance)
- local n = #row > 0 and rowdistance or 0
- for i=1,#row do
- local ri = row[i]
- n = n + ri[2] + (ri[3] or 0)
- end
- return n
-end
-
--- local function append(list,what)
--- for i=1,#what do
--- local l = what[i]
--- list[#list+1] = l[1]
--- local k = l[2] + (l[3] or 0)
--- if k ~= 0 then
--- list[#list+1] = new_kern(k)
--- end
--- end
--- end
-
-local function spanheight(body,i)
- local height, n = 0, 1
- while true do
- local bi = body[i]
- if bi then
- height = height + bi[2] + (bi[3] or 0)
- if bi[4] then
- n = n + 1
- i = i + 1
- else
- break
- end
- else
- break
- end
- end
- return height, n
-end
-
-function xtables.flush(directives) -- todo split by size / no inbetween then .. glue list kern blank
- local vsize = directives.vsize
- local method = directives.method or v_normal
- local settings = data.settings
- local results = data.results
- local rowdistance = settings.rowdistance
- local head = results[head_mode]
- local foot = results[foot_mode]
- local more = results[more_mode]
- local body = results[body_mode]
- local repeatheader = settings.header == v_repeat
- local repeatfooter = settings.footer == v_repeat
- if vsize and vsize > 0 then
- context_beginvbox()
- local bodystart = data.bodystart or 1
- local bodystop = data.bodystop or #body
- if bodystart > 0 and bodystart <= bodystop then
- local bodysize = vsize
- local footsize = total(foot,rowdistance)
- local headsize = total(head,rowdistance)
- local moresize = total(more,rowdistance)
- local firstsize, firstspans = spanheight(body,bodystart)
- if bodystart == 1 then -- first chunk gets head
- bodysize = bodysize - headsize - footsize
- if headsize > 0 and bodysize >= firstsize then
- for i=1,#head do
- inject(head[i],repeatheader)
- end
- if rowdistance > 0 then
- context(new_glue(rowdistance))
- end
- if not repeatheader then
- results[head_mode] = { }
- end
- end
- elseif moresize > 0 then -- following chunk gets next
- bodysize = bodysize - footsize - moresize
- if bodysize >= firstsize then
- for i=1,#more do
- inject(more[i],true)
- end
- if rowdistance > 0 then
- context(new_glue(rowdistance))
- end
- end
- elseif headsize > 0 and repeatheader then -- following chunk gets head
- bodysize = bodysize - footsize - headsize
- if bodysize >= firstsize then
- for i=1,#head do
- inject(head[i],true)
- end
- if rowdistance > 0 then
- context(new_glue(rowdistance))
- end
- end
- else -- following chunk gets nothing
- bodysize = bodysize - footsize
- end
- if bodysize >= firstsize then
- local i = bodystart
- while i <= bodystop do -- room for improvement
- local total, spans = spanheight(body,i)
- local bs = bodysize - total
- if bs > 0 then
- bodysize = bs
- for s=1,spans do
- inject(body[i])
- body[i] = nil
- i = i + 1
- end
- bodystart = i
- else
- break
- end
- end
- if bodystart > bodystop then
- -- all is flushed and footer fits
- if footsize > 0 then
- if rowdistance > 0 then
- context(new_glue(rowdistance))
- end
- for i=1,#foot do
- inject(foot[i])
- end
- results[foot_mode] = { }
- end
- results[body_mode] = { }
- texsetcount("global","c_tabl_x_state",0)
- else
- -- some is left so footer is delayed
- -- todo: try to flush a few more lines
- if repeatfooter and footsize > 0 then
- if rowdistance > 0 then
- context(new_glue(rowdistance))
- end
- for i=1,#foot do
- inject(foot[i],true)
- end
- else
- -- todo: try to fit more of body
- end
- texsetcount("global","c_tabl_x_state",2)
- end
- else
- if firstsize > vsize then
- -- get rid of the too large cell
- for s=1,firstspans do
- inject(body[bodystart])
- body[bodystart] = nil
- bodystart = bodystart + 1
- end
- end
- texsetcount("global","c_tabl_x_state",2) -- 1
- end
- else
- texsetcount("global","c_tabl_x_state",0)
- end
- data.bodystart = bodystart
- data.bodystop = bodystop
- context_endvbox()
- else
- if method == variables.split then
- -- maybe also a non float mode with header/footer repeat although
- -- we can also use a float without caption
- for i=1,#head do
- inject(head[i],false,true)
- end
- if #head > 0 and rowdistance > 0 then
- context_blank(rowdistance .. "sp")
- end
- for i=1,#body do
- inject(body[i],false,true)
- end
- if #foot > 0 and rowdistance > 0 then
- context_blank(rowdistance .. "sp")
- end
- for i=1,#foot do
- inject(foot[i],false,true)
- end
- else -- normal
- context_beginvbox()
- for i=1,#head do
- inject(head[i])
- end
- if #head > 0 and rowdistance > 0 then
- context(new_glue(rowdistance))
- end
- for i=1,#body do
- inject(body[i])
- end
- if #foot > 0 and rowdistance > 0 then
- context(new_glue(rowdistance))
- end
- for i=1,#foot do
- inject(foot[i])
- end
- context_endvbox()
- end
- results[head_mode] = { }
- results[body_mode] = { }
- results[foot_mode] = { }
- texsetcount("global","c_tabl_x_state",0)
- end
-end
-
-function xtables.cleanup()
- for mode, result in next, data.results do
- for _, r in next, result do
- flush_node_list(r[1])
- end
- end
- data = table.remove(stack)
-end
-
-function xtables.next_row()
- local r = data.currentrow + 1
- data.modes[r] = texcount.c_tabl_x_mode
- data.currentrow = r
- data.currentcolumn = 0
-end
-
--- eventually we might only have commands
-
-commands.x_table_create = xtables.create
-commands.x_table_reflow_width = xtables.reflow_width
-commands.x_table_reflow_height = xtables.reflow_height
-commands.x_table_construct = xtables.construct
-commands.x_table_flush = xtables.flush
-commands.x_table_cleanup = xtables.cleanup
-commands.x_table_next_row = xtables.next_row
-commands.x_table_init_reflow_width = xtables.initialize_reflow_width
-commands.x_table_init_reflow_height = xtables.initialize_reflow_height
-commands.x_table_init_construct = xtables.initialize_construct
-commands.x_table_set_reflow_width = xtables.set_reflow_width
-commands.x_table_set_reflow_height = xtables.set_reflow_height
-commands.x_table_set_construct = xtables.set_construct
+if not modules then modules = { } end modules ['tabl-xtb'] = {
+ version = 1.001,
+ comment = "companion to tabl-xtb.mkvi",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[
+
+This table mechanism is a combination between TeX and Lua. We do process
+cells at the TeX end and inspect them at the Lua end. After some analysis
+we have a second pass using the calculated widths, and if needed cells
+will go through a third pass to get the heights right. This last pass is
+avoided when possible which is why some code below looks a bit more
+complex than needed. The reason for such optimizations is that each cells
+is actually a framed instance and because tables like this can be hundreds
+of pages we want to keep processing time reasonable.
+
+To a large extend the behaviour is comparable with the way bTABLE/eTABLE
+works and there is a module that maps that one onto this one. Eventually
+this mechamism will be improved so that it can replace its older cousin.
+
+]]--
+
+-- todo: use linked list instead of r/c array
+
+local commands, context, tex, node = commands, context, tex, node
+
+local texdimen = tex.dimen
+local texcount = tex.count
+local texbox = tex.box
+local texsetcount = tex.setcount
+local texsetdimen = tex.setdimen
+
+local format = string.format
+local concat = table.concat
+local points = number.points
+
+local context = context
+local context_beginvbox = context.beginvbox
+local context_endvbox = context.endvbox
+local context_blank = context.blank
+local context_nointerlineskip = context.nointerlineskip
+
+local variables = interfaces.variables
+
+local setmetatableindex = table.setmetatableindex
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+local copy_node_list = node.copy_list
+local hpack_node_list = node.hpack
+local vpack_node_list = node.vpack
+local slide_node_list = node.slide
+local flush_node_list = node.flush_list
+
+local nodepool = nodes.pool
+
+local new_glue = nodepool.glue
+local new_kern = nodepool.kern
+local new_penalty = nodepool.penalty
+local new_hlist = nodepool.hlist
+
+local v_stretch = variables.stretch
+local v_normal = variables.normal
+local v_width = variables.width
+local v_height = variables.height
+local v_repeat = variables["repeat"]
+local v_max = variables.max
+local v_fixed = variables.fixed
+
+local xtables = { }
+typesetters.xtables = xtables
+
+local trace_xtable = false
+local report_xtable = logs.reporter("xtable")
+
+trackers.register("xtable.construct", function(v) trace_xtable = v end)
+
+local null_mode = 0
+local head_mode = 1
+local foot_mode = 2
+local more_mode = 3
+local body_mode = 4
+
+local namedmodes = { [0] =
+ "null",
+ "head",
+ "foot",
+ "next",
+ "body",
+}
+
+local stack, data = { }, nil
+
+function xtables.create(settings)
+ table.insert(stack,data)
+ local rows = { }
+ local widths = { }
+ local heights = { }
+ local depths = { }
+ local spans = { }
+ local distances = { }
+ local autowidths = { }
+ local modes = { }
+ local fixedrows = { }
+ local fixedcolumns = { }
+ local frozencolumns = { }
+ local options = { }
+ data = {
+ rows = rows,
+ widths = widths,
+ heights = heights,
+ depths = depths,
+ spans = spans,
+ distances = distances,
+ modes = modes,
+ autowidths = autowidths,
+ fixedrows = fixedrows,
+ fixedcolumns = fixedcolumns,
+ frozencolumns = frozencolumns,
+ options = options,
+ nofrows = 0,
+ nofcolumns = 0,
+ currentrow = 0,
+ currentcolumn = 0,
+ settings = settings or { },
+ }
+ local function add_zero(t,k)
+ t[k] = 0
+ return 0
+ end
+ local function add_table(t,k)
+ local v = { }
+ t[k] = v
+ return v
+ end
+ local function add_cell(row,c)
+ local cell = {
+ nx = 0,
+ ny = 0,
+ list = false,
+ }
+ row[c] = cell
+ if c > data.nofcolumns then
+ data.nofcolumns = c
+ end
+ return cell
+ end
+ local function add_row(rows,r)
+ local row = { }
+ setmetatableindex(row,add_cell)
+ rows[r] = row
+ if r > data.nofrows then
+ data.nofrows = r
+ end
+ return row
+ end
+ setmetatableindex(rows,add_row)
+ setmetatableindex(widths,add_zero)
+ setmetatableindex(heights,add_zero)
+ setmetatableindex(depths,add_zero)
+ setmetatableindex(distances,add_zero)
+ setmetatableindex(modes,add_zero)
+ setmetatableindex(fixedrows,add_zero)
+ setmetatableindex(fixedcolumns,add_zero)
+ setmetatableindex(options,add_table)
+ --
+ settings.columndistance = tonumber(settings.columndistance) or 0
+ settings.rowdistance = tonumber(settings.rowdistance) or 0
+ settings.leftmargindistance = tonumber(settings.leftmargindistance) or 0
+ settings.rightmargindistance = tonumber(settings.rightmargindistance) or 0
+ settings.options = settings_to_hash(settings.option)
+ settings.textwidth = tonumber(settings.textwidth) or tex.hsize
+ settings.lineheight = tonumber(settings.lineheight) or texdimen.lineheight
+ settings.maxwidth = tonumber(settings.maxwidth) or settings.textwidth/8
+ -- if #stack > 0 then
+ -- settings.textwidth = tex.hsize
+ -- end
+ data.criterium_v = 2 * data.settings.lineheight
+ data.criterium_h = .75 * data.settings.textwidth
+
+end
+
+function xtables.initialize_reflow_width(option)
+ local r = data.currentrow
+ local c = data.currentcolumn + 1
+ local drc = data.rows[r][c]
+ drc.nx = texcount.c_tabl_x_nx
+ drc.ny = texcount.c_tabl_x_ny
+ local distances = data.distances
+ local distance = texdimen.d_tabl_x_distance
+ if distance > distances[c] then
+ distances[c] = distance
+ end
+ if option and option ~= "" then
+ local options = settings_to_hash(option)
+ data.options[r][c] = options
+ if options[v_fixed] then
+ data.frozencolumns[c] = true
+ end
+ end
+ data.currentcolumn = c
+end
+
+-- local function rather_fixed(n)
+-- for n in node.
+
+function xtables.set_reflow_width()
+ local r = data.currentrow
+ local c = data.currentcolumn
+ local rows = data.rows
+ local row = rows[r]
+ while row[c].span do -- can also be previous row ones
+ c = c + 1
+ end
+ local tb = texbox.b_tabl_x
+ local drc = row[c]
+ --
+ drc.list = true -- we don't need to keep the content around as we're in trial mode (no: copy_node_list(tb))
+ --
+ local widths, width = data.widths, tb.width
+ if width > widths[c] then
+ widths[c] = width
+ end
+ local heights, height = data.heights, tb.height
+ if height > heights[r] then
+ heights[r] = height
+ end
+ local depths, depth = data.depths, tb.depth
+ if depth > depths[r] then
+ depths[r] = depth
+ end
+ --
+ local dimensionstate = texcount.frameddimensionstate
+ local fixedcolumns = data.fixedcolumns
+ local fixedrows = data.fixedrows
+ if dimensionstate == 1 then
+ if width > fixedcolumns[c] then -- how about a span here?
+ fixedcolumns[c] = width
+ end
+ elseif dimensionstate == 2 then
+ fixedrows[r] = height
+ elseif dimensionstate == 3 then
+ fixedrows[r] = height -- width
+ fixedcolumns[c] = width -- height
+ else -- probably something frozen, like an image -- we could parse the list
+ if width <= data.criterium_h and height >= data.criterium_v then
+ if width > fixedcolumns[c] then -- how about a span here?
+ fixedcolumns[c] = width
+ end
+ end
+ end
+ drc.dimensionstate = dimensionstate
+ --
+ local nx, ny = drc.nx, drc.ny
+ if nx > 1 or ny > 1 then
+ local spans = data.spans
+ local self = true
+ for y=1,ny do
+ for x=1,nx do
+ if self then
+ self = false
+ else
+ local ry = r + y - 1
+ local cx = c + x - 1
+ if y > 1 then
+ spans[ry] = true
+ end
+ rows[ry][cx].span = true
+ end
+ end
+ end
+ c = c + nx - 1
+ end
+ if c > data.nofcolumns then
+ data.nofcolumns = c
+ end
+ data.currentcolumn = c
+end
+
+function xtables.initialize_reflow_height()
+ local r = data.currentrow
+ local c = data.currentcolumn + 1
+ local rows = data.rows
+ local row = rows[r]
+ while row[c].span do -- can also be previous row ones
+ c = c + 1
+ end
+ data.currentcolumn = c
+ local widths = data.widths
+ local w = widths[c]
+ local drc = row[c]
+ for x=1,drc.nx-1 do
+ w = w + widths[c+x]
+ end
+ texdimen.d_tabl_x_width = w
+ local dimensionstate = drc.dimensionstate or 0
+ if dimensionstate == 1 or dimensionstate == 3 then
+ -- width was fixed so height is known
+ texcount.c_tabl_x_skip_mode = 1
+ elseif dimensionstate == 2 then
+ -- height is enforced
+ texcount.c_tabl_x_skip_mode = 1
+ elseif data.autowidths[c] then
+ -- width has changed so we need to recalculate the height
+ texcount.c_tabl_x_skip_mode = 0
+ else
+ texcount.c_tabl_x_skip_mode = 1
+ end
+end
+
+function xtables.set_reflow_height()
+ local r = data.currentrow
+ local c = data.currentcolumn
+ local rows = data.rows
+ local row = rows[r]
+-- while row[c].span do -- we could adapt drc.nx instead
+-- c = c + 1
+-- end
+ local tb = texbox.b_tabl_x
+ local drc = row[c]
+ if data.fixedrows[r] == 0 then -- and drc.dimensionstate < 2
+ local heights, height = data.heights, tb.height
+ if height > heights[r] then
+ heights[r] = height
+ end
+ local depths, depth = data.depths, tb.depth
+ if depth > depths[r] then
+ depths[r] = depth
+ end
+ end
+-- c = c + drc.nx - 1
+-- data.currentcolumn = c
+end
+
+function xtables.initialize_construct()
+ local r = data.currentrow
+ local c = data.currentcolumn + 1
+ local rows = data.rows
+ local row = rows[r]
+ while row[c].span do -- can also be previous row ones
+ c = c + 1
+ end
+ data.currentcolumn = c
+ local widths = data.widths
+ local heights = data.heights
+ local depths = data.depths
+ local w = widths[c]
+ local h = heights[r]
+ local d = depths[r]
+ local drc = row[c]
+ for x=1,drc.nx-1 do
+ w = w + widths[c+x]
+ end
+ for y=1,drc.ny-1 do
+ h = h + heights[r+y]
+ d = d + depths[r+y]
+ end
+ texdimen.d_tabl_x_width = w
+ texdimen.d_tabl_x_height = h + d
+ texdimen.d_tabl_x_depth = 0
+end
+
+function xtables.set_construct()
+ local r = data.currentrow
+ local c = data.currentcolumn
+ local rows = data.rows
+ local row = rows[r]
+-- while row[c].span do -- can also be previous row ones
+-- c = c + 1
+-- end
+ local drc = row[c]
+ -- this will change as soon as in luatex we can reset a box list without freeing
+ drc.list = copy_node_list(texbox.b_tabl_x)
+-- c = c + drc.nx - 1
+-- data.currentcolumn = c
+end
+
+local function showwidths(where,widths,autowidths)
+ local result = { }
+ for i=1,#widths do
+ result[#result+1] = format("%12s%s",points(widths[i]),autowidths[i] and "*" or " ")
+ end
+ return report_xtable("%s : %s",where,concat(result," "))
+end
+
+function xtables.reflow_width()
+ local nofrows = data.nofrows
+ local nofcolumns = data.nofcolumns
+ local rows = data.rows
+ for r=1,nofrows do
+ local row = rows[r]
+ for c=1,nofcolumns do
+ local drc = row[c]
+ if drc.list then
+ -- flush_node_list(drc.list)
+ drc.list = false
+ end
+ end
+ end
+ -- spread
+ local settings = data.settings
+ local options = settings.options
+ local maxwidth = settings.maxwidth
+ -- calculate width
+ local widths = data.widths
+ local distances = data.distances
+ local autowidths = data.autowidths
+ local fixedcolumns = data.fixedcolumns
+ local frozencolumns = data.frozencolumns
+ local width = 0
+ local distance = 0
+ local nofwide = 0
+ local widetotal = 0
+ local available = settings.textwidth - settings.leftmargindistance - settings.rightmargindistance
+ if trace_xtable then
+ showwidths("stage 1",widths,autowidths)
+ end
+ local noffrozen = 0
+ if options[v_max] then
+ for c=1,nofcolumns do
+ width = width + widths[c]
+ if width > maxwidth then
+ autowidths[c] = true
+ nofwide = nofwide + 1
+ widetotal = widetotal + widths[c]
+ end
+ if c < nofcolumns then
+ distance = distance + distances[c]
+ end
+ if frozencolumns[c] then
+ noffrozen = noffrozen + 1 -- brr, should be nx or so
+ end
+ end
+ else
+ for c=1,nofcolumns do -- also keep track of forced
+ local fixedwidth = fixedcolumns[c]
+ if fixedwidth > 0 then
+ widths[c] = fixedwidth
+ width = width + fixedwidth
+ else
+ width = width + widths[c]
+ if width > maxwidth then
+ autowidths[c] = true
+ nofwide = nofwide + 1
+ widetotal = widetotal + widths[c]
+ end
+ end
+ if c < nofcolumns then
+ distance = distance + distances[c]
+ end
+ if frozencolumns[c] then
+ noffrozen = noffrozen + 1 -- brr, should be nx or so
+ end
+ end
+ end
+ if trace_xtable then
+ showwidths("stage 2",widths,autowidths)
+ end
+ local delta = available - width - distance - (nofcolumns-1) * settings.columndistance
+ if delta == 0 then
+ -- nothing to be done
+ if trace_xtable then
+ report_xtable("perfect fit")
+ end
+ elseif delta > 0 then
+ -- we can distribute some
+ if not options[v_stretch] then
+ -- not needed
+ if trace_xtable then
+ report_xtable("too wide but no stretch, delta %p",delta)
+ end
+ elseif options[v_width] then
+ local factor = delta / width
+ if trace_xtable then
+ report_xtable("proportional stretch, delta %p, width %p, factor %a",delta,width,factor)
+ end
+ for c=1,nofcolumns do
+ widths[c] = widths[c] + factor * widths[c]
+ end
+ else
+ -- frozen -> a column with option=fixed will not stretch
+ local extra = delta / (nofcolumns - noffrozen)
+ if trace_xtable then
+ report_xtable("normal stretch, delta %p, extra %p",delta,extra)
+ end
+ for c=1,nofcolumns do
+ if not frozencolumns[c] then
+ widths[c] = widths[c] + extra
+ end
+ end
+ end
+ elseif nofwide > 0 then
+ while true do
+ done = false
+ local available = (widetotal + delta) / nofwide
+ if trace_xtable then
+ report_xtable("shrink check, total %p, delta %p, columns %s, fixed %p",widetotal,delta,nofwide,available)
+ end
+ for c=1,nofcolumns do
+ if autowidths[c] and available >= widths[c] then
+ autowidths[c] = nil
+ nofwide = nofwide - 1
+ widetotal = widetotal - widths[c]
+ done = true
+ end
+ end
+ if not done then
+ break
+ end
+ end
+ -- maybe also options[v_width] here but tricky as width does not say
+ -- much about amount
+ if options[v_width] then -- not that much (we could have a clever vpack loop balancing .. no fun)
+ local factor = (widetotal + delta) / width
+ if trace_xtable then
+ report_xtable("proportional shrink used, total %p, delta %p, columns %s, factor %s",widetotal,delta,nofwide,factor)
+ end
+ for c=1,nofcolumns do
+ if autowidths[c] then
+ widths[c] = factor * widths[c]
+ end
+ end
+ else
+ local available = (widetotal + delta) / nofwide
+ if trace_xtable then
+ report_xtable("normal shrink used, total %p, delta %p, columns %s, fixed %p",widetotal,delta,nofwide,available)
+ end
+ for c=1,nofcolumns do
+ if autowidths[c] then
+ widths[c] = available
+ end
+ end
+ end
+ end
+ if trace_xtable then
+ showwidths("stage 3",widths,autowidths)
+ end
+ --
+ data.currentrow = 0
+ data.currentcolumn = 0
+end
+
+function xtables.reflow_height()
+ data.currentrow = 0
+ data.currentcolumn = 0
+ local settings = data.settings
+ if settings.options[v_height] then
+ local heights = data.heights
+ local depths = data.depths
+ local nofrows = data.nofrows
+ local totalheight = 0
+ local totaldepth = 0
+ for i=1,nofrows do
+ totalheight = totalheight + heights[i]
+ totalheight = totalheight + depths [i]
+ end
+ local total = totalheight + totaldepth
+ local leftover = settings.textheight - total
+ if leftover > 0 then
+ local leftheight = (totalheight / total ) * leftover / #heights
+ local leftdepth = (totaldepth / total ) * leftover / #depths
+ for i=1,nofrows do
+ heights[i] = heights[i] + leftheight
+ depths [i] = depths [i] + leftdepth
+ end
+ end
+ end
+end
+
+local function showspans(data)
+ local rows = data.rows
+ local modes = data.modes
+ local nofcolumns = data.nofcolumns
+ local nofrows = data.nofrows
+ for r=1,nofrows do
+ local line = { }
+ local row = rows[r]
+ for c=1,nofcolumns do
+ local cell =row[c]
+ if cell.list then
+ line[#line+1] = "list"
+ elseif cell.span then
+ line[#line+1] = "span"
+ else
+ line[#line+1] = "none"
+ end
+ end
+ report_xtable("%3d : %s : % t",r,namedmodes[modes[r]] or "----",line)
+ end
+end
+
+function xtables.construct()
+ local rows = data.rows
+ local heights = data.heights
+ local depths = data.depths
+ local widths = data.widths
+ local spans = data.spans
+ local distances = data.distances
+ local modes = data.modes
+ local settings = data.settings
+ local nofcolumns = data.nofcolumns
+ local nofrows = data.nofrows
+ local columndistance = settings.columndistance
+ local rowdistance = settings.rowdistance
+ local leftmargindistance = settings.leftmargindistance
+ local rightmargindistance = settings.rightmargindistance
+ -- ranges can be mixes so we collect
+
+ if trace_xtable then
+ showspans(data)
+ end
+
+ local ranges = {
+ [head_mode] = { },
+ [foot_mode] = { },
+ [more_mode] = { },
+ [body_mode] = { },
+ }
+ for r=1,nofrows do
+ local m = modes[r]
+ if m == 0 then
+ m = body_mode
+ end
+ local range = ranges[m]
+ range[#range+1] = r
+ end
+ -- todo: hook in the splitter ... the splitter can ask for a chunk of
+ -- a certain size ... no longer a split memory issue then and header
+ -- footer then has to happen here too .. target height
+ local function packaged_column(r)
+ local row = rows[r]
+ local start = nil
+ local stop = nil
+ if leftmargindistance > 0 then
+ start = new_kern(leftmargindistance)
+ stop = start
+ end
+ local hasspan = false
+ for c=1,nofcolumns do
+ local drc = row[c]
+ if not hasspan then
+ hasspan = drc.span
+ end
+ local list = drc.list
+ if list then
+ list.shift = list.height + list.depth
+ -- list = hpack_node_list(list) -- is somehow needed
+ -- list.width = 0
+ -- list.height = 0
+ -- list.depth = 0
+ -- faster:
+ local h = new_hlist()
+ h.list = list
+ list = h
+ --
+ if start then
+ stop.next = list
+ list.prev = stop
+ else
+ start = list
+ end
+ stop = list -- one node anyway, so not needed: slide_node_list(list)
+ end
+ local step = widths[c]
+ if c < nofcolumns then
+ step = step + columndistance + distances[c]
+ end
+ local kern = new_kern(step)
+ if stop then
+ stop.prev = kern
+ stop.next = kern
+ else -- can be first spanning next row (ny=...)
+ start = kern
+ end
+ stop = kern
+ end
+ if start then
+ if rightmargindistance > 0 then
+ local kern = new_kern(rightmargindistance)
+ stop.next = kern
+ kern.prev = stop
+ -- stop = kern
+ end
+ return start, heights[r] + depths[r], hasspan
+ end
+ end
+ local function collect_range(range)
+ local result, nofr = { }, 0
+ local nofrange = #range
+ for i=1,#range do
+ local r = range[i]
+ -- local row = rows[r]
+ local list, size, hasspan = packaged_column(r)
+ if list then
+ if hasspan and nofr > 0 then
+ result[nofr][4] = true
+ end
+ nofr = nofr + 1
+ result[nofr] = {
+ hpack_node_list(list),
+ size,
+ i < nofrange and rowdistance > 0 and rowdistance or false, -- might move
+ false
+ }
+ end
+ end
+ return result
+ end
+ local body = collect_range(ranges[body_mode])
+ data.results = {
+ [head_mode] = collect_range(ranges[head_mode]),
+ [foot_mode] = collect_range(ranges[foot_mode]),
+ [more_mode] = collect_range(ranges[more_mode]),
+ [body_mode] = body,
+ }
+ if #body == 0 then
+ texsetcount("global","c_tabl_x_state",0)
+ texsetdimen("global","d_tabl_x_final_width",0)
+ else
+ texsetcount("global","c_tabl_x_state",1)
+ texsetdimen("global","d_tabl_x_final_width",body[1][1].width)
+ end
+end
+
+local function inject(row,copy,package)
+ local list = row[1]
+ if copy then
+ row[1] = copy_node_list(list)
+ end
+ if package then
+ context_beginvbox()
+ context(list)
+ context(new_kern(row[2]))
+ context_endvbox()
+ context_nointerlineskip() -- figure out a better way
+ if row[4] then
+ -- nothing as we have a span
+ elseif row[3] then
+ context_blank(row[3] .. "sp") -- why blank ?
+ else
+ context(new_glue(0))
+ end
+ else
+ context(list)
+ context(new_kern(row[2]))
+ if row[3] then
+ context(new_glue(row[3]))
+ end
+ end
+end
+
+local function total(row,distance)
+ local n = #row > 0 and rowdistance or 0
+ for i=1,#row do
+ local ri = row[i]
+ n = n + ri[2] + (ri[3] or 0)
+ end
+ return n
+end
+
+-- local function append(list,what)
+-- for i=1,#what do
+-- local l = what[i]
+-- list[#list+1] = l[1]
+-- local k = l[2] + (l[3] or 0)
+-- if k ~= 0 then
+-- list[#list+1] = new_kern(k)
+-- end
+-- end
+-- end
+
+local function spanheight(body,i)
+ local height, n = 0, 1
+ while true do
+ local bi = body[i]
+ if bi then
+ height = height + bi[2] + (bi[3] or 0)
+ if bi[4] then
+ n = n + 1
+ i = i + 1
+ else
+ break
+ end
+ else
+ break
+ end
+ end
+ return height, n
+end
+
+function xtables.flush(directives) -- todo split by size / no inbetween then .. glue list kern blank
+ local vsize = directives.vsize
+ local method = directives.method or v_normal
+ local settings = data.settings
+ local results = data.results
+ local rowdistance = settings.rowdistance
+ local head = results[head_mode]
+ local foot = results[foot_mode]
+ local more = results[more_mode]
+ local body = results[body_mode]
+ local repeatheader = settings.header == v_repeat
+ local repeatfooter = settings.footer == v_repeat
+ if vsize and vsize > 0 then
+ context_beginvbox()
+ local bodystart = data.bodystart or 1
+ local bodystop = data.bodystop or #body
+ if bodystart > 0 and bodystart <= bodystop then
+ local bodysize = vsize
+ local footsize = total(foot,rowdistance)
+ local headsize = total(head,rowdistance)
+ local moresize = total(more,rowdistance)
+ local firstsize, firstspans = spanheight(body,bodystart)
+ if bodystart == 1 then -- first chunk gets head
+ bodysize = bodysize - headsize - footsize
+ if headsize > 0 and bodysize >= firstsize then
+ for i=1,#head do
+ inject(head[i],repeatheader)
+ end
+ if rowdistance > 0 then
+ context(new_glue(rowdistance))
+ end
+ if not repeatheader then
+ results[head_mode] = { }
+ end
+ end
+ elseif moresize > 0 then -- following chunk gets next
+ bodysize = bodysize - footsize - moresize
+ if bodysize >= firstsize then
+ for i=1,#more do
+ inject(more[i],true)
+ end
+ if rowdistance > 0 then
+ context(new_glue(rowdistance))
+ end
+ end
+ elseif headsize > 0 and repeatheader then -- following chunk gets head
+ bodysize = bodysize - footsize - headsize
+ if bodysize >= firstsize then
+ for i=1,#head do
+ inject(head[i],true)
+ end
+ if rowdistance > 0 then
+ context(new_glue(rowdistance))
+ end
+ end
+ else -- following chunk gets nothing
+ bodysize = bodysize - footsize
+ end
+ if bodysize >= firstsize then
+ local i = bodystart
+ while i <= bodystop do -- room for improvement
+ local total, spans = spanheight(body,i)
+ local bs = bodysize - total
+ if bs > 0 then
+ bodysize = bs
+ for s=1,spans do
+ inject(body[i])
+ body[i] = nil
+ i = i + 1
+ end
+ bodystart = i
+ else
+ break
+ end
+ end
+ if bodystart > bodystop then
+ -- all is flushed and footer fits
+ if footsize > 0 then
+ if rowdistance > 0 then
+ context(new_glue(rowdistance))
+ end
+ for i=1,#foot do
+ inject(foot[i])
+ end
+ results[foot_mode] = { }
+ end
+ results[body_mode] = { }
+ texsetcount("global","c_tabl_x_state",0)
+ else
+ -- some is left so footer is delayed
+ -- todo: try to flush a few more lines
+ if repeatfooter and footsize > 0 then
+ if rowdistance > 0 then
+ context(new_glue(rowdistance))
+ end
+ for i=1,#foot do
+ inject(foot[i],true)
+ end
+ else
+ -- todo: try to fit more of body
+ end
+ texsetcount("global","c_tabl_x_state",2)
+ end
+ else
+ if firstsize > vsize then
+ -- get rid of the too large cell
+ for s=1,firstspans do
+ inject(body[bodystart])
+ body[bodystart] = nil
+ bodystart = bodystart + 1
+ end
+ end
+ texsetcount("global","c_tabl_x_state",2) -- 1
+ end
+ else
+ texsetcount("global","c_tabl_x_state",0)
+ end
+ data.bodystart = bodystart
+ data.bodystop = bodystop
+ context_endvbox()
+ else
+ if method == variables.split then
+ -- maybe also a non float mode with header/footer repeat although
+ -- we can also use a float without caption
+ for i=1,#head do
+ inject(head[i],false,true)
+ end
+ if #head > 0 and rowdistance > 0 then
+ context_blank(rowdistance .. "sp")
+ end
+ for i=1,#body do
+ inject(body[i],false,true)
+ end
+ if #foot > 0 and rowdistance > 0 then
+ context_blank(rowdistance .. "sp")
+ end
+ for i=1,#foot do
+ inject(foot[i],false,true)
+ end
+ else -- normal
+ context_beginvbox()
+ for i=1,#head do
+ inject(head[i])
+ end
+ if #head > 0 and rowdistance > 0 then
+ context(new_glue(rowdistance))
+ end
+ for i=1,#body do
+ inject(body[i])
+ end
+ if #foot > 0 and rowdistance > 0 then
+ context(new_glue(rowdistance))
+ end
+ for i=1,#foot do
+ inject(foot[i])
+ end
+ context_endvbox()
+ end
+ results[head_mode] = { }
+ results[body_mode] = { }
+ results[foot_mode] = { }
+ texsetcount("global","c_tabl_x_state",0)
+ end
+end
+
+function xtables.cleanup()
+ for mode, result in next, data.results do
+ for _, r in next, result do
+ flush_node_list(r[1])
+ end
+ end
+ data = table.remove(stack)
+end
+
+function xtables.next_row()
+ local r = data.currentrow + 1
+ data.modes[r] = texcount.c_tabl_x_mode
+ data.currentrow = r
+ data.currentcolumn = 0
+end
+
+-- eventually we might only have commands
+
+commands.x_table_create = xtables.create
+commands.x_table_reflow_width = xtables.reflow_width
+commands.x_table_reflow_height = xtables.reflow_height
+commands.x_table_construct = xtables.construct
+commands.x_table_flush = xtables.flush
+commands.x_table_cleanup = xtables.cleanup
+commands.x_table_next_row = xtables.next_row
+commands.x_table_init_reflow_width = xtables.initialize_reflow_width
+commands.x_table_init_reflow_height = xtables.initialize_reflow_height
+commands.x_table_init_construct = xtables.initialize_construct
+commands.x_table_set_reflow_width = xtables.set_reflow_width
+commands.x_table_set_reflow_height = xtables.set_reflow_height
+commands.x_table_set_construct = xtables.set_construct
diff --git a/tex/context/base/task-ini.lua b/tex/context/base/task-ini.lua
index 41f045ac9..0f477cb6e 100644
--- a/tex/context/base/task-ini.lua
+++ b/tex/context/base/task-ini.lua
@@ -1,191 +1,191 @@
-if not modules then modules = { } end modules ['task-ini'] = {
- version = 1.001,
- comment = "companion to task-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- this is a temporary solution, we need to isolate some modules and then
--- the load order can determine the trickery to be applied to node lists
---
--- we can disable more handlers and enable then when really used (*)
---
--- todo: two finalizers: real shipout (can be imposed page) and page shipout (individual page)
-
-local tasks = nodes.tasks
-local appendaction = tasks.appendaction
-local disableaction = tasks.disableaction
-local freezegroup = tasks.freezegroup
-local freezecallbacks = callbacks.freeze
-
-appendaction("processors", "normalizers", "typesetters.characters.handler") -- always on
-appendaction("processors", "normalizers", "fonts.collections.process") -- disabled
-appendaction("processors", "normalizers", "fonts.checkers.missing") -- disabled
-
-appendaction("processors", "characters", "scripts.autofontfeature.handler")
-appendaction("processors", "characters", "scripts.splitters.handler") -- disabled
-appendaction("processors", "characters", "typesetters.cleaners.handler") -- disabled
-appendaction("processors", "characters", "typesetters.directions.handler") -- disabled
-appendaction("processors", "characters", "typesetters.cases.handler") -- disabled
-appendaction("processors", "characters", "typesetters.breakpoints.handler") -- disabled
-appendaction("processors", "characters", "scripts.injectors.handler") -- disabled
-
-appendaction("processors", "words", "builders.kernel.hyphenation") -- always on
-appendaction("processors", "words", "languages.words.check") -- disabled
-
-appendaction("processors", "fonts", "builders.paragraphs.solutions.splitters.split") -- experimental
-appendaction("processors", "fonts", "nodes.handlers.characters") -- maybe todo
-appendaction("processors", "fonts", "nodes.injections.handler") -- maybe todo
-appendaction("processors", "fonts", "nodes.handlers.protectglyphs", nil, "nohead") -- maybe todo
-appendaction("processors", "fonts", "builders.kernel.ligaturing") -- always on (could be selective: if only node mode)
-appendaction("processors", "fonts", "builders.kernel.kerning") -- always on (could be selective: if only node mode)
-appendaction("processors", "fonts", "nodes.handlers.stripping") -- disabled (might move)
-------------("processors", "fonts", "typesetters.italics.handler") -- disabled (after otf/kern handling)
-
-appendaction("processors", "lists", "typesetters.spacings.handler") -- disabled
-appendaction("processors", "lists", "typesetters.kerns.handler") -- disabled
-appendaction("processors", "lists", "typesetters.digits.handler") -- disabled (after otf handling)
-appendaction("processors", "lists", "typesetters.italics.handler") -- disabled (after otf/kern handling)
-appendaction("processors", "lists", "typesetters.paragraphs.handler") -- disabled
-
-appendaction("shipouts", "normalizers", "nodes.handlers.cleanuppage") -- disabled
-appendaction("shipouts", "normalizers", "typesetters.alignments.handler")
-appendaction("shipouts", "normalizers", "nodes.references.handler") -- disabled
-appendaction("shipouts", "normalizers", "nodes.destinations.handler") -- disabled
-appendaction("shipouts", "normalizers", "nodes.rules.handler") -- disabled
-appendaction("shipouts", "normalizers", "nodes.shifts.handler") -- disabled
-appendaction("shipouts", "normalizers", "structures.tags.handler") -- disabled
-appendaction("shipouts", "normalizers", "nodes.handlers.accessibility") -- disabled
-appendaction("shipouts", "normalizers", "nodes.handlers.backgrounds") -- disabled
-appendaction("shipouts", "normalizers", "nodes.handlers.alignbackgrounds") -- disabled
-------------("shipouts", "normalizers", "nodes.handlers.export") -- disabled
-
-appendaction("shipouts", "finishers", "nodes.visualizers.handler") -- disabled
-appendaction("shipouts", "finishers", "attributes.colors.handler") -- disabled
-appendaction("shipouts", "finishers", "attributes.transparencies.handler") -- disabled
-appendaction("shipouts", "finishers", "attributes.colorintents.handler") -- disabled
-appendaction("shipouts", "finishers", "attributes.negatives.handler") -- disabled
-appendaction("shipouts", "finishers", "attributes.effects.handler") -- disabled
-appendaction("shipouts", "finishers", "attributes.viewerlayers.handler") -- disabled
-
---maybe integrate relocate and families
-
-appendaction("math", "normalizers", "noads.handlers.unscript", nil, "nohead") -- always on (maybe disabled)
-appendaction("math", "normalizers", "noads.handlers.variants", nil, "nohead") -- always on
-appendaction("math", "normalizers", "noads.handlers.relocate", nil, "nohead") -- always on
-appendaction("math", "normalizers", "noads.handlers.families", nil, "nohead") -- always on
-
-appendaction("math", "normalizers", "noads.handlers.render", nil, "nohead") -- always on
-appendaction("math", "normalizers", "noads.handlers.collapse", nil, "nohead") -- always on
-appendaction("math", "normalizers", "noads.handlers.resize", nil, "nohead") -- always on
-------------("math", "normalizers", "noads.handlers.respace", nil, "nohead") -- always on
-appendaction("math", "normalizers", "noads.handlers.check", nil, "nohead") -- always on
-appendaction("math", "normalizers", "noads.handlers.tags", nil, "nohead") -- disabled
-appendaction("math", "normalizers", "noads.handlers.italics", nil, "nohead") -- disabled
-
-appendaction("math", "builders", "builders.kernel.mlist_to_hlist") -- always on
-------------("math", "builders", "noads.handlers.italics", nil, "nohead") -- disabled
-
--- quite experimental (nodes.handlers.graphicvadjust might go away)
-
-appendaction("finalizers", "lists", "builders.paragraphs.keeptogether")
-appendaction("finalizers", "lists", "nodes.handlers.graphicvadjust") -- todo
-appendaction("finalizers", "fonts", "builders.paragraphs.solutions.splitters.optimize") -- experimental
-appendaction("finalizers", "lists", "builders.paragraphs.tag")
-
--- still experimental
-
-appendaction("mvlbuilders", "normalizers", "nodes.handlers.migrate") --
-appendaction("mvlbuilders", "normalizers", "builders.vspacing.pagehandler") -- last !
-
-appendaction("vboxbuilders", "normalizers", "builders.vspacing.vboxhandler") --
-
--- experimental too
-
-appendaction("mvlbuilders","normalizers","typesetters.checkers.handler")
-appendaction("vboxbuilders","normalizers","typesetters.checkers.handler")
-
--- speedup: only kick in when used
-
-disableaction("processors", "scripts.autofontfeature.handler")
-disableaction("processors", "scripts.splitters.handler")
-disableaction("processors", "scripts.injectors.handler") -- was enabled
-disableaction("processors", "fonts.collections.process")
-disableaction("processors", "fonts.checkers.missing")
-disableaction("processors", "chars.handle_breakpoints")
-disableaction("processors", "typesetters.cleaners.handler")
-disableaction("processors", "typesetters.cases.handler")
-disableaction("processors", "typesetters.digits.handler")
-disableaction("processors", "typesetters.breakpoints.handler")
-disableaction("processors", "typesetters.directions.handler")
-disableaction("processors", "languages.words.check")
-disableaction("processors", "typesetters.spacings.handler")
-disableaction("processors", "typesetters.kerns.handler")
-disableaction("processors", "typesetters.italics.handler")
-disableaction("processors", "nodes.handlers.stripping")
-disableaction("processors", "typesetters.paragraphs.handler")
-
-disableaction("shipouts", "typesetters.alignments.handler")
-disableaction("shipouts", "nodes.rules.handler")
-disableaction("shipouts", "nodes.shifts.handler")
-disableaction("shipouts", "attributes.colors.handler")
-disableaction("shipouts", "attributes.transparencies.handler")
-disableaction("shipouts", "attributes.colorintents.handler")
-disableaction("shipouts", "attributes.effects.handler")
-disableaction("shipouts", "attributes.negatives.handler")
-disableaction("shipouts", "attributes.viewerlayers.handler")
-disableaction("shipouts", "structures.tags.handler")
-disableaction("shipouts", "nodes.visualizers.handler")
-disableaction("shipouts", "nodes.handlers.accessibility")
-disableaction("shipouts", "nodes.handlers.backgrounds")
-disableaction("shipouts", "nodes.handlers.alignbackgrounds")
-disableaction("shipouts", "nodes.handlers.cleanuppage")
-
-disableaction("shipouts", "nodes.references.handler")
-disableaction("shipouts", "nodes.destinations.handler")
-
---~ disableaction("shipouts", "nodes.handlers.export")
-
-disableaction("mvlbuilders", "nodes.handlers.migrate")
-
-disableaction("processors", "builders.paragraphs.solutions.splitters.split")
-
-disableaction("finalizers", "builders.paragraphs.keeptogether")
-disableaction("finalizers", "builders.paragraphs.solutions.splitters.optimize")
-disableaction("finalizers", "nodes.handlers.graphicvadjust") -- sort of obsolete
-disableaction("finalizers", "builders.paragraphs.tag")
-
-disableaction("math", "noads.handlers.tags")
-disableaction("math", "noads.handlers.italics")
-
-disableaction("mvlbuilders", "typesetters.checkers.handler")
-disableaction("vboxbuilders","typesetters.checkers.handler")
-
-freezecallbacks("find_.*_file", "find file using resolver")
-freezecallbacks("read_.*_file", "read file at once")
-freezecallbacks("open_.*_file", "open file for reading")
-
--- experimental:
-
-freezegroup("processors", "normalizers")
-freezegroup("processors", "characters")
-freezegroup("processors", "words")
-freezegroup("processors", "fonts")
-freezegroup("processors", "lists")
-
-freezegroup("finalizers", "normalizers")
-freezegroup("finalizers", "fonts")
-freezegroup("finalizers", "lists")
-
-freezegroup("shipouts", "normalizers")
-freezegroup("shipouts", "finishers")
-
-freezegroup("mvlbuilders", "normalizers")
-freezegroup("vboxbuilders", "normalizers")
-
------------("parbuilders", "lists")
------------("pagebuilders", "lists")
-
-freezegroup("math", "normalizers")
-freezegroup("math", "builders")
+if not modules then modules = { } end modules ['task-ini'] = {
+ version = 1.001,
+ comment = "companion to task-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- this is a temporary solution, we need to isolate some modules and then
+-- the load order can determine the trickery to be applied to node lists
+--
+-- we can disable more handlers and enable then when really used (*)
+--
+-- todo: two finalizers: real shipout (can be imposed page) and page shipout (individual page)
+
+local tasks = nodes.tasks
+local appendaction = tasks.appendaction
+local disableaction = tasks.disableaction
+local freezegroup = tasks.freezegroup
+local freezecallbacks = callbacks.freeze
+
+appendaction("processors", "normalizers", "typesetters.characters.handler") -- always on
+appendaction("processors", "normalizers", "fonts.collections.process") -- disabled
+appendaction("processors", "normalizers", "fonts.checkers.missing") -- disabled
+
+appendaction("processors", "characters", "scripts.autofontfeature.handler")
+appendaction("processors", "characters", "scripts.splitters.handler") -- disabled
+appendaction("processors", "characters", "typesetters.cleaners.handler") -- disabled
+appendaction("processors", "characters", "typesetters.directions.handler") -- disabled
+appendaction("processors", "characters", "typesetters.cases.handler") -- disabled
+appendaction("processors", "characters", "typesetters.breakpoints.handler") -- disabled
+appendaction("processors", "characters", "scripts.injectors.handler") -- disabled
+
+appendaction("processors", "words", "builders.kernel.hyphenation") -- always on
+appendaction("processors", "words", "languages.words.check") -- disabled
+
+appendaction("processors", "fonts", "builders.paragraphs.solutions.splitters.split") -- experimental
+appendaction("processors", "fonts", "nodes.handlers.characters") -- maybe todo
+appendaction("processors", "fonts", "nodes.injections.handler") -- maybe todo
+appendaction("processors", "fonts", "nodes.handlers.protectglyphs", nil, "nohead") -- maybe todo
+appendaction("processors", "fonts", "builders.kernel.ligaturing") -- always on (could be selective: if only node mode)
+appendaction("processors", "fonts", "builders.kernel.kerning") -- always on (could be selective: if only node mode)
+appendaction("processors", "fonts", "nodes.handlers.stripping") -- disabled (might move)
+------------("processors", "fonts", "typesetters.italics.handler") -- disabled (after otf/kern handling)
+
+appendaction("processors", "lists", "typesetters.spacings.handler") -- disabled
+appendaction("processors", "lists", "typesetters.kerns.handler") -- disabled
+appendaction("processors", "lists", "typesetters.digits.handler") -- disabled (after otf handling)
+appendaction("processors", "lists", "typesetters.italics.handler") -- disabled (after otf/kern handling)
+appendaction("processors", "lists", "typesetters.paragraphs.handler") -- disabled
+
+appendaction("shipouts", "normalizers", "nodes.handlers.cleanuppage") -- disabled
+appendaction("shipouts", "normalizers", "typesetters.alignments.handler")
+appendaction("shipouts", "normalizers", "nodes.references.handler") -- disabled
+appendaction("shipouts", "normalizers", "nodes.destinations.handler") -- disabled
+appendaction("shipouts", "normalizers", "nodes.rules.handler") -- disabled
+appendaction("shipouts", "normalizers", "nodes.shifts.handler") -- disabled
+appendaction("shipouts", "normalizers", "structures.tags.handler") -- disabled
+appendaction("shipouts", "normalizers", "nodes.handlers.accessibility") -- disabled
+appendaction("shipouts", "normalizers", "nodes.handlers.backgrounds") -- disabled
+appendaction("shipouts", "normalizers", "nodes.handlers.alignbackgrounds") -- disabled
+------------("shipouts", "normalizers", "nodes.handlers.export") -- disabled
+
+appendaction("shipouts", "finishers", "nodes.visualizers.handler") -- disabled
+appendaction("shipouts", "finishers", "attributes.colors.handler") -- disabled
+appendaction("shipouts", "finishers", "attributes.transparencies.handler") -- disabled
+appendaction("shipouts", "finishers", "attributes.colorintents.handler") -- disabled
+appendaction("shipouts", "finishers", "attributes.negatives.handler") -- disabled
+appendaction("shipouts", "finishers", "attributes.effects.handler") -- disabled
+appendaction("shipouts", "finishers", "attributes.viewerlayers.handler") -- disabled
+
+--maybe integrate relocate and families
+
+appendaction("math", "normalizers", "noads.handlers.unscript", nil, "nohead") -- always on (maybe disabled)
+appendaction("math", "normalizers", "noads.handlers.variants", nil, "nohead") -- always on
+appendaction("math", "normalizers", "noads.handlers.relocate", nil, "nohead") -- always on
+appendaction("math", "normalizers", "noads.handlers.families", nil, "nohead") -- always on
+
+appendaction("math", "normalizers", "noads.handlers.render", nil, "nohead") -- always on
+appendaction("math", "normalizers", "noads.handlers.collapse", nil, "nohead") -- always on
+appendaction("math", "normalizers", "noads.handlers.resize", nil, "nohead") -- always on
+------------("math", "normalizers", "noads.handlers.respace", nil, "nohead") -- always on
+appendaction("math", "normalizers", "noads.handlers.check", nil, "nohead") -- always on
+appendaction("math", "normalizers", "noads.handlers.tags", nil, "nohead") -- disabled
+appendaction("math", "normalizers", "noads.handlers.italics", nil, "nohead") -- disabled
+
+appendaction("math", "builders", "builders.kernel.mlist_to_hlist") -- always on
+------------("math", "builders", "noads.handlers.italics", nil, "nohead") -- disabled
+
+-- quite experimental (nodes.handlers.graphicvadjust might go away)
+
+appendaction("finalizers", "lists", "builders.paragraphs.keeptogether")
+appendaction("finalizers", "lists", "nodes.handlers.graphicvadjust") -- todo
+appendaction("finalizers", "fonts", "builders.paragraphs.solutions.splitters.optimize") -- experimental
+appendaction("finalizers", "lists", "builders.paragraphs.tag")
+
+-- still experimental
+
+appendaction("mvlbuilders", "normalizers", "nodes.handlers.migrate") --
+appendaction("mvlbuilders", "normalizers", "builders.vspacing.pagehandler") -- last !
+
+appendaction("vboxbuilders", "normalizers", "builders.vspacing.vboxhandler") --
+
+-- experimental too
+
+appendaction("mvlbuilders","normalizers","typesetters.checkers.handler")
+appendaction("vboxbuilders","normalizers","typesetters.checkers.handler")
+
+-- speedup: only kick in when used
+
+disableaction("processors", "scripts.autofontfeature.handler")
+disableaction("processors", "scripts.splitters.handler")
+disableaction("processors", "scripts.injectors.handler") -- was enabled
+disableaction("processors", "fonts.collections.process")
+disableaction("processors", "fonts.checkers.missing")
+disableaction("processors", "chars.handle_breakpoints")
+disableaction("processors", "typesetters.cleaners.handler")
+disableaction("processors", "typesetters.cases.handler")
+disableaction("processors", "typesetters.digits.handler")
+disableaction("processors", "typesetters.breakpoints.handler")
+disableaction("processors", "typesetters.directions.handler")
+disableaction("processors", "languages.words.check")
+disableaction("processors", "typesetters.spacings.handler")
+disableaction("processors", "typesetters.kerns.handler")
+disableaction("processors", "typesetters.italics.handler")
+disableaction("processors", "nodes.handlers.stripping")
+disableaction("processors", "typesetters.paragraphs.handler")
+
+disableaction("shipouts", "typesetters.alignments.handler")
+disableaction("shipouts", "nodes.rules.handler")
+disableaction("shipouts", "nodes.shifts.handler")
+disableaction("shipouts", "attributes.colors.handler")
+disableaction("shipouts", "attributes.transparencies.handler")
+disableaction("shipouts", "attributes.colorintents.handler")
+disableaction("shipouts", "attributes.effects.handler")
+disableaction("shipouts", "attributes.negatives.handler")
+disableaction("shipouts", "attributes.viewerlayers.handler")
+disableaction("shipouts", "structures.tags.handler")
+disableaction("shipouts", "nodes.visualizers.handler")
+disableaction("shipouts", "nodes.handlers.accessibility")
+disableaction("shipouts", "nodes.handlers.backgrounds")
+disableaction("shipouts", "nodes.handlers.alignbackgrounds")
+disableaction("shipouts", "nodes.handlers.cleanuppage")
+
+disableaction("shipouts", "nodes.references.handler")
+disableaction("shipouts", "nodes.destinations.handler")
+
+--~ disableaction("shipouts", "nodes.handlers.export")
+
+disableaction("mvlbuilders", "nodes.handlers.migrate")
+
+disableaction("processors", "builders.paragraphs.solutions.splitters.split")
+
+disableaction("finalizers", "builders.paragraphs.keeptogether")
+disableaction("finalizers", "builders.paragraphs.solutions.splitters.optimize")
+disableaction("finalizers", "nodes.handlers.graphicvadjust") -- sort of obsolete
+disableaction("finalizers", "builders.paragraphs.tag")
+
+disableaction("math", "noads.handlers.tags")
+disableaction("math", "noads.handlers.italics")
+
+disableaction("mvlbuilders", "typesetters.checkers.handler")
+disableaction("vboxbuilders","typesetters.checkers.handler")
+
+freezecallbacks("find_.*_file", "find file using resolver")
+freezecallbacks("read_.*_file", "read file at once")
+freezecallbacks("open_.*_file", "open file for reading")
+
+-- experimental:
+
+freezegroup("processors", "normalizers")
+freezegroup("processors", "characters")
+freezegroup("processors", "words")
+freezegroup("processors", "fonts")
+freezegroup("processors", "lists")
+
+freezegroup("finalizers", "normalizers")
+freezegroup("finalizers", "fonts")
+freezegroup("finalizers", "lists")
+
+freezegroup("shipouts", "normalizers")
+freezegroup("shipouts", "finishers")
+
+freezegroup("mvlbuilders", "normalizers")
+freezegroup("vboxbuilders", "normalizers")
+
+-----------("parbuilders", "lists")
+-----------("pagebuilders", "lists")
+
+freezegroup("math", "normalizers")
+freezegroup("math", "builders")
diff --git a/tex/context/base/toks-ini.lua b/tex/context/base/toks-ini.lua
index 0136f274f..ef4b5406b 100644
--- a/tex/context/base/toks-ini.lua
+++ b/tex/context/base/toks-ini.lua
@@ -1,341 +1,341 @@
-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"
-}
-
-local utfbyte, utfchar, utfvalues = utf.byte, utf.char, utf.values
-local format, gsub = string.format, string.gsub
-
---[[ldx--
-<p>This code is experimental and needs a cleanup. The visualizers will move to
-a module.</p>
---ldx]]--
-
--- 1 = command, 2 = modifier (char), 3 = controlsequence id
---
--- callback.register('token_filter', token.get_next)
---
--- token.get_next()
--- token.expand()
--- token.create()
--- token.csname_id()
--- token.csname_name(v)
--- token.command_id()
--- token.command_name(v)
--- token.is_expandable()
--- token.is_activechar()
--- token.lookup(v)
-
--- actually, we can use token registers to store tokens
-
-local token, tex = token, tex
-
-local createtoken = token.create
-local csname_id = token.csname_id
-local command_id = token.command_id
-local command_name = token.command_name
-local get_next = token.get_next
-local expand = token.expand
-local is_activechar = token.is_activechar
-local csname_name = token.csname_name
-
-tokens = tokens or { }
-local tokens = tokens
-
-tokens.vbox = createtoken("vbox")
-tokens.hbox = createtoken("hbox")
-tokens.vtop = createtoken("vtop")
-tokens.bgroup = createtoken(utfbyte("{"), 1)
-tokens.egroup = createtoken(utfbyte("}"), 2)
-
-tokens.letter = function(chr) return createtoken(utfbyte(chr), 11) end
-tokens.other = function(chr) return createtoken(utfbyte(chr), 12) end
-
-tokens.letters = function(str)
- local t, n = { }, 0
- for chr in utfvalues(str) do
- n = n + 1
- t[n] = createtoken(chr, 11)
- end
- return t
-end
-
-tokens.collectors = tokens.collectors or { }
-local collectors = tokens.collectors
-
-collectors.data = collectors.data or { }
-local collectordata = collectors.data
-
-collectors.registered = collectors.registered or { }
-local registered = collectors.registered
-
-local function printlist(data)
- callbacks.push('token_filter', function ()
- callbacks.pop('token_filter') -- tricky but the nil assignment helps
- return data
- end)
-end
-
-tex.printlist = printlist -- will change to another namespace
-
-function collectors.flush(tag)
- printlist(collectordata[tag])
-end
-
-function collectors.test(tag)
- printlist(collectordata[tag])
-end
-
-function collectors.register(name)
- registered[csname_id(name)] = name
-end
-
-local call = command_id("call")
-local letter = command_id("letter")
-local other = command_id("other_char")
-
-function collectors.install(tag,end_cs)
- local data, d = { }, 0
- collectordata[tag] = data
- local endcs = csname_id(end_cs)
- while true do
- local t = get_next()
- local a, b = t[1], t[3]
- if b == endcs then
- context["end_cs"]()
- return
- elseif a == call and registered[b] then
- expand()
- else
- d = d + 1
- data[d] = t
- end
- end
-end
-
-function collectors.handle(tag,handle,flush)
- collectordata[tag] = handle(collectordata[tag])
- if flush then
- collectors.flush(tag)
- end
-end
-
-local show_methods = { }
-collectors.show_methods = show_methods
-
-function collectors.show(tag, method)
- if type(tag) == "table" then
- show_methods[method or 'a'](tag)
- else
- show_methods[method or 'a'](collectordata[tag])
- end
-end
-
-function collectors.defaultwords(t,str)
- local n = #t
- n = n + 1
- t[n] = tokens.bgroup
- n = n + 1
- t[n] = createtoken("red")
- for i=1,#str do
- n = n + 1
- t[n] = tokens.other('*')
- end
- n = n + 1
- t[n] = tokens.egroup
-end
-
-function collectors.dowithwords(tag,handle)
- local t, w, tn, wn = { }, { }, 0, 0
- handle = handle or collectors.defaultwords
- local tagdata = collectordata[tag]
- for k=1,#tagdata do
- local v = tagdata[k]
- if v[1] == letter then
- wn = wn + 1
- w[wn] = v[2]
- else
- if wn > 0 then
- handle(t,w)
- wn = 0
- end
- tn = tn + 1
- t[tn] = v
- end
- end
- if wn > 0 then
- handle(t,w)
- end
- collectordata[tag] = t
-end
-
-local function showtoken(t)
- if t then
- local cmd, chr, id, cs, name = t[1], t[2], t[3], nil, command_name(t) or ""
- if cmd == letter or cmd == other then
- return format("%s-> %s -> %s", name, chr, utfchar(chr))
- elseif id > 0 then
- cs = csname_name(t) or nil
- if cs then
- return format("%s-> %s", name, cs)
- elseif tonumber(chr) < 0 then
- return format("%s-> %s", name, id)
- else
- return format("%s-> (%s,%s)", name, chr, id)
- end
- else
- return format("%s", name)
- end
- else
- return "no node"
- end
-end
-
-collectors.showtoken = showtoken
-
-function collectors.trace()
- local t = get_next()
- logs.report("tokenlist",showtoken(t))
- return t
-end
-
--- these might move to a runtime module
-
-show_methods.a = function(data) -- no need to store the table, just pass directly
- local function row(one,two,three,four,five)
- context.NC() context(one)
- context.NC() context(two)
- context.NC() context(three)
- context.NC() context(four)
- context.NC() context(five)
- context.NC() context.NR()
- end
- context.starttabulate { "|T|Tr|cT|Tr|T|" }
- row("cmd","chr","","id","name")
- context.HL()
- for _,v in next, data do
- local cmd, chr, id, cs, sym = v[1], v[2], v[3], "", ""
- local name = gsub(command_name(v) or "","_","\\_")
- if id > 0 then
- cs = csname_name(v) or ""
- if cs ~= "" then cs = "\\string " .. cs end
- else
- id = ""
- end
- if cmd == letter or cmd == other then
- sym = "\\char " .. chr
- end
- if tonumber(chr) < 0 then
- row(name,"",sym,id,cs)
- else
- row(name,chr,sym,id,cs)
- end
- end
- context.stoptabulate()
-end
-
-local function show_b_c(data,swap) -- no need to store the table, just pass directly
- local function row(one,two,three)
- context.NC() context(one)
- context.NC() context(two)
- context.NC() context(three)
- context.NC() context.NR()
- end
- if swap then
- context.starttabulate { "|Tl|Tl|Tr|" }
- else
- context.starttabulate { "|Tl|Tr|Tl|" }
- end
- row("cmd","chr","name")
- context.HL()
- for _,v in next, data do
- local cmd, chr, id, cs, sym = v[1], v[2], v[3], "", ""
- local name = gsub(command_name(v) or "","_","\\_")
- if id > 0 then
- cs = csname_name(v) or ""
- end
- if cmd == letter or cmd == other then
- sym = "\\char " .. chr
- elseif cs == "" then
- -- okay
- elseif is_activechar(v) then
- sym = "\\string " .. cs
- else
- sym = "\\string\\" .. cs
- end
- if swap then
- row(name,sym,chr)
- elseif tonumber(chr) < 0 then
- row(name,"",sym)
- else
- row(name,chr,sym)
- end
- end
- context.stoptabulate()
-end
-
--- Even more experimental ...
-
-show_methods.b = function(data) show_b_c(data,false) end
-show_methods.c = function(data) show_b_c(data,true ) end
-
-local remapper = { } -- namespace
-collectors.remapper = remapper
-
-local remapperdata = { } -- user mappings
-remapper.data = remapperdata
-
-function remapper.store(tag,class,key)
- local s = remapperdata[class]
- if not s then
- s = { }
- remapperdata[class] = s
- end
- s[key] = collectordata[tag]
- collectordata[tag] = nil
-end
-
-function remapper.convert(tag,toks)
- local data = remapperdata[tag]
- local leftbracket, rightbracket = utfbyte('['), utfbyte(']')
- local skipping = 0
- -- todo: math
- if data then
- local t, n = { }, 0
- for s=1,#toks do
- local tok = toks[s]
- local one, two = tok[1], tok[2]
- if one == 11 or one == 12 then
- if two == leftbracket then
- skipping = skipping + 1
- n = n + 1 ; t[n] = tok
- elseif two == rightbracket then
- skipping = skipping - 1
- n = n + 1 ; t[n] = tok
- elseif skipping == 0 then
- local new = data[two]
- if new then
- if #new > 1 then
- for n=1,#new do
- n = n + 1 ; t[n] = new[n]
- end
- else
- n = n + 1 ; t[n] = new[1]
- end
- else
- n = n + 1 ; t[n] = tok
- end
- else
- n = n + 1 ; t[n] = tok
- end
- else
- n = n + 1 ; t[n] = tok
- end
- end
- return t
- else
- return toks
- end
-end
+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"
+}
+
+local utfbyte, utfchar, utfvalues = utf.byte, utf.char, utf.values
+local format, gsub = string.format, string.gsub
+
+--[[ldx--
+<p>This code is experimental and needs a cleanup. The visualizers will move to
+a module.</p>
+--ldx]]--
+
+-- 1 = command, 2 = modifier (char), 3 = controlsequence id
+--
+-- callback.register('token_filter', token.get_next)
+--
+-- token.get_next()
+-- token.expand()
+-- token.create()
+-- token.csname_id()
+-- token.csname_name(v)
+-- token.command_id()
+-- token.command_name(v)
+-- token.is_expandable()
+-- token.is_activechar()
+-- token.lookup(v)
+
+-- actually, we can use token registers to store tokens
+
+local token, tex = token, tex
+
+local createtoken = token.create
+local csname_id = token.csname_id
+local command_id = token.command_id
+local command_name = token.command_name
+local get_next = token.get_next
+local expand = token.expand
+local is_activechar = token.is_activechar
+local csname_name = token.csname_name
+
+tokens = tokens or { }
+local tokens = tokens
+
+tokens.vbox = createtoken("vbox")
+tokens.hbox = createtoken("hbox")
+tokens.vtop = createtoken("vtop")
+tokens.bgroup = createtoken(utfbyte("{"), 1)
+tokens.egroup = createtoken(utfbyte("}"), 2)
+
+tokens.letter = function(chr) return createtoken(utfbyte(chr), 11) end
+tokens.other = function(chr) return createtoken(utfbyte(chr), 12) end
+
+tokens.letters = function(str)
+ local t, n = { }, 0
+ for chr in utfvalues(str) do
+ n = n + 1
+ t[n] = createtoken(chr, 11)
+ end
+ return t
+end
+
+tokens.collectors = tokens.collectors or { }
+local collectors = tokens.collectors
+
+collectors.data = collectors.data or { }
+local collectordata = collectors.data
+
+collectors.registered = collectors.registered or { }
+local registered = collectors.registered
+
+local function printlist(data)
+ callbacks.push('token_filter', function ()
+ callbacks.pop('token_filter') -- tricky but the nil assignment helps
+ return data
+ end)
+end
+
+tex.printlist = printlist -- will change to another namespace
+
+function collectors.flush(tag)
+ printlist(collectordata[tag])
+end
+
+function collectors.test(tag)
+ printlist(collectordata[tag])
+end
+
+function collectors.register(name)
+ registered[csname_id(name)] = name
+end
+
+local call = command_id("call")
+local letter = command_id("letter")
+local other = command_id("other_char")
+
+function collectors.install(tag,end_cs)
+ local data, d = { }, 0
+ collectordata[tag] = data
+ local endcs = csname_id(end_cs)
+ while true do
+ local t = get_next()
+ local a, b = t[1], t[3]
+ if b == endcs then
+ context["end_cs"]()
+ return
+ elseif a == call and registered[b] then
+ expand()
+ else
+ d = d + 1
+ data[d] = t
+ end
+ end
+end
+
+function collectors.handle(tag,handle,flush)
+ collectordata[tag] = handle(collectordata[tag])
+ if flush then
+ collectors.flush(tag)
+ end
+end
+
+local show_methods = { }
+collectors.show_methods = show_methods
+
+function collectors.show(tag, method)
+ if type(tag) == "table" then
+ show_methods[method or 'a'](tag)
+ else
+ show_methods[method or 'a'](collectordata[tag])
+ end
+end
+
+function collectors.defaultwords(t,str)
+ local n = #t
+ n = n + 1
+ t[n] = tokens.bgroup
+ n = n + 1
+ t[n] = createtoken("red")
+ for i=1,#str do
+ n = n + 1
+ t[n] = tokens.other('*')
+ end
+ n = n + 1
+ t[n] = tokens.egroup
+end
+
+function collectors.dowithwords(tag,handle)
+ local t, w, tn, wn = { }, { }, 0, 0
+ handle = handle or collectors.defaultwords
+ local tagdata = collectordata[tag]
+ for k=1,#tagdata do
+ local v = tagdata[k]
+ if v[1] == letter then
+ wn = wn + 1
+ w[wn] = v[2]
+ else
+ if wn > 0 then
+ handle(t,w)
+ wn = 0
+ end
+ tn = tn + 1
+ t[tn] = v
+ end
+ end
+ if wn > 0 then
+ handle(t,w)
+ end
+ collectordata[tag] = t
+end
+
+local function showtoken(t)
+ if t then
+ local cmd, chr, id, cs, name = t[1], t[2], t[3], nil, command_name(t) or ""
+ if cmd == letter or cmd == other then
+ return format("%s-> %s -> %s", name, chr, utfchar(chr))
+ elseif id > 0 then
+ cs = csname_name(t) or nil
+ if cs then
+ return format("%s-> %s", name, cs)
+ elseif tonumber(chr) < 0 then
+ return format("%s-> %s", name, id)
+ else
+ return format("%s-> (%s,%s)", name, chr, id)
+ end
+ else
+ return format("%s", name)
+ end
+ else
+ return "no node"
+ end
+end
+
+collectors.showtoken = showtoken
+
+function collectors.trace()
+ local t = get_next()
+ logs.report("tokenlist",showtoken(t))
+ return t
+end
+
+-- these might move to a runtime module
+
+show_methods.a = function(data) -- no need to store the table, just pass directly
+ local function row(one,two,three,four,five)
+ context.NC() context(one)
+ context.NC() context(two)
+ context.NC() context(three)
+ context.NC() context(four)
+ context.NC() context(five)
+ context.NC() context.NR()
+ end
+ context.starttabulate { "|T|Tr|cT|Tr|T|" }
+ row("cmd","chr","","id","name")
+ context.HL()
+ for _,v in next, data do
+ local cmd, chr, id, cs, sym = v[1], v[2], v[3], "", ""
+ local name = gsub(command_name(v) or "","_","\\_")
+ if id > 0 then
+ cs = csname_name(v) or ""
+ if cs ~= "" then cs = "\\string " .. cs end
+ else
+ id = ""
+ end
+ if cmd == letter or cmd == other then
+ sym = "\\char " .. chr
+ end
+ if tonumber(chr) < 0 then
+ row(name,"",sym,id,cs)
+ else
+ row(name,chr,sym,id,cs)
+ end
+ end
+ context.stoptabulate()
+end
+
+local function show_b_c(data,swap) -- no need to store the table, just pass directly
+ local function row(one,two,three)
+ context.NC() context(one)
+ context.NC() context(two)
+ context.NC() context(three)
+ context.NC() context.NR()
+ end
+ if swap then
+ context.starttabulate { "|Tl|Tl|Tr|" }
+ else
+ context.starttabulate { "|Tl|Tr|Tl|" }
+ end
+ row("cmd","chr","name")
+ context.HL()
+ for _,v in next, data do
+ local cmd, chr, id, cs, sym = v[1], v[2], v[3], "", ""
+ local name = gsub(command_name(v) or "","_","\\_")
+ if id > 0 then
+ cs = csname_name(v) or ""
+ end
+ if cmd == letter or cmd == other then
+ sym = "\\char " .. chr
+ elseif cs == "" then
+ -- okay
+ elseif is_activechar(v) then
+ sym = "\\string " .. cs
+ else
+ sym = "\\string\\" .. cs
+ end
+ if swap then
+ row(name,sym,chr)
+ elseif tonumber(chr) < 0 then
+ row(name,"",sym)
+ else
+ row(name,chr,sym)
+ end
+ end
+ context.stoptabulate()
+end
+
+-- Even more experimental ...
+
+show_methods.b = function(data) show_b_c(data,false) end
+show_methods.c = function(data) show_b_c(data,true ) end
+
+local remapper = { } -- namespace
+collectors.remapper = remapper
+
+local remapperdata = { } -- user mappings
+remapper.data = remapperdata
+
+function remapper.store(tag,class,key)
+ local s = remapperdata[class]
+ if not s then
+ s = { }
+ remapperdata[class] = s
+ end
+ s[key] = collectordata[tag]
+ collectordata[tag] = nil
+end
+
+function remapper.convert(tag,toks)
+ local data = remapperdata[tag]
+ local leftbracket, rightbracket = utfbyte('['), utfbyte(']')
+ local skipping = 0
+ -- todo: math
+ if data then
+ local t, n = { }, 0
+ for s=1,#toks do
+ local tok = toks[s]
+ local one, two = tok[1], tok[2]
+ if one == 11 or one == 12 then
+ if two == leftbracket then
+ skipping = skipping + 1
+ n = n + 1 ; t[n] = tok
+ elseif two == rightbracket then
+ skipping = skipping - 1
+ n = n + 1 ; t[n] = tok
+ elseif skipping == 0 then
+ local new = data[two]
+ if new then
+ if #new > 1 then
+ for n=1,#new do
+ n = n + 1 ; t[n] = new[n]
+ end
+ else
+ n = n + 1 ; t[n] = new[1]
+ end
+ else
+ n = n + 1 ; t[n] = tok
+ end
+ else
+ n = n + 1 ; t[n] = tok
+ end
+ else
+ n = n + 1 ; t[n] = tok
+ end
+ end
+ return t
+ else
+ return toks
+ end
+end
diff --git a/tex/context/base/trac-ctx.lua b/tex/context/base/trac-ctx.lua
index 8153d079a..706e7a244 100644
--- a/tex/context/base/trac-ctx.lua
+++ b/tex/context/base/trac-ctx.lua
@@ -1,48 +1,48 @@
-if not modules then modules = { } end modules ['trac-ctx'] = {
- version = 1.001,
- comment = "companion to trac-ctx.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local commands = commands
-local context = context
-local register = trackers.register
-
-local textrackers = tex.trackers or { }
-local texdirectives = tex.directives or { }
-
-tex.trackers = textrackers
-tex.directives = texdirectives
-
-storage.register("tex/trackers", textrackers, "tex.trackers")
-storage.register("tex/directives",texdirectives,"tex.directives")
-
-local function doit(category,tag,v)
- local tt = category[tag]
- if tt then
- context.unprotect()
- context(v and tt[1] or tt[2]) -- could be one call
- context.protect()
- end
-end
-
-local function initialize(category,register)
- for tag, commands in next, category do
- register(tag, function(v) doit(category,tag,v) end) -- todo: v,tag in caller
- end
-end
-
-local function install(category,register,tag,enable,disable)
- category[tag] = { enable, disable }
- register(tag, function(v) doit(category,tag,v) end) -- todo: v,tag in caller
-end
-
-function commands.initializetextrackers () initialize(textrackers ,trackers .register ) end
-function commands.initializetexdirectives() initialize(texdirectives,directives.register) end
-
--- commands.install(tag,enable,disable):
-
-function commands.installtextracker (...) install(textrackers ,trackers .register,...) end
-function commands.installtexdirective(...) install(texdirectives,directives.register,...) end
+if not modules then modules = { } end modules ['trac-ctx'] = {
+ version = 1.001,
+ comment = "companion to trac-ctx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local commands = commands
+local context = context
+local register = trackers.register
+
+local textrackers = tex.trackers or { }
+local texdirectives = tex.directives or { }
+
+tex.trackers = textrackers
+tex.directives = texdirectives
+
+storage.register("tex/trackers", textrackers, "tex.trackers")
+storage.register("tex/directives",texdirectives,"tex.directives")
+
+local function doit(category,tag,v)
+ local tt = category[tag]
+ if tt then
+ context.unprotect()
+ context(v and tt[1] or tt[2]) -- could be one call
+ context.protect()
+ end
+end
+
+local function initialize(category,register)
+ for tag, commands in next, category do
+ register(tag, function(v) doit(category,tag,v) end) -- todo: v,tag in caller
+ end
+end
+
+local function install(category,register,tag,enable,disable)
+ category[tag] = { enable, disable }
+ register(tag, function(v) doit(category,tag,v) end) -- todo: v,tag in caller
+end
+
+function commands.initializetextrackers () initialize(textrackers ,trackers .register ) end
+function commands.initializetexdirectives() initialize(texdirectives,directives.register) end
+
+-- commands.install(tag,enable,disable):
+
+function commands.installtextracker (...) install(textrackers ,trackers .register,...) end
+function commands.installtexdirective(...) install(texdirectives,directives.register,...) end
diff --git a/tex/context/base/trac-deb.lua b/tex/context/base/trac-deb.lua
index b2a86df88..fe167c343 100644
--- a/tex/context/base/trac-deb.lua
+++ b/tex/context/base/trac-deb.lua
@@ -1,248 +1,248 @@
-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"
-}
-
-local lpeg, status = lpeg, status
-
-local lpegmatch = lpeg.match
-local format, concat, match = string.format, table.concat, string.match
-local tonumber, tostring = tonumber, tostring
-local texdimen, textoks, texcount = tex.dimen, tex.toks, tex.count
-
--- maybe tracers -> tracers.tex (and tracers.lua for current debugger)
-
-local report_system = logs.reporter("system","tex")
-
-tracers = tracers or { }
-local tracers = tracers
-
-tracers.lists = { }
-local lists = tracers.lists
-
-tracers.strings = { }
-local strings = tracers.strings
-
-strings.undefined = "undefined"
-
-lists.scratch = {
- 0, 2, 4, 6, 8
-}
-
-lists.internals = {
- 'p:hsize', 'p:parindent', 'p:leftskip','p:rightskip',
- 'p:vsize', 'p:parskip', 'p:baselineskip', 'p:lineskip', 'p:topskip'
-}
-
-lists.context = {
- 'd:lineheight',
- 'c:realpageno', 'c:userpageno', 'c:pageno', 'c:subpageno'
-}
-
-local types = {
- ['d'] = tracers.dimen,
- ['c'] = tracers.count,
- ['t'] = tracers.toks,
- ['p'] = tracers.primitive
-}
-
-local splitboth = lpeg.splitat(":")
-local splittype = lpeg.firstofsplit(":")
-local splitname = lpeg.secondofsplit(":")
-
-function tracers.type(csname)
- return lpegmatch(splittype,csname)
-end
-
-function tracers.name(csname)
- return lpegmatch(splitname,csname) or csname
-end
-
-function tracers.cs(csname)
- local tag, name = lpegmatch(splitboth,csname)
- if name and types[tag] then
- return types[tag](name)
- else
- return tracers.primitive(csname)
- end
-end
-
-function tracers.dimen(name)
- local d = texdimen[name]
- return d and number.topoints(d) or strings.undefined
-end
-
-function tracers.count(name)
- return texcount[name] or strings.undefined
-end
-
-function tracers.toks(name,limit)
- local t = textoks[name]
- return t and string.limit(t,tonumber(limit) or 40) or strings.undefined
-end
-
-function tracers.primitive(name)
- return tex[name] or strings.undefined
-end
-
-function tracers.knownlist(name)
- local l = lists[name]
- return l and #l > 0
-end
-
-function tracers.showlines(filename,linenumber,offset,errorstr)
- 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 lines = data and string.splitlines(data)
- if lines and #lines > 0 then
- -- This does not work completely as we cannot access the last Lua error using
- -- table.print(status.list()). This is on the agenda. Eventually we will
- -- have a sequence of checks here (tex, lua, mp) at this end.
- --
- -- Actually, in 0.75+ the lua error message is even weirder as you can
- -- get:
- --
- -- LuaTeX error [string "\directlua "]:3: unexpected symbol near '1' ...
- --
- -- <inserted text> \endgroup \directlua {
- --
- -- So there is some work to be done in the LuaTeX engine.
- --
- local what, where = match(errorstr,[[LuaTeX error <main (%a+) instance>:(%d+)]])
- or match(errorstr,[[LuaTeX error %[string "\\(.-lua) "%]:(%d+)]]) -- buglet
- if where then
- -- lua error: linenumber points to last line
- local start = "\\startluacode"
- local stop = "\\stopluacode"
- local where = tonumber(where)
- if lines[linenumber] == start then
- local n = linenumber
- for i=n,1,-1 do
- if lines[i] == start then
- local n = i + where
- if n <= linenumber then
- linenumber = n
- end
- end
- 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
-
-function tracers.printerror(offset)
- local inputstack = resolvers.inputstack
- local filename = inputstack[#inputstack] or status.filename
- local linenumber = tonumber(status.linenumber) or 0
- if not filename then
- report_system("error not related to input file: %s ...",status.lasterrorstring)
- elseif type(filename) == "number" then
- report_system("error on line %s of filehandle %s: %s ...",linenumber,filename,status.lasterrorstring)
- else
- -- currently we still get the error message printed to the log/console so we
- -- add a bit of spacing around our variant
- texio.write_nl("\n")
- local errorstr = status.lasterrorstring or "?"
- -- inspect(status.list())
- report_system("error on line %s in file %s: %s ...\n",linenumber,filename,errorstr) -- lua error?
- texio.write_nl(tracers.showlines(filename,linenumber,offset,errorstr),"\n")
- end
-end
-
-directives.register("system.errorcontext", function(v)
- if v then
- callback.register('show_error_hook', function() tracers.printerror(v) end)
- else
- callback.register('show_error_hook', 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
-
-function lmx.showdebuginfo(lmxname)
- local variables = {
- ['title'] = 'ConTeXt Debug Information',
- ['color-background-one'] = lmx.get('color-background-green'),
- ['color-background-two'] = lmx.get('color-background-blue'),
- }
- if lmxname == false then
- return variables
- else
- lmx.show(lmxname or 'context-debug.lmx',variables)
- end
-end
-
-function lmx.showerror(lmxname)
- local filename, linenumber, errorcontext = status.filename, tonumber(status.linenumber) or 0, ""
- if not filename then
- filename, errorcontext = 'unknown', 'error in filename'
- elseif type(filename) == "number" then
- filename, errorcontext = format("<read %s>",filename), 'unknown error'
- else
- errorcontext = tracers.showlines(filename,linenumber,offset)
- end
- local variables = {
- ['title'] = 'ConTeXt Error Information',
- ['errormessage'] = status.lasterrorstring,
- ['linenumber'] = linenumber,
- ['color-background-one'] = lmx.get('color-background-yellow'),
- ['color-background-two'] = lmx.get('color-background-purple'),
- ['filename'] = filename,
- ['errorcontext'] = errorcontext,
- }
- if lmxname == false then
- return variables
- else
- lmx.show(lmxname or 'context-error.lmx',variables)
- end
-end
-
-function lmx.overloaderror()
- callback.register('show_error_hook', function() lmx.showerror() end) -- prevents arguments being passed
-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
+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"
+}
+
+local lpeg, status = lpeg, status
+
+local lpegmatch = lpeg.match
+local format, concat, match = string.format, table.concat, string.match
+local tonumber, tostring = tonumber, tostring
+local texdimen, textoks, texcount = tex.dimen, tex.toks, tex.count
+
+-- maybe tracers -> tracers.tex (and tracers.lua for current debugger)
+
+local report_system = logs.reporter("system","tex")
+
+tracers = tracers or { }
+local tracers = tracers
+
+tracers.lists = { }
+local lists = tracers.lists
+
+tracers.strings = { }
+local strings = tracers.strings
+
+strings.undefined = "undefined"
+
+lists.scratch = {
+ 0, 2, 4, 6, 8
+}
+
+lists.internals = {
+ 'p:hsize', 'p:parindent', 'p:leftskip','p:rightskip',
+ 'p:vsize', 'p:parskip', 'p:baselineskip', 'p:lineskip', 'p:topskip'
+}
+
+lists.context = {
+ 'd:lineheight',
+ 'c:realpageno', 'c:userpageno', 'c:pageno', 'c:subpageno'
+}
+
+local types = {
+ ['d'] = tracers.dimen,
+ ['c'] = tracers.count,
+ ['t'] = tracers.toks,
+ ['p'] = tracers.primitive
+}
+
+local splitboth = lpeg.splitat(":")
+local splittype = lpeg.firstofsplit(":")
+local splitname = lpeg.secondofsplit(":")
+
+function tracers.type(csname)
+ return lpegmatch(splittype,csname)
+end
+
+function tracers.name(csname)
+ return lpegmatch(splitname,csname) or csname
+end
+
+function tracers.cs(csname)
+ local tag, name = lpegmatch(splitboth,csname)
+ if name and types[tag] then
+ return types[tag](name)
+ else
+ return tracers.primitive(csname)
+ end
+end
+
+function tracers.dimen(name)
+ local d = texdimen[name]
+ return d and number.topoints(d) or strings.undefined
+end
+
+function tracers.count(name)
+ return texcount[name] or strings.undefined
+end
+
+function tracers.toks(name,limit)
+ local t = textoks[name]
+ return t and string.limit(t,tonumber(limit) or 40) or strings.undefined
+end
+
+function tracers.primitive(name)
+ return tex[name] or strings.undefined
+end
+
+function tracers.knownlist(name)
+ local l = lists[name]
+ return l and #l > 0
+end
+
+function tracers.showlines(filename,linenumber,offset,errorstr)
+ 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 lines = data and string.splitlines(data)
+ if lines and #lines > 0 then
+ -- This does not work completely as we cannot access the last Lua error using
+ -- table.print(status.list()). This is on the agenda. Eventually we will
+ -- have a sequence of checks here (tex, lua, mp) at this end.
+ --
+ -- Actually, in 0.75+ the lua error message is even weirder as you can
+ -- get:
+ --
+ -- LuaTeX error [string "\directlua "]:3: unexpected symbol near '1' ...
+ --
+ -- <inserted text> \endgroup \directlua {
+ --
+ -- So there is some work to be done in the LuaTeX engine.
+ --
+ local what, where = match(errorstr,[[LuaTeX error <main (%a+) instance>:(%d+)]])
+ or match(errorstr,[[LuaTeX error %[string "\\(.-lua) "%]:(%d+)]]) -- buglet
+ if where then
+ -- lua error: linenumber points to last line
+ local start = "\\startluacode"
+ local stop = "\\stopluacode"
+ local where = tonumber(where)
+ if lines[linenumber] == start then
+ local n = linenumber
+ for i=n,1,-1 do
+ if lines[i] == start then
+ local n = i + where
+ if n <= linenumber then
+ linenumber = n
+ end
+ end
+ 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
+
+function tracers.printerror(offset)
+ local inputstack = resolvers.inputstack
+ local filename = inputstack[#inputstack] or status.filename
+ local linenumber = tonumber(status.linenumber) or 0
+ if not filename then
+ report_system("error not related to input file: %s ...",status.lasterrorstring)
+ elseif type(filename) == "number" then
+ report_system("error on line %s of filehandle %s: %s ...",linenumber,filename,status.lasterrorstring)
+ else
+ -- currently we still get the error message printed to the log/console so we
+ -- add a bit of spacing around our variant
+ texio.write_nl("\n")
+ local errorstr = status.lasterrorstring or "?"
+ -- inspect(status.list())
+ report_system("error on line %s in file %s: %s ...\n",linenumber,filename,errorstr) -- lua error?
+ texio.write_nl(tracers.showlines(filename,linenumber,offset,errorstr),"\n")
+ end
+end
+
+directives.register("system.errorcontext", function(v)
+ if v then
+ callback.register('show_error_hook', function() tracers.printerror(v) end)
+ else
+ callback.register('show_error_hook', 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
+
+function lmx.showdebuginfo(lmxname)
+ local variables = {
+ ['title'] = 'ConTeXt Debug Information',
+ ['color-background-one'] = lmx.get('color-background-green'),
+ ['color-background-two'] = lmx.get('color-background-blue'),
+ }
+ if lmxname == false then
+ return variables
+ else
+ lmx.show(lmxname or 'context-debug.lmx',variables)
+ end
+end
+
+function lmx.showerror(lmxname)
+ local filename, linenumber, errorcontext = status.filename, tonumber(status.linenumber) or 0, ""
+ if not filename then
+ filename, errorcontext = 'unknown', 'error in filename'
+ elseif type(filename) == "number" then
+ filename, errorcontext = format("<read %s>",filename), 'unknown error'
+ else
+ errorcontext = tracers.showlines(filename,linenumber,offset)
+ end
+ local variables = {
+ ['title'] = 'ConTeXt Error Information',
+ ['errormessage'] = status.lasterrorstring,
+ ['linenumber'] = linenumber,
+ ['color-background-one'] = lmx.get('color-background-yellow'),
+ ['color-background-two'] = lmx.get('color-background-purple'),
+ ['filename'] = filename,
+ ['errorcontext'] = errorcontext,
+ }
+ if lmxname == false then
+ return variables
+ else
+ lmx.show(lmxname or 'context-error.lmx',variables)
+ end
+end
+
+function lmx.overloaderror()
+ callback.register('show_error_hook', function() lmx.showerror() end) -- prevents arguments being passed
+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
diff --git a/tex/context/base/trac-exp.lua b/tex/context/base/trac-exp.lua
index 9daf86357..5879f1b7b 100644
--- a/tex/context/base/trac-exp.lua
+++ b/tex/context/base/trac-exp.lua
@@ -1,229 +1,229 @@
-if not modules then modules = { } end modules ['trac-exp'] = {
- 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"
-}
-
-local formatters = string.formatters
-local reporters = logs.reporters
-local xmlserialize = xml.serialize
-local xmlcollected = xml.collected
-local xmltext = xml.text
-local xmlfirst = xml.first
-local xmlfilter = xml.filter
-
--- there is no need for a newhandlers { name = "help", parent = "string" }
-
-local function flagdata(flag)
- local name = flag.at.name or ""
- local value = flag.at.value or ""
- -- local short = xmlfirst(s,"/short")
- -- local short = xmlserialize(short,xs)
- local short = xmltext(xmlfirst(flag,"/short")) or ""
- return name, value, short
-end
-
-local function exampledata(example)
- local command = xmltext(xmlfirst(example,"/command")) or ""
- local comment = xmltext(xmlfirst(example,"/comment")) or ""
- return command, comment
-end
-
-local function categorytitle(category)
- return xmltext(xmlfirst(category,"/title")) or ""
-end
-
-local exporters = logs.exporters
-
-function exporters.man(specification,...)
- local root = xml.convert(specification.helpinfo or "")
- if not root then
- return
- end
- local xs = xml.gethandlers("string")
- xml.sethandlersfunction(xs,"short",function(e,handler) xmlserialize(e.dt,handler) end)
- xml.sethandlersfunction(xs,"ref", function(e,handler) handler.handle("--"..e.at.name) end)
- local wantedcategories = select("#",...) == 0 and true or table.tohash { ... }
- local nofcategories = xml.count(root,"/application/flags/category")
- local name = xmlfilter(root,"/application/metadata/entry[@name='name']/text()")
- local detail = xmlfilter(root,"/application/metadata/entry[@name='detail']/text()") or name
- local version = xmlfilter(root,"/application/metadata/entry[@name='version']/text()") or "0.00"
- local banner = specification.banner or detail or name
- --
- local result = { }
- --
- -- .TH "context" "1" "some date" "version" "ConTeXt" -- we use a fake date as I don't want to polute the git repos
- --
- local runner = string.match(name,"^mtx%-(.*)")
- if runner then
- runner = formatters["mtxrun --script %s"](runner)
- else
- runner = name
- end
- --
- result[#result+1] = formatters['.TH "%s" "1" "%s" "version %s" "%s"'](name,os.date("01-01-%Y"),version,detail)
- result[#result+1] = formatters[".SH NAME\n.B %s"](name)
- result[#result+1] = formatters[".SH SYNOPSIS\n.B %s [\n.I OPTIONS ...\n.B ] [\n.I FILENAMES\n.B ]"](runner)
- result[#result+1] = formatters[".SH DESCRIPTION\n.B %s"](detail)
- --
- for category in xmlcollected(root,"/application/flags/category") do
- if nofcategories > 1 then
- result[#result+1] = formatters['.SH OPTIONS: %s'](string.upper(category.at.name or "all"))
- else
- result[#result+1] = ".SH OPTIONS"
- end
- for subcategory in xmlcollected(category,"/subcategory") do
- for flag in xmlcollected(subcategory,"/flag") do
- local name, value, short = flagdata(flag)
- if value == "" then
- result[#result+1] = formatters[".TP\n.B --%s\n%s"](name,short)
- else
- result[#result+1] = formatters[".TP\n.B --%s=%s\n%s"](name,value,short)
- end
- end
- end
- end
- local moreinfo = specification.moreinfo
- if moreinfo and moreinfo ~= "" then
- moreinfo = string.gsub(moreinfo,"[\n\r]([%a]+)%s*:%s*",'\n\n.B "%1:"\n')
- result[#result+1] = formatters[".SH AUTHOR\n%s"](moreinfo)
- end
- return table.concat(result,"\n")
-end
-
-local craptemplate = [[
-<?xml version="1.0"?>
-<application>
-<metadata>
-<entry name="banner">%s</entry>
-</metadata>
-<verbose>
-%s
-</verbose>
-]]
-
-function exporters.xml(specification,...)
- local helpinfo = specification.helpinfo
- if type(helpinfo) == "string" then
- if string.find(helpinfo,"^<%?xml") then
- return helpinfo
- end
- elseif type(helpinfo) == "table" then
- helpinfo = table.concat(helpinfo,"\n\n")
- else
- helpinfo = "no help"
- end
- return formatters[craptemplate](specification.banner or "?",helpinfo)
-end
-
--- the following template is optimized a bit for space
-
--- local bodytemplate = [[
--- <h1>Command line options</h1>
--- <table>
--- <tr>
--- <th style="width: 10em">flag</th>
--- <th style="width: 8em">value</th>
--- <th>description</th>
--- </tr>
--- <?lua
--- for category in xml.collected(variables.root,"/application/flags/category") do
--- if variables.nofcategories > 1 then
--- ?><tr>
--- <th colspan="3"><?lua inject(category.at.name) ?></th>
--- </tr><?lua
--- end
--- for subcategory in xml.collected(category,"/subcategory") do
--- ?><tr><th/><td/><td/></tr><?lua
--- for flag in xml.collected(subcategory,"/flag") do
--- local name, value, short = variables.flagdata(flag)
--- ?><tr>
--- <th>--<?lua inject(name) ?></th>
--- <td><?lua inject(value) ?></td>
--- <td><?lua inject(short) ?></td>
--- </tr><?lua
--- end
--- end
--- end
--- ?>
--- </table>
--- <br/>
--- <?lua
--- for category in xml.collected(variables.root,"/application/examples/category") do
--- local title = variables.categorytitle(category)
--- if title ~= "" then
--- ?><h1><?lua inject(title) ?></h1><?lua
--- end
--- for subcategory in xml.collected(category,"/subcategory") do
--- for example in xml.collected(subcategory,"/example") do
--- local command, comment = variables.exampledata(example)
--- ?><tt><?lua inject(command) ?></tt><br/><?lua
--- end
--- ?><br/><?lua
--- end
--- end
--- for comment in xml.collected(root,"/application/comments/comment") do
--- ?><br/><?lua inject(xml.text(comment)) ?><br/><?lua
--- end
--- ?>
--- ]]
-
-local bodytemplate = [[
-<h1>Command line options</h1>
-<table>
- <tr><th style="width: 10em">flag</th><th style="width: 8em">value</th><th>description</th></tr>
- <?lua for category in xml.collected(variables.root,"/application/flags/category") do if variables.nofcategories > 1 then ?>
- <tr><th colspan="3"><?lua inject(category.at.name) ?></th></tr>
- <?lua end for subcategory in xml.collected(category,"/subcategory") do ?>
- <tr><th/><td/><td/></tr>
- <?lua for flag in xml.collected(subcategory,"/flag") do local name, value, short = variables.flagdata(flag) ?>
- <tr><th>--<?lua inject(name) ?></th><td><?lua inject(value) ?></td><td><?lua inject(short) ?></td></tr>
- <?lua end end end ?>
-</table>
-<br/>
-<?lua for category in xml.collected(variables.root,"/application/examples/category") do local title = variables.categorytitle(category) if title ~= "" then ?>
-<h1><?lua inject(title) ?></h1>
-<?lua end for subcategory in xml.collected(category,"/subcategory") do for example in xml.collected(subcategory,"/example") do local command, comment = variables.exampledata(example) ?>
-<tt><?lua inject(command) ?></tt>
-<br/><?lua end ?><br/><?lua end end for comment in xml.collected(root,"/application/comments/comment") do ?>
-<br/><?lua inject(xml.text(comment)) ?><br/><?lua end ?>
-]]
-
-function exporters.html(specification,...)
- local root = xml.convert(specification.helpinfo or "")
- if not root then
- return
- end
- local xs = xml.gethandlers("string")
- xml.sethandlersfunction(xs,"short",function(e,handler) xmlserialize(e.dt,handler) end)
- xml.sethandlersfunction(xs,"ref", function(e,handler) handler.handle("--"..e.at.name) end)
- local wantedcategories = select("#",...) == 0 and true or table.tohash { ... }
- local nofcategories = xml.count(root,"/application/flags/category")
- local name = xmlfilter(root,"/application/metadata/entry[@name='name']/text()")
- local detail = xmlfilter(root,"/application/metadata/entry[@name='detail']/text()") or name
- local version = xmlfilter(root,"/application/metadata/entry[@name='version']/text()") or "0.00"
- local banner = specification.banner or detail or name
- --
- dofile(resolvers.findfile("trac-lmx.lua","tex"))
- --
- local htmltemplate = io.loaddata(resolvers.findfile("context-base.lmx","tex")) or "no template"
- --
- local body = lmx.convertstring(bodytemplate, {
- nofcategories = nofcategories,
- wantedcategories = wantedcategories,
- root = root,
- -- moreinfo = specification.moreinfo,
- flagdata = flagdata,
- exampledata = exampledata,
- categorytitle = categorytitle,
- })
- local html = lmx.convertstring(htmltemplate, {
- maintext = body,
- title = banner,
- bottomtext = "wiki: http://contextgarden.net | mail: ntg-context@ntg.nl | website: http://www.pragma-ade.nl",
- })
- --
- return html
-end
+if not modules then modules = { } end modules ['trac-exp'] = {
+ 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"
+}
+
+local formatters = string.formatters
+local reporters = logs.reporters
+local xmlserialize = xml.serialize
+local xmlcollected = xml.collected
+local xmltext = xml.text
+local xmlfirst = xml.first
+local xmlfilter = xml.filter
+
+-- there is no need for a newhandlers { name = "help", parent = "string" }
+
+local function flagdata(flag)
+ local name = flag.at.name or ""
+ local value = flag.at.value or ""
+ -- local short = xmlfirst(s,"/short")
+ -- local short = xmlserialize(short,xs)
+ local short = xmltext(xmlfirst(flag,"/short")) or ""
+ return name, value, short
+end
+
+local function exampledata(example)
+ local command = xmltext(xmlfirst(example,"/command")) or ""
+ local comment = xmltext(xmlfirst(example,"/comment")) or ""
+ return command, comment
+end
+
+local function categorytitle(category)
+ return xmltext(xmlfirst(category,"/title")) or ""
+end
+
+local exporters = logs.exporters
+
+function exporters.man(specification,...)
+ local root = xml.convert(specification.helpinfo or "")
+ if not root then
+ return
+ end
+ local xs = xml.gethandlers("string")
+ xml.sethandlersfunction(xs,"short",function(e,handler) xmlserialize(e.dt,handler) end)
+ xml.sethandlersfunction(xs,"ref", function(e,handler) handler.handle("--"..e.at.name) end)
+ local wantedcategories = select("#",...) == 0 and true or table.tohash { ... }
+ local nofcategories = xml.count(root,"/application/flags/category")
+ local name = xmlfilter(root,"/application/metadata/entry[@name='name']/text()")
+ local detail = xmlfilter(root,"/application/metadata/entry[@name='detail']/text()") or name
+ local version = xmlfilter(root,"/application/metadata/entry[@name='version']/text()") or "0.00"
+ local banner = specification.banner or detail or name
+ --
+ local result = { }
+ --
+ -- .TH "context" "1" "some date" "version" "ConTeXt" -- we use a fake date as I don't want to polute the git repos
+ --
+ local runner = string.match(name,"^mtx%-(.*)")
+ if runner then
+ runner = formatters["mtxrun --script %s"](runner)
+ else
+ runner = name
+ end
+ --
+ result[#result+1] = formatters['.TH "%s" "1" "%s" "version %s" "%s"'](name,os.date("01-01-%Y"),version,detail)
+ result[#result+1] = formatters[".SH NAME\n.B %s"](name)
+ result[#result+1] = formatters[".SH SYNOPSIS\n.B %s [\n.I OPTIONS ...\n.B ] [\n.I FILENAMES\n.B ]"](runner)
+ result[#result+1] = formatters[".SH DESCRIPTION\n.B %s"](detail)
+ --
+ for category in xmlcollected(root,"/application/flags/category") do
+ if nofcategories > 1 then
+ result[#result+1] = formatters['.SH OPTIONS: %s'](string.upper(category.at.name or "all"))
+ else
+ result[#result+1] = ".SH OPTIONS"
+ end
+ for subcategory in xmlcollected(category,"/subcategory") do
+ for flag in xmlcollected(subcategory,"/flag") do
+ local name, value, short = flagdata(flag)
+ if value == "" then
+ result[#result+1] = formatters[".TP\n.B --%s\n%s"](name,short)
+ else
+ result[#result+1] = formatters[".TP\n.B --%s=%s\n%s"](name,value,short)
+ end
+ end
+ end
+ end
+ local moreinfo = specification.moreinfo
+ if moreinfo and moreinfo ~= "" then
+ moreinfo = string.gsub(moreinfo,"[\n\r]([%a]+)%s*:%s*",'\n\n.B "%1:"\n')
+ result[#result+1] = formatters[".SH AUTHOR\n%s"](moreinfo)
+ end
+ return table.concat(result,"\n")
+end
+
+local craptemplate = [[
+<?xml version="1.0"?>
+<application>
+<metadata>
+<entry name="banner">%s</entry>
+</metadata>
+<verbose>
+%s
+</verbose>
+]]
+
+function exporters.xml(specification,...)
+ local helpinfo = specification.helpinfo
+ if type(helpinfo) == "string" then
+ if string.find(helpinfo,"^<%?xml") then
+ return helpinfo
+ end
+ elseif type(helpinfo) == "table" then
+ helpinfo = table.concat(helpinfo,"\n\n")
+ else
+ helpinfo = "no help"
+ end
+ return formatters[craptemplate](specification.banner or "?",helpinfo)
+end
+
+-- the following template is optimized a bit for space
+
+-- local bodytemplate = [[
+-- <h1>Command line options</h1>
+-- <table>
+-- <tr>
+-- <th style="width: 10em">flag</th>
+-- <th style="width: 8em">value</th>
+-- <th>description</th>
+-- </tr>
+-- <?lua
+-- for category in xml.collected(variables.root,"/application/flags/category") do
+-- if variables.nofcategories > 1 then
+-- ?><tr>
+-- <th colspan="3"><?lua inject(category.at.name) ?></th>
+-- </tr><?lua
+-- end
+-- for subcategory in xml.collected(category,"/subcategory") do
+-- ?><tr><th/><td/><td/></tr><?lua
+-- for flag in xml.collected(subcategory,"/flag") do
+-- local name, value, short = variables.flagdata(flag)
+-- ?><tr>
+-- <th>--<?lua inject(name) ?></th>
+-- <td><?lua inject(value) ?></td>
+-- <td><?lua inject(short) ?></td>
+-- </tr><?lua
+-- end
+-- end
+-- end
+-- ?>
+-- </table>
+-- <br/>
+-- <?lua
+-- for category in xml.collected(variables.root,"/application/examples/category") do
+-- local title = variables.categorytitle(category)
+-- if title ~= "" then
+-- ?><h1><?lua inject(title) ?></h1><?lua
+-- end
+-- for subcategory in xml.collected(category,"/subcategory") do
+-- for example in xml.collected(subcategory,"/example") do
+-- local command, comment = variables.exampledata(example)
+-- ?><tt><?lua inject(command) ?></tt><br/><?lua
+-- end
+-- ?><br/><?lua
+-- end
+-- end
+-- for comment in xml.collected(root,"/application/comments/comment") do
+-- ?><br/><?lua inject(xml.text(comment)) ?><br/><?lua
+-- end
+-- ?>
+-- ]]
+
+local bodytemplate = [[
+<h1>Command line options</h1>
+<table>
+ <tr><th style="width: 10em">flag</th><th style="width: 8em">value</th><th>description</th></tr>
+ <?lua for category in xml.collected(variables.root,"/application/flags/category") do if variables.nofcategories > 1 then ?>
+ <tr><th colspan="3"><?lua inject(category.at.name) ?></th></tr>
+ <?lua end for subcategory in xml.collected(category,"/subcategory") do ?>
+ <tr><th/><td/><td/></tr>
+ <?lua for flag in xml.collected(subcategory,"/flag") do local name, value, short = variables.flagdata(flag) ?>
+ <tr><th>--<?lua inject(name) ?></th><td><?lua inject(value) ?></td><td><?lua inject(short) ?></td></tr>
+ <?lua end end end ?>
+</table>
+<br/>
+<?lua for category in xml.collected(variables.root,"/application/examples/category") do local title = variables.categorytitle(category) if title ~= "" then ?>
+<h1><?lua inject(title) ?></h1>
+<?lua end for subcategory in xml.collected(category,"/subcategory") do for example in xml.collected(subcategory,"/example") do local command, comment = variables.exampledata(example) ?>
+<tt><?lua inject(command) ?></tt>
+<br/><?lua end ?><br/><?lua end end for comment in xml.collected(root,"/application/comments/comment") do ?>
+<br/><?lua inject(xml.text(comment)) ?><br/><?lua end ?>
+]]
+
+function exporters.html(specification,...)
+ local root = xml.convert(specification.helpinfo or "")
+ if not root then
+ return
+ end
+ local xs = xml.gethandlers("string")
+ xml.sethandlersfunction(xs,"short",function(e,handler) xmlserialize(e.dt,handler) end)
+ xml.sethandlersfunction(xs,"ref", function(e,handler) handler.handle("--"..e.at.name) end)
+ local wantedcategories = select("#",...) == 0 and true or table.tohash { ... }
+ local nofcategories = xml.count(root,"/application/flags/category")
+ local name = xmlfilter(root,"/application/metadata/entry[@name='name']/text()")
+ local detail = xmlfilter(root,"/application/metadata/entry[@name='detail']/text()") or name
+ local version = xmlfilter(root,"/application/metadata/entry[@name='version']/text()") or "0.00"
+ local banner = specification.banner or detail or name
+ --
+ dofile(resolvers.findfile("trac-lmx.lua","tex"))
+ --
+ local htmltemplate = io.loaddata(resolvers.findfile("context-base.lmx","tex")) or "no template"
+ --
+ local body = lmx.convertstring(bodytemplate, {
+ nofcategories = nofcategories,
+ wantedcategories = wantedcategories,
+ root = root,
+ -- moreinfo = specification.moreinfo,
+ flagdata = flagdata,
+ exampledata = exampledata,
+ categorytitle = categorytitle,
+ })
+ local html = lmx.convertstring(htmltemplate, {
+ maintext = body,
+ title = banner,
+ bottomtext = "wiki: http://contextgarden.net | mail: ntg-context@ntg.nl | website: http://www.pragma-ade.nl",
+ })
+ --
+ return html
+end
diff --git a/tex/context/base/trac-fil.lua b/tex/context/base/trac-fil.lua
index d6d40356d..8cc903e2a 100644
--- a/tex/context/base/trac-fil.lua
+++ b/tex/context/base/trac-fil.lua
@@ -1,181 +1,181 @@
-if not modules then modules = { } end modules ['trac-fil'] = {
- version = 1.001,
- comment = "for the moment for myself",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local rawset, tonumber, type, pcall = rawset, tonumber, type, pcall
-local format, concat = string.format, table.concat
-local openfile = io.open
-local date = os.date
-local sortedpairs = table.sortedpairs
-
-local P, C, Cc, Cg, Cf, Ct, Cs, Carg = lpeg.P, lpeg.C, lpeg.Cc, lpeg.Cg, lpeg.Cf, lpeg.Ct, lpeg.Cs, lpeg.Carg
-local lpegmatch = lpeg.match
-
-local patterns = lpeg.patterns
-local cardinal = patterns.cardinal
-local whitespace = patterns.whitespace^0
-
-local timestamp = Cf(Ct("") * (
- Cg (Cc("year") * (cardinal/tonumber)) * P("-")
- * Cg (Cc("month") * (cardinal/tonumber)) * P("-")
- * Cg (Cc("day") * (cardinal/tonumber)) * P(" ")
- * Cg (Cc("hour") * (cardinal/tonumber)) * P(":")
- * Cg (Cc("minute") * (cardinal/tonumber)) * P(":")
- * Cg (Cc("second") * (cardinal/tonumber)) * P("+")
- * Cg (Cc("thour") * (cardinal/tonumber)) * P(":")
- * Cg (Cc("tminute") * (cardinal/tonumber))
-)^0, rawset)
-
-local keysvalues = Cf(Ct("") * (
- Cg(C(patterns.letter^0) * whitespace * "=" * whitespace * Cs(patterns.unquoted) * whitespace)
-)^0, rawset)
-
-local statusline = Cf(Ct("") * (
- whitespace * P("[") * Cg(Cc("timestamp") * timestamp ) * P("]")
- * whitespace * Cg(Cc("status" ) * keysvalues)
-),rawset)
-
-patterns.keysvalues = keysvalues
-patterns.statusline = statusline
-patterns.timestamp = timestamp
-
-loggers = loggers or { }
-
-local timeformat = format("[%%s%s]",os.timezone(true))
-local dateformat = "!%Y-%m-%d %H:%M:%S"
-
-function loggers.makeline(t)
- local result = { } -- minimize time that file is open
- result[#result+1] = format(timeformat,date(dateformat))
- for k, v in sortedpairs(t) do
- local tv = type(v)
- if tv == "string" then
- if v ~= "password" then
- result[#result+1] = format(" %s=%q",k,v)
- end
- elseif tv == "number" or tv == "boolean" then
- result[#result+1] = format(" %s=%q",k,tostring(v))
- end
- end
- return concat(result," ")
-end
-
-local function append(filename,...)
- local f = openfile(filename,"a+")
- if not f then
- dir.mkdirs(file.dirname(filename))
- f = openfile(filename,"a+")
- end
- if f then
- f:write(...)
- f:close()
- return true
- else
- return false
- end
-end
-
-function loggers.store(filename,data) -- a log service is nicer
- if type(data) == "table"then
- data = loggers.makeline(data)
- end
- pcall(append,filename,data,"\n")
-end
-
-function loggers.collect(filename,result)
- if lfs.isfile(filename) then
- local r = lpegmatch(Ct(statusline^0),io.loaddata(filename))
- if result then -- append
- local nofresult = #result
- for i=1,#r do
- nofresult = nofresult + 1
- result[nofresult] = r[i]
- end
- return result
- else
- return r
- end
- else
- return result or { }
- end
-end
-
-function loggers.fields(results) -- returns hash of fields with counts so that we can decide on importance
- local fields = { }
- if results then
- for i=1,#results do
- local r = results[i]
- for k, v in next, r do
- local f = fields[k]
- if not f then
- fields[k] = 1
- else
- fields[k] = f + 1
- end
- end
- end
- end
- return fields
-end
-
-local template = [[<!-- log entries: begin --!>
-<table>
-<tr>%s</tr>
-%s
-</table>
-<!-- log entries: end --!>
-]]
-
-function loggers.tohtml(entries,fields)
- if not fields or #fields == 0 then
- return ""
- end
- if type(entries) == "string" then
- entries = loggers.collect(entries)
- end
- local scratch, lines = { }, { }
- for i=1,#entries do
- local entry = entries[i]
- local status = entry.status
- for i=1,#fields do
- local field = fields[i]
- local v = status[field.name]
- if v ~= nil then
- v = tostring(v)
- local f = field.format
- if f then
- v = format(f,v)
- end
- scratch[i] = format("<td nowrap='nowrap' align='%s'>%s</td>",field.align or "left",v)
- else
- scratch[i] = "<td/>"
- end
- end
- lines[i] = format("<tr>%s</tr>",concat(scratch))
- end
- for i=1,#fields do
- local field = fields[i]
- scratch[i] = format("<th nowrap='nowrap' align='left'>%s</th>", field.label or field.name)
- end
- local result = format(template,concat(scratch),concat(lines,"\n"))
- return result, entries
-end
-
--- loggers.store("test.log", { name = "whatever", more = math.random(1,100) })
-
--- local fields = {
--- { name = "name", align = "left" },
--- { name = "more", align = "right" },
--- }
-
--- local entries = loggers.collect("test.log")
--- local html = loggers.tohtml(entries,fields)
-
--- inspect(entries)
--- inspect(fields)
--- inspect(html)
-
+if not modules then modules = { } end modules ['trac-fil'] = {
+ version = 1.001,
+ comment = "for the moment for myself",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local rawset, tonumber, type, pcall = rawset, tonumber, type, pcall
+local format, concat = string.format, table.concat
+local openfile = io.open
+local date = os.date
+local sortedpairs = table.sortedpairs
+
+local P, C, Cc, Cg, Cf, Ct, Cs, Carg = lpeg.P, lpeg.C, lpeg.Cc, lpeg.Cg, lpeg.Cf, lpeg.Ct, lpeg.Cs, lpeg.Carg
+local lpegmatch = lpeg.match
+
+local patterns = lpeg.patterns
+local cardinal = patterns.cardinal
+local whitespace = patterns.whitespace^0
+
+local timestamp = Cf(Ct("") * (
+ Cg (Cc("year") * (cardinal/tonumber)) * P("-")
+ * Cg (Cc("month") * (cardinal/tonumber)) * P("-")
+ * Cg (Cc("day") * (cardinal/tonumber)) * P(" ")
+ * Cg (Cc("hour") * (cardinal/tonumber)) * P(":")
+ * Cg (Cc("minute") * (cardinal/tonumber)) * P(":")
+ * Cg (Cc("second") * (cardinal/tonumber)) * P("+")
+ * Cg (Cc("thour") * (cardinal/tonumber)) * P(":")
+ * Cg (Cc("tminute") * (cardinal/tonumber))
+)^0, rawset)
+
+local keysvalues = Cf(Ct("") * (
+ Cg(C(patterns.letter^0) * whitespace * "=" * whitespace * Cs(patterns.unquoted) * whitespace)
+)^0, rawset)
+
+local statusline = Cf(Ct("") * (
+ whitespace * P("[") * Cg(Cc("timestamp") * timestamp ) * P("]")
+ * whitespace * Cg(Cc("status" ) * keysvalues)
+),rawset)
+
+patterns.keysvalues = keysvalues
+patterns.statusline = statusline
+patterns.timestamp = timestamp
+
+loggers = loggers or { }
+
+local timeformat = format("[%%s%s]",os.timezone(true))
+local dateformat = "!%Y-%m-%d %H:%M:%S"
+
+function loggers.makeline(t)
+ local result = { } -- minimize time that file is open
+ result[#result+1] = format(timeformat,date(dateformat))
+ for k, v in sortedpairs(t) do
+ local tv = type(v)
+ if tv == "string" then
+ if v ~= "password" then
+ result[#result+1] = format(" %s=%q",k,v)
+ end
+ elseif tv == "number" or tv == "boolean" then
+ result[#result+1] = format(" %s=%q",k,tostring(v))
+ end
+ end
+ return concat(result," ")
+end
+
+local function append(filename,...)
+ local f = openfile(filename,"a+")
+ if not f then
+ dir.mkdirs(file.dirname(filename))
+ f = openfile(filename,"a+")
+ end
+ if f then
+ f:write(...)
+ f:close()
+ return true
+ else
+ return false
+ end
+end
+
+function loggers.store(filename,data) -- a log service is nicer
+ if type(data) == "table"then
+ data = loggers.makeline(data)
+ end
+ pcall(append,filename,data,"\n")
+end
+
+function loggers.collect(filename,result)
+ if lfs.isfile(filename) then
+ local r = lpegmatch(Ct(statusline^0),io.loaddata(filename))
+ if result then -- append
+ local nofresult = #result
+ for i=1,#r do
+ nofresult = nofresult + 1
+ result[nofresult] = r[i]
+ end
+ return result
+ else
+ return r
+ end
+ else
+ return result or { }
+ end
+end
+
+function loggers.fields(results) -- returns hash of fields with counts so that we can decide on importance
+ local fields = { }
+ if results then
+ for i=1,#results do
+ local r = results[i]
+ for k, v in next, r do
+ local f = fields[k]
+ if not f then
+ fields[k] = 1
+ else
+ fields[k] = f + 1
+ end
+ end
+ end
+ end
+ return fields
+end
+
+local template = [[<!-- log entries: begin --!>
+<table>
+<tr>%s</tr>
+%s
+</table>
+<!-- log entries: end --!>
+]]
+
+function loggers.tohtml(entries,fields)
+ if not fields or #fields == 0 then
+ return ""
+ end
+ if type(entries) == "string" then
+ entries = loggers.collect(entries)
+ end
+ local scratch, lines = { }, { }
+ for i=1,#entries do
+ local entry = entries[i]
+ local status = entry.status
+ for i=1,#fields do
+ local field = fields[i]
+ local v = status[field.name]
+ if v ~= nil then
+ v = tostring(v)
+ local f = field.format
+ if f then
+ v = format(f,v)
+ end
+ scratch[i] = format("<td nowrap='nowrap' align='%s'>%s</td>",field.align or "left",v)
+ else
+ scratch[i] = "<td/>"
+ end
+ end
+ lines[i] = format("<tr>%s</tr>",concat(scratch))
+ end
+ for i=1,#fields do
+ local field = fields[i]
+ scratch[i] = format("<th nowrap='nowrap' align='left'>%s</th>", field.label or field.name)
+ end
+ local result = format(template,concat(scratch),concat(lines,"\n"))
+ return result, entries
+end
+
+-- loggers.store("test.log", { name = "whatever", more = math.random(1,100) })
+
+-- local fields = {
+-- { name = "name", align = "left" },
+-- { name = "more", align = "right" },
+-- }
+
+-- local entries = loggers.collect("test.log")
+-- local html = loggers.tohtml(entries,fields)
+
+-- inspect(entries)
+-- inspect(fields)
+-- inspect(html)
+
diff --git a/tex/context/base/trac-inf.lua b/tex/context/base/trac-inf.lua
index aa7704d3f..eefc15a6f 100644
--- a/tex/context/base/trac-inf.lua
+++ b/tex/context/base/trac-inf.lua
@@ -1,193 +1,193 @@
-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 = type, tonumber
-local format, lower = string.format, string.lower
-local concat = table.concat
-local clock = os.gettimeofday or os.clock -- should go in environment
-
-statistics = statistics or { }
-local statistics = statistics
-
-statistics.enable = true
-statistics.threshold = 0.01
-
-local statusinfo, n, registered, timers = { }, 0, { }, { }
-
-table.setmetatableindex(timers,function(t,k)
- local v = { timing = 0, loadtime = 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 }
-end
-
-local function starttiming(instance)
- local timer = timers[instance or "notimer"]
- local it = timer.timing or 0
- if it == 0 then
- timer.starttime = clock()
- 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 then
- local stoptime = clock()
- local loadtime = stoptime - starttime
- timer.stoptime = stoptime
- timer.loadtime = timer.loadtime + loadtime
- timer.timing = 0
- return loadtime
- end
- end
- return 0
-end
-
-local function elapsed(instance)
- if type(instance) == "number" then
- return instance or 0
- else
- local timer = timers[instance or "notimer"]
- return timer and timer.loadtime or 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.elapsed = elapsed
-statistics.elapsedtime = elapsedtime
-statistics.elapsedindeed = elapsedindeed
-statistics.elapsedseconds = elapsedseconds
-
--- general function .. we might split this module
-
-function statistics.register(tag,fnc)
- if statistics.enable and type(fnc) == "function" then
- 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("luatex banner", function()
- return lower(status.banner)
- end)
- register("control sequences", function()
- return format("%s of %s + %s", status.cs_count, status.hash_size,status.hash_extra)
- end)
- register("callbacks", function()
- local total, indirect = status.callbacks or 0, status.indirect_callbacks or 0
- return format("%s direct, %s indirect, %s total", total-indirect, indirect, total)
- end)
- if jit then
- local status = { jit.status() }
- if status[1] then
- register("luajit status", function()
- return concat(status," ",2)
- end)
- end
- end
- -- so far
- -- collectgarbage("collect")
- register("current memory usage",statistics.memused)
- 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
- return format("%s MB (ctx: %s MB)",round(collectgarbage("count")/1000), round(status.luastate_bytes/1000000))
-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)
- return statistics.formatruntime(elapsedtime(statistics))
-end
-
-local report = logs.reporter("system")
-
-function statistics.timed(action)
- starttiming("run")
- action()
- stoptiming("run")
- report("total runtime: %s",elapsedtime("run"))
-end
-
--- where, not really the best spot for this:
-
-commands = commands or { }
-
-function commands.resettimer(name)
- resettiming(name or "whatever")
- starttiming(name or "whatever")
-end
-
-function commands.elapsedtime(name)
- stoptiming(name or "whatever")
- context(elapsedtime(name or "whatever"))
-end
+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 = type, tonumber
+local format, lower = string.format, string.lower
+local concat = table.concat
+local clock = os.gettimeofday or os.clock -- should go in environment
+
+statistics = statistics or { }
+local statistics = statistics
+
+statistics.enable = true
+statistics.threshold = 0.01
+
+local statusinfo, n, registered, timers = { }, 0, { }, { }
+
+table.setmetatableindex(timers,function(t,k)
+ local v = { timing = 0, loadtime = 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 }
+end
+
+local function starttiming(instance)
+ local timer = timers[instance or "notimer"]
+ local it = timer.timing or 0
+ if it == 0 then
+ timer.starttime = clock()
+ 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 then
+ local stoptime = clock()
+ local loadtime = stoptime - starttime
+ timer.stoptime = stoptime
+ timer.loadtime = timer.loadtime + loadtime
+ timer.timing = 0
+ return loadtime
+ end
+ end
+ return 0
+end
+
+local function elapsed(instance)
+ if type(instance) == "number" then
+ return instance or 0
+ else
+ local timer = timers[instance or "notimer"]
+ return timer and timer.loadtime or 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.elapsed = elapsed
+statistics.elapsedtime = elapsedtime
+statistics.elapsedindeed = elapsedindeed
+statistics.elapsedseconds = elapsedseconds
+
+-- general function .. we might split this module
+
+function statistics.register(tag,fnc)
+ if statistics.enable and type(fnc) == "function" then
+ 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("luatex banner", function()
+ return lower(status.banner)
+ end)
+ register("control sequences", function()
+ return format("%s of %s + %s", status.cs_count, status.hash_size,status.hash_extra)
+ end)
+ register("callbacks", function()
+ local total, indirect = status.callbacks or 0, status.indirect_callbacks or 0
+ return format("%s direct, %s indirect, %s total", total-indirect, indirect, total)
+ end)
+ if jit then
+ local status = { jit.status() }
+ if status[1] then
+ register("luajit status", function()
+ return concat(status," ",2)
+ end)
+ end
+ end
+ -- so far
+ -- collectgarbage("collect")
+ register("current memory usage",statistics.memused)
+ 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
+ return format("%s MB (ctx: %s MB)",round(collectgarbage("count")/1000), round(status.luastate_bytes/1000000))
+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)
+ return statistics.formatruntime(elapsedtime(statistics))
+end
+
+local report = logs.reporter("system")
+
+function statistics.timed(action)
+ starttiming("run")
+ action()
+ stoptiming("run")
+ report("total runtime: %s",elapsedtime("run"))
+end
+
+-- where, not really the best spot for this:
+
+commands = commands or { }
+
+function commands.resettimer(name)
+ resettiming(name or "whatever")
+ starttiming(name or "whatever")
+end
+
+function commands.elapsedtime(name)
+ stoptiming(name or "whatever")
+ context(elapsedtime(name or "whatever"))
+end
diff --git a/tex/context/base/trac-jus.lua b/tex/context/base/trac-jus.lua
index 4be9b30f8..9d99f059d 100644
--- a/tex/context/base/trac-jus.lua
+++ b/tex/context/base/trac-jus.lua
@@ -1,136 +1,136 @@
-if not modules then modules = { } end modules ['trac-jus'] = {
- version = 1.001,
- comment = "companion to trac-jus.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local checkers = typesetters.checkers or { }
-typesetters.checkers = checkers
-
------ report_justification = logs.reporter("visualize","justification")
-
-local a_alignstate = attributes.private("alignstate")
-local a_justification = attributes.private("justification")
-
-local tracers = nodes.tracers
-local setcolor = tracers.colors.set
-local settransparency = tracers.transparencies.set
-
-local new_rule = nodes.pool.rule
-local new_glue = nodes.pool.glue
-local new_kern = nodes.pool.kern
-local concat_nodes = nodes.concat
-local hpack_nodes = node.hpack
-local copy_node = node.copy
-local get_list_dimensions = node.dimensions
-local hlist_code = nodes.nodecodes.hlist
-
-local tex_set_attribute = tex.setattribute
-local unsetvalue = attributes.unsetvalue
-
-local min_threshold = 0
-local max_threshold = 0
-
-local function set(n)
- nodes.tasks.enableaction("mvlbuilders", "typesetters.checkers.handler")
- nodes.tasks.enableaction("vboxbuilders","typesetters.checkers.handler")
- tex_set_attribute(a_justification,n or 1)
- function typesetters.checkers.set(n)
- tex_set_attribute(a_justification,n or 1)
- end
-end
-
-local function reset()
- tex_set_attribute(a_justification,unsetvalue)
-end
-
-checkers.set = set
-checkers.reset = reset
-
-function commands.showjustification(n)
- set(n)
-end
-
-trackers.register("visualizers.justification", function(v)
- if v then
- set(1)
- else
- reset()
- end
-end)
-
-function checkers.handler(head)
- for current in node.traverse_id(hlist_code,head) do
- if current[a_justification] == 1 then
- current[a_justification] = 0
- local width = current.width
- if width > 0 then
- local list = current.list
- if list then
- local naturalwidth, naturalheight, naturaldepth = get_list_dimensions(list)
- local delta = naturalwidth - width
- if naturalwidth == 0 or delta == 0 then
- -- special box
- elseif delta >= max_threshold then
- local rule = new_rule(delta,naturalheight,naturaldepth)
- list = hpack_nodes(list,width,"exactly")
- if list.glue_set == 1 then
- setcolor(rule,"trace:dr")
- settransparency(rule,"trace:dr")
- else
- setcolor(rule,"trace:db")
- settransparency(rule,"trace:db")
- end
- rule = hpack_nodes(rule)
- rule.width = 0
- rule.height = 0
- rule.depth = 0
- current.list = concat_nodes { list, rule }
- -- current.list = concat_nodes { list, new_kern(-naturalwidth+width), rule }
- elseif delta <= min_threshold then
- local alignstate = list[a_alignstate]
- if alignstate == 1 then
- local rule = new_rule(-delta,naturalheight,naturaldepth)
- setcolor(rule,"trace:dc")
- settransparency(rule,"trace:dc")
- rule = hpack_nodes(rule)
- rule.height = 0
- rule.depth = 0
- rule.width = 0
- current.list = nodes.concat { rule, list }
- elseif alignstate == 2 then
- local rule = new_rule(-delta/2,naturalheight,naturaldepth)
- setcolor(rule,"trace:dy")
- settransparency(rule,"trace:dy")
- rule = hpack_nodes(rule)
- rule.width = 0
- rule.height = 0
- rule.depth = 0
- current.list = concat_nodes { copy_node(rule), list, new_kern(delta/2), rule }
- elseif alignstate == 3 then
- local rule = new_rule(-delta,naturalheight,naturaldepth)
- setcolor(rule,"trace:dm")
- settransparency(rule,"trace:dm")
- rule = hpack_nodes(rule)
- rule.height = 0
- rule.depth = 0
- current.list = concat_nodes { list, new_kern(delta), rule }
- else
- local rule = new_rule(-delta,naturalheight,naturaldepth)
- setcolor(rule,"trace:dg")
- settransparency(rule,"trace:dg")
- rule = hpack_nodes(rule)
- rule.height = 0
- rule.depth = 0
- rule.width = 0
- current.list = concat_nodes { list, new_kern(delta), rule }
- end
- end
- end
- end
- end
- end
- return head
-end
+if not modules then modules = { } end modules ['trac-jus'] = {
+ version = 1.001,
+ comment = "companion to trac-jus.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local checkers = typesetters.checkers or { }
+typesetters.checkers = checkers
+
+----- report_justification = logs.reporter("visualize","justification")
+
+local a_alignstate = attributes.private("alignstate")
+local a_justification = attributes.private("justification")
+
+local tracers = nodes.tracers
+local setcolor = tracers.colors.set
+local settransparency = tracers.transparencies.set
+
+local new_rule = nodes.pool.rule
+local new_glue = nodes.pool.glue
+local new_kern = nodes.pool.kern
+local concat_nodes = nodes.concat
+local hpack_nodes = node.hpack
+local copy_node = node.copy
+local get_list_dimensions = node.dimensions
+local hlist_code = nodes.nodecodes.hlist
+
+local tex_set_attribute = tex.setattribute
+local unsetvalue = attributes.unsetvalue
+
+local min_threshold = 0
+local max_threshold = 0
+
+local function set(n)
+ nodes.tasks.enableaction("mvlbuilders", "typesetters.checkers.handler")
+ nodes.tasks.enableaction("vboxbuilders","typesetters.checkers.handler")
+ tex_set_attribute(a_justification,n or 1)
+ function typesetters.checkers.set(n)
+ tex_set_attribute(a_justification,n or 1)
+ end
+end
+
+local function reset()
+ tex_set_attribute(a_justification,unsetvalue)
+end
+
+checkers.set = set
+checkers.reset = reset
+
+function commands.showjustification(n)
+ set(n)
+end
+
+trackers.register("visualizers.justification", function(v)
+ if v then
+ set(1)
+ else
+ reset()
+ end
+end)
+
+function checkers.handler(head)
+ for current in node.traverse_id(hlist_code,head) do
+ if current[a_justification] == 1 then
+ current[a_justification] = 0
+ local width = current.width
+ if width > 0 then
+ local list = current.list
+ if list then
+ local naturalwidth, naturalheight, naturaldepth = get_list_dimensions(list)
+ local delta = naturalwidth - width
+ if naturalwidth == 0 or delta == 0 then
+ -- special box
+ elseif delta >= max_threshold then
+ local rule = new_rule(delta,naturalheight,naturaldepth)
+ list = hpack_nodes(list,width,"exactly")
+ if list.glue_set == 1 then
+ setcolor(rule,"trace:dr")
+ settransparency(rule,"trace:dr")
+ else
+ setcolor(rule,"trace:db")
+ settransparency(rule,"trace:db")
+ end
+ rule = hpack_nodes(rule)
+ rule.width = 0
+ rule.height = 0
+ rule.depth = 0
+ current.list = concat_nodes { list, rule }
+ -- current.list = concat_nodes { list, new_kern(-naturalwidth+width), rule }
+ elseif delta <= min_threshold then
+ local alignstate = list[a_alignstate]
+ if alignstate == 1 then
+ local rule = new_rule(-delta,naturalheight,naturaldepth)
+ setcolor(rule,"trace:dc")
+ settransparency(rule,"trace:dc")
+ rule = hpack_nodes(rule)
+ rule.height = 0
+ rule.depth = 0
+ rule.width = 0
+ current.list = nodes.concat { rule, list }
+ elseif alignstate == 2 then
+ local rule = new_rule(-delta/2,naturalheight,naturaldepth)
+ setcolor(rule,"trace:dy")
+ settransparency(rule,"trace:dy")
+ rule = hpack_nodes(rule)
+ rule.width = 0
+ rule.height = 0
+ rule.depth = 0
+ current.list = concat_nodes { copy_node(rule), list, new_kern(delta/2), rule }
+ elseif alignstate == 3 then
+ local rule = new_rule(-delta,naturalheight,naturaldepth)
+ setcolor(rule,"trace:dm")
+ settransparency(rule,"trace:dm")
+ rule = hpack_nodes(rule)
+ rule.height = 0
+ rule.depth = 0
+ current.list = concat_nodes { list, new_kern(delta), rule }
+ else
+ local rule = new_rule(-delta,naturalheight,naturaldepth)
+ setcolor(rule,"trace:dg")
+ settransparency(rule,"trace:dg")
+ rule = hpack_nodes(rule)
+ rule.height = 0
+ rule.depth = 0
+ rule.width = 0
+ current.list = concat_nodes { list, new_kern(delta), rule }
+ end
+ end
+ end
+ end
+ end
+ end
+ return head
+end
diff --git a/tex/context/base/trac-lmx.lua b/tex/context/base/trac-lmx.lua
index 1a12d2078..18c7f6020 100644
--- a/tex/context/base/trac-lmx.lua
+++ b/tex/context/base/trac-lmx.lua
@@ -1,732 +1,732 @@
-if not modules then modules = { } end modules ['trac-lmx'] = {
- version = 1.002,
- comment = "companion to trac-lmx.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- this one will be adpated to the latest helpers
-
-local type, tostring, rawget, loadstring, pcall = type, tostring, rawget, loadstring, pcall
-local format, sub, gsub = string.format, string.sub, string.gsub
-local concat = table.concat
-local collapsespaces = string.collapsespaces
-local P, Cc, Cs, C, Carg, lpegmatch = lpeg.P, lpeg.Cc, lpeg.Cs, lpeg.C, lpeg.Carg, lpeg.match
-local joinpath, replacesuffix, pathpart, filesuffix = file.join, file.replacesuffix, file.pathpart, file.suffix
-
-local allocate = utilities.storage.allocate
-local setmetatableindex = table.setmetatableindex
-
------ trace_templates = false trackers .register("lmx.templates", function(v) trace_templates = v end)
-local trace_variables = false trackers .register("lmx.variables", function(v) trace_variables = v end)
-
-local cache_templates = true directives.register("lmx.cache.templates",function(v) cache_templates = v end)
-local cache_files = true directives.register("lmx.cache.files", function(v) cache_files = v end)
-
-local report_lmx = logs.reporter("lmx")
-local report_error = logs.reporter("lmx","error")
-
-lmx = lmx or { }
-local lmx = lmx
-
--- This will change: we will just pass the global defaults as argument, but then we need
--- to rewrite some older code or come up with an ugly trick.
-
-local lmxvariables = {
- ['title-default'] = 'ConTeXt LMX File',
- ['color-background-green'] = '#4F6F6F',
- ['color-background-blue'] = '#6F6F8F',
- ['color-background-yellow'] = '#8F8F6F',
- ['color-background-purple'] = '#8F6F8F',
- ['color-background-body'] = '#808080',
- ['color-background-main'] = '#3F3F3F',
-}
-
-local lmxinherited = {
- ['title'] = 'title-default',
- ['color-background-one'] = 'color-background-green',
- ['color-background-two'] = 'color-background-blue',
- ['color-background-three'] = 'color-background-one',
- ['color-background-four'] = 'color-background-two',
-}
-
-lmx.variables = lmxvariables
-lmx.inherited = lmxinherited
-
-setmetatableindex(lmxvariables,function(t,k)
- k = lmxinherited[k]
- while k do
- local v = rawget(lmxvariables,k)
- if v then
- return v
- end
- k = lmxinherited[k]
- end
-end)
-
-function lmx.set(key,value)
- lmxvariables[key] = value
-end
-
-function lmx.get(key)
- return lmxvariables[key] or ""
-end
-
-lmx.report = report_lmx
-
--- helpers
-
--- the variables table is an empty one that gets linked to a defaults table
--- that gets passed with a creation (first time only) and that itself links
--- to one that gets passed to the converter
-
-local variables = { } -- we assume no nesting
-local result = { } -- we assume no nesting
-
-local function do_print(one,two,...)
- if two then
- result[#result+1] = concat { one, two, ... }
- else
- result[#result+1] = one
- end
-end
-
--- Although it does not make much sense for most elements, we provide a mechanism
--- to print wrapped content, something that is more efficient when we are constructing
--- tables.
-
-local html = { }
-lmx.html = html
-
-function html.td(str)
- if type(str) == "table" then
- for i=1,#str do -- spoils t !
- str[i] = format("<td>%s</td>",str[i] or "")
- end
- result[#result+1] = concat(str)
- else
- result[#result+1] = format("<td>%s</td>",str or "")
- end
-end
-
-function html.th(str)
- if type(str) == "table" then
- for i=1,#str do -- spoils t !
- str[i] = format("<th>%s</th>",str[i])
- end
- result[#result+1] = concat(str)
- else
- result[#result+1] = format("<th>%s</th>",str or "")
- end
-end
-
-function html.a(text,url)
- result[#result+1] = format("<a href=%q>%s</a>",url,text)
-end
-
-setmetatableindex(html,function(t,k)
- local f = format("<%s>%%s</%s>",k,k)
- local v = function(str) result[#result+1] = format(f,str or "") end
- t[k] = v
- return v
-end)
-
--- Loading templates:
-
-local function loadedfile(name)
- name = resolvers and resolvers.findfile and resolvers.findfile(name) or name
- local data = io.loaddata(name)
- if not data or data == "" then
- report_lmx("file %a is empty",name)
- end
- return data
-end
-
-local function loadedsubfile(name)
- return io.loaddata(resolvers and resolvers.findfile and resolvers.findfile(name) or name)
-end
-
-lmx.loadedfile = loadedfile
-
--- A few helpers (the next one could end up in l-lpeg):
-
-local usedpaths = { }
-local givenpath = nil
-
-local do_nested_include = nil
-
-local pattern = lpeg.replacer {
- ["&"] = "&amp;",
- [">"] = "&gt;",
- ["<"] = "&lt;",
- ['"'] = "&quot;",
-}
-
-local function do_escape(str)
- return lpegmatch(pattern,str) or str
-end
-
-local function do_variable(str)
- local value = variables[str]
- if not trace_variables then
- -- nothing
- elseif type(value) == "string" then
- if #value > 80 then
- report_lmx("variable %a is set to: %s ...",str,collapsespaces(sub(value,1,80)))
- else
- report_lmx("variable %a is set to: %s",str,collapsespaces(value))
- end
- elseif type(value) == "nil" then
- report_lmx("variable %a is set to: %s",str,"<!-- unset -->")
- else
- report_lmx("variable %a is set to: %S",str,value)
- end
- if type(value) == "function" then -- obsolete ... will go away
- return value(str)
- else
- return value
- end
-end
-
-local function do_type(str)
- if str and str ~= "" then
- result[#result+1] = format("<tt>%s</tt>",do_escape(str))
- end
-end
-
-local function do_fprint(str,...)
- if str and str ~= "" then
- result[#result+1] = format(str,...)
- end
-end
-
-local function do_eprint(str,...)
- if str and str ~= "" then
- result[#result+1] = lpegmatch(pattern,format(str,...))
- end
-end
-
-local function do_print_variable(str)
- local str = do_variable(str) -- variables[str]
- if str and str ~= "" then
- result[#result+1] = str
- end
-end
-
-local function do_type_variable(str)
- local str = do_variable(str) -- variables[str]
- if str and str ~= "" then
- result[#result+1] = format("<tt>%s</tt>",do_escape(str))
- end
-end
-
-local function do_include(filename,option)
- local data = loadedsubfile(filename)
- if (not data or data == "") and givenpath then
- data = loadedsubfile(joinpath(givenpath,filename))
- end
- if (not data or data == "") and type(usedpaths) == "table" then
- for i=1,#usedpaths do
- data = loadedsubfile(joinpath(usedpaths[i],filename))
- if data and data ~= "" then
- break
- end
- end
- end
- if not data or data == "" then
- data = format("<!-- unknown lmx include file: %s -->",filename)
- report_lmx("include file %a is empty",filename)
- else
- -- report_lmx("included file: %s",filename)
- data = do_nested_include(data)
- end
- if filesuffix(filename,"css") and option == "strip" then -- new
- data = lmx.stripcss(data)
- end
- return data
-end
-
--- Flushers:
-
-lmx.print = do_print
-lmx.type = do_type
-lmx.eprint = do_eprint
-lmx.fprint = do_fprint
-
-lmx.escape = do_escape
-lmx.urlescape = url.escape
-lmx.variable = do_variable
-lmx.include = do_include
-
-lmx.inject = do_print
-lmx.finject = do_fprint
-lmx.einject = do_eprint
-
-lmx.pv = do_print_variable
-lmx.tv = do_type_variable
-
--- The next functions set up the closure.
-
-function lmx.initialize(d,v)
- if not v then
- setmetatableindex(d,lmxvariables)
- if variables ~= d then
- setmetatableindex(variables,d)
- if trace_variables then
- report_lmx("using chain: variables => given defaults => lmx variables")
- end
- elseif trace_variables then
- report_lmx("using chain: variables == given defaults => lmx variables")
- end
- elseif d ~= v then
- setmetatableindex(v,d)
- if d ~= lmxvariables then
- setmetatableindex(d,lmxvariables)
- if variables ~= v then
- setmetatableindex(variables,v)
- if trace_variables then
- report_lmx("using chain: variables => given variables => given defaults => lmx variables")
- end
- elseif trace_variables then
- report_lmx("using chain: variables == given variables => given defaults => lmx variables")
- end
- else
- if variables ~= v then
- setmetatableindex(variables,v)
- if trace_variables then
- report_lmx("using chain: variabes => given variables => given defaults")
- end
- elseif trace_variables then
- report_lmx("using chain: variables == given variables => given defaults")
- end
- end
- else
- setmetatableindex(v,lmxvariables)
- if variables ~= v then
- setmetatableindex(variables,v)
- if trace_variables then
- report_lmx("using chain: variables => given variables => lmx variables")
- end
- elseif trace_variables then
- report_lmx("using chain: variables == given variables => lmx variables")
- end
- end
- result = { }
-end
-
-function lmx.finalized()
- local collapsed = concat(result)
- result = { } -- free memory
- return collapsed
-end
-
-function lmx.getvariables()
- return variables
-end
-
-function lmx.reset()
- -- obsolete
-end
-
--- Creation: (todo: strip <!-- -->)
-
--- local template = [[
--- return function(defaults,variables)
---
--- -- initialize
---
--- lmx.initialize(defaults,variables)
---
--- -- interface
---
--- local definitions = { }
--- local variables = lmx.getvariables()
--- local html = lmx.html
--- local inject = lmx.print
--- local finject = lmx.fprint
--- local einject = lmx.eprint
--- local escape = lmx.escape
--- local verbose = lmx.type
---
--- -- shortcuts (sort of obsolete as there is no gain)
---
--- local p = lmx.print
--- local f = lmx.fprint
--- local v = lmx.variable
--- local e = lmx.escape
--- local t = lmx.type
--- local pv = lmx.pv
--- local tv = lmx.tv
---
--- -- generator
---
--- %s
---
--- -- finalize
---
--- return lmx.finalized()
---
--- end
--- ]]
-
-local template = [[
--- interface
-
-local html = lmx.html
-local inject = lmx.print
-local finject = lmx.fprint -- better use the following
-local einject = lmx.eprint -- better use the following
-local injectf = lmx.fprint
-local injecte = lmx.eprint
-local injectfmt = lmx.fprint
-local injectesc = lmx.eprint
-local escape = lmx.escape
-local verbose = lmx.type
-
-local i_n_j_e_c_t = lmx.print
-
--- shortcuts (sort of obsolete as there is no gain)
-
-local p = lmx.print
-local f = lmx.fprint
-local v = lmx.variable
-local e = lmx.escape
-local t = lmx.type
-local pv = lmx.pv
-local tv = lmx.tv
-
-local lmx_initialize = lmx.initialize
-local lmx_finalized = lmx.finalized
-local lmx_getvariables = lmx.getvariables
-
--- generator
-
-return function(defaults,variables)
-
- lmx_initialize(defaults,variables)
-
- local definitions = { }
- local variables = lmx_getvariables()
-
- %s -- the action: appends to result
-
- return lmx_finalized()
-
-end
-]]
-
-local function savedefinition(definitions,tag,content)
- definitions[tag] = content
- return ""
-end
-
-local function getdefinition(definitions,tag)
- return definitions[tag] or ""
-end
-
-local whitespace = lpeg.patterns.whitespace
-local optionalspaces = whitespace^0
-
-local dquote = P('"')
-
-local begincomment = P("<!--")
-local endcomment = P("-->")
-
-local beginembedxml = P("<?")
-local endembedxml = P("?>")
-
-local beginembedcss = P("/*")
-local endembedcss = P("*/")
-
-local gobbledendxml = (optionalspaces * endembedxml) / ""
------ argumentxml = (1-gobbledendxml)^0
-local argumentxml = (whitespace^1 + dquote * C((1-dquote)^1) * dquote + C((1-gobbledendxml-whitespace)^1))^0
-
-local gobbledendcss = (optionalspaces * endembedcss) / ""
------ argumentcss = (1-gobbledendcss)^0
-local argumentcss = (whitespace^1 + dquote * C((1-dquote)^1) * dquote + C((1-gobbledendcss-whitespace)^1))^0
-
-local commentxml = (begincomment * (1-endcomment)^0 * endcomment) / ""
-
-local beginluaxml = (beginembedxml * P("lua")) / ""
-local endluaxml = endembedxml / ""
-
-local luacodexml = beginluaxml
- * (1-endluaxml)^1
- * endluaxml
-
-local beginluacss = (beginembedcss * P("lua")) / ""
-local endluacss = endembedcss / ""
-
-local luacodecss = beginluacss
- * (1-endluacss)^1
- * endluacss
-
-local othercode = (1-beginluaxml-beginluacss)^1 / " i_n_j_e_c_t[==[%0]==] "
-
-local includexml = ((beginembedxml * P("lmx-include") * optionalspaces) / "")
- * (argumentxml / do_include)
- * gobbledendxml
-
-local includecss = ((beginembedcss * P("lmx-include") * optionalspaces) / "")
- * (argumentcss / do_include)
- * gobbledendcss
-
-local definexml_b = ((beginembedxml * P("lmx-define-begin") * optionalspaces) / "")
- * argumentxml
- * gobbledendxml
-
-local definexml_e = ((beginembedxml * P("lmx-define-end") * optionalspaces) / "")
- * argumentxml
- * gobbledendxml
-
-local definexml_c = C((1-definexml_e)^0)
-
-local definexml = (Carg(1) * C(definexml_b) * definexml_c * definexml_e) / savedefinition
-
-local resolvexml = ((beginembedxml * P("lmx-resolve") * optionalspaces) / "")
- * ((Carg(1) * C(argumentxml)) / getdefinition)
- * gobbledendxml
-
-local definecss_b = ((beginembedcss * P("lmx-define-begin") * optionalspaces) / "")
- * argumentcss
- * gobbledendcss
-
-local definecss_e = ((beginembedcss * P("lmx-define-end") * optionalspaces) / "")
- * argumentcss
- * gobbledendcss
-
-local definecss_c = C((1-definecss_e)^0)
-
-local definecss = (Carg(1) * C(definecss_b) * definecss_c * definecss_e) / savedefinition
-
-local resolvecss = ((beginembedcss * P("lmx-resolve") * optionalspaces) / "")
- * ((Carg(1) * C(argumentcss)) / getdefinition)
- * gobbledendcss
-
-local pattern_1 = Cs((commentxml + includexml + includecss + P(1))^0) -- get rid of xml comments asap
-local pattern_2 = Cs((definexml + resolvexml + definecss + resolvecss + P(1))^0)
-local pattern_3 = Cs((luacodexml + luacodecss + othercode)^0)
-
-local cache = { }
-
-local function lmxerror(str)
- report_error(str)
- return html.tt(str)
-end
-
-local function wrapper(converter,defaults,variables)
- local outcome, message = pcall(converter,defaults,variables)
- if not outcome then
- return lmxerror(format("error in conversion: %s",message))
- else
- return message
- end
-end
-
-do_nested_include = function(data) -- also used in include
- return lpegmatch(pattern_1,data)
-end
-
-function lmxnew(data,defaults,nocache,path) -- todo: use defaults in calling routines
- data = data or ""
- local known = cache[data]
- if not known then
- givenpath = path
- usedpaths = lmxvariables.includepath or { }
- if type(usedpaths) == "string" then
- usedpaths = { usedpaths }
- end
- data = lpegmatch(pattern_1,data)
- data = lpegmatch(pattern_2,data,1,{})
- data = lpegmatch(pattern_3,data)
- local converted = loadstring(format(template,data))
- if converted then
- converted = converted()
- end
- defaults = defaults or { }
- local converter
- if converted then
- converter = function(variables)
- return wrapper(converted,defaults,variables)
- end
- else
- report_error("error in:\n%s\n:",data)
- converter = function() lmxerror("error in template") end
- end
- known = {
- data = defaults.trace and data or "",
- variables = defaults,
- converter = converter,
- }
- if cache_templates and nocache ~= false then
- cache[data] = known
- end
- elseif variables then
- known.variables = variables
- end
- return known, known.variables
-end
-
-local function lmxresult(self,variables)
- if self then
- local converter = self.converter
- if converter then
- local converted = converter(variables)
- if trace_variables then -- will become templates
- report_lmx("converted size: %s",#converted)
- end
- return converted or lmxerror("no result from converter")
- else
- return lmxerror("invalid converter")
- end
- else
- return lmxerror("invalid specification")
- end
-end
-
-lmx.new = lmxnew
-lmx.result = lmxresult
-
-local loadedfiles = { }
-
-function lmx.convertstring(templatestring,variables,nocache,path)
- return lmxresult(lmxnew(templatestring,nil,nocache,path),variables)
-end
-
-function lmx.convertfile(templatefile,variables,nocache)
- if trace_variables then -- will become templates
- report_lmx("converting file %a",templatefile)
- end
- local converter = loadedfiles[templatefile]
- if not converter then
- converter = lmxnew(loadedfile(templatefile),nil,nocache,pathpart(templatefile))
- loadedfiles[templatefile] = converter
- end
- return lmxresult(converter,variables)
-end
-
-function lmxconvert(templatefile,resultfile,variables,nocache) -- or (templatefile,variables)
- if trace_variables then -- will become templates
- report_lmx("converting file %a",templatefile)
- end
- if not variables and type(resultfile) == "table" then
- variables = resultfile
- end
- local converter = loadedfiles[templatefile]
- if not converter then
- converter = lmxnew(loadedfile(templatefile),nil,nocache,pathpart(templatefile))
- if cache_files then
- loadedfiles[templatefile] = converter
- end
- end
- local result = lmxresult(converter,variables)
- if resultfile then
- io.savedata(resultfile,result)
- else
- return result
- end
-end
-
-lmx.convert = lmxconvert
-
--- helpers
-
-local nocomment = (beginembedcss * (1 - endembedcss)^1 * endembedcss) / ""
-local nowhitespace = whitespace^1 / " " -- ""
-local semistripped = whitespace^1 / "" * P(";")
-local stripper = Cs((nocomment + semistripped + nowhitespace + 1)^1)
-
-function lmx.stripcss(str)
- return lpegmatch(stripper,str)
-end
-
-function lmx.color(r,g,b,a)
- if r > 1 then
- r = 1
- end
- if g > 1 then
- g = 1
- end
- if b > 1 then
- b = 1
- end
- if not a then
- a= 0
- elseif a > 1 then
- a = 1
- end
- if a > 0 then
- return format("rgba(%s%%,%s%%,%s%%,%s)",r*100,g*100,b*100,a)
- else
- return format("rgb(%s%%,%s%%,%s%%)",r*100,g*100,b*100)
- end
-end
-
-
--- these can be overloaded
-
-lmx.lmxfile = string.itself
-lmx.htmfile = string.itself
-lmx.popupfile = os.launch
-
-function lmxmake(name,variables)
- local lmxfile = lmx.lmxfile(name)
- local htmfile = lmx.htmfile(name)
- if lmxfile == htmfile then
- htmfile = replacesuffix(lmxfile,"html")
- end
- lmxconvert(lmxfile,htmfile,variables)
- return htmfile
-end
-
-lmxmake = lmx.make
-
-function lmx.show(name,variables)
- local htmfile = lmxmake(name,variables)
- lmx.popupfile(htmfile)
- return htmfile
-end
-
--- Command line (will become mtx-lmx):
-
-if arg then
- if arg[1] == "--show" then if arg[2] then lmx.show (arg[2]) end
- elseif arg[1] == "--convert" then if arg[2] then lmx.convert(arg[2], arg[3] or "temp.html") end
- end
-end
-
--- Test 1:
-
--- inspect(lmx.result(lmx.new(io.loaddata("t:/sources/context-timing.lmx"))))
-
--- Test 2:
-
--- local str = [[
--- <?lmx-include context.css strip ?>
--- <test>
--- <?lmx-define-begin whatever?>some content a<?lmx-define-end ?>
--- <?lmx-define-begin somemore?>some content b<?lmx-define-end ?>
--- <more>
--- <?lmx-resolve whatever ?>
--- <?lua
--- for i=1,10 do end
--- ?>
--- <?lmx-resolve somemore ?>
--- </more>
--- <td><?lua p(100) ?></td>
--- <td><?lua p(variables.a) ?></td>
--- <td><?lua p(variables.b) ?></td>
--- <td><?lua p(variables.c) ?></td>
--- <td><?lua pv('title-default') ?></td>
--- </test>
--- ]]
-
--- local defaults = { trace = true, a = 3, b = 3 }
--- local result = lmx.new(str,defaults)
--- inspect(result.data)
--- inspect(result.converter(defaults))
--- inspect(result.converter { a = 1 })
--- inspect(lmx.result(result, { b = 2 }))
--- inspect(lmx.result(result, { a = 20000, b = 40000 }))
+if not modules then modules = { } end modules ['trac-lmx'] = {
+ version = 1.002,
+ comment = "companion to trac-lmx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- this one will be adpated to the latest helpers
+
+local type, tostring, rawget, loadstring, pcall = type, tostring, rawget, loadstring, pcall
+local format, sub, gsub = string.format, string.sub, string.gsub
+local concat = table.concat
+local collapsespaces = string.collapsespaces
+local P, Cc, Cs, C, Carg, lpegmatch = lpeg.P, lpeg.Cc, lpeg.Cs, lpeg.C, lpeg.Carg, lpeg.match
+local joinpath, replacesuffix, pathpart, filesuffix = file.join, file.replacesuffix, file.pathpart, file.suffix
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+
+----- trace_templates = false trackers .register("lmx.templates", function(v) trace_templates = v end)
+local trace_variables = false trackers .register("lmx.variables", function(v) trace_variables = v end)
+
+local cache_templates = true directives.register("lmx.cache.templates",function(v) cache_templates = v end)
+local cache_files = true directives.register("lmx.cache.files", function(v) cache_files = v end)
+
+local report_lmx = logs.reporter("lmx")
+local report_error = logs.reporter("lmx","error")
+
+lmx = lmx or { }
+local lmx = lmx
+
+-- This will change: we will just pass the global defaults as argument, but then we need
+-- to rewrite some older code or come up with an ugly trick.
+
+local lmxvariables = {
+ ['title-default'] = 'ConTeXt LMX File',
+ ['color-background-green'] = '#4F6F6F',
+ ['color-background-blue'] = '#6F6F8F',
+ ['color-background-yellow'] = '#8F8F6F',
+ ['color-background-purple'] = '#8F6F8F',
+ ['color-background-body'] = '#808080',
+ ['color-background-main'] = '#3F3F3F',
+}
+
+local lmxinherited = {
+ ['title'] = 'title-default',
+ ['color-background-one'] = 'color-background-green',
+ ['color-background-two'] = 'color-background-blue',
+ ['color-background-three'] = 'color-background-one',
+ ['color-background-four'] = 'color-background-two',
+}
+
+lmx.variables = lmxvariables
+lmx.inherited = lmxinherited
+
+setmetatableindex(lmxvariables,function(t,k)
+ k = lmxinherited[k]
+ while k do
+ local v = rawget(lmxvariables,k)
+ if v then
+ return v
+ end
+ k = lmxinherited[k]
+ end
+end)
+
+function lmx.set(key,value)
+ lmxvariables[key] = value
+end
+
+function lmx.get(key)
+ return lmxvariables[key] or ""
+end
+
+lmx.report = report_lmx
+
+-- helpers
+
+-- the variables table is an empty one that gets linked to a defaults table
+-- that gets passed with a creation (first time only) and that itself links
+-- to one that gets passed to the converter
+
+local variables = { } -- we assume no nesting
+local result = { } -- we assume no nesting
+
+local function do_print(one,two,...)
+ if two then
+ result[#result+1] = concat { one, two, ... }
+ else
+ result[#result+1] = one
+ end
+end
+
+-- Although it does not make much sense for most elements, we provide a mechanism
+-- to print wrapped content, something that is more efficient when we are constructing
+-- tables.
+
+local html = { }
+lmx.html = html
+
+function html.td(str)
+ if type(str) == "table" then
+ for i=1,#str do -- spoils t !
+ str[i] = format("<td>%s</td>",str[i] or "")
+ end
+ result[#result+1] = concat(str)
+ else
+ result[#result+1] = format("<td>%s</td>",str or "")
+ end
+end
+
+function html.th(str)
+ if type(str) == "table" then
+ for i=1,#str do -- spoils t !
+ str[i] = format("<th>%s</th>",str[i])
+ end
+ result[#result+1] = concat(str)
+ else
+ result[#result+1] = format("<th>%s</th>",str or "")
+ end
+end
+
+function html.a(text,url)
+ result[#result+1] = format("<a href=%q>%s</a>",url,text)
+end
+
+setmetatableindex(html,function(t,k)
+ local f = format("<%s>%%s</%s>",k,k)
+ local v = function(str) result[#result+1] = format(f,str or "") end
+ t[k] = v
+ return v
+end)
+
+-- Loading templates:
+
+local function loadedfile(name)
+ name = resolvers and resolvers.findfile and resolvers.findfile(name) or name
+ local data = io.loaddata(name)
+ if not data or data == "" then
+ report_lmx("file %a is empty",name)
+ end
+ return data
+end
+
+local function loadedsubfile(name)
+ return io.loaddata(resolvers and resolvers.findfile and resolvers.findfile(name) or name)
+end
+
+lmx.loadedfile = loadedfile
+
+-- A few helpers (the next one could end up in l-lpeg):
+
+local usedpaths = { }
+local givenpath = nil
+
+local do_nested_include = nil
+
+local pattern = lpeg.replacer {
+ ["&"] = "&amp;",
+ [">"] = "&gt;",
+ ["<"] = "&lt;",
+ ['"'] = "&quot;",
+}
+
+local function do_escape(str)
+ return lpegmatch(pattern,str) or str
+end
+
+local function do_variable(str)
+ local value = variables[str]
+ if not trace_variables then
+ -- nothing
+ elseif type(value) == "string" then
+ if #value > 80 then
+ report_lmx("variable %a is set to: %s ...",str,collapsespaces(sub(value,1,80)))
+ else
+ report_lmx("variable %a is set to: %s",str,collapsespaces(value))
+ end
+ elseif type(value) == "nil" then
+ report_lmx("variable %a is set to: %s",str,"<!-- unset -->")
+ else
+ report_lmx("variable %a is set to: %S",str,value)
+ end
+ if type(value) == "function" then -- obsolete ... will go away
+ return value(str)
+ else
+ return value
+ end
+end
+
+local function do_type(str)
+ if str and str ~= "" then
+ result[#result+1] = format("<tt>%s</tt>",do_escape(str))
+ end
+end
+
+local function do_fprint(str,...)
+ if str and str ~= "" then
+ result[#result+1] = format(str,...)
+ end
+end
+
+local function do_eprint(str,...)
+ if str and str ~= "" then
+ result[#result+1] = lpegmatch(pattern,format(str,...))
+ end
+end
+
+local function do_print_variable(str)
+ local str = do_variable(str) -- variables[str]
+ if str and str ~= "" then
+ result[#result+1] = str
+ end
+end
+
+local function do_type_variable(str)
+ local str = do_variable(str) -- variables[str]
+ if str and str ~= "" then
+ result[#result+1] = format("<tt>%s</tt>",do_escape(str))
+ end
+end
+
+local function do_include(filename,option)
+ local data = loadedsubfile(filename)
+ if (not data or data == "") and givenpath then
+ data = loadedsubfile(joinpath(givenpath,filename))
+ end
+ if (not data or data == "") and type(usedpaths) == "table" then
+ for i=1,#usedpaths do
+ data = loadedsubfile(joinpath(usedpaths[i],filename))
+ if data and data ~= "" then
+ break
+ end
+ end
+ end
+ if not data or data == "" then
+ data = format("<!-- unknown lmx include file: %s -->",filename)
+ report_lmx("include file %a is empty",filename)
+ else
+ -- report_lmx("included file: %s",filename)
+ data = do_nested_include(data)
+ end
+ if filesuffix(filename,"css") and option == "strip" then -- new
+ data = lmx.stripcss(data)
+ end
+ return data
+end
+
+-- Flushers:
+
+lmx.print = do_print
+lmx.type = do_type
+lmx.eprint = do_eprint
+lmx.fprint = do_fprint
+
+lmx.escape = do_escape
+lmx.urlescape = url.escape
+lmx.variable = do_variable
+lmx.include = do_include
+
+lmx.inject = do_print
+lmx.finject = do_fprint
+lmx.einject = do_eprint
+
+lmx.pv = do_print_variable
+lmx.tv = do_type_variable
+
+-- The next functions set up the closure.
+
+function lmx.initialize(d,v)
+ if not v then
+ setmetatableindex(d,lmxvariables)
+ if variables ~= d then
+ setmetatableindex(variables,d)
+ if trace_variables then
+ report_lmx("using chain: variables => given defaults => lmx variables")
+ end
+ elseif trace_variables then
+ report_lmx("using chain: variables == given defaults => lmx variables")
+ end
+ elseif d ~= v then
+ setmetatableindex(v,d)
+ if d ~= lmxvariables then
+ setmetatableindex(d,lmxvariables)
+ if variables ~= v then
+ setmetatableindex(variables,v)
+ if trace_variables then
+ report_lmx("using chain: variables => given variables => given defaults => lmx variables")
+ end
+ elseif trace_variables then
+ report_lmx("using chain: variables == given variables => given defaults => lmx variables")
+ end
+ else
+ if variables ~= v then
+ setmetatableindex(variables,v)
+ if trace_variables then
+ report_lmx("using chain: variabes => given variables => given defaults")
+ end
+ elseif trace_variables then
+ report_lmx("using chain: variables == given variables => given defaults")
+ end
+ end
+ else
+ setmetatableindex(v,lmxvariables)
+ if variables ~= v then
+ setmetatableindex(variables,v)
+ if trace_variables then
+ report_lmx("using chain: variables => given variables => lmx variables")
+ end
+ elseif trace_variables then
+ report_lmx("using chain: variables == given variables => lmx variables")
+ end
+ end
+ result = { }
+end
+
+function lmx.finalized()
+ local collapsed = concat(result)
+ result = { } -- free memory
+ return collapsed
+end
+
+function lmx.getvariables()
+ return variables
+end
+
+function lmx.reset()
+ -- obsolete
+end
+
+-- Creation: (todo: strip <!-- -->)
+
+-- local template = [[
+-- return function(defaults,variables)
+--
+-- -- initialize
+--
+-- lmx.initialize(defaults,variables)
+--
+-- -- interface
+--
+-- local definitions = { }
+-- local variables = lmx.getvariables()
+-- local html = lmx.html
+-- local inject = lmx.print
+-- local finject = lmx.fprint
+-- local einject = lmx.eprint
+-- local escape = lmx.escape
+-- local verbose = lmx.type
+--
+-- -- shortcuts (sort of obsolete as there is no gain)
+--
+-- local p = lmx.print
+-- local f = lmx.fprint
+-- local v = lmx.variable
+-- local e = lmx.escape
+-- local t = lmx.type
+-- local pv = lmx.pv
+-- local tv = lmx.tv
+--
+-- -- generator
+--
+-- %s
+--
+-- -- finalize
+--
+-- return lmx.finalized()
+--
+-- end
+-- ]]
+
+local template = [[
+-- interface
+
+local html = lmx.html
+local inject = lmx.print
+local finject = lmx.fprint -- better use the following
+local einject = lmx.eprint -- better use the following
+local injectf = lmx.fprint
+local injecte = lmx.eprint
+local injectfmt = lmx.fprint
+local injectesc = lmx.eprint
+local escape = lmx.escape
+local verbose = lmx.type
+
+local i_n_j_e_c_t = lmx.print
+
+-- shortcuts (sort of obsolete as there is no gain)
+
+local p = lmx.print
+local f = lmx.fprint
+local v = lmx.variable
+local e = lmx.escape
+local t = lmx.type
+local pv = lmx.pv
+local tv = lmx.tv
+
+local lmx_initialize = lmx.initialize
+local lmx_finalized = lmx.finalized
+local lmx_getvariables = lmx.getvariables
+
+-- generator
+
+return function(defaults,variables)
+
+ lmx_initialize(defaults,variables)
+
+ local definitions = { }
+ local variables = lmx_getvariables()
+
+ %s -- the action: appends to result
+
+ return lmx_finalized()
+
+end
+]]
+
+local function savedefinition(definitions,tag,content)
+ definitions[tag] = content
+ return ""
+end
+
+local function getdefinition(definitions,tag)
+ return definitions[tag] or ""
+end
+
+local whitespace = lpeg.patterns.whitespace
+local optionalspaces = whitespace^0
+
+local dquote = P('"')
+
+local begincomment = P("<!--")
+local endcomment = P("-->")
+
+local beginembedxml = P("<?")
+local endembedxml = P("?>")
+
+local beginembedcss = P("/*")
+local endembedcss = P("*/")
+
+local gobbledendxml = (optionalspaces * endembedxml) / ""
+----- argumentxml = (1-gobbledendxml)^0
+local argumentxml = (whitespace^1 + dquote * C((1-dquote)^1) * dquote + C((1-gobbledendxml-whitespace)^1))^0
+
+local gobbledendcss = (optionalspaces * endembedcss) / ""
+----- argumentcss = (1-gobbledendcss)^0
+local argumentcss = (whitespace^1 + dquote * C((1-dquote)^1) * dquote + C((1-gobbledendcss-whitespace)^1))^0
+
+local commentxml = (begincomment * (1-endcomment)^0 * endcomment) / ""
+
+local beginluaxml = (beginembedxml * P("lua")) / ""
+local endluaxml = endembedxml / ""
+
+local luacodexml = beginluaxml
+ * (1-endluaxml)^1
+ * endluaxml
+
+local beginluacss = (beginembedcss * P("lua")) / ""
+local endluacss = endembedcss / ""
+
+local luacodecss = beginluacss
+ * (1-endluacss)^1
+ * endluacss
+
+local othercode = (1-beginluaxml-beginluacss)^1 / " i_n_j_e_c_t[==[%0]==] "
+
+local includexml = ((beginembedxml * P("lmx-include") * optionalspaces) / "")
+ * (argumentxml / do_include)
+ * gobbledendxml
+
+local includecss = ((beginembedcss * P("lmx-include") * optionalspaces) / "")
+ * (argumentcss / do_include)
+ * gobbledendcss
+
+local definexml_b = ((beginembedxml * P("lmx-define-begin") * optionalspaces) / "")
+ * argumentxml
+ * gobbledendxml
+
+local definexml_e = ((beginembedxml * P("lmx-define-end") * optionalspaces) / "")
+ * argumentxml
+ * gobbledendxml
+
+local definexml_c = C((1-definexml_e)^0)
+
+local definexml = (Carg(1) * C(definexml_b) * definexml_c * definexml_e) / savedefinition
+
+local resolvexml = ((beginembedxml * P("lmx-resolve") * optionalspaces) / "")
+ * ((Carg(1) * C(argumentxml)) / getdefinition)
+ * gobbledendxml
+
+local definecss_b = ((beginembedcss * P("lmx-define-begin") * optionalspaces) / "")
+ * argumentcss
+ * gobbledendcss
+
+local definecss_e = ((beginembedcss * P("lmx-define-end") * optionalspaces) / "")
+ * argumentcss
+ * gobbledendcss
+
+local definecss_c = C((1-definecss_e)^0)
+
+local definecss = (Carg(1) * C(definecss_b) * definecss_c * definecss_e) / savedefinition
+
+local resolvecss = ((beginembedcss * P("lmx-resolve") * optionalspaces) / "")
+ * ((Carg(1) * C(argumentcss)) / getdefinition)
+ * gobbledendcss
+
+local pattern_1 = Cs((commentxml + includexml + includecss + P(1))^0) -- get rid of xml comments asap
+local pattern_2 = Cs((definexml + resolvexml + definecss + resolvecss + P(1))^0)
+local pattern_3 = Cs((luacodexml + luacodecss + othercode)^0)
+
+local cache = { }
+
+local function lmxerror(str)
+ report_error(str)
+ return html.tt(str)
+end
+
+local function wrapper(converter,defaults,variables)
+ local outcome, message = pcall(converter,defaults,variables)
+ if not outcome then
+ return lmxerror(format("error in conversion: %s",message))
+ else
+ return message
+ end
+end
+
+do_nested_include = function(data) -- also used in include
+ return lpegmatch(pattern_1,data)
+end
+
+function lmxnew(data,defaults,nocache,path) -- todo: use defaults in calling routines
+ data = data or ""
+ local known = cache[data]
+ if not known then
+ givenpath = path
+ usedpaths = lmxvariables.includepath or { }
+ if type(usedpaths) == "string" then
+ usedpaths = { usedpaths }
+ end
+ data = lpegmatch(pattern_1,data)
+ data = lpegmatch(pattern_2,data,1,{})
+ data = lpegmatch(pattern_3,data)
+ local converted = loadstring(format(template,data))
+ if converted then
+ converted = converted()
+ end
+ defaults = defaults or { }
+ local converter
+ if converted then
+ converter = function(variables)
+ return wrapper(converted,defaults,variables)
+ end
+ else
+ report_error("error in:\n%s\n:",data)
+ converter = function() lmxerror("error in template") end
+ end
+ known = {
+ data = defaults.trace and data or "",
+ variables = defaults,
+ converter = converter,
+ }
+ if cache_templates and nocache ~= false then
+ cache[data] = known
+ end
+ elseif variables then
+ known.variables = variables
+ end
+ return known, known.variables
+end
+
+local function lmxresult(self,variables)
+ if self then
+ local converter = self.converter
+ if converter then
+ local converted = converter(variables)
+ if trace_variables then -- will become templates
+ report_lmx("converted size: %s",#converted)
+ end
+ return converted or lmxerror("no result from converter")
+ else
+ return lmxerror("invalid converter")
+ end
+ else
+ return lmxerror("invalid specification")
+ end
+end
+
+lmx.new = lmxnew
+lmx.result = lmxresult
+
+local loadedfiles = { }
+
+function lmx.convertstring(templatestring,variables,nocache,path)
+ return lmxresult(lmxnew(templatestring,nil,nocache,path),variables)
+end
+
+function lmx.convertfile(templatefile,variables,nocache)
+ if trace_variables then -- will become templates
+ report_lmx("converting file %a",templatefile)
+ end
+ local converter = loadedfiles[templatefile]
+ if not converter then
+ converter = lmxnew(loadedfile(templatefile),nil,nocache,pathpart(templatefile))
+ loadedfiles[templatefile] = converter
+ end
+ return lmxresult(converter,variables)
+end
+
+function lmxconvert(templatefile,resultfile,variables,nocache) -- or (templatefile,variables)
+ if trace_variables then -- will become templates
+ report_lmx("converting file %a",templatefile)
+ end
+ if not variables and type(resultfile) == "table" then
+ variables = resultfile
+ end
+ local converter = loadedfiles[templatefile]
+ if not converter then
+ converter = lmxnew(loadedfile(templatefile),nil,nocache,pathpart(templatefile))
+ if cache_files then
+ loadedfiles[templatefile] = converter
+ end
+ end
+ local result = lmxresult(converter,variables)
+ if resultfile then
+ io.savedata(resultfile,result)
+ else
+ return result
+ end
+end
+
+lmx.convert = lmxconvert
+
+-- helpers
+
+local nocomment = (beginembedcss * (1 - endembedcss)^1 * endembedcss) / ""
+local nowhitespace = whitespace^1 / " " -- ""
+local semistripped = whitespace^1 / "" * P(";")
+local stripper = Cs((nocomment + semistripped + nowhitespace + 1)^1)
+
+function lmx.stripcss(str)
+ return lpegmatch(stripper,str)
+end
+
+function lmx.color(r,g,b,a)
+ if r > 1 then
+ r = 1
+ end
+ if g > 1 then
+ g = 1
+ end
+ if b > 1 then
+ b = 1
+ end
+ if not a then
+ a= 0
+ elseif a > 1 then
+ a = 1
+ end
+ if a > 0 then
+ return format("rgba(%s%%,%s%%,%s%%,%s)",r*100,g*100,b*100,a)
+ else
+ return format("rgb(%s%%,%s%%,%s%%)",r*100,g*100,b*100)
+ end
+end
+
+
+-- these can be overloaded
+
+lmx.lmxfile = string.itself
+lmx.htmfile = string.itself
+lmx.popupfile = os.launch
+
+function lmxmake(name,variables)
+ local lmxfile = lmx.lmxfile(name)
+ local htmfile = lmx.htmfile(name)
+ if lmxfile == htmfile then
+ htmfile = replacesuffix(lmxfile,"html")
+ end
+ lmxconvert(lmxfile,htmfile,variables)
+ return htmfile
+end
+
+lmxmake = lmx.make
+
+function lmx.show(name,variables)
+ local htmfile = lmxmake(name,variables)
+ lmx.popupfile(htmfile)
+ return htmfile
+end
+
+-- Command line (will become mtx-lmx):
+
+if arg then
+ if arg[1] == "--show" then if arg[2] then lmx.show (arg[2]) end
+ elseif arg[1] == "--convert" then if arg[2] then lmx.convert(arg[2], arg[3] or "temp.html") end
+ end
+end
+
+-- Test 1:
+
+-- inspect(lmx.result(lmx.new(io.loaddata("t:/sources/context-timing.lmx"))))
+
+-- Test 2:
+
+-- local str = [[
+-- <?lmx-include context.css strip ?>
+-- <test>
+-- <?lmx-define-begin whatever?>some content a<?lmx-define-end ?>
+-- <?lmx-define-begin somemore?>some content b<?lmx-define-end ?>
+-- <more>
+-- <?lmx-resolve whatever ?>
+-- <?lua
+-- for i=1,10 do end
+-- ?>
+-- <?lmx-resolve somemore ?>
+-- </more>
+-- <td><?lua p(100) ?></td>
+-- <td><?lua p(variables.a) ?></td>
+-- <td><?lua p(variables.b) ?></td>
+-- <td><?lua p(variables.c) ?></td>
+-- <td><?lua pv('title-default') ?></td>
+-- </test>
+-- ]]
+
+-- local defaults = { trace = true, a = 3, b = 3 }
+-- local result = lmx.new(str,defaults)
+-- inspect(result.data)
+-- inspect(result.converter(defaults))
+-- inspect(result.converter { a = 1 })
+-- inspect(lmx.result(result, { b = 2 }))
+-- inspect(lmx.result(result, { a = 20000, b = 40000 }))
diff --git a/tex/context/base/trac-log.lua b/tex/context/base/trac-log.lua
index 73e302e26..1f2520130 100644
--- a/tex/context/base/trac-log.lua
+++ b/tex/context/base/trac-log.lua
@@ -1,816 +1,816 @@
-if not modules then modules = { } end modules ['trac-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"
-}
-
--- if tex and (tex.jobname or tex.formatname) then
---
--- -- quick hack, awaiting speedup in engine (8 -> 6.4 sec for --make with console2)
--- -- still needed for luajittex
---
--- local texio_write_nl = texio.write_nl
--- local texio_write = texio.write
--- local io_write = io.write
-
--- local write_nl = function(target,...)
--- if not io_write then
--- io_write = io.write
--- end
--- if target == "term and log" then
--- texio_write_nl("log",...)
--- texio_write_nl("term","")
--- io_write(...)
--- elseif target == "log" then
--- texio_write_nl("log",...)
--- elseif target == "term" then
--- texio_write_nl("term","")
--- io_write(...)
--- else
--- texio_write_nl("log",target,...)
--- texio_write_nl("term","")
--- io_write(target,...)
--- end
--- end
-
--- local write = function(target,...)
--- if not io_write then
--- io_write = io.write
--- end
--- if target == "term and log" then
--- texio_write("log",...)
--- io_write(...)
--- elseif target == "log" then
--- texio_write("log",...)
--- elseif target == "term" then
--- io_write(...)
--- else
--- texio_write("log",target,...)
--- io_write(target,...)
--- end
--- end
-
--- texio.write = write
--- texio.write_nl = write_nl
---
--- else
---
--- -- texlua or just lua
---
--- end
-
--- todo: less categories, more subcategories (e.g. nodes)
--- todo: split into basics and ctx specific
-
-local write_nl, write = texio and texio.write_nl or print, texio and texio.write or io.write
-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 texcount = tex and tex.count
-local next, type, select = next, type, select
-local utfchar = utf.char
-
-local setmetatableindex = table.setmetatableindex
-local formatters = string.formatters
-
---[[ldx--
-<p>This is a prelude to a more extensive logging module. We no longer
-provide <l n='xml'/> based logging as parsing is relatively easy anyway.</p>
---ldx]]--
-
-logs = logs or { }
-local logs = logs
-
-local moreinfo = [[
-More information about ConTeXt and the tools that come with it can be found at:
-]] .. "\n" .. [[
-maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context
-webpage : http://www.pragma-ade.nl / http://tex.aanhet.net
-wiki : http://contextgarden.net
-]]
-
--- -- we extend the formatters:
---
--- function utilities.strings.unichr(s) return "U+" .. format("%05X",s) .. " (" .. utfchar(s) .. ")" end
--- function utilities.strings.chruni(s) return utfchar(s) .. " (U+" .. format("%05X",s) .. ")" end
---
--- utilities.strings.formatters.add (
--- string.formatters, "uni",
--- [[unichr(%s)]],
--- [[local unichr = utilities.strings.unichr]]
--- )
---
--- utilities.strings.formatters.add (
--- string.formatters, "chr",
--- [[chruni(%s)]],
--- [[local chruni = utilities.strings.chruni]]
--- )
-
-utilities.strings.formatters.add (
- formatters, "unichr",
- [["U+" .. format("%%05X",%s) .. " (" .. utfchar(%s) .. ")"]]
-)
-
-utilities.strings.formatters.add (
- formatters, "chruni",
- [[utfchar(%s) .. " (U+" .. format("%%05X",%s) .. ")"]]
-)
-
--- print(formatters["Missing character %!chruni! in font."](234))
--- print(formatters["Missing character %!unichr! in font."](234))
-
--- 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
-
-if tex and (tex.jobname or tex.formatname) then
-
- -- local format = string.formatter
-
- local valueiskey = { __index = function(t,k) t[k] = k return k end } -- will be helper
-
- local target = "term and log"
-
- logs.flush = io.flush
-
- local formats = { } setmetatable(formats, valueiskey)
- local translations = { } setmetatable(translations,valueiskey)
-
- writer = function(...)
- write_nl(target,...)
- end
-
- newline = function()
- write_nl(target,"\n")
- end
-
- local f_one = formatters["%-15s > %s\n"]
- local f_two = formatters["%-15s >\n"]
-
- -- we can use formatters but best check for % then because for simple messages
- -- we con't want this overhead for single messages (not that there are that
- -- many; we could have a special weak table)
-
- report = function(a,b,c,...)
- if c then
- write_nl(target,f_one(translations[a],formatters[formats[b]](c,...)))
- elseif b then
- write_nl(target,f_one(translations[a],formats[b]))
- elseif a then
- write_nl(target,f_two(translations[a]))
- else
- write_nl(target,"\n")
- end
- end
-
- local f_one = formatters["%-15s > %s"]
- local f_two = formatters["%-15s >"]
-
- direct = function(a,b,c,...)
- if c then
- return f_one(translations[a],formatters[formats[b]](c,...))
- elseif b then
- return f_one(translations[a],formats[b])
- elseif a then
- return f_two(translations[a])
- else
- return ""
- end
- end
-
- local f_one = formatters["%-15s > %s > %s\n"]
- local f_two = formatters["%-15s > %s >\n"]
-
- subreport = function(a,s,b,c,...)
- if c then
- write_nl(target,f_one(translations[a],translations[s],formatters[formats[b]](c,...)))
- elseif b then
- write_nl(target,f_one(translations[a],translations[s],formats[b]))
- elseif a then
- write_nl(target,f_two(translations[a],translations[s]))
- else
- write_nl(target,"\n")
- end
- end
-
- local f_one = formatters["%-15s > %s > %s"]
- local f_two = formatters["%-15s > %s >"]
-
- subdirect = function(a,s,b,c,...)
- if c then
- return f_one(translations[a],translations[s],formatters[formats[b]](c,...))
- elseif b then
- return f_one(translations[a],translations[s],formats[b])
- elseif a then
- return f_two(translations[a],translations[s])
- else
- return ""
- end
- end
-
- local f_one = formatters["%-15s : %s\n"]
- local f_two = formatters["%-15s :\n"]
-
- status = function(a,b,c,...)
- if c then
- write_nl(target,f_one(translations[a],formatters[formats[b]](c,...)))
- elseif b then
- write_nl(target,f_one(translations[a],formats[b]))
- elseif a then
- write_nl(target,f_two(translations[a]))
- else
- write_nl(target,"\n")
- end
- end
-
- local targets = {
- logfile = "log",
- log = "log",
- file = "log",
- console = "term",
- terminal = "term",
- both = "term and log",
- }
-
- settarget = function(whereto)
- target = targets[whereto or "both"] or targets.both
- if target == "term" or target == "term and log" 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
-
-else
-
- logs.flush = ignore
-
- writer = write_nl
-
- newline = function()
- write_nl("\n")
- end
-
- local f_one = formatters["%-15s | %s"]
- local f_two = formatters["%-15s |"]
-
- report = function(a,b,c,...)
- if c then
- write_nl(f_one(a,formatters[b](c,...)))
- elseif b then
- write_nl(f_one(a,b))
- elseif a then
- write_nl(f_two(a))
- else
- write_nl("")
- end
- end
-
- local f_one = formatters["%-15s | %s | %s"]
- local f_two = formatters["%-15s | %s |"]
-
- subreport = function(a,sub,b,c,...)
- if c then
- write_nl(f_one(a,sub,formatters[b](c,...)))
- elseif b then
- write_nl(f_one(a,sub,b))
- elseif a then
- write_nl(f_two(a,sub))
- else
- write_nl("")
- end
- end
-
- local f_one = formatters["%-15s : %s\n"]
- local f_two = formatters["%-15s :\n"]
-
- status = function(a,b,c,...) -- not to be used in lua anyway
- if c then
- write_nl(f_one(a,formatters[b](c,...)))
- elseif b then
- write_nl(f_one(a,b)) -- b can have %'s
- elseif a then
- write_nl(f_two(a))
- else
- write_nl("\n")
- end
- end
-
- direct = ignore
- subdirect = ignore
-
- settarget = ignore
- pushtarget = ignore
- poptarget = ignore
- setformats = ignore
- settranslations = 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.direct = direct
-logs.subdirect = subdirect
-logs.writer = writer
-logs.newline = newline
-
--- installer
-
--- todo: renew (un) locks when a new one is added and wildcard
-
-local data, states = { }, nil
-
-function logs.reporter(category,subcategory)
- local logger = data[category]
- if not logger then
- local state = false
- if states == true then
- state = true
- elseif 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 not logger.state then
- subreport(category,subcategory,...)
- end
- end
- logger.reporters[subcategory] = reporter
- else
- local tag = category
- reporter = function(...)
- if 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)
- if category == true 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
- states = utilities.parsers.settings_to_hash(category)
- for c, _ in next, states do
- if data[c] 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 table.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(table.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)
-
--- tex specific loggers (might move elsewhere)
-
-local report_pages = logs.reporter("pages") -- not needed but saves checking when we grep for it
-
-local real, user, sub
-
-function logs.start_page_number()
- real, user, sub = texcount.realpageno, texcount.userpageno, texcount.subpageno
--- real, user, sub = 0, 0, 0
-end
-
-local timing = false
-local starttime = nil
-local lasttime = nil
-
-trackers.register("pages.timing", function(v) -- only for myself (diagnostics)
- starttime = os.clock()
- timing = true
-end)
-
-function logs.stop_page_number() -- the first page can includes the initialization so we omit this in average
- if timing then
- local elapsed, average
- local stoptime = os.clock()
- if not lasttime or real < 2 then
- elapsed = stoptime
- average = stoptime
- starttime = stoptime
- else
- elapsed = stoptime - lasttime
- average = (stoptime - starttime) / (real - 1)
- end
- lasttime = stoptime
- if real <= 0 then
- report_pages("flushing page, time %0.04f / %0.04f",elapsed,average)
- elseif user <= 0 then
- report_pages("flushing realpage %s, time %0.04f / %0.04f",real,elapsed,average)
- elseif sub <= 0 then
- report_pages("flushing realpage %s, userpage %s, time %0.04f / %0.04f",real,user,elapsed,average)
- else
- report_pages("flushing realpage %s, userpage %s, subpage %s, time %0.04f / %0.04f",real,user,sub,elapsed,average)
- end
- else
- if real <= 0 then
- report_pages("flushing page")
- elseif user <= 0 then
- report_pages("flushing realpage %s",real)
- elseif sub <= 0 then
- report_pages("flushing realpage %s, userpage %s",real,user)
- else
- report_pages("flushing realpage %s, userpage %s, subpage %s",real,user,sub)
- end
- end
- logs.flush()
-end
-
--- we don't have show_open and show_close callbacks yet
-
-local report_files = logs.reporter("files")
-local nesting = 0
-local verbose = false
-local hasscheme = url.hasscheme
-
-function logs.show_open(name)
- -- if hasscheme(name) ~= "virtual" then
- -- if verbose then
- -- nesting = nesting + 1
- -- report_files("level %s, opening %s",nesting,name)
- -- else
- -- write(formatters["(%s"](name)) -- tex adds a space
- -- end
- -- end
-end
-
-function logs.show_close(name)
- -- if hasscheme(name) ~= "virtual" then
- -- if verbose then
- -- report_files("level %s, closing %s",nesting,name)
- -- nesting = nesting - 1
- -- else
- -- write(")") -- tex adds a space
- -- end
- -- end
-end
-
-function logs.show_load(name)
- -- if hasscheme(name) ~= "virtual" then
- -- if verbose then
- -- report_files("level %s, loading %s",nesting+1,name)
- -- else
- -- write(formatters["(%s)"](name))
- -- end
- -- end
-end
-
--- there may be scripts out there using this:
-
-local simple = logs.reporter("comment")
-
-logs.simple = simple
-logs.simpleline = simple
-
--- obsolete
-
-function logs.setprogram () end -- obsolete
-function logs.extendbanner() end -- obsolete
-function logs.reportlines () end -- obsolete
-function logs.reportbanner() end -- obsolete
-function logs.reportline () end -- obsolete
-function logs.simplelines () end -- obsolete
-function logs.help () end -- obsolete
-
--- applications
-
--- local function reportlines(t,str)
--- if str then
--- for line in gmatch(str,"([^\n\r]*)[\n\r]") do
--- t.report(line)
--- end
--- end
--- end
-
-local Carg, C, lpegmatch = lpeg.Carg, lpeg.C, lpeg.match
-local p_newline = lpeg.patterns.newline
-
-local linewise = (
- Carg(1) * C((1-p_newline)^1) / function(t,s) t.report(s) end
- + Carg(1) * p_newline^2 / function(t) t.report() end
- + p_newline
-)^1
-
-local function reportlines(t,str)
- if str then
- lpegmatch(linewise,str,1,t)
- end
-end
-
-local function reportbanner(t)
- local banner = t.banner
- if banner then
- t.report(banner)
- t.report()
- end
-end
-
-local function reportversion(t)
- local banner = t.banner
- if banner then
- t.report(banner)
- end
-end
-
-local function reporthelp(t,...)
- local helpinfo = t.helpinfo
- if type(helpinfo) == "string" then
- reportlines(t,helpinfo)
- elseif type(helpinfo) == "table" then
- for i=1,select("#",...) do
- reportlines(t,t.helpinfo[select(i,...)])
- if i < n then
- t.report()
- end
- end
- end
-end
-
-local function reportinfo(t)
- t.report()
- reportlines(t,t.moreinfo)
-end
-
-local function reportexport(t,method)
- report(t.helpinfo)
-end
-
-local reporters = {
- lines = reportlines, -- not to be overloaded
- banner = reportbanner,
- version = reportversion,
- help = reporthelp,
- info = reportinfo,
- export = reportexport,
-}
-
-local exporters = {
- -- empty
-}
-
-logs.reporters = reporters
-logs.exporters = exporters
-
-function logs.application(t)
- t.name = t.name or "unknown"
- t.banner = t.banner
- t.moreinfo = moreinfo
- t.report = logs.reporter(t.name)
- t.help = function(...)
- reporters.banner(t)
- reporters.help(t,...)
- reporters.info(t)
- end
- t.export = function(...)
- reporters.export(t,...)
- end
- t.identify = function()
- reporters.banner(t)
- end
- t.version = function()
- reporters.version(t)
- end
- return t
-end
-
--- somewhat special .. will be redone (already a better solution in place in lmx)
-
--- logging to a file
-
--- local syslogname = "oeps.xxx"
---
--- for i=1,10 do
--- logs.system(syslogname,"context","test","fonts","font %s recached due to newer version (%s)","blabla","123")
--- end
-
-function logs.system(whereto,process,jobname,category,...)
- local message = formatters["%s %s => %s => %s => %s\r"](os.date("%d/%m/%y %H:%m:%S"),process,jobname,category,format(...))
- for i=1,10 do
- local f = io.open(whereto,"a") -- we can consider keeping the file open
- if f then
- f:write(message)
- f:close()
- break
- else
- sleep(0.1)
- end
- end
-end
-
-local report_system = logs.reporter("system","logs")
-
-function logs.obsolete(old,new)
- local o = loadstring("return " .. new)()
- if type(o) == "function" then
- return function(...)
- report_system("function %a is obsolete, use %a",old,new)
- loadstring(old .. "=" .. new .. " return ".. old)()(...)
- end
- elseif type(o) == "table" then
- local t, m = { }, { }
- m.__index = function(t,k)
- report_system("table %a is obsolete, use %a",old,new)
- m.__index, m.__newindex = o, o
- return o[k]
- end
- m.__newindex = function(t,k,v)
- report_system("table %a is obsolete, use %a",old,new)
- m.__index, m.__newindex = o, o
- o[k] = v
- end
- if libraries then
- libraries.obsolete[old] = t -- true
- end
- setmetatable(t,m)
- return t
- end
-end
-
-if utilities then
- utilities.report = report_system
-end
-
-if tex and tex.error then
- function logs.texerrormessage(...) -- for the moment we put this function here
- tex.error(format(...), { })
- end
-else
- function logs.texerrormessage(...)
- print(format(...))
- 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
+if not modules then modules = { } end modules ['trac-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"
+}
+
+-- if tex and (tex.jobname or tex.formatname) then
+--
+-- -- quick hack, awaiting speedup in engine (8 -> 6.4 sec for --make with console2)
+-- -- still needed for luajittex
+--
+-- local texio_write_nl = texio.write_nl
+-- local texio_write = texio.write
+-- local io_write = io.write
+
+-- local write_nl = function(target,...)
+-- if not io_write then
+-- io_write = io.write
+-- end
+-- if target == "term and log" then
+-- texio_write_nl("log",...)
+-- texio_write_nl("term","")
+-- io_write(...)
+-- elseif target == "log" then
+-- texio_write_nl("log",...)
+-- elseif target == "term" then
+-- texio_write_nl("term","")
+-- io_write(...)
+-- else
+-- texio_write_nl("log",target,...)
+-- texio_write_nl("term","")
+-- io_write(target,...)
+-- end
+-- end
+
+-- local write = function(target,...)
+-- if not io_write then
+-- io_write = io.write
+-- end
+-- if target == "term and log" then
+-- texio_write("log",...)
+-- io_write(...)
+-- elseif target == "log" then
+-- texio_write("log",...)
+-- elseif target == "term" then
+-- io_write(...)
+-- else
+-- texio_write("log",target,...)
+-- io_write(target,...)
+-- end
+-- end
+
+-- texio.write = write
+-- texio.write_nl = write_nl
+--
+-- else
+--
+-- -- texlua or just lua
+--
+-- end
+
+-- todo: less categories, more subcategories (e.g. nodes)
+-- todo: split into basics and ctx specific
+
+local write_nl, write = texio and texio.write_nl or print, texio and texio.write or io.write
+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 texcount = tex and tex.count
+local next, type, select = next, type, select
+local utfchar = utf.char
+
+local setmetatableindex = table.setmetatableindex
+local formatters = string.formatters
+
+--[[ldx--
+<p>This is a prelude to a more extensive logging module. We no longer
+provide <l n='xml'/> based logging as parsing is relatively easy anyway.</p>
+--ldx]]--
+
+logs = logs or { }
+local logs = logs
+
+local moreinfo = [[
+More information about ConTeXt and the tools that come with it can be found at:
+]] .. "\n" .. [[
+maillist : ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context
+webpage : http://www.pragma-ade.nl / http://tex.aanhet.net
+wiki : http://contextgarden.net
+]]
+
+-- -- we extend the formatters:
+--
+-- function utilities.strings.unichr(s) return "U+" .. format("%05X",s) .. " (" .. utfchar(s) .. ")" end
+-- function utilities.strings.chruni(s) return utfchar(s) .. " (U+" .. format("%05X",s) .. ")" end
+--
+-- utilities.strings.formatters.add (
+-- string.formatters, "uni",
+-- [[unichr(%s)]],
+-- [[local unichr = utilities.strings.unichr]]
+-- )
+--
+-- utilities.strings.formatters.add (
+-- string.formatters, "chr",
+-- [[chruni(%s)]],
+-- [[local chruni = utilities.strings.chruni]]
+-- )
+
+utilities.strings.formatters.add (
+ formatters, "unichr",
+ [["U+" .. format("%%05X",%s) .. " (" .. utfchar(%s) .. ")"]]
+)
+
+utilities.strings.formatters.add (
+ formatters, "chruni",
+ [[utfchar(%s) .. " (U+" .. format("%%05X",%s) .. ")"]]
+)
+
+-- print(formatters["Missing character %!chruni! in font."](234))
+-- print(formatters["Missing character %!unichr! in font."](234))
+
+-- 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
+
+if tex and (tex.jobname or tex.formatname) then
+
+ -- local format = string.formatter
+
+ local valueiskey = { __index = function(t,k) t[k] = k return k end } -- will be helper
+
+ local target = "term and log"
+
+ logs.flush = io.flush
+
+ local formats = { } setmetatable(formats, valueiskey)
+ local translations = { } setmetatable(translations,valueiskey)
+
+ writer = function(...)
+ write_nl(target,...)
+ end
+
+ newline = function()
+ write_nl(target,"\n")
+ end
+
+ local f_one = formatters["%-15s > %s\n"]
+ local f_two = formatters["%-15s >\n"]
+
+ -- we can use formatters but best check for % then because for simple messages
+ -- we con't want this overhead for single messages (not that there are that
+ -- many; we could have a special weak table)
+
+ report = function(a,b,c,...)
+ if c then
+ write_nl(target,f_one(translations[a],formatters[formats[b]](c,...)))
+ elseif b then
+ write_nl(target,f_one(translations[a],formats[b]))
+ elseif a then
+ write_nl(target,f_two(translations[a]))
+ else
+ write_nl(target,"\n")
+ end
+ end
+
+ local f_one = formatters["%-15s > %s"]
+ local f_two = formatters["%-15s >"]
+
+ direct = function(a,b,c,...)
+ if c then
+ return f_one(translations[a],formatters[formats[b]](c,...))
+ elseif b then
+ return f_one(translations[a],formats[b])
+ elseif a then
+ return f_two(translations[a])
+ else
+ return ""
+ end
+ end
+
+ local f_one = formatters["%-15s > %s > %s\n"]
+ local f_two = formatters["%-15s > %s >\n"]
+
+ subreport = function(a,s,b,c,...)
+ if c then
+ write_nl(target,f_one(translations[a],translations[s],formatters[formats[b]](c,...)))
+ elseif b then
+ write_nl(target,f_one(translations[a],translations[s],formats[b]))
+ elseif a then
+ write_nl(target,f_two(translations[a],translations[s]))
+ else
+ write_nl(target,"\n")
+ end
+ end
+
+ local f_one = formatters["%-15s > %s > %s"]
+ local f_two = formatters["%-15s > %s >"]
+
+ subdirect = function(a,s,b,c,...)
+ if c then
+ return f_one(translations[a],translations[s],formatters[formats[b]](c,...))
+ elseif b then
+ return f_one(translations[a],translations[s],formats[b])
+ elseif a then
+ return f_two(translations[a],translations[s])
+ else
+ return ""
+ end
+ end
+
+ local f_one = formatters["%-15s : %s\n"]
+ local f_two = formatters["%-15s :\n"]
+
+ status = function(a,b,c,...)
+ if c then
+ write_nl(target,f_one(translations[a],formatters[formats[b]](c,...)))
+ elseif b then
+ write_nl(target,f_one(translations[a],formats[b]))
+ elseif a then
+ write_nl(target,f_two(translations[a]))
+ else
+ write_nl(target,"\n")
+ end
+ end
+
+ local targets = {
+ logfile = "log",
+ log = "log",
+ file = "log",
+ console = "term",
+ terminal = "term",
+ both = "term and log",
+ }
+
+ settarget = function(whereto)
+ target = targets[whereto or "both"] or targets.both
+ if target == "term" or target == "term and log" 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
+
+else
+
+ logs.flush = ignore
+
+ writer = write_nl
+
+ newline = function()
+ write_nl("\n")
+ end
+
+ local f_one = formatters["%-15s | %s"]
+ local f_two = formatters["%-15s |"]
+
+ report = function(a,b,c,...)
+ if c then
+ write_nl(f_one(a,formatters[b](c,...)))
+ elseif b then
+ write_nl(f_one(a,b))
+ elseif a then
+ write_nl(f_two(a))
+ else
+ write_nl("")
+ end
+ end
+
+ local f_one = formatters["%-15s | %s | %s"]
+ local f_two = formatters["%-15s | %s |"]
+
+ subreport = function(a,sub,b,c,...)
+ if c then
+ write_nl(f_one(a,sub,formatters[b](c,...)))
+ elseif b then
+ write_nl(f_one(a,sub,b))
+ elseif a then
+ write_nl(f_two(a,sub))
+ else
+ write_nl("")
+ end
+ end
+
+ local f_one = formatters["%-15s : %s\n"]
+ local f_two = formatters["%-15s :\n"]
+
+ status = function(a,b,c,...) -- not to be used in lua anyway
+ if c then
+ write_nl(f_one(a,formatters[b](c,...)))
+ elseif b then
+ write_nl(f_one(a,b)) -- b can have %'s
+ elseif a then
+ write_nl(f_two(a))
+ else
+ write_nl("\n")
+ end
+ end
+
+ direct = ignore
+ subdirect = ignore
+
+ settarget = ignore
+ pushtarget = ignore
+ poptarget = ignore
+ setformats = ignore
+ settranslations = 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.direct = direct
+logs.subdirect = subdirect
+logs.writer = writer
+logs.newline = newline
+
+-- installer
+
+-- todo: renew (un) locks when a new one is added and wildcard
+
+local data, states = { }, nil
+
+function logs.reporter(category,subcategory)
+ local logger = data[category]
+ if not logger then
+ local state = false
+ if states == true then
+ state = true
+ elseif 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 not logger.state then
+ subreport(category,subcategory,...)
+ end
+ end
+ logger.reporters[subcategory] = reporter
+ else
+ local tag = category
+ reporter = function(...)
+ if 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)
+ if category == true 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
+ states = utilities.parsers.settings_to_hash(category)
+ for c, _ in next, states do
+ if data[c] 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 table.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(table.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)
+
+-- tex specific loggers (might move elsewhere)
+
+local report_pages = logs.reporter("pages") -- not needed but saves checking when we grep for it
+
+local real, user, sub
+
+function logs.start_page_number()
+ real, user, sub = texcount.realpageno, texcount.userpageno, texcount.subpageno
+-- real, user, sub = 0, 0, 0
+end
+
+local timing = false
+local starttime = nil
+local lasttime = nil
+
+trackers.register("pages.timing", function(v) -- only for myself (diagnostics)
+ starttime = os.clock()
+ timing = true
+end)
+
+function logs.stop_page_number() -- the first page can includes the initialization so we omit this in average
+ if timing then
+ local elapsed, average
+ local stoptime = os.clock()
+ if not lasttime or real < 2 then
+ elapsed = stoptime
+ average = stoptime
+ starttime = stoptime
+ else
+ elapsed = stoptime - lasttime
+ average = (stoptime - starttime) / (real - 1)
+ end
+ lasttime = stoptime
+ if real <= 0 then
+ report_pages("flushing page, time %0.04f / %0.04f",elapsed,average)
+ elseif user <= 0 then
+ report_pages("flushing realpage %s, time %0.04f / %0.04f",real,elapsed,average)
+ elseif sub <= 0 then
+ report_pages("flushing realpage %s, userpage %s, time %0.04f / %0.04f",real,user,elapsed,average)
+ else
+ report_pages("flushing realpage %s, userpage %s, subpage %s, time %0.04f / %0.04f",real,user,sub,elapsed,average)
+ end
+ else
+ if real <= 0 then
+ report_pages("flushing page")
+ elseif user <= 0 then
+ report_pages("flushing realpage %s",real)
+ elseif sub <= 0 then
+ report_pages("flushing realpage %s, userpage %s",real,user)
+ else
+ report_pages("flushing realpage %s, userpage %s, subpage %s",real,user,sub)
+ end
+ end
+ logs.flush()
+end
+
+-- we don't have show_open and show_close callbacks yet
+
+local report_files = logs.reporter("files")
+local nesting = 0
+local verbose = false
+local hasscheme = url.hasscheme
+
+function logs.show_open(name)
+ -- if hasscheme(name) ~= "virtual" then
+ -- if verbose then
+ -- nesting = nesting + 1
+ -- report_files("level %s, opening %s",nesting,name)
+ -- else
+ -- write(formatters["(%s"](name)) -- tex adds a space
+ -- end
+ -- end
+end
+
+function logs.show_close(name)
+ -- if hasscheme(name) ~= "virtual" then
+ -- if verbose then
+ -- report_files("level %s, closing %s",nesting,name)
+ -- nesting = nesting - 1
+ -- else
+ -- write(")") -- tex adds a space
+ -- end
+ -- end
+end
+
+function logs.show_load(name)
+ -- if hasscheme(name) ~= "virtual" then
+ -- if verbose then
+ -- report_files("level %s, loading %s",nesting+1,name)
+ -- else
+ -- write(formatters["(%s)"](name))
+ -- end
+ -- end
+end
+
+-- there may be scripts out there using this:
+
+local simple = logs.reporter("comment")
+
+logs.simple = simple
+logs.simpleline = simple
+
+-- obsolete
+
+function logs.setprogram () end -- obsolete
+function logs.extendbanner() end -- obsolete
+function logs.reportlines () end -- obsolete
+function logs.reportbanner() end -- obsolete
+function logs.reportline () end -- obsolete
+function logs.simplelines () end -- obsolete
+function logs.help () end -- obsolete
+
+-- applications
+
+-- local function reportlines(t,str)
+-- if str then
+-- for line in gmatch(str,"([^\n\r]*)[\n\r]") do
+-- t.report(line)
+-- end
+-- end
+-- end
+
+local Carg, C, lpegmatch = lpeg.Carg, lpeg.C, lpeg.match
+local p_newline = lpeg.patterns.newline
+
+local linewise = (
+ Carg(1) * C((1-p_newline)^1) / function(t,s) t.report(s) end
+ + Carg(1) * p_newline^2 / function(t) t.report() end
+ + p_newline
+)^1
+
+local function reportlines(t,str)
+ if str then
+ lpegmatch(linewise,str,1,t)
+ end
+end
+
+local function reportbanner(t)
+ local banner = t.banner
+ if banner then
+ t.report(banner)
+ t.report()
+ end
+end
+
+local function reportversion(t)
+ local banner = t.banner
+ if banner then
+ t.report(banner)
+ end
+end
+
+local function reporthelp(t,...)
+ local helpinfo = t.helpinfo
+ if type(helpinfo) == "string" then
+ reportlines(t,helpinfo)
+ elseif type(helpinfo) == "table" then
+ for i=1,select("#",...) do
+ reportlines(t,t.helpinfo[select(i,...)])
+ if i < n then
+ t.report()
+ end
+ end
+ end
+end
+
+local function reportinfo(t)
+ t.report()
+ reportlines(t,t.moreinfo)
+end
+
+local function reportexport(t,method)
+ report(t.helpinfo)
+end
+
+local reporters = {
+ lines = reportlines, -- not to be overloaded
+ banner = reportbanner,
+ version = reportversion,
+ help = reporthelp,
+ info = reportinfo,
+ export = reportexport,
+}
+
+local exporters = {
+ -- empty
+}
+
+logs.reporters = reporters
+logs.exporters = exporters
+
+function logs.application(t)
+ t.name = t.name or "unknown"
+ t.banner = t.banner
+ t.moreinfo = moreinfo
+ t.report = logs.reporter(t.name)
+ t.help = function(...)
+ reporters.banner(t)
+ reporters.help(t,...)
+ reporters.info(t)
+ end
+ t.export = function(...)
+ reporters.export(t,...)
+ end
+ t.identify = function()
+ reporters.banner(t)
+ end
+ t.version = function()
+ reporters.version(t)
+ end
+ return t
+end
+
+-- somewhat special .. will be redone (already a better solution in place in lmx)
+
+-- logging to a file
+
+-- local syslogname = "oeps.xxx"
+--
+-- for i=1,10 do
+-- logs.system(syslogname,"context","test","fonts","font %s recached due to newer version (%s)","blabla","123")
+-- end
+
+function logs.system(whereto,process,jobname,category,...)
+ local message = formatters["%s %s => %s => %s => %s\r"](os.date("%d/%m/%y %H:%m:%S"),process,jobname,category,format(...))
+ for i=1,10 do
+ local f = io.open(whereto,"a") -- we can consider keeping the file open
+ if f then
+ f:write(message)
+ f:close()
+ break
+ else
+ sleep(0.1)
+ end
+ end
+end
+
+local report_system = logs.reporter("system","logs")
+
+function logs.obsolete(old,new)
+ local o = loadstring("return " .. new)()
+ if type(o) == "function" then
+ return function(...)
+ report_system("function %a is obsolete, use %a",old,new)
+ loadstring(old .. "=" .. new .. " return ".. old)()(...)
+ end
+ elseif type(o) == "table" then
+ local t, m = { }, { }
+ m.__index = function(t,k)
+ report_system("table %a is obsolete, use %a",old,new)
+ m.__index, m.__newindex = o, o
+ return o[k]
+ end
+ m.__newindex = function(t,k,v)
+ report_system("table %a is obsolete, use %a",old,new)
+ m.__index, m.__newindex = o, o
+ o[k] = v
+ end
+ if libraries then
+ libraries.obsolete[old] = t -- true
+ end
+ setmetatable(t,m)
+ return t
+ end
+end
+
+if utilities then
+ utilities.report = report_system
+end
+
+if tex and tex.error then
+ function logs.texerrormessage(...) -- for the moment we put this function here
+ tex.error(format(...), { })
+ end
+else
+ function logs.texerrormessage(...)
+ print(format(...))
+ 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
diff --git a/tex/context/base/trac-pro.lua b/tex/context/base/trac-pro.lua
index 401fa9275..d6e0d0339 100644
--- a/tex/context/base/trac-pro.lua
+++ b/tex/context/base/trac-pro.lua
@@ -1,208 +1,208 @@
-if not modules then modules = { } end modules ['trac-pro'] = {
- 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 getmetatable, setmetatable, rawset, type = getmetatable, setmetatable, rawset, type
-
--- The protection implemented here is probably not that tight but good enough to catch
--- problems due to naive usage.
---
--- There's a more extensive version (trac-xxx.lua) that supports nesting.
---
--- This will change when we have _ENV in lua 5.2+
-
-local trace_namespaces = false trackers.register("system.namespaces", function(v) trace_namespaces = v end)
-
-local report_system = logs.reporter("system","protection")
-
-namespaces = namespaces or { }
-local namespaces = namespaces
-
-local registered = { }
-
-local function report_index(k,name)
- if trace_namespaces then
- report_system("reference to %a in protected namespace %a: %s",k,name,debug.traceback())
- else
- report_system("reference to %a in protected namespace %a",k,name)
- end
-end
-
-local function report_newindex(k,name)
- if trace_namespaces then
- report_system("assignment to %a in protected namespace %a: %s",k,name,debug.traceback())
- else
- report_system("assignment to %a in protected namespace %a",k,name)
- end
-end
-
-local function register(name)
- local data = name == "global" and _G or _G[name]
- if not data then
- return -- error
- end
- registered[name] = data
- local m = getmetatable(data)
- if not m then
- m = { }
- setmetatable(data,m)
- end
- local index, newindex = { }, { }
- m.__saved__index = m.__index
- m.__no__index = function(t,k)
- if not index[k] then
- index[k] = true
- report_index(k,name)
- end
- return nil
- end
- m.__saved__newindex = m.__newindex
- m.__no__newindex = function(t,k,v)
- if not newindex[k] then
- newindex[k] = true
- report_newindex(k,name)
- end
- rawset(t,k,v)
- end
- m.__protection__depth = 0
-end
-
-local function private(name) -- maybe save name
- local data = registered[name]
- if not data then
- data = _G[name]
- if not data then
- data = { }
- _G[name] = data
- end
- register(name)
- end
- return data
-end
-
-local function protect(name)
- local data = registered[name]
- if not data then
- return
- end
- local m = getmetatable(data)
- local pd = m.__protection__depth
- if pd > 0 then
- m.__protection__depth = pd + 1
- else
- m.__save_d_index, m.__saved__newindex = m.__index, m.__newindex
- m.__index, m.__newindex = m.__no__index, m.__no__newindex
- m.__protection__depth = 1
- end
-end
-
-local function unprotect(name)
- local data = registered[name]
- if not data then
- return
- end
- local m = getmetatable(data)
- local pd = m.__protection__depth
- if pd > 1 then
- m.__protection__depth = pd - 1
- else
- m.__index, m.__newindex = m.__saved__index, m.__saved__newindex
- m.__protection__depth = 0
- end
-end
-
-local function protectall()
- for name, _ in next, registered do
- if name ~= "global" then
- protect(name)
- end
- end
-end
-
-local function unprotectall()
- for name, _ in next, registered do
- if name ~= "global" then
- unprotect(name)
- end
- end
-end
-
-namespaces.register = register -- register when defined
-namespaces.private = private -- allocate and register if needed
-namespaces.protect = protect
-namespaces.unprotect = unprotect
-namespaces.protectall = protectall
-namespaces.unprotectall = unprotectall
-
-namespaces.private("namespaces") registered = { } register("global") -- unreachable
-
-directives.register("system.protect", function(v)
- if v then
- protectall()
- else
- unprotectall()
- end
-end)
-
-directives.register("system.checkglobals", function(v)
- if v then
- report_system("enabling global namespace guard")
- protect("global")
- else
- report_system("disabling global namespace guard")
- unprotect("global")
- end
-end)
-
--- dummy section (will go to luat-dum.lua)
-
---~ if not namespaces.private then
---~ -- somewhat protected
---~ local registered = { }
---~ function namespaces.private(name)
---~ local data = registered[name]
---~ if data then
---~ return data
---~ end
---~ local data = _G[name]
---~ if not data then
---~ data = { }
---~ _G[name] = data
---~ end
---~ registered[name] = data
---~ return data
---~ end
---~ function namespaces.protectall(list)
---~ for name, data in next, list or registered do
---~ setmetatable(data, { __newindex = function() print(string.format("table %s is protected",name)) end })
---~ end
---~ end
---~ namespaces.protectall { namespaces = namespaces }
---~ end
-
---~ directives.enable("system.checkglobals")
-
---~ namespaces.register("resolvers","trackers")
---~ namespaces.protect("resolvers")
---~ namespaces.protect("resolvers")
---~ namespaces.protect("resolvers")
---~ namespaces.unprotect("resolvers")
---~ namespaces.unprotect("resolvers")
---~ namespaces.unprotect("resolvers")
---~ namespaces.protect("trackers")
-
---~ resolvers.x = true
---~ resolvers.y = true
---~ trackers.a = ""
---~ resolvers.z = true
---~ oeps = { }
-
---~ resolvers = namespaces.private("resolvers")
---~ fonts = namespaces.private("fonts")
---~ directives.enable("system.protect")
---~ namespaces.protectall()
---~ resolvers.xx = { }
+if not modules then modules = { } end modules ['trac-pro'] = {
+ 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 getmetatable, setmetatable, rawset, type = getmetatable, setmetatable, rawset, type
+
+-- The protection implemented here is probably not that tight but good enough to catch
+-- problems due to naive usage.
+--
+-- There's a more extensive version (trac-xxx.lua) that supports nesting.
+--
+-- This will change when we have _ENV in lua 5.2+
+
+local trace_namespaces = false trackers.register("system.namespaces", function(v) trace_namespaces = v end)
+
+local report_system = logs.reporter("system","protection")
+
+namespaces = namespaces or { }
+local namespaces = namespaces
+
+local registered = { }
+
+local function report_index(k,name)
+ if trace_namespaces then
+ report_system("reference to %a in protected namespace %a: %s",k,name,debug.traceback())
+ else
+ report_system("reference to %a in protected namespace %a",k,name)
+ end
+end
+
+local function report_newindex(k,name)
+ if trace_namespaces then
+ report_system("assignment to %a in protected namespace %a: %s",k,name,debug.traceback())
+ else
+ report_system("assignment to %a in protected namespace %a",k,name)
+ end
+end
+
+local function register(name)
+ local data = name == "global" and _G or _G[name]
+ if not data then
+ return -- error
+ end
+ registered[name] = data
+ local m = getmetatable(data)
+ if not m then
+ m = { }
+ setmetatable(data,m)
+ end
+ local index, newindex = { }, { }
+ m.__saved__index = m.__index
+ m.__no__index = function(t,k)
+ if not index[k] then
+ index[k] = true
+ report_index(k,name)
+ end
+ return nil
+ end
+ m.__saved__newindex = m.__newindex
+ m.__no__newindex = function(t,k,v)
+ if not newindex[k] then
+ newindex[k] = true
+ report_newindex(k,name)
+ end
+ rawset(t,k,v)
+ end
+ m.__protection__depth = 0
+end
+
+local function private(name) -- maybe save name
+ local data = registered[name]
+ if not data then
+ data = _G[name]
+ if not data then
+ data = { }
+ _G[name] = data
+ end
+ register(name)
+ end
+ return data
+end
+
+local function protect(name)
+ local data = registered[name]
+ if not data then
+ return
+ end
+ local m = getmetatable(data)
+ local pd = m.__protection__depth
+ if pd > 0 then
+ m.__protection__depth = pd + 1
+ else
+ m.__save_d_index, m.__saved__newindex = m.__index, m.__newindex
+ m.__index, m.__newindex = m.__no__index, m.__no__newindex
+ m.__protection__depth = 1
+ end
+end
+
+local function unprotect(name)
+ local data = registered[name]
+ if not data then
+ return
+ end
+ local m = getmetatable(data)
+ local pd = m.__protection__depth
+ if pd > 1 then
+ m.__protection__depth = pd - 1
+ else
+ m.__index, m.__newindex = m.__saved__index, m.__saved__newindex
+ m.__protection__depth = 0
+ end
+end
+
+local function protectall()
+ for name, _ in next, registered do
+ if name ~= "global" then
+ protect(name)
+ end
+ end
+end
+
+local function unprotectall()
+ for name, _ in next, registered do
+ if name ~= "global" then
+ unprotect(name)
+ end
+ end
+end
+
+namespaces.register = register -- register when defined
+namespaces.private = private -- allocate and register if needed
+namespaces.protect = protect
+namespaces.unprotect = unprotect
+namespaces.protectall = protectall
+namespaces.unprotectall = unprotectall
+
+namespaces.private("namespaces") registered = { } register("global") -- unreachable
+
+directives.register("system.protect", function(v)
+ if v then
+ protectall()
+ else
+ unprotectall()
+ end
+end)
+
+directives.register("system.checkglobals", function(v)
+ if v then
+ report_system("enabling global namespace guard")
+ protect("global")
+ else
+ report_system("disabling global namespace guard")
+ unprotect("global")
+ end
+end)
+
+-- dummy section (will go to luat-dum.lua)
+
+--~ if not namespaces.private then
+--~ -- somewhat protected
+--~ local registered = { }
+--~ function namespaces.private(name)
+--~ local data = registered[name]
+--~ if data then
+--~ return data
+--~ end
+--~ local data = _G[name]
+--~ if not data then
+--~ data = { }
+--~ _G[name] = data
+--~ end
+--~ registered[name] = data
+--~ return data
+--~ end
+--~ function namespaces.protectall(list)
+--~ for name, data in next, list or registered do
+--~ setmetatable(data, { __newindex = function() print(string.format("table %s is protected",name)) end })
+--~ end
+--~ end
+--~ namespaces.protectall { namespaces = namespaces }
+--~ end
+
+--~ directives.enable("system.checkglobals")
+
+--~ namespaces.register("resolvers","trackers")
+--~ namespaces.protect("resolvers")
+--~ namespaces.protect("resolvers")
+--~ namespaces.protect("resolvers")
+--~ namespaces.unprotect("resolvers")
+--~ namespaces.unprotect("resolvers")
+--~ namespaces.unprotect("resolvers")
+--~ namespaces.protect("trackers")
+
+--~ resolvers.x = true
+--~ resolvers.y = true
+--~ trackers.a = ""
+--~ resolvers.z = true
+--~ oeps = { }
+
+--~ resolvers = namespaces.private("resolvers")
+--~ fonts = namespaces.private("fonts")
+--~ directives.enable("system.protect")
+--~ namespaces.protectall()
+--~ resolvers.xx = { }
diff --git a/tex/context/base/trac-set.lua b/tex/context/base/trac-set.lua
index 5ab189f55..95fdc43b3 100644
--- a/tex/context/base/trac-set.lua
+++ b/tex/context/base/trac-set.lua
@@ -1,379 +1,379 @@
-if not modules then modules = { } end modules ['trac-set'] = { -- might become util-set.lua
- 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"
-}
-
--- maybe this should be util-set.lua
-
-local type, next, tostring = type, next, tostring
-local concat = table.concat
-local format, find, lower, gsub, topattern = string.format, string.find, string.lower, string.gsub, string.topattern
-local is_boolean = string.is_boolean
-local settings_to_hash = utilities.parsers.settings_to_hash
-local allocate = utilities.storage.allocate
-
-utilities = utilities or { }
-local utilities = utilities
-
-local setters = utilities.setters or { }
-utilities.setters = setters
-
-local data = { }
-
--- We can initialize from the cnf file. This is sort of tricky as
--- later defined setters also need to be initialized then. If set
--- this way, we need to ensure that they are not reset later on.
-
-local trace_initialize = false -- only for testing during development
-
-function setters.initialize(filename,name,values) -- filename only for diagnostics
- local setter = data[name]
- if setter then
- frozen = true -- don't permitoverload
- -- trace_initialize = true
- local data = setter.data
- if data then
- for key, newvalue in next, values do
- local newvalue = is_boolean(newvalue,newvalue)
- local functions = data[key]
- if functions then
- local oldvalue = functions.value
- if functions.frozen then
- if trace_initialize then
- setter.report("%s: %a is %s to %a",filename,key,"frozen",oldvalue)
- end
- elseif #functions > 0 and not oldvalue then
--- elseif #functions > 0 and oldvalue == nil then
- if trace_initialize then
- setter.report("%s: %a is %s to %a",filename,key,"set",newvalue)
- end
- for i=1,#functions do
- functions[i](newvalue)
- end
- functions.value = newvalue
- functions.frozen = functions.frozen or frozen
- else
- if trace_initialize then
- setter.report("%s: %a is %s as %a",filename,key,"kept",oldvalue)
- end
- end
- else
- -- we do a simple preregistration i.e. not in the
- -- list as it might be an obsolete entry
- functions = { default = newvalue, frozen = frozen }
- data[key] = functions
- if trace_initialize then
- setter.report("%s: %a is %s to %a",filename,key,"defaulted",newvalue)
- end
- end
- end
- return true
- end
- end
-end
-
--- user interface code
-
-local function set(t,what,newvalue)
- local data = t.data
- if not data.frozen then
- local done = t.done
- if type(what) == "string" then
- what = settings_to_hash(what) -- inefficient but ok
- end
- if type(what) ~= "table" then
- return
- end
- if not done then -- catch ... why not set?
- done = { }
- t.done = done
- end
- for w, value in next, what do
- if value == "" then
- value = newvalue
- elseif not value then
- value = false -- catch nil
- else
- value = is_boolean(value,value)
- end
- w = topattern(w,true,true)
- for name, functions in next, data do
- if done[name] then
- -- prevent recursion due to wildcards
- elseif find(name,w) then
- done[name] = true
- for i=1,#functions do
- functions[i](value)
- end
- functions.value = value
- end
- end
- end
- end
-end
-
-local function reset(t)
- local data = t.data
- if not data.frozen then
- for name, functions in next, data do
- for i=1,#functions do
- functions[i](false)
- end
- functions.value = false
- end
- end
-end
-
-local function enable(t,what)
- set(t,what,true)
-end
-
-local function disable(t,what)
- local data = t.data
- if not what or what == "" then
- t.done = { }
- reset(t)
- else
- set(t,what,false)
- end
-end
-
-function setters.register(t,what,...)
- local data = t.data
- what = lower(what)
- local functions = data[what]
- if not functions then
- functions = { }
- data[what] = functions
- if trace_initialize then
- t.report("defining %a",what)
- end
- end
- local default = functions.default -- can be set from cnf file
- for i=1,select("#",...) do
- local fnc = select(i,...)
- local typ = type(fnc)
- if typ == "string" then
- if trace_initialize then
- t.report("coupling %a to %a",what,fnc)
- end
- local s = fnc -- else wrong reference
- fnc = function(value) set(t,s,value) end
- elseif typ ~= "function" then
- fnc = nil
- end
- if fnc then
- functions[#functions+1] = fnc
- -- default: set at command line or in cnf file
- -- value : set in tex run (needed when loading runtime)
- local value = functions.value or default
- if value ~= nil then
- fnc(value)
- functions.value = value
- end
- end
- end
- return false -- so we can use it in an assignment
-end
-
-function setters.enable(t,what)
- local e = t.enable
- t.enable, t.done = enable, { }
- enable(t,what)
- t.enable, t.done = e, { }
-end
-
-function setters.disable(t,what)
- local e = t.disable
- t.disable, t.done = disable, { }
- disable(t,what)
- t.disable, t.done = e, { }
-end
-
-function setters.reset(t)
- t.done = { }
- reset(t)
-end
-
-function setters.list(t) -- pattern
- local list = table.sortedkeys(t.data)
- local user, system = { }, { }
- for l=1,#list do
- local what = list[l]
- if find(what,"^%*") then
- system[#system+1] = what
- else
- user[#user+1] = what
- end
- end
- return user, system
-end
-
-function setters.show(t)
- local category = t.name
- local list = setters.list(t)
- t.report()
- for k=1,#list do
- local name = list[k]
- local functions = t.data[name]
- if functions then
- local value, default, modules = functions.value, functions.default, #functions
- value = value == nil and "unset" or tostring(value)
- default = default == nil and "unset" or tostring(default)
- t.report("%-50s modules: %2i default: %-12s value: %-12s",name,modules,default,value)
- end
- end
- t.report()
-end
-
--- we could have used a bit of oo and the trackers:enable syntax but
--- there is already a lot of code around using the singular tracker
-
--- we could make this into a module but we also want the rest avaliable
-
-local enable, disable, register, list, show = setters.enable, setters.disable, setters.register, setters.list, setters.show
-
-function setters.report(setter,...)
- print(format("%-15s : %s\n",setter.name,format(...)))
-end
-
-local function default(setter,name)
- local d = setter.data[name]
- return d and d.default
-end
-
-local function value(setter,name)
- local d = setter.data[name]
- return d and (d.value or d.default)
-end
-
-function setters.new(name) -- we could use foo:bar syntax (but not used that often)
- local setter -- we need to access it in setter itself
- setter = {
- data = allocate(), -- indexed, but also default and value fields
- name = name,
- report = function(...) setters.report (setter,...) end,
- enable = function(...) enable (setter,...) end,
- disable = function(...) disable (setter,...) end,
- register = function(...) register(setter,...) end,
- list = function(...) list (setter,...) end,
- show = function(...) show (setter,...) end,
- default = function(...) return default (setter,...) end,
- value = function(...) return value (setter,...) end,
- }
- data[name] = setter
- return setter
-end
-
-trackers = setters.new("trackers")
-directives = setters.new("directives")
-experiments = setters.new("experiments")
-
-local t_enable, t_disable = trackers .enable, trackers .disable
-local d_enable, d_disable = directives .enable, directives .disable
-local e_enable, e_disable = experiments.enable, experiments.disable
-
--- nice trick: we overload two of the directives related functions with variants that
--- do tracing (itself using a tracker) .. proof of concept
-
-local trace_directives = false local trace_directives = false trackers.register("system.directives", function(v) trace_directives = v end)
-local trace_experiments = false local trace_experiments = false trackers.register("system.experiments", function(v) trace_experiments = v end)
-
-function directives.enable(...)
- if trace_directives then
- directives.report("enabling: % t",{...})
- end
- d_enable(...)
-end
-
-function directives.disable(...)
- if trace_directives then
- directives.report("disabling: % t",{...})
- end
- d_disable(...)
-end
-
-function experiments.enable(...)
- if trace_experiments then
- experiments.report("enabling: % t",{...})
- end
- e_enable(...)
-end
-
-function experiments.disable(...)
- if trace_experiments then
- experiments.report("disabling: % t",{...})
- end
- e_disable(...)
-end
-
--- a useful example
-
-directives.register("system.nostatistics", function(v)
- if statistics then
- statistics.enable = not v
- else
- -- forget about it
- end
-end)
-
-directives.register("system.nolibraries", function(v)
- if libraries then
- libraries = nil -- we discard this tracing for security
- else
- -- no libraries defined
- end
-end)
-
--- experiment
-
-if environment then
-
- -- The engineflags are known earlier than environment.arguments but maybe we
- -- need to handle them both as the later are parsed differently. The c: prefix
- -- is used by mtx-context to isolate the flags from those that concern luatex.
-
- local engineflags = environment.engineflags
-
- if engineflags then
- local list = engineflags["c:trackers"] or engineflags["trackers"]
- if type(list) == "string" then
- setters.initialize("commandline flags","trackers",settings_to_hash(list))
- -- t_enable(list)
- end
- local list = engineflags["c:directives"] or engineflags["directives"]
- if type(list) == "string" then
- setters.initialize("commandline flags","directives", settings_to_hash(list))
- -- d_enable(list)
- end
- end
-
-end
-
--- here
-
-if texconfig then
-
- -- this happens too late in ini mode but that is no problem
-
- local function set(k,v)
- v = tonumber(v)
- if v then
- texconfig[k] = v
- end
- end
-
- directives.register("luatex.expanddepth", function(v) set("expand_depth",v) end)
- directives.register("luatex.hashextra", function(v) set("hash_extra",v) end)
- directives.register("luatex.nestsize", function(v) set("nest_size",v) end)
- directives.register("luatex.maxinopen", function(v) set("max_in_open",v) end)
- directives.register("luatex.maxprintline", function(v) set("max_print_line",v) end)
- directives.register("luatex.maxstrings", function(v) set("max_strings",v) end)
- directives.register("luatex.paramsize", function(v) set("param_size",v) end)
- directives.register("luatex.savesize", function(v) set("save_size",v) end)
- directives.register("luatex.stacksize", function(v) set("stack_size",v) end)
-
-end
+if not modules then modules = { } end modules ['trac-set'] = { -- might become util-set.lua
+ 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"
+}
+
+-- maybe this should be util-set.lua
+
+local type, next, tostring = type, next, tostring
+local concat = table.concat
+local format, find, lower, gsub, topattern = string.format, string.find, string.lower, string.gsub, string.topattern
+local is_boolean = string.is_boolean
+local settings_to_hash = utilities.parsers.settings_to_hash
+local allocate = utilities.storage.allocate
+
+utilities = utilities or { }
+local utilities = utilities
+
+local setters = utilities.setters or { }
+utilities.setters = setters
+
+local data = { }
+
+-- We can initialize from the cnf file. This is sort of tricky as
+-- later defined setters also need to be initialized then. If set
+-- this way, we need to ensure that they are not reset later on.
+
+local trace_initialize = false -- only for testing during development
+
+function setters.initialize(filename,name,values) -- filename only for diagnostics
+ local setter = data[name]
+ if setter then
+ frozen = true -- don't permitoverload
+ -- trace_initialize = true
+ local data = setter.data
+ if data then
+ for key, newvalue in next, values do
+ local newvalue = is_boolean(newvalue,newvalue)
+ local functions = data[key]
+ if functions then
+ local oldvalue = functions.value
+ if functions.frozen then
+ if trace_initialize then
+ setter.report("%s: %a is %s to %a",filename,key,"frozen",oldvalue)
+ end
+ elseif #functions > 0 and not oldvalue then
+-- elseif #functions > 0 and oldvalue == nil then
+ if trace_initialize then
+ setter.report("%s: %a is %s to %a",filename,key,"set",newvalue)
+ end
+ for i=1,#functions do
+ functions[i](newvalue)
+ end
+ functions.value = newvalue
+ functions.frozen = functions.frozen or frozen
+ else
+ if trace_initialize then
+ setter.report("%s: %a is %s as %a",filename,key,"kept",oldvalue)
+ end
+ end
+ else
+ -- we do a simple preregistration i.e. not in the
+ -- list as it might be an obsolete entry
+ functions = { default = newvalue, frozen = frozen }
+ data[key] = functions
+ if trace_initialize then
+ setter.report("%s: %a is %s to %a",filename,key,"defaulted",newvalue)
+ end
+ end
+ end
+ return true
+ end
+ end
+end
+
+-- user interface code
+
+local function set(t,what,newvalue)
+ local data = t.data
+ if not data.frozen then
+ local done = t.done
+ if type(what) == "string" then
+ what = settings_to_hash(what) -- inefficient but ok
+ end
+ if type(what) ~= "table" then
+ return
+ end
+ if not done then -- catch ... why not set?
+ done = { }
+ t.done = done
+ end
+ for w, value in next, what do
+ if value == "" then
+ value = newvalue
+ elseif not value then
+ value = false -- catch nil
+ else
+ value = is_boolean(value,value)
+ end
+ w = topattern(w,true,true)
+ for name, functions in next, data do
+ if done[name] then
+ -- prevent recursion due to wildcards
+ elseif find(name,w) then
+ done[name] = true
+ for i=1,#functions do
+ functions[i](value)
+ end
+ functions.value = value
+ end
+ end
+ end
+ end
+end
+
+local function reset(t)
+ local data = t.data
+ if not data.frozen then
+ for name, functions in next, data do
+ for i=1,#functions do
+ functions[i](false)
+ end
+ functions.value = false
+ end
+ end
+end
+
+local function enable(t,what)
+ set(t,what,true)
+end
+
+local function disable(t,what)
+ local data = t.data
+ if not what or what == "" then
+ t.done = { }
+ reset(t)
+ else
+ set(t,what,false)
+ end
+end
+
+function setters.register(t,what,...)
+ local data = t.data
+ what = lower(what)
+ local functions = data[what]
+ if not functions then
+ functions = { }
+ data[what] = functions
+ if trace_initialize then
+ t.report("defining %a",what)
+ end
+ end
+ local default = functions.default -- can be set from cnf file
+ for i=1,select("#",...) do
+ local fnc = select(i,...)
+ local typ = type(fnc)
+ if typ == "string" then
+ if trace_initialize then
+ t.report("coupling %a to %a",what,fnc)
+ end
+ local s = fnc -- else wrong reference
+ fnc = function(value) set(t,s,value) end
+ elseif typ ~= "function" then
+ fnc = nil
+ end
+ if fnc then
+ functions[#functions+1] = fnc
+ -- default: set at command line or in cnf file
+ -- value : set in tex run (needed when loading runtime)
+ local value = functions.value or default
+ if value ~= nil then
+ fnc(value)
+ functions.value = value
+ end
+ end
+ end
+ return false -- so we can use it in an assignment
+end
+
+function setters.enable(t,what)
+ local e = t.enable
+ t.enable, t.done = enable, { }
+ enable(t,what)
+ t.enable, t.done = e, { }
+end
+
+function setters.disable(t,what)
+ local e = t.disable
+ t.disable, t.done = disable, { }
+ disable(t,what)
+ t.disable, t.done = e, { }
+end
+
+function setters.reset(t)
+ t.done = { }
+ reset(t)
+end
+
+function setters.list(t) -- pattern
+ local list = table.sortedkeys(t.data)
+ local user, system = { }, { }
+ for l=1,#list do
+ local what = list[l]
+ if find(what,"^%*") then
+ system[#system+1] = what
+ else
+ user[#user+1] = what
+ end
+ end
+ return user, system
+end
+
+function setters.show(t)
+ local category = t.name
+ local list = setters.list(t)
+ t.report()
+ for k=1,#list do
+ local name = list[k]
+ local functions = t.data[name]
+ if functions then
+ local value, default, modules = functions.value, functions.default, #functions
+ value = value == nil and "unset" or tostring(value)
+ default = default == nil and "unset" or tostring(default)
+ t.report("%-50s modules: %2i default: %-12s value: %-12s",name,modules,default,value)
+ end
+ end
+ t.report()
+end
+
+-- we could have used a bit of oo and the trackers:enable syntax but
+-- there is already a lot of code around using the singular tracker
+
+-- we could make this into a module but we also want the rest avaliable
+
+local enable, disable, register, list, show = setters.enable, setters.disable, setters.register, setters.list, setters.show
+
+function setters.report(setter,...)
+ print(format("%-15s : %s\n",setter.name,format(...)))
+end
+
+local function default(setter,name)
+ local d = setter.data[name]
+ return d and d.default
+end
+
+local function value(setter,name)
+ local d = setter.data[name]
+ return d and (d.value or d.default)
+end
+
+function setters.new(name) -- we could use foo:bar syntax (but not used that often)
+ local setter -- we need to access it in setter itself
+ setter = {
+ data = allocate(), -- indexed, but also default and value fields
+ name = name,
+ report = function(...) setters.report (setter,...) end,
+ enable = function(...) enable (setter,...) end,
+ disable = function(...) disable (setter,...) end,
+ register = function(...) register(setter,...) end,
+ list = function(...) list (setter,...) end,
+ show = function(...) show (setter,...) end,
+ default = function(...) return default (setter,...) end,
+ value = function(...) return value (setter,...) end,
+ }
+ data[name] = setter
+ return setter
+end
+
+trackers = setters.new("trackers")
+directives = setters.new("directives")
+experiments = setters.new("experiments")
+
+local t_enable, t_disable = trackers .enable, trackers .disable
+local d_enable, d_disable = directives .enable, directives .disable
+local e_enable, e_disable = experiments.enable, experiments.disable
+
+-- nice trick: we overload two of the directives related functions with variants that
+-- do tracing (itself using a tracker) .. proof of concept
+
+local trace_directives = false local trace_directives = false trackers.register("system.directives", function(v) trace_directives = v end)
+local trace_experiments = false local trace_experiments = false trackers.register("system.experiments", function(v) trace_experiments = v end)
+
+function directives.enable(...)
+ if trace_directives then
+ directives.report("enabling: % t",{...})
+ end
+ d_enable(...)
+end
+
+function directives.disable(...)
+ if trace_directives then
+ directives.report("disabling: % t",{...})
+ end
+ d_disable(...)
+end
+
+function experiments.enable(...)
+ if trace_experiments then
+ experiments.report("enabling: % t",{...})
+ end
+ e_enable(...)
+end
+
+function experiments.disable(...)
+ if trace_experiments then
+ experiments.report("disabling: % t",{...})
+ end
+ e_disable(...)
+end
+
+-- a useful example
+
+directives.register("system.nostatistics", function(v)
+ if statistics then
+ statistics.enable = not v
+ else
+ -- forget about it
+ end
+end)
+
+directives.register("system.nolibraries", function(v)
+ if libraries then
+ libraries = nil -- we discard this tracing for security
+ else
+ -- no libraries defined
+ end
+end)
+
+-- experiment
+
+if environment then
+
+ -- The engineflags are known earlier than environment.arguments but maybe we
+ -- need to handle them both as the later are parsed differently. The c: prefix
+ -- is used by mtx-context to isolate the flags from those that concern luatex.
+
+ local engineflags = environment.engineflags
+
+ if engineflags then
+ local list = engineflags["c:trackers"] or engineflags["trackers"]
+ if type(list) == "string" then
+ setters.initialize("commandline flags","trackers",settings_to_hash(list))
+ -- t_enable(list)
+ end
+ local list = engineflags["c:directives"] or engineflags["directives"]
+ if type(list) == "string" then
+ setters.initialize("commandline flags","directives", settings_to_hash(list))
+ -- d_enable(list)
+ end
+ end
+
+end
+
+-- here
+
+if texconfig then
+
+ -- this happens too late in ini mode but that is no problem
+
+ local function set(k,v)
+ v = tonumber(v)
+ if v then
+ texconfig[k] = v
+ end
+ end
+
+ directives.register("luatex.expanddepth", function(v) set("expand_depth",v) end)
+ directives.register("luatex.hashextra", function(v) set("hash_extra",v) end)
+ directives.register("luatex.nestsize", function(v) set("nest_size",v) end)
+ directives.register("luatex.maxinopen", function(v) set("max_in_open",v) end)
+ directives.register("luatex.maxprintline", function(v) set("max_print_line",v) end)
+ directives.register("luatex.maxstrings", function(v) set("max_strings",v) end)
+ directives.register("luatex.paramsize", function(v) set("param_size",v) end)
+ directives.register("luatex.savesize", function(v) set("save_size",v) end)
+ directives.register("luatex.stacksize", function(v) set("stack_size",v) end)
+
+end
diff --git a/tex/context/base/trac-tex.lua b/tex/context/base/trac-tex.lua
index aecf1799b..7e3406073 100644
--- a/tex/context/base/trac-tex.lua
+++ b/tex/context/base/trac-tex.lua
@@ -1,75 +1,75 @@
-if not modules then modules = { } end modules ['trac-tex'] = {
- 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"
-}
-
--- moved from trac-deb.lua
-
-local format = string.format
-
-local texhashtokens = tex.hashtokens
-
-local trackers = trackers
-
-local saved = { }
-
-function trackers.savehash()
- saved = texhashtokens()
-end
-
-function trackers.dumphashtofile(filename,delta)
- local list, hash, command_name = { }, texhashtokens(), token.command_name
- for name, token in next, hash do
- if not delta or not saved[name] then
- -- token: cmd, chr, csid -- combination cmd,chr determines name
- local category = command_name(token)
- local dk = list[category]
- if not dk then
- -- a bit funny names but this sorts better (easier to study)
- dk = { names = { }, found = 0, code = token[1] }
- list[category] = dk
- end
- dk.names[name] = { token[2], token[3] }
- dk.found = dk.found + 1
- end
- end
- io.savedata(filename or tex.jobname .. "-hash.log",table.serialize(list,true))
-end
-
-local delta = nil
-
-local function dump_hash(wanteddelta)
- if delta == nil then
- saved = saved or texhashtokens() -- no need for trackers.dump_hash
- luatex.registerstopactions(1,function() dump_hash(nil,wanteddelta) end) -- at front
- end
- delta = wanteddelta
-end
-
-directives.register("system.dumphash", function() dump_hash(false) end)
-directives.register("system.dumpdelta", function() dump_hash(true ) end)
-
-local report_dump = logs.reporter("resolvers","dump")
-
-local function saveusedfilesintrees(format)
- local data = {
- jobname = environment.jobname or "?",
- version = environment.version or "?",
- kind = environment.kind or "?",
- files = resolvers.instance.foundintrees
- }
- local filename = file.replacesuffix(environment.jobname or "context-job",'jlg')
- if format == "lua" then
- io.savedata(filename,table.serialize(data,true))
- else
- io.savedata(filename,table.toxml(data,"job"))
- end
-end
-
-directives.register("system.dumpfiles", function(v)
- luatex.registerstopactions(function() saveusedfilesintrees(v) end)
-end)
-
+if not modules then modules = { } end modules ['trac-tex'] = {
+ 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"
+}
+
+-- moved from trac-deb.lua
+
+local format = string.format
+
+local texhashtokens = tex.hashtokens
+
+local trackers = trackers
+
+local saved = { }
+
+function trackers.savehash()
+ saved = texhashtokens()
+end
+
+function trackers.dumphashtofile(filename,delta)
+ local list, hash, command_name = { }, texhashtokens(), token.command_name
+ for name, token in next, hash do
+ if not delta or not saved[name] then
+ -- token: cmd, chr, csid -- combination cmd,chr determines name
+ local category = command_name(token)
+ local dk = list[category]
+ if not dk then
+ -- a bit funny names but this sorts better (easier to study)
+ dk = { names = { }, found = 0, code = token[1] }
+ list[category] = dk
+ end
+ dk.names[name] = { token[2], token[3] }
+ dk.found = dk.found + 1
+ end
+ end
+ io.savedata(filename or tex.jobname .. "-hash.log",table.serialize(list,true))
+end
+
+local delta = nil
+
+local function dump_hash(wanteddelta)
+ if delta == nil then
+ saved = saved or texhashtokens() -- no need for trackers.dump_hash
+ luatex.registerstopactions(1,function() dump_hash(nil,wanteddelta) end) -- at front
+ end
+ delta = wanteddelta
+end
+
+directives.register("system.dumphash", function() dump_hash(false) end)
+directives.register("system.dumpdelta", function() dump_hash(true ) end)
+
+local report_dump = logs.reporter("resolvers","dump")
+
+local function saveusedfilesintrees(format)
+ local data = {
+ jobname = environment.jobname or "?",
+ version = environment.version or "?",
+ kind = environment.kind or "?",
+ files = resolvers.instance.foundintrees
+ }
+ local filename = file.replacesuffix(environment.jobname or "context-job",'jlg')
+ if format == "lua" then
+ io.savedata(filename,table.serialize(data,true))
+ else
+ io.savedata(filename,table.toxml(data,"job"))
+ end
+end
+
+directives.register("system.dumpfiles", function(v)
+ luatex.registerstopactions(function() saveusedfilesintrees(v) end)
+end)
+
diff --git a/tex/context/base/trac-tim.lua b/tex/context/base/trac-tim.lua
index e62e7e149..15ac9bf1b 100644
--- a/tex/context/base/trac-tim.lua
+++ b/tex/context/base/trac-tim.lua
@@ -1,138 +1,138 @@
-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.parameters = nodes and nodes.snapshots.getparameters
-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 parameters = progress.parameters()
-
-local function convert(name)
- name = name ~= "" and name or progress.defaultfilename
- if not processed[name] then
- local names, top, bot, pages, paths, keys = { }, { }, { }, 0, { }, { }
- local data = table.load(name .. ".lut")
- if data then
- pages = #data
- if pages > 1 then
- local factor = 100
- for k=1,#data do
- for k, v in next, data[k].node_memory do
- keys[k] = true
- end
- end
- for k=1,#data do
- local m = data[k].node_memory
- for k, v in next, keys do
- if not m[k] then m[k] = 0 end
- end
- end
- local function path(tag,subtag)
- local b, t, s = nil, nil, { }
- for k=1,#data do
- local v = data[k][tag]
- v = v and (subtag and v[subtag]) or v
- if v then
- v = tonumber(v)
- if b then
- if v > t then t = v end
- if v < b then b = v end
- else
- t = v
- b = v
- end
- s[k] = v
- else
- s[k] = 0
- end
- end
- local tagname = subtag or tag
- 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("(%s,%s)",k,(s[k]-b)*delta)
- end
- paths[tagname] = concat(s,"--")
- end
- for i=1,#parameters do
- path(parameters[i])
- end
- for tag, _ in next, keys do
- path("node_memory",tag)
- names[#names+1] = tag
- end
- pages = pages - 1
- end
- end
- sort(names)
- processed[name] = {
- names = names,
- top = top,
- bot = bot,
- pages = pages,
- paths = paths,
- }
- end
- return processed[name]
-end
-
-progress.convert = convert
-
-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.nodes(name)
- return convert(name).names or { }
-end
-
+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.parameters = nodes and nodes.snapshots.getparameters
+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 parameters = progress.parameters()
+
+local function convert(name)
+ name = name ~= "" and name or progress.defaultfilename
+ if not processed[name] then
+ local names, top, bot, pages, paths, keys = { }, { }, { }, 0, { }, { }
+ local data = table.load(name .. ".lut")
+ if data then
+ pages = #data
+ if pages > 1 then
+ local factor = 100
+ for k=1,#data do
+ for k, v in next, data[k].node_memory do
+ keys[k] = true
+ end
+ end
+ for k=1,#data do
+ local m = data[k].node_memory
+ for k, v in next, keys do
+ if not m[k] then m[k] = 0 end
+ end
+ end
+ local function path(tag,subtag)
+ local b, t, s = nil, nil, { }
+ for k=1,#data do
+ local v = data[k][tag]
+ v = v and (subtag and v[subtag]) or v
+ if v then
+ v = tonumber(v)
+ if b then
+ if v > t then t = v end
+ if v < b then b = v end
+ else
+ t = v
+ b = v
+ end
+ s[k] = v
+ else
+ s[k] = 0
+ end
+ end
+ local tagname = subtag or tag
+ 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("(%s,%s)",k,(s[k]-b)*delta)
+ end
+ paths[tagname] = concat(s,"--")
+ end
+ for i=1,#parameters do
+ path(parameters[i])
+ end
+ for tag, _ in next, keys do
+ path("node_memory",tag)
+ names[#names+1] = tag
+ end
+ pages = pages - 1
+ end
+ end
+ sort(names)
+ processed[name] = {
+ names = names,
+ top = top,
+ bot = bot,
+ pages = pages,
+ paths = paths,
+ }
+ end
+ return processed[name]
+end
+
+progress.convert = convert
+
+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.nodes(name)
+ return convert(name).names or { }
+end
+
diff --git a/tex/context/base/trac-vis.lua b/tex/context/base/trac-vis.lua
index 3dc7aa9d2..df4909c3e 100644
--- a/tex/context/base/trac-vis.lua
+++ b/tex/context/base/trac-vis.lua
@@ -1,926 +1,926 @@
-if not modules then modules = { } end modules ['trac-vis'] = {
- version = 1.001,
- 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 string, number, table = string, number, table
-local node, nodes, attributes, fonts, tex = node, nodes, attributes, fonts, tex
-local type = type
-local format = string.format
-local formatters = string.formatters
-
--- 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.
-
--- We could use pdf literals and re stream codes but it's not worth the
--- trouble because we would end up in color etc mess. Maybe one day I'll
--- make a nodeinjection variant.
-
--- todo: global switch (so no attributes)
--- todo: maybe also xoffset, yoffset of glyph
--- todo: inline concat (more efficient)
-
-local nodecodes = nodes.nodecodes
-local disc_code = nodecodes.disc
-local kern_code = nodecodes.kern
-local glyph_code = nodecodes.glyph
-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 whatsit_code = nodecodes.whatsit
-local user_code = nodecodes.user
-local gluespec_code = nodecodes.gluespec
-
-local kerncodes = nodes.kerncodes
-local font_kern_code = kerncodes.fontkern
-local user_kern_code = kerncodes.userkern
-
-local gluecodes = nodes.gluecodes
-local cleaders_code = gluecodes.cleaders
-local userskip_code = gluecodes.userskip
-local space_code = gluecodes.space
-local xspace_code = gluecodes.xspace
-local leftskip_code = gluecodes.leftskip
-local rightskip_code = gluecodes.rightskip
-
-local whatsitcodes = nodes.whatsitcodes
-
-local concat_nodes = nodes.concat
-local hpack_nodes = node.hpack
-local vpack_nodes = node.vpack
-local hpack_string = typesetters.hpack
-local fast_hpack_string = typesetters.fast_hpack
-local copy_node = node.copy
-local copy_list = node.copy_list
-local free_node = node.free
-local free_node_list = node.flush_list
-local insert_node_before = node.insert_before
-local insert_node_after = node.insert_after
-local fast_hpack = nodes.fasthpack
-local traverse_nodes = node.traverse
-
-local tex_attribute = tex.attribute
-local tex_box = tex.box
-local unsetvalue = attributes.unsetvalue
-
-local current_font = font.current
-
-local exheights = fonts.hashes.exheights
-local emwidths = fonts.hashes.emwidths
-local pt_factor = number.dimenfactors.pt
-
-local nodepool = nodes.pool
-local new_rule = nodepool.rule
-local new_kern = nodepool.kern
-local new_glue = nodepool.glue
-local new_penalty = nodepool.penalty
-
-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_fontkern = attributes.private("fontkern")
-local a_layer = attributes.private("viewerlayer")
-
-local hasbit = number.hasbit
-local bit = number.bit
-local setbit = number.setbit
-local clearbit = number.clearbit
-
-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 report_visualize = logs.reporter("visualize")
-
-local modes = {
- hbox = 1,
- vbox = 2,
- vtop = 4,
- kern = 8,
- glue = 16,
- penalty = 32,
- fontkern = 64,
- strut = 128,
- whatsit = 256,
- glyph = 512,
- simple = 1024,
- simplehbox = 1024 + 1,
- simplevbox = 1024 + 2,
- simplevtop = 1024 + 4,
- user = 2048,
-}
-
-local modes_makeup = { "hbox", "vbox", "kern", "glue", "penalty" }
-local modes_boxes = { "hbox", "vbox" }
-local modes_all = { "hbox", "vbox", "kern", "glue", "penalty", "fontkern", "whatsit", "glyph", "user" }
-
-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
-
-local enabled = false
-local layers = { }
-
-local preset_boxes = modes.hbox + modes.vbox
-local preset_makeup = preset_boxes + modes.kern + modes.glue + modes.penalty
-local preset_all = preset_makeup + modes.fontkern + modes.whatsit + modes.glyph + modes.user
-
-function visualizers.setfont(id)
- usedfont = id or current_font()
- exheight = exheights[usedfont]
- emwidth = emwidths[usedfont]
-end
-
--- we can preset a bunch of bits
-
-local function enable()
- if not usedfont then
- -- we use a narrow monospaced font
- 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
- nodes.tasks.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) -- this will become more efficient when we have the bit lib linked in
- if not n or n == "reset" then
- return unsetvalue
- elseif n == "makeup" then
- if not a or a == 0 or a == unsetvalue then
- a = preset_makeup
- else
- a = setbit(a,preset_makeup)
- -- for i=1,#modes_makeup do
- -- a = setvisual(modes_makeup[i],a)
- -- end
- end
- elseif n == "boxes" then
- if not a or a == 0 or a == unsetvalue then
- a = preset_boxes
- else
- a = setbit(a,preset_boxes)
- -- for i=1,#modes_boxes do
- -- a = setvisual(modes_boxes[i],a)
- -- end
- 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 = setbit(a,preset_all)
- -- for i=1,#modes_all do
- -- a = setvisual(modes_all[i],a)
- -- end
- end
- else
- local m = modes[n]
- if not m then
- -- go on
- elseif a == unsetvalue then
- if what == false then
- return unsetvalue
- else
- -- a = setbit(0,m)
- a = m
- end
- elseif what == false then
- a = clearbit(a,m)
- elseif not a or a == 0 then
- a = m
- else
- a = setbit(a,m)
- 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 visualizers.setvisual(n)
- tex_attribute[a_visual] = setvisual(n,tex_attribute[a_visual])
-end
-
-function visualizers.setlayer(n)
- tex_attribute[a_layer] = layers[n] or unsetvalue
-end
-
-commands.setvisual = visualizers.setvisual
-commands.setlayer = visualizers.setlayer
-
-function commands.visual(n)
- context(setvisual(n))
-end
-
-local function set(mode,v)
- tex_attribute[a_visual] = setvisual(mode,tex_attribute[a_visual],v)
-end
-
-for mode, value in next, modes do
- trackers.register(formatters["visualizers.%s"](mode), function(v) set(mode,v) end)
-end
-
-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)
-
-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_skip_a = "trace:c"
-local c_skip_b = "trace:m"
-local c_glyph = "trace:o"
-local c_white = "trace:w"
-
-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_skip_a_d = "trace:dc"
-local c_skip_b_d = "trace:dm"
-local c_glyph_d = "trace:do"
-local c_white_d = "trace:dw"
-
-local function sometext(str,layer,color,textcolor) -- we can just paste verbatim together .. no typesteting needed
- local text = fast_hpack_string(str,usedfont)
- local size = text.width
- 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(text.list,textcolor)
- end
- local info = concat_nodes {
- rule,
- kern,
- text,
- }
- setlisttransparency(info,c_zero)
- info = fast_hpack(info)
- if layer then
- info[a_layer] = layer
- end
- local width = info.width
- info.width = 0
- info.height = 0
- info.depth = 0
- return info, width
-end
-
-local f_cache = { }
-
-local function fontkern(head,current)
- local kern = current.kern
- local info = f_cache[kern]
- if info then
- -- print("hit fontkern")
- else
- local text = fast_hpack_string(formatters[" %0.3f"](kern*pt_factor),usedfont)
- local rule = new_rule(emwidth/10,6*exheight,2*exheight)
- local list = text.list
- 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,c_text_d)
- settransparency(rule,c_text_d)
- text.shift = -5 * exheight
- info = concat_nodes {
- rule,
- text,
- }
- info = fast_hpack(info)
- info[a_layer] = l_fontkern
- info.width = 0
- info.height = 0
- info.depth = 0
- f_cache[kern] = info
- end
- head = insert_node_before(head,current,copy_list(info))
- return head, current
-end
-
-local w_cache = { }
-
-local tags = {
- open = "FIC",
- write = "FIW",
- close = "FIC",
- special = "SPE",
- localpar = "PAR",
- dir = "DIR",
- pdfliteral = "PDF",
- pdfrefobj = "PDF",
- pdfrefxform = "PDF",
- pdfrefximage = "PDF",
- pdfannot = "PDF",
- pdfstartlink = "PDF",
- pdfendlink = "PDF",
- pdfdest = "PDF",
- pdfthread = "PDF",
- pdfstartthread = "PDF",
- pdfendthread = "PDF",
- pdfsavepos = "PDF",
- pdfthreaddata = "PDF",
- pdflinkdata = "PDF",
- pdfcolorstack = "PDF",
- pdfsetmatrix = "PDF",
- pdfsave = "PDF",
- pdfrestore = "PDF",
- latelua = "LUA",
- closelua = "LUA",
- cancelboundary = "CBD",
- userdefined = "USR",
-}
-
-local function whatsit(head,current)
- local what = current.subtype
- local info = w_cache[what]
- if info then
- -- print("hit whatsit")
- else
- local tag = whatsitcodes[what]
- -- maybe different text colors per tag
- info = sometext(formatters["W:%s"](tag and tags[tag] or what),usedfont,nil,c_white)
- info[a_layer] = l_whatsit
- w_cache[what] = info
- end
- head, current = insert_node_after(head,current,copy_list(info))
- return head, current
-end
-
-local function user(head,current)
- local what = current.subtype
- local info = w_cache[what]
- if info then
- -- print("hit user")
- else
- info = sometext(formatters["U:%s"](what),usedfont)
- info[a_layer] = l_user
- w_cache[what] = info
- end
- head, current = insert_node_after(head,current,copy_list(info))
- return head, current
-end
-
-local b_cache = { }
-
-local function ruledbox(head,current,vertical,layer,what,simple)
- local wd = current.width
- if wd ~= 0 then
- local ht, dp = current.height, current.depth
- local next, prev = current.next, current.prev
- current.next, current.prev = nil, nil
- local linewidth = emwidth/10
- local baseline, baseskip
- if dp ~= 0 and ht ~= 0 then
- if wd > 20*linewidth then
- baseline = b_cache.baseline
- if not baseline then
- -- due to an optimized leader color/transparency we need to set the glue node in order
- -- to trigger this mechanism
- local leader = concat_nodes {
- new_glue(2*linewidth), -- 2.5
- new_rule(6*linewidth,linewidth,0), -- 5.0
- new_glue(2*linewidth), -- 2.5
- }
- -- setlisttransparency(leader,c_text)
- leader = fast_hpack(leader)
- -- setlisttransparency(leader,c_text)
- baseline = new_glue(0)
- baseline.leader = leader
- baseline.subtype = cleaders_code
- baseline.spec.stretch = 65536
- baseline.spec.stretch_order = 2
- setlisttransparency(baseline,c_text)
- b_cache.baseline = baseline
- end
- baseline = copy_list(baseline)
- baseline = fast_hpack(baseline,wd-2*linewidth)
- -- or new hpack node, set head and also:
- -- baseline.width = wd
- -- baseline.glue_set = wd/65536
- -- baseline.glue_order = 2
- -- baseline.glue_sign = 1
- baseskip = new_kern(-wd+linewidth)
- else
- baseline = new_rule(wd-2*linewidth,linewidth,0)
- baseskip = new_kern(-wd+2*linewidth)
- end
- end
- local this
- if not simple then
- this = b_cache[what]
- if not this then
- local text = fast_hpack_string(what,usedfont)
- this = concat_nodes {
- new_kern(-text.width),
- text,
- }
- setlisttransparency(this,c_text)
- this = fast_hpack(this)
- this.width = 0
- this.height = 0
- this.depth = 0
- b_cache[what] = this
- end
- end
- local info = concat_nodes {
- this and copy_list(this) or nil, -- this also triggers the right mode (else sometimes no whatits)
- new_rule(linewidth,ht,dp),
- new_rule(wd-2*linewidth,-dp+linewidth,dp),
- new_rule(linewidth,ht,dp),
- new_kern(-wd+linewidth),
- new_rule(wd-2*linewidth,ht,-ht+linewidth),
- baseskip,
- baseline,
- }
- setlisttransparency(info,c_text)
- info = fast_hpack(info)
- info.width = 0
- info.height = 0
- info.depth = 0
- info[a_layer] = layer
- local info = concat_nodes {
- current,
- new_kern(-wd),
- info,
- }
- info = fast_hpack(info,wd)
- if vertical then
- info = vpack_nodes(info)
- end
- if next then
- info.next = next
- next.prev = info
- end
- if prev then
-if prev.id == gluespec_code then
- -- weird, how can this happen, an inline glue-spec
-else
- info.prev = prev
- prev.next = info
-end
- end
- if head == current then
- return info, info
- else
- return head, info
- end
- else
- return head, current
- end
-end
-
-local function ruledglyph(head,current)
- local wd = current.width
- if wd ~= 0 then
- local ht, dp = current.height, current.depth
- local next, prev = current.next, current.prev
- current.next, current.prev = nil, nil
- local linewidth = emwidth/20
- local baseline
- if dp ~= 0 and ht ~= 0 then
- baseline = new_rule(wd-2*linewidth,linewidth,0)
- end
- local doublelinewidth = 2*linewidth
- local info = concat_nodes {
- new_rule(linewidth,ht,dp),
- new_rule(wd-doublelinewidth,-dp+linewidth,dp),
- new_rule(linewidth,ht,dp),
- new_kern(-wd+linewidth),
- new_rule(wd-doublelinewidth,ht,-ht+linewidth),
- new_kern(-wd+doublelinewidth),
- baseline,
- }
- setlistcolor(info,c_glyph)
- setlisttransparency(info,c_glyph_d)
- info = fast_hpack(info)
- info.width = 0
- info.height = 0
- info.depth = 0
- info[a_layer] = l_glyph
- local info = concat_nodes {
- current,
- new_kern(-wd),
- info,
- }
- info = fast_hpack(info)
- info.width = wd
- if next then
- info.next = next
- next.prev = info
- end
- if prev then
- info.prev = prev
- prev.next = info
- end
- if head == current then
- return info, info
- else
- return head, info
- end
- else
- return head, current
- end
-end
-
-local g_cache = { }
-
-local tags = {
- -- userskip = "US",
- lineskip = "LS",
- baselineskip = "BS",
- parskip = "PS",
- abovedisplayskip = "DA",
- belowdisplayskip = "DB",
- abovedisplayshortskip = "SA",
- belowdisplayshortskip = "SB",
- leftskip = "LS",
- rightskip = "RS",
- topskip = "TS",
- splittopskip = "ST",
- tabskip = "AS",
- spaceskip = "SS",
- xspaceskip = "XS",
- parfillskip = "PF",
- thinmuskip = "MS",
- medmuskip = "MM",
- thickmuskip = "ML",
- leaders = "NL",
- cleaders = "CL",
- xleaders = "XL",
- gleaders = "GL",
- -- true = "VS",
- -- false = "HS",
-}
-
-local function ruledglue(head,current,vertical)
- local spec = current.spec
- local width = spec.width
- local subtype = current.subtype
- local amount = formatters["%s:%0.3f"](tags[subtype] or (vertical and "VS") or "HS",width*pt_factor)
- local info = g_cache[amount]
- if info then
- -- print("glue hit")
- else
- if subtype == space_code or subtype == xspace_code then -- not yet all space
- info = sometext(amount,l_glue,c_space)
- elseif subtype == leftskip_code or subtype == rightskip_code then
- info = sometext(amount,l_glue,c_skip_a)
- 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
- else
- info = sometext(amount,l_glue,c_skip_b)
- end
- g_cache[amount] = info
- end
- info = copy_list(info)
- if vertical then
- info = vpack_nodes(info)
- end
- head, current = insert_node_before(head,current,info)
- return head, current.next
-end
-
-local k_cache = { }
-
-local function ruledkern(head,current,vertical)
- local kern = current.kern
- local info = k_cache[kern]
- if info then
- -- print("kern hit")
- else
- local amount = formatters["%s:%0.3f"](vertical and "VK" 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
- k_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, current.next
-end
-
-local p_cache = { }
-
-local function ruledpenalty(head,current,vertical)
- local penalty = current.penalty
- local info = p_cache[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
- p_cache[penalty] = info
- end
- info = copy_list(info)
- if vertical then
- info = vpack_nodes(info)
- end
- head, current = insert_node_before(head,current,info)
- return head, current.next
-end
-
-local function visualize(head,vertical)
- 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 current = head
- local prev_trace_fontkern = nil
- local attr = unsetvalue
- while current do
- local id = current.id
- local a = current[a_visual] or unsetvalue
- if a ~= attr then
- prev_trace_fontkern = trace_fontkern
- 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
- else -- dead slow:
- trace_hbox = hasbit(a, 1)
- trace_vbox = hasbit(a, 2)
- trace_vtop = hasbit(a, 4)
- trace_kern = hasbit(a, 8)
- trace_glue = hasbit(a, 16)
- trace_penalty = hasbit(a, 32)
- trace_fontkern = hasbit(a, 64)
- trace_strut = hasbit(a, 128)
- trace_whatsit = hasbit(a, 256)
- trace_glyph = hasbit(a, 512)
- trace_simple = hasbit(a,1024)
- trace_user = hasbit(a,2048)
- end
- attr = a
- end
- if trace_strut then
- current[a_layer] = l_strut
- elseif id == glyph_code then
- if trace_glyph then
- head, current = ruledglyph(head,current)
- end
- elseif id == disc_code then
- if trace_glyph then
- local pre = current.pre
- if pre then
- current.pre = ruledglyph(pre,pre)
- end
- local post = current.post
- if post then
- current.post = ruledglyph(post,post)
- end
- local replace = current.replace
- if replace then
- current.replace = ruledglyph(replace,replace)
- end
- end
- elseif id == kern_code then
- local subtype = current.subtype
- -- tricky ... we don't copy the trace attribute in node-inj (yet)
- if subtype == font_kern_code or current[a_fontkern] then
- if trace_fontkern or prev_trace_fontkern then
- head, current = fontkern(head,current)
- end
- elseif subtype == user_kern_code then
- if trace_kern then
- head, current = ruledkern(head,current,vertical)
- end
- end
- elseif id == glue_code then
- local content = current.leader
- if content then
- current.leader = visualize(content,false)
- elseif trace_glue then
- head, current = ruledglue(head,current,vertical)
- end
- elseif id == penalty_code then
- if trace_penalty then
- head, current = ruledpenalty(head,current,vertical)
- end
- elseif id == disc_code then
- current.pre = visualize(current.pre)
- current.post = visualize(current.post)
- current.replace = visualize(current.replace)
- elseif id == hlist_code then
- local content = current.list
- if content then
- current.list = visualize(content,false)
- end
- if trace_hbox then
- head, current = ruledbox(head,current,false,l_hbox,"H__",trace_simple)
- end
- elseif id == vlist_code then
- local content = current.list
- if content then
- current.list = visualize(content,true)
- end
- if trace_vtop then
- head, current = ruledbox(head,current,true,l_vtop,"_T_",trace_simple)
- elseif trace_vbox then
- head, current = ruledbox(head,current,true,l_vbox,"__V",trace_simple)
- end
- elseif id == whatsit_code then
- if trace_whatsit then
- head, current = whatsit(head,current)
- end
- elseif id == user_code then
- if trace_whatsit then
- head, current = user(head,current)
- end
- end
- current = current.next
- end
- return head
-end
-
-local function freed(cache)
- local n = 0
- for k, v in next, cache do
- free_node_list(v)
- n = n + 1
- end
- if n == 0 then
- return 0, cache
- else
- return n, { }
- end
-end
-
-local function cleanup()
- local hf, ng, np, nk, nw
- nf, f_cache = freed(f_cache)
- ng, g_cache = freed(g_cache)
- np, p_cache = freed(p_cache)
- nk, k_cache = freed(k_cache)
- nw, w_cache = freed(w_cache)
- nb, b_cache = freed(b_cache)
- -- report_visualize("cache: %s fontkerns, %s skips, %s penalties, %s kerns, %s whatsits, %s boxes",nf,ng,np,nk,nw,nb)
-end
-
-function visualizers.handler(head)
- if usedfont then
- starttiming(visualizers)
- -- local l = tex_attribute[a_layer]
- -- local v = tex_attribute[a_visual]
- -- tex_attribute[a_layer] = unsetvalue
- -- tex_attribute[a_visual] = unsetvalue
- head = visualize(head)
- -- tex_attribute[a_layer] = l
- -- tex_attribute[a_visual] = v
- -- -- cleanup()
- stoptiming(visualizers)
- end
- return head, false
-end
-
-function visualizers.box(n)
- tex_box[n].list = visualizers.handler(tex_box[n].list)
-end
-
-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 in traverse_nodes(list) do
- local id = n.id
- if id == glyph_code then
- local font = n.font
- 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(n.list)
- end
- end
-end
-
-function visualizers.markfonts(list)
- last, used = 0, { }
- markfonts(type(n) == "number" and tex_box[n].list or n)
-end
-
-function commands.markfonts(n)
- visualizers.markfonts(n)
-end
-
-statistics.register("visualization time",function()
- if enabled then
- cleanup() -- in case we don't don't do it each time
- return format("%s seconds",statistics.elapsedtime(visualizers))
- end
-end)
+if not modules then modules = { } end modules ['trac-vis'] = {
+ version = 1.001,
+ 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 string, number, table = string, number, table
+local node, nodes, attributes, fonts, tex = node, nodes, attributes, fonts, tex
+local type = type
+local format = string.format
+local formatters = string.formatters
+
+-- 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.
+
+-- We could use pdf literals and re stream codes but it's not worth the
+-- trouble because we would end up in color etc mess. Maybe one day I'll
+-- make a nodeinjection variant.
+
+-- todo: global switch (so no attributes)
+-- todo: maybe also xoffset, yoffset of glyph
+-- todo: inline concat (more efficient)
+
+local nodecodes = nodes.nodecodes
+local disc_code = nodecodes.disc
+local kern_code = nodecodes.kern
+local glyph_code = nodecodes.glyph
+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 whatsit_code = nodecodes.whatsit
+local user_code = nodecodes.user
+local gluespec_code = nodecodes.gluespec
+
+local kerncodes = nodes.kerncodes
+local font_kern_code = kerncodes.fontkern
+local user_kern_code = kerncodes.userkern
+
+local gluecodes = nodes.gluecodes
+local cleaders_code = gluecodes.cleaders
+local userskip_code = gluecodes.userskip
+local space_code = gluecodes.space
+local xspace_code = gluecodes.xspace
+local leftskip_code = gluecodes.leftskip
+local rightskip_code = gluecodes.rightskip
+
+local whatsitcodes = nodes.whatsitcodes
+
+local concat_nodes = nodes.concat
+local hpack_nodes = node.hpack
+local vpack_nodes = node.vpack
+local hpack_string = typesetters.hpack
+local fast_hpack_string = typesetters.fast_hpack
+local copy_node = node.copy
+local copy_list = node.copy_list
+local free_node = node.free
+local free_node_list = node.flush_list
+local insert_node_before = node.insert_before
+local insert_node_after = node.insert_after
+local fast_hpack = nodes.fasthpack
+local traverse_nodes = node.traverse
+
+local tex_attribute = tex.attribute
+local tex_box = tex.box
+local unsetvalue = attributes.unsetvalue
+
+local current_font = font.current
+
+local exheights = fonts.hashes.exheights
+local emwidths = fonts.hashes.emwidths
+local pt_factor = number.dimenfactors.pt
+
+local nodepool = nodes.pool
+local new_rule = nodepool.rule
+local new_kern = nodepool.kern
+local new_glue = nodepool.glue
+local new_penalty = nodepool.penalty
+
+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_fontkern = attributes.private("fontkern")
+local a_layer = attributes.private("viewerlayer")
+
+local hasbit = number.hasbit
+local bit = number.bit
+local setbit = number.setbit
+local clearbit = number.clearbit
+
+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 report_visualize = logs.reporter("visualize")
+
+local modes = {
+ hbox = 1,
+ vbox = 2,
+ vtop = 4,
+ kern = 8,
+ glue = 16,
+ penalty = 32,
+ fontkern = 64,
+ strut = 128,
+ whatsit = 256,
+ glyph = 512,
+ simple = 1024,
+ simplehbox = 1024 + 1,
+ simplevbox = 1024 + 2,
+ simplevtop = 1024 + 4,
+ user = 2048,
+}
+
+local modes_makeup = { "hbox", "vbox", "kern", "glue", "penalty" }
+local modes_boxes = { "hbox", "vbox" }
+local modes_all = { "hbox", "vbox", "kern", "glue", "penalty", "fontkern", "whatsit", "glyph", "user" }
+
+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
+
+local enabled = false
+local layers = { }
+
+local preset_boxes = modes.hbox + modes.vbox
+local preset_makeup = preset_boxes + modes.kern + modes.glue + modes.penalty
+local preset_all = preset_makeup + modes.fontkern + modes.whatsit + modes.glyph + modes.user
+
+function visualizers.setfont(id)
+ usedfont = id or current_font()
+ exheight = exheights[usedfont]
+ emwidth = emwidths[usedfont]
+end
+
+-- we can preset a bunch of bits
+
+local function enable()
+ if not usedfont then
+ -- we use a narrow monospaced font
+ 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
+ nodes.tasks.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) -- this will become more efficient when we have the bit lib linked in
+ if not n or n == "reset" then
+ return unsetvalue
+ elseif n == "makeup" then
+ if not a or a == 0 or a == unsetvalue then
+ a = preset_makeup
+ else
+ a = setbit(a,preset_makeup)
+ -- for i=1,#modes_makeup do
+ -- a = setvisual(modes_makeup[i],a)
+ -- end
+ end
+ elseif n == "boxes" then
+ if not a or a == 0 or a == unsetvalue then
+ a = preset_boxes
+ else
+ a = setbit(a,preset_boxes)
+ -- for i=1,#modes_boxes do
+ -- a = setvisual(modes_boxes[i],a)
+ -- end
+ 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 = setbit(a,preset_all)
+ -- for i=1,#modes_all do
+ -- a = setvisual(modes_all[i],a)
+ -- end
+ end
+ else
+ local m = modes[n]
+ if not m then
+ -- go on
+ elseif a == unsetvalue then
+ if what == false then
+ return unsetvalue
+ else
+ -- a = setbit(0,m)
+ a = m
+ end
+ elseif what == false then
+ a = clearbit(a,m)
+ elseif not a or a == 0 then
+ a = m
+ else
+ a = setbit(a,m)
+ 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 visualizers.setvisual(n)
+ tex_attribute[a_visual] = setvisual(n,tex_attribute[a_visual])
+end
+
+function visualizers.setlayer(n)
+ tex_attribute[a_layer] = layers[n] or unsetvalue
+end
+
+commands.setvisual = visualizers.setvisual
+commands.setlayer = visualizers.setlayer
+
+function commands.visual(n)
+ context(setvisual(n))
+end
+
+local function set(mode,v)
+ tex_attribute[a_visual] = setvisual(mode,tex_attribute[a_visual],v)
+end
+
+for mode, value in next, modes do
+ trackers.register(formatters["visualizers.%s"](mode), function(v) set(mode,v) end)
+end
+
+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)
+
+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_skip_a = "trace:c"
+local c_skip_b = "trace:m"
+local c_glyph = "trace:o"
+local c_white = "trace:w"
+
+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_skip_a_d = "trace:dc"
+local c_skip_b_d = "trace:dm"
+local c_glyph_d = "trace:do"
+local c_white_d = "trace:dw"
+
+local function sometext(str,layer,color,textcolor) -- we can just paste verbatim together .. no typesteting needed
+ local text = fast_hpack_string(str,usedfont)
+ local size = text.width
+ 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(text.list,textcolor)
+ end
+ local info = concat_nodes {
+ rule,
+ kern,
+ text,
+ }
+ setlisttransparency(info,c_zero)
+ info = fast_hpack(info)
+ if layer then
+ info[a_layer] = layer
+ end
+ local width = info.width
+ info.width = 0
+ info.height = 0
+ info.depth = 0
+ return info, width
+end
+
+local f_cache = { }
+
+local function fontkern(head,current)
+ local kern = current.kern
+ local info = f_cache[kern]
+ if info then
+ -- print("hit fontkern")
+ else
+ local text = fast_hpack_string(formatters[" %0.3f"](kern*pt_factor),usedfont)
+ local rule = new_rule(emwidth/10,6*exheight,2*exheight)
+ local list = text.list
+ 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,c_text_d)
+ settransparency(rule,c_text_d)
+ text.shift = -5 * exheight
+ info = concat_nodes {
+ rule,
+ text,
+ }
+ info = fast_hpack(info)
+ info[a_layer] = l_fontkern
+ info.width = 0
+ info.height = 0
+ info.depth = 0
+ f_cache[kern] = info
+ end
+ head = insert_node_before(head,current,copy_list(info))
+ return head, current
+end
+
+local w_cache = { }
+
+local tags = {
+ open = "FIC",
+ write = "FIW",
+ close = "FIC",
+ special = "SPE",
+ localpar = "PAR",
+ dir = "DIR",
+ pdfliteral = "PDF",
+ pdfrefobj = "PDF",
+ pdfrefxform = "PDF",
+ pdfrefximage = "PDF",
+ pdfannot = "PDF",
+ pdfstartlink = "PDF",
+ pdfendlink = "PDF",
+ pdfdest = "PDF",
+ pdfthread = "PDF",
+ pdfstartthread = "PDF",
+ pdfendthread = "PDF",
+ pdfsavepos = "PDF",
+ pdfthreaddata = "PDF",
+ pdflinkdata = "PDF",
+ pdfcolorstack = "PDF",
+ pdfsetmatrix = "PDF",
+ pdfsave = "PDF",
+ pdfrestore = "PDF",
+ latelua = "LUA",
+ closelua = "LUA",
+ cancelboundary = "CBD",
+ userdefined = "USR",
+}
+
+local function whatsit(head,current)
+ local what = current.subtype
+ local info = w_cache[what]
+ if info then
+ -- print("hit whatsit")
+ else
+ local tag = whatsitcodes[what]
+ -- maybe different text colors per tag
+ info = sometext(formatters["W:%s"](tag and tags[tag] or what),usedfont,nil,c_white)
+ info[a_layer] = l_whatsit
+ w_cache[what] = info
+ end
+ head, current = insert_node_after(head,current,copy_list(info))
+ return head, current
+end
+
+local function user(head,current)
+ local what = current.subtype
+ local info = w_cache[what]
+ if info then
+ -- print("hit user")
+ else
+ info = sometext(formatters["U:%s"](what),usedfont)
+ info[a_layer] = l_user
+ w_cache[what] = info
+ end
+ head, current = insert_node_after(head,current,copy_list(info))
+ return head, current
+end
+
+local b_cache = { }
+
+local function ruledbox(head,current,vertical,layer,what,simple)
+ local wd = current.width
+ if wd ~= 0 then
+ local ht, dp = current.height, current.depth
+ local next, prev = current.next, current.prev
+ current.next, current.prev = nil, nil
+ local linewidth = emwidth/10
+ local baseline, baseskip
+ if dp ~= 0 and ht ~= 0 then
+ if wd > 20*linewidth then
+ baseline = b_cache.baseline
+ if not baseline then
+ -- due to an optimized leader color/transparency we need to set the glue node in order
+ -- to trigger this mechanism
+ local leader = concat_nodes {
+ new_glue(2*linewidth), -- 2.5
+ new_rule(6*linewidth,linewidth,0), -- 5.0
+ new_glue(2*linewidth), -- 2.5
+ }
+ -- setlisttransparency(leader,c_text)
+ leader = fast_hpack(leader)
+ -- setlisttransparency(leader,c_text)
+ baseline = new_glue(0)
+ baseline.leader = leader
+ baseline.subtype = cleaders_code
+ baseline.spec.stretch = 65536
+ baseline.spec.stretch_order = 2
+ setlisttransparency(baseline,c_text)
+ b_cache.baseline = baseline
+ end
+ baseline = copy_list(baseline)
+ baseline = fast_hpack(baseline,wd-2*linewidth)
+ -- or new hpack node, set head and also:
+ -- baseline.width = wd
+ -- baseline.glue_set = wd/65536
+ -- baseline.glue_order = 2
+ -- baseline.glue_sign = 1
+ baseskip = new_kern(-wd+linewidth)
+ else
+ baseline = new_rule(wd-2*linewidth,linewidth,0)
+ baseskip = new_kern(-wd+2*linewidth)
+ end
+ end
+ local this
+ if not simple then
+ this = b_cache[what]
+ if not this then
+ local text = fast_hpack_string(what,usedfont)
+ this = concat_nodes {
+ new_kern(-text.width),
+ text,
+ }
+ setlisttransparency(this,c_text)
+ this = fast_hpack(this)
+ this.width = 0
+ this.height = 0
+ this.depth = 0
+ b_cache[what] = this
+ end
+ end
+ local info = concat_nodes {
+ this and copy_list(this) or nil, -- this also triggers the right mode (else sometimes no whatits)
+ new_rule(linewidth,ht,dp),
+ new_rule(wd-2*linewidth,-dp+linewidth,dp),
+ new_rule(linewidth,ht,dp),
+ new_kern(-wd+linewidth),
+ new_rule(wd-2*linewidth,ht,-ht+linewidth),
+ baseskip,
+ baseline,
+ }
+ setlisttransparency(info,c_text)
+ info = fast_hpack(info)
+ info.width = 0
+ info.height = 0
+ info.depth = 0
+ info[a_layer] = layer
+ local info = concat_nodes {
+ current,
+ new_kern(-wd),
+ info,
+ }
+ info = fast_hpack(info,wd)
+ if vertical then
+ info = vpack_nodes(info)
+ end
+ if next then
+ info.next = next
+ next.prev = info
+ end
+ if prev then
+if prev.id == gluespec_code then
+ -- weird, how can this happen, an inline glue-spec
+else
+ info.prev = prev
+ prev.next = info
+end
+ end
+ if head == current then
+ return info, info
+ else
+ return head, info
+ end
+ else
+ return head, current
+ end
+end
+
+local function ruledglyph(head,current)
+ local wd = current.width
+ if wd ~= 0 then
+ local ht, dp = current.height, current.depth
+ local next, prev = current.next, current.prev
+ current.next, current.prev = nil, nil
+ local linewidth = emwidth/20
+ local baseline
+ if dp ~= 0 and ht ~= 0 then
+ baseline = new_rule(wd-2*linewidth,linewidth,0)
+ end
+ local doublelinewidth = 2*linewidth
+ local info = concat_nodes {
+ new_rule(linewidth,ht,dp),
+ new_rule(wd-doublelinewidth,-dp+linewidth,dp),
+ new_rule(linewidth,ht,dp),
+ new_kern(-wd+linewidth),
+ new_rule(wd-doublelinewidth,ht,-ht+linewidth),
+ new_kern(-wd+doublelinewidth),
+ baseline,
+ }
+ setlistcolor(info,c_glyph)
+ setlisttransparency(info,c_glyph_d)
+ info = fast_hpack(info)
+ info.width = 0
+ info.height = 0
+ info.depth = 0
+ info[a_layer] = l_glyph
+ local info = concat_nodes {
+ current,
+ new_kern(-wd),
+ info,
+ }
+ info = fast_hpack(info)
+ info.width = wd
+ if next then
+ info.next = next
+ next.prev = info
+ end
+ if prev then
+ info.prev = prev
+ prev.next = info
+ end
+ if head == current then
+ return info, info
+ else
+ return head, info
+ end
+ else
+ return head, current
+ end
+end
+
+local g_cache = { }
+
+local tags = {
+ -- userskip = "US",
+ lineskip = "LS",
+ baselineskip = "BS",
+ parskip = "PS",
+ abovedisplayskip = "DA",
+ belowdisplayskip = "DB",
+ abovedisplayshortskip = "SA",
+ belowdisplayshortskip = "SB",
+ leftskip = "LS",
+ rightskip = "RS",
+ topskip = "TS",
+ splittopskip = "ST",
+ tabskip = "AS",
+ spaceskip = "SS",
+ xspaceskip = "XS",
+ parfillskip = "PF",
+ thinmuskip = "MS",
+ medmuskip = "MM",
+ thickmuskip = "ML",
+ leaders = "NL",
+ cleaders = "CL",
+ xleaders = "XL",
+ gleaders = "GL",
+ -- true = "VS",
+ -- false = "HS",
+}
+
+local function ruledglue(head,current,vertical)
+ local spec = current.spec
+ local width = spec.width
+ local subtype = current.subtype
+ local amount = formatters["%s:%0.3f"](tags[subtype] or (vertical and "VS") or "HS",width*pt_factor)
+ local info = g_cache[amount]
+ if info then
+ -- print("glue hit")
+ else
+ if subtype == space_code or subtype == xspace_code then -- not yet all space
+ info = sometext(amount,l_glue,c_space)
+ elseif subtype == leftskip_code or subtype == rightskip_code then
+ info = sometext(amount,l_glue,c_skip_a)
+ 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
+ else
+ info = sometext(amount,l_glue,c_skip_b)
+ end
+ g_cache[amount] = info
+ end
+ info = copy_list(info)
+ if vertical then
+ info = vpack_nodes(info)
+ end
+ head, current = insert_node_before(head,current,info)
+ return head, current.next
+end
+
+local k_cache = { }
+
+local function ruledkern(head,current,vertical)
+ local kern = current.kern
+ local info = k_cache[kern]
+ if info then
+ -- print("kern hit")
+ else
+ local amount = formatters["%s:%0.3f"](vertical and "VK" 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
+ k_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, current.next
+end
+
+local p_cache = { }
+
+local function ruledpenalty(head,current,vertical)
+ local penalty = current.penalty
+ local info = p_cache[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
+ p_cache[penalty] = info
+ end
+ info = copy_list(info)
+ if vertical then
+ info = vpack_nodes(info)
+ end
+ head, current = insert_node_before(head,current,info)
+ return head, current.next
+end
+
+local function visualize(head,vertical)
+ 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 current = head
+ local prev_trace_fontkern = nil
+ local attr = unsetvalue
+ while current do
+ local id = current.id
+ local a = current[a_visual] or unsetvalue
+ if a ~= attr then
+ prev_trace_fontkern = trace_fontkern
+ 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
+ else -- dead slow:
+ trace_hbox = hasbit(a, 1)
+ trace_vbox = hasbit(a, 2)
+ trace_vtop = hasbit(a, 4)
+ trace_kern = hasbit(a, 8)
+ trace_glue = hasbit(a, 16)
+ trace_penalty = hasbit(a, 32)
+ trace_fontkern = hasbit(a, 64)
+ trace_strut = hasbit(a, 128)
+ trace_whatsit = hasbit(a, 256)
+ trace_glyph = hasbit(a, 512)
+ trace_simple = hasbit(a,1024)
+ trace_user = hasbit(a,2048)
+ end
+ attr = a
+ end
+ if trace_strut then
+ current[a_layer] = l_strut
+ elseif id == glyph_code then
+ if trace_glyph then
+ head, current = ruledglyph(head,current)
+ end
+ elseif id == disc_code then
+ if trace_glyph then
+ local pre = current.pre
+ if pre then
+ current.pre = ruledglyph(pre,pre)
+ end
+ local post = current.post
+ if post then
+ current.post = ruledglyph(post,post)
+ end
+ local replace = current.replace
+ if replace then
+ current.replace = ruledglyph(replace,replace)
+ end
+ end
+ elseif id == kern_code then
+ local subtype = current.subtype
+ -- tricky ... we don't copy the trace attribute in node-inj (yet)
+ if subtype == font_kern_code or current[a_fontkern] then
+ if trace_fontkern or prev_trace_fontkern then
+ head, current = fontkern(head,current)
+ end
+ elseif subtype == user_kern_code then
+ if trace_kern then
+ head, current = ruledkern(head,current,vertical)
+ end
+ end
+ elseif id == glue_code then
+ local content = current.leader
+ if content then
+ current.leader = visualize(content,false)
+ elseif trace_glue then
+ head, current = ruledglue(head,current,vertical)
+ end
+ elseif id == penalty_code then
+ if trace_penalty then
+ head, current = ruledpenalty(head,current,vertical)
+ end
+ elseif id == disc_code then
+ current.pre = visualize(current.pre)
+ current.post = visualize(current.post)
+ current.replace = visualize(current.replace)
+ elseif id == hlist_code then
+ local content = current.list
+ if content then
+ current.list = visualize(content,false)
+ end
+ if trace_hbox then
+ head, current = ruledbox(head,current,false,l_hbox,"H__",trace_simple)
+ end
+ elseif id == vlist_code then
+ local content = current.list
+ if content then
+ current.list = visualize(content,true)
+ end
+ if trace_vtop then
+ head, current = ruledbox(head,current,true,l_vtop,"_T_",trace_simple)
+ elseif trace_vbox then
+ head, current = ruledbox(head,current,true,l_vbox,"__V",trace_simple)
+ end
+ elseif id == whatsit_code then
+ if trace_whatsit then
+ head, current = whatsit(head,current)
+ end
+ elseif id == user_code then
+ if trace_whatsit then
+ head, current = user(head,current)
+ end
+ end
+ current = current.next
+ end
+ return head
+end
+
+local function freed(cache)
+ local n = 0
+ for k, v in next, cache do
+ free_node_list(v)
+ n = n + 1
+ end
+ if n == 0 then
+ return 0, cache
+ else
+ return n, { }
+ end
+end
+
+local function cleanup()
+ local hf, ng, np, nk, nw
+ nf, f_cache = freed(f_cache)
+ ng, g_cache = freed(g_cache)
+ np, p_cache = freed(p_cache)
+ nk, k_cache = freed(k_cache)
+ nw, w_cache = freed(w_cache)
+ nb, b_cache = freed(b_cache)
+ -- report_visualize("cache: %s fontkerns, %s skips, %s penalties, %s kerns, %s whatsits, %s boxes",nf,ng,np,nk,nw,nb)
+end
+
+function visualizers.handler(head)
+ if usedfont then
+ starttiming(visualizers)
+ -- local l = tex_attribute[a_layer]
+ -- local v = tex_attribute[a_visual]
+ -- tex_attribute[a_layer] = unsetvalue
+ -- tex_attribute[a_visual] = unsetvalue
+ head = visualize(head)
+ -- tex_attribute[a_layer] = l
+ -- tex_attribute[a_visual] = v
+ -- -- cleanup()
+ stoptiming(visualizers)
+ end
+ return head, false
+end
+
+function visualizers.box(n)
+ tex_box[n].list = visualizers.handler(tex_box[n].list)
+end
+
+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 in traverse_nodes(list) do
+ local id = n.id
+ if id == glyph_code then
+ local font = n.font
+ 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(n.list)
+ end
+ end
+end
+
+function visualizers.markfonts(list)
+ last, used = 0, { }
+ markfonts(type(n) == "number" and tex_box[n].list or n)
+end
+
+function commands.markfonts(n)
+ visualizers.markfonts(n)
+end
+
+statistics.register("visualization time",function()
+ if enabled then
+ cleanup() -- in case we don't don't do it each time
+ return format("%s seconds",statistics.elapsedtime(visualizers))
+ end
+end)
diff --git a/tex/context/base/trac-xml.lua b/tex/context/base/trac-xml.lua
index aba82ef52..cd8b8c0a5 100644
--- a/tex/context/base/trac-xml.lua
+++ b/tex/context/base/trac-xml.lua
@@ -1,183 +1,183 @@
-if not modules then modules = { } end modules ['trac-xml'] = {
- 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"
-}
-
--- Application helpinfo can be defined in several ways:
---
--- helpinfo = "big blob of help"
---
--- helpinfo = { basic = "blob of basic help", extra = "blob of extra help" }
---
--- helpinfo = "<?xml version=1.0?><application>...</application/>"
---
--- helpinfo = "somefile.xml"
---
--- In the case of an xml file, the file should be either present on the same path
--- as the script, or we should be be able to locate it using the resolver.
-
-local formatters = string.formatters
-local reporters = logs.reporters
-local xmlserialize = xml.serialize
-local xmlcollected = xml.collected
-local xmltext = xml.text
-local xmlfirst = xml.first
-
--- there is no need for a newhandlers { name = "help", parent = "string" }
-
-local function showhelp(specification,...)
- local root = xml.convert(specification.helpinfo or "")
- if not root then
- return
- end
- local xs = xml.gethandlers("string")
- xml.sethandlersfunction(xs,"short",function(e,handler) xmlserialize(e.dt,handler) end)
- xml.sethandlersfunction(xs,"ref", function(e,handler) handler.handle("--"..e.at.name) end)
- local wantedcategories = select("#",...) == 0 and true or table.tohash { ... }
- local nofcategories = xml.count(root,"/application/flags/category")
- local report = specification.report
- for category in xmlcollected(root,"/application/flags/category") do
- local categoryname = category.at.name or ""
- if wantedcategories == true or wantedcategories[categoryname] then
- if nofcategories > 1 then
- report("%s options:",categoryname)
- report()
- end
- for subcategory in xmlcollected(category,"/subcategory") do
- for flag in xmlcollected(subcategory,"/flag") do
- local name = flag.at.name
- local value = flag.at.value
- -- local short = xmlfirst(s,"/short")
- -- local short = xmlserialize(short,xs)
- local short = xmltext(xmlfirst(flag,"/short"))
- if value then
- report("--%-20s %s",formatters["%s=%s"](name,value),short)
- else
- report("--%-20s %s",name,short)
- end
- end
- report()
- end
- end
- end
- for category in xmlcollected(root,"/application/examples/category") do
- local title = xmltext(xmlfirst(category,"/title"))
- if title and title ~= "" then
- report()
- report(title)
- report()
- end
- for subcategory in xmlcollected(category,"/subcategory") do
- for example in xmlcollected(subcategory,"/example") do
- local command = xmltext(xmlfirst(example,"/command"))
- local comment = xmltext(xmlfirst(example,"/comment"))
- report(command)
- end
- report()
- end
- end
- for comment in xmlcollected(root,"/application/comments/comment") do
- local comment = xmltext(comment)
- report()
- report(comment)
- report()
- end
-end
-
-local reporthelp = reporters.help
-local exporthelp = reporters.export
-
-local function xmlfound(t)
- local helpinfo = t.helpinfo
- if type(helpinfo) == "table" then
- return false
- end
- if type(helpinfo) ~= "string" then
- helpinfo = "Warning: no helpinfo found."
- t.helpinfo = helpinfo
- return false
- end
- if string.find(helpinfo,".xml$") then
- local ownscript = environment.ownscript
- local helpdata = false
- if ownscript then
- local helpfile = file.join(file.pathpart(ownscript),helpinfo)
- helpdata = io.loaddata(helpfile)
- if helpdata == "" then
- helpdata = false
- end
- end
- if not helpdata then
- local helpfile = resolvers.findfile(helpinfo,"tex")
- helpdata = helpfile and io.loaddata(helpfile)
- end
- if helpdata and helpdata ~= "" then
- helpinfo = helpdata
- else
- helpinfo = formatters["Warning: help file %a is not found."](helpinfo)
- end
- end
- t.helpinfo = helpinfo
- return string.find(t.helpinfo,"^<%?xml") and true or false
-end
-
-function reporters.help(t,...)
- if xmlfound(t) then
- showhelp(t,...)
- else
- reporthelp(t,...)
- end
-end
-
-function reporters.export(t,methods,filename)
- if not xmlfound(t) then
- return exporthelp(t)
- end
- if not methods or methods == "" then
- methods = environment.arguments["exporthelp"]
- end
- if not filename or filename == "" then
- filename = environment.files[1]
- end
- dofile(resolvers.findfile("trac-exp.lua","tex"))
- local exporters = logs.exporters
- if not exporters or not methods then
- return exporthelp(t)
- end
- if methods == "all" then
- methods = table.keys(exporters)
- elseif type(methods) == "string" then
- methods = utilities.parsers.settings_to_array(methods)
- else
- return exporthelp(t)
- end
- if type(filename) ~= "string" or filename == "" then
- filename = false
- elseif file.pathpart(filename) == "" then
- t.report("export file %a will not be saved on the current path (safeguard)",filename)
- return
- end
- for i=1,#methods do
- local method = methods[i]
- local exporter = exporters[method]
- if exporter then
- local result = exporter(t,method)
- if result and result ~= "" then
- if filename then
- local fullname = file.replacesuffix(filename,method)
- t.report("saving export in %a",fullname)
- io.savedata(fullname,result)
- else
- reporters.lines(t,result)
- end
- else
- t.report("no output from exporter %a",method)
- end
- else
- t.report("unknown exporter %a",method)
- end
- end
-end
+if not modules then modules = { } end modules ['trac-xml'] = {
+ 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"
+}
+
+-- Application helpinfo can be defined in several ways:
+--
+-- helpinfo = "big blob of help"
+--
+-- helpinfo = { basic = "blob of basic help", extra = "blob of extra help" }
+--
+-- helpinfo = "<?xml version=1.0?><application>...</application/>"
+--
+-- helpinfo = "somefile.xml"
+--
+-- In the case of an xml file, the file should be either present on the same path
+-- as the script, or we should be be able to locate it using the resolver.
+
+local formatters = string.formatters
+local reporters = logs.reporters
+local xmlserialize = xml.serialize
+local xmlcollected = xml.collected
+local xmltext = xml.text
+local xmlfirst = xml.first
+
+-- there is no need for a newhandlers { name = "help", parent = "string" }
+
+local function showhelp(specification,...)
+ local root = xml.convert(specification.helpinfo or "")
+ if not root then
+ return
+ end
+ local xs = xml.gethandlers("string")
+ xml.sethandlersfunction(xs,"short",function(e,handler) xmlserialize(e.dt,handler) end)
+ xml.sethandlersfunction(xs,"ref", function(e,handler) handler.handle("--"..e.at.name) end)
+ local wantedcategories = select("#",...) == 0 and true or table.tohash { ... }
+ local nofcategories = xml.count(root,"/application/flags/category")
+ local report = specification.report
+ for category in xmlcollected(root,"/application/flags/category") do
+ local categoryname = category.at.name or ""
+ if wantedcategories == true or wantedcategories[categoryname] then
+ if nofcategories > 1 then
+ report("%s options:",categoryname)
+ report()
+ end
+ for subcategory in xmlcollected(category,"/subcategory") do
+ for flag in xmlcollected(subcategory,"/flag") do
+ local name = flag.at.name
+ local value = flag.at.value
+ -- local short = xmlfirst(s,"/short")
+ -- local short = xmlserialize(short,xs)
+ local short = xmltext(xmlfirst(flag,"/short"))
+ if value then
+ report("--%-20s %s",formatters["%s=%s"](name,value),short)
+ else
+ report("--%-20s %s",name,short)
+ end
+ end
+ report()
+ end
+ end
+ end
+ for category in xmlcollected(root,"/application/examples/category") do
+ local title = xmltext(xmlfirst(category,"/title"))
+ if title and title ~= "" then
+ report()
+ report(title)
+ report()
+ end
+ for subcategory in xmlcollected(category,"/subcategory") do
+ for example in xmlcollected(subcategory,"/example") do
+ local command = xmltext(xmlfirst(example,"/command"))
+ local comment = xmltext(xmlfirst(example,"/comment"))
+ report(command)
+ end
+ report()
+ end
+ end
+ for comment in xmlcollected(root,"/application/comments/comment") do
+ local comment = xmltext(comment)
+ report()
+ report(comment)
+ report()
+ end
+end
+
+local reporthelp = reporters.help
+local exporthelp = reporters.export
+
+local function xmlfound(t)
+ local helpinfo = t.helpinfo
+ if type(helpinfo) == "table" then
+ return false
+ end
+ if type(helpinfo) ~= "string" then
+ helpinfo = "Warning: no helpinfo found."
+ t.helpinfo = helpinfo
+ return false
+ end
+ if string.find(helpinfo,".xml$") then
+ local ownscript = environment.ownscript
+ local helpdata = false
+ if ownscript then
+ local helpfile = file.join(file.pathpart(ownscript),helpinfo)
+ helpdata = io.loaddata(helpfile)
+ if helpdata == "" then
+ helpdata = false
+ end
+ end
+ if not helpdata then
+ local helpfile = resolvers.findfile(helpinfo,"tex")
+ helpdata = helpfile and io.loaddata(helpfile)
+ end
+ if helpdata and helpdata ~= "" then
+ helpinfo = helpdata
+ else
+ helpinfo = formatters["Warning: help file %a is not found."](helpinfo)
+ end
+ end
+ t.helpinfo = helpinfo
+ return string.find(t.helpinfo,"^<%?xml") and true or false
+end
+
+function reporters.help(t,...)
+ if xmlfound(t) then
+ showhelp(t,...)
+ else
+ reporthelp(t,...)
+ end
+end
+
+function reporters.export(t,methods,filename)
+ if not xmlfound(t) then
+ return exporthelp(t)
+ end
+ if not methods or methods == "" then
+ methods = environment.arguments["exporthelp"]
+ end
+ if not filename or filename == "" then
+ filename = environment.files[1]
+ end
+ dofile(resolvers.findfile("trac-exp.lua","tex"))
+ local exporters = logs.exporters
+ if not exporters or not methods then
+ return exporthelp(t)
+ end
+ if methods == "all" then
+ methods = table.keys(exporters)
+ elseif type(methods) == "string" then
+ methods = utilities.parsers.settings_to_array(methods)
+ else
+ return exporthelp(t)
+ end
+ if type(filename) ~= "string" or filename == "" then
+ filename = false
+ elseif file.pathpart(filename) == "" then
+ t.report("export file %a will not be saved on the current path (safeguard)",filename)
+ return
+ end
+ for i=1,#methods do
+ local method = methods[i]
+ local exporter = exporters[method]
+ if exporter then
+ local result = exporter(t,method)
+ if result and result ~= "" then
+ if filename then
+ local fullname = file.replacesuffix(filename,method)
+ t.report("saving export in %a",fullname)
+ io.savedata(fullname,result)
+ else
+ reporters.lines(t,result)
+ end
+ else
+ t.report("no output from exporter %a",method)
+ end
+ else
+ t.report("unknown exporter %a",method)
+ end
+ end
+end
diff --git a/tex/context/base/type-ini.lua b/tex/context/base/type-ini.lua
index fd9aa1e6d..9ee97acae 100644
--- a/tex/context/base/type-ini.lua
+++ b/tex/context/base/type-ini.lua
@@ -1,76 +1,76 @@
-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"
-}
-
--- more code will move here
-
-local commands, context = commands, context
-
-local gsub = string.gsub
-
-local report_typescripts = logs.reporter("fonts","typescripts")
-
-local patterns = { "type-imp-%s.mkiv", "type-imp-%s.tex", "type-%s.mkiv", "type-%s.tex" } -- this will be imp only
-
-local function action(name,foundname)
- -- context.startreadingfile()
- -- context.unprotect()
- -- context.pushendofline()
- -- context.input(foundname)
- -- context.popendofline()
- -- context.protect()
- -- context.stopreadingfile()
- context.loadfoundtypescriptfile(foundname)
-end
-
-local name_one, name_two
-
-local function failure_two(name)
- report_typescripts("unknown library %a or %a",name_one,name_two)
-end
-
-local function failure_one(name)
- name_two = gsub(name,"%-.*$","")
- if name_two == name then
- report_typescripts("unknown library %a",name_one)
- else
- commands.uselibrary {
- name = name_two,
- patterns = patterns,
- action = action,
- failure = failure_two,
- onlyonce = false, -- will become true
- }
- end
-end
-
-function commands.doprocesstypescriptfile(name)
- name_one = gsub(name,"^type%-","")
- commands.uselibrary {
- name = name_one,
- patterns = patterns,
- action = action,
- failure = failure_one,
- onlyonce = false, -- will become true
- }
-end
-
-local patterns = { "type-imp-%s.mkiv", "type-imp-%s.tex" }
-
-local function failure(name)
- report_typescripts("unknown library %a",name)
-end
-
-function commands.loadtypescriptfile(name) -- a more specific name
- commands.uselibrary {
- name = gsub(name,"^type%-",""),
- patterns = patterns,
- action = action,
- failure = failure,
- onlyonce = false, -- will become true
- }
-end
+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"
+}
+
+-- more code will move here
+
+local commands, context = commands, context
+
+local gsub = string.gsub
+
+local report_typescripts = logs.reporter("fonts","typescripts")
+
+local patterns = { "type-imp-%s.mkiv", "type-imp-%s.tex", "type-%s.mkiv", "type-%s.tex" } -- this will be imp only
+
+local function action(name,foundname)
+ -- context.startreadingfile()
+ -- context.unprotect()
+ -- context.pushendofline()
+ -- context.input(foundname)
+ -- context.popendofline()
+ -- context.protect()
+ -- context.stopreadingfile()
+ context.loadfoundtypescriptfile(foundname)
+end
+
+local name_one, name_two
+
+local function failure_two(name)
+ report_typescripts("unknown library %a or %a",name_one,name_two)
+end
+
+local function failure_one(name)
+ name_two = gsub(name,"%-.*$","")
+ if name_two == name then
+ report_typescripts("unknown library %a",name_one)
+ else
+ commands.uselibrary {
+ name = name_two,
+ patterns = patterns,
+ action = action,
+ failure = failure_two,
+ onlyonce = false, -- will become true
+ }
+ end
+end
+
+function commands.doprocesstypescriptfile(name)
+ name_one = gsub(name,"^type%-","")
+ commands.uselibrary {
+ name = name_one,
+ patterns = patterns,
+ action = action,
+ failure = failure_one,
+ onlyonce = false, -- will become true
+ }
+end
+
+local patterns = { "type-imp-%s.mkiv", "type-imp-%s.tex" }
+
+local function failure(name)
+ report_typescripts("unknown library %a",name)
+end
+
+function commands.loadtypescriptfile(name) -- a more specific name
+ commands.uselibrary {
+ name = gsub(name,"^type%-",""),
+ patterns = patterns,
+ action = action,
+ failure = failure,
+ onlyonce = false, -- will become true
+ }
+end
diff --git a/tex/context/base/typo-bld.lua b/tex/context/base/typo-bld.lua
index 125b9946c..ed700add7 100644
--- a/tex/context/base/typo-bld.lua
+++ b/tex/context/base/typo-bld.lua
@@ -1,185 +1,185 @@
-if not 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"
-}
-
-local insert, remove = table.insert, table.remove
-
-local builders, nodes, node = builders, nodes, node
-
-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 nodepool = nodes.pool
-local new_baselineskip = nodepool.baselineskip
-local new_lineskip = nodepool.lineskip
-local insert_node_before = node.insert_before
-local hpack_node = node.hpack
-
-local starttiming = statistics.starttiming
-local stoptiming = statistics.stoptiming
-
-storage.register("builders/paragraphs/constructors/names", names, "builders.paragraphs.constructors.names")
-storage.register("builders/paragraphs/constructors/numbers", numbers, "builders.paragraphs.constructors.numbers")
-
-local report_parbuilders = logs.reporter("parbuilders")
-
-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_parbuilders("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_parbuilders("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 = 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_parbuilders("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 builders.paragraphs.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 = tex.baselineskip.width - t.prevdepth - h.height
- t.prevdepth = h.depth
- t.prevgraf = 1
- if d < tex.lineskiplimit then
- return insert_node_before(h,h,new_lineskip(tex.lineskip))
- 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)
- local head = actions(head,followed_by_display)
- 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
-
-
-callbacks.register('linebreak_filter', processor, "breaking paragraps into lines")
-
-statistics.register("linebreak processing time", function()
- return statistics.elapsedseconds(parbuilders)
-end)
-
--- interface
-
-commands.defineparbuilder = constructors.define
-commands.startparbuilder = constructors.start
-commands.stopparbuilder = constructors.stop
-commands.setparbuilder = constructors.set
-commands.enableparbuilder = constructors.enable
-commands.disableparbuilder = constructors.disable
+if not 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"
+}
+
+local insert, remove = table.insert, table.remove
+
+local builders, nodes, node = builders, nodes, node
+
+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 nodepool = nodes.pool
+local new_baselineskip = nodepool.baselineskip
+local new_lineskip = nodepool.lineskip
+local insert_node_before = node.insert_before
+local hpack_node = node.hpack
+
+local starttiming = statistics.starttiming
+local stoptiming = statistics.stoptiming
+
+storage.register("builders/paragraphs/constructors/names", names, "builders.paragraphs.constructors.names")
+storage.register("builders/paragraphs/constructors/numbers", numbers, "builders.paragraphs.constructors.numbers")
+
+local report_parbuilders = logs.reporter("parbuilders")
+
+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_parbuilders("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_parbuilders("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 = 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_parbuilders("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 builders.paragraphs.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 = tex.baselineskip.width - t.prevdepth - h.height
+ t.prevdepth = h.depth
+ t.prevgraf = 1
+ if d < tex.lineskiplimit then
+ return insert_node_before(h,h,new_lineskip(tex.lineskip))
+ 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)
+ local head = actions(head,followed_by_display)
+ 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
+
+
+callbacks.register('linebreak_filter', processor, "breaking paragraps into lines")
+
+statistics.register("linebreak processing time", function()
+ return statistics.elapsedseconds(parbuilders)
+end)
+
+-- interface
+
+commands.defineparbuilder = constructors.define
+commands.startparbuilder = constructors.start
+commands.stopparbuilder = constructors.stop
+commands.setparbuilder = constructors.set
+commands.enableparbuilder = constructors.enable
+commands.disableparbuilder = constructors.disable
diff --git a/tex/context/base/typo-brk.lua b/tex/context/base/typo-brk.lua
index 532909a30..d6326ebeb 100644
--- a/tex/context/base/typo-brk.lua
+++ b/tex/context/base/typo-brk.lua
@@ -1,302 +1,302 @@
-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 = next, type, tonumber
-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 copy_node = node.copy
-local copy_nodelist = node.copy_list
-local free_node = node.free
-local insert_node_before = node.insert_before
-local insert_node_after = node.insert_after
-local remove_node = nodes.remove -- ! nodes
-
-local tonodes = nodes.tonodes
-
-local texattribute = tex.attribute
-local unsetvalue = attributes.unsetvalue
-
-local nodepool = nodes.pool
-local tasks = nodes.tasks
-
-local v_reset = interfaces.variables.reset
-
-local new_penalty = nodepool.penalty
-local new_glue = nodepool.glue
-local new_disc = nodepool.disc
-
-local nodecodes = nodes.nodecodes
-local kerncodes = nodes.kerncodes
-
-local glyph_code = nodecodes.glyph
-local kern_code = nodecodes.kern
-
-local kerning_code = kerncodes.kerning
-
-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")
-breakpoints.attribute = a_breakpoints
-
-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
-
-local function insert_break(head,start,before,after)
- insert_node_before(head,start,new_penalty(before))
- insert_node_before(head,start,new_glue(0))
- insert_node_after(head,start,new_glue(0))
- insert_node_after(head,start,new_penalty(after))
-end
-
-methods[1] = function(head,start)
- if start.prev and start.next then
- insert_break(head,start,10000,0)
- end
- return head, start
-end
-
-methods[2] = function(head,start) -- ( => (-
- if start.prev and start.next then
- local tmp
- head, start, tmp = remove_node(head,start)
- head, start = insert_node_before(head,start,new_disc())
- start.attr = copy_nodelist(tmp.attr) -- todo: critical only
- start.replace = tmp
- local tmp, hyphen = copy_node(tmp), copy_node(tmp)
- hyphen.char = languages.prehyphenchar(tmp.lang)
- tmp.next, hyphen.prev = hyphen, tmp
- start.post = tmp
- insert_break(head,start,10000,10000)
- end
- return head, start
-end
-
-methods[3] = function(head,start) -- ) => -)
- if start.prev and start.next then
- local tmp
- head, start, tmp = remove_node(head,start)
- head, start = insert_node_before(head,start,new_disc())
- start.attr = copy_nodelist(tmp.attr) -- todo: critical only
- start.replace = tmp
- local tmp, hyphen = copy_node(tmp), copy_node(tmp)
- hyphen.char = languages.prehyphenchar(tmp.lang)
- tmp.prev, hyphen.next = hyphen, tmp
- start.pre = hyphen
- insert_break(head,start,10000,10000)
- end
- return head, start
-end
-
-methods[4] = function(head,start) -- - => - - -
- if start.prev and start.next then
- local tmp
- head, start, tmp = remove_node(head,start)
- head, start = insert_node_before(head,start,new_disc())
- start.attr = copy_nodelist(tmp.attr) -- todo: critical only
- start.pre, start.post, start.replace = copy_node(tmp), copy_node(tmp), tmp
- insert_break(head,start,10000,10000)
- end
- return head, start
-end
-
-methods[5] = function(head,start,settings) -- x => p q r
- if start.prev and start.next then
- local tmp
- head, start, tmp = remove_node(head,start)
- head, start = insert_node_before(head,start,new_disc())
- local attr = tmp.attr
- local font = tmp.font
- start.attr = copy_nodelist(attr) -- todo: critical only
- local left, right, middle = settings.left, settings.right, settings.middle
- if left then
- start.pre = tonodes(tostring(left),font,attr) -- was right
- end
- if right then
- start.post = tonodes(tostring(right),font,attr) -- was left
- end
- if middle then
- start.replace = tonodes(tostring(middle),font,attr)
- end
- free_node(tmp)
- insert_break(head,start,10000,10000)
- end
- return head, start
-end
-
-local function process(namespace,attribute,head)
- local done, numbers = false, languages.numbers
- local start, n = head, 0
- while start do
- local id = start.id
- if id == glyph_code then
- local attr = start[a_breakpoints]
- if attr and attr > 0 then
- start[a_breakpoints] = unsetvalue -- maybe test for subtype > 256 (faster)
- -- look ahead and back n chars
- local data = mapping[attr]
- if data then
- local map = data.characters
- local cmap = map[start.char]
- if cmap then
- local lang = start.lang
- -- we do a sanity check for language
- local smap = lang and lang >= 0 and lang < 0x7FFF and (cmap[numbers[lang]] or cmap[""])
- if smap then
- if n >= smap.nleft then
- local m = smap.nright
- local next = start.next
- while next do -- gamble on same attribute (not that important actually)
- local id = next.id
- if id == glyph_code then -- gamble on same attribute (not that important actually)
- if map[next.char] then
- break
- elseif m == 1 then
- local method = methods[smap.type]
- if method then
- head, start = method(head,start,smap)
- done = true
- end
- break
- else
- m = m - 1
- next = next.next
- end
- elseif id == kern_code and next.subtype == kerning_code then
- next = next.next
- -- ignore intercharacter kerning, will go way
- else
- -- we can do clever and set n and jump ahead but ... not now
- break
- end
- end
- end
- n = 0
- else
- n = n + 1
- end
- else
- n = n + 1
- end
- else
- n = 0
- end
- else
- -- n = n + 1 -- if we want single char handling (|-|) then we will use grouping and then we need this
- end
- elseif id == kern_code and start.subtype == kerning_code then
- -- ignore intercharacter kerning, will go way
- else
- n = 0
- end
- start = start.next
- end
- return head, done
-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,
- } -- 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
- tasks.enableaction("processors","typesetters.breakpoints.handler")
- end
- n = n.number
- end
- end
- texattribute[a_breakpoints] = n
-end
-
-breakpoints.handler = nodes.installattributehandler {
- name = "breakpoint",
- namespace = breakpoints,
- processor = process,
-}
-
--- function breakpoints.enable()
--- tasks.enableaction("processors","typesetters.breakpoints.handler")
--- end
-
--- interface
-
-commands.definebreakpoints = breakpoints.define
-commands.definebreakpoint = breakpoints.setreplacement
-commands.setbreakpoints = breakpoints.set
+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 = next, type, tonumber
+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 copy_node = node.copy
+local copy_nodelist = node.copy_list
+local free_node = node.free
+local insert_node_before = node.insert_before
+local insert_node_after = node.insert_after
+local remove_node = nodes.remove -- ! nodes
+
+local tonodes = nodes.tonodes
+
+local texattribute = tex.attribute
+local unsetvalue = attributes.unsetvalue
+
+local nodepool = nodes.pool
+local tasks = nodes.tasks
+
+local v_reset = interfaces.variables.reset
+
+local new_penalty = nodepool.penalty
+local new_glue = nodepool.glue
+local new_disc = nodepool.disc
+
+local nodecodes = nodes.nodecodes
+local kerncodes = nodes.kerncodes
+
+local glyph_code = nodecodes.glyph
+local kern_code = nodecodes.kern
+
+local kerning_code = kerncodes.kerning
+
+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")
+breakpoints.attribute = a_breakpoints
+
+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
+
+local function insert_break(head,start,before,after)
+ insert_node_before(head,start,new_penalty(before))
+ insert_node_before(head,start,new_glue(0))
+ insert_node_after(head,start,new_glue(0))
+ insert_node_after(head,start,new_penalty(after))
+end
+
+methods[1] = function(head,start)
+ if start.prev and start.next then
+ insert_break(head,start,10000,0)
+ end
+ return head, start
+end
+
+methods[2] = function(head,start) -- ( => (-
+ if start.prev and start.next then
+ local tmp
+ head, start, tmp = remove_node(head,start)
+ head, start = insert_node_before(head,start,new_disc())
+ start.attr = copy_nodelist(tmp.attr) -- todo: critical only
+ start.replace = tmp
+ local tmp, hyphen = copy_node(tmp), copy_node(tmp)
+ hyphen.char = languages.prehyphenchar(tmp.lang)
+ tmp.next, hyphen.prev = hyphen, tmp
+ start.post = tmp
+ insert_break(head,start,10000,10000)
+ end
+ return head, start
+end
+
+methods[3] = function(head,start) -- ) => -)
+ if start.prev and start.next then
+ local tmp
+ head, start, tmp = remove_node(head,start)
+ head, start = insert_node_before(head,start,new_disc())
+ start.attr = copy_nodelist(tmp.attr) -- todo: critical only
+ start.replace = tmp
+ local tmp, hyphen = copy_node(tmp), copy_node(tmp)
+ hyphen.char = languages.prehyphenchar(tmp.lang)
+ tmp.prev, hyphen.next = hyphen, tmp
+ start.pre = hyphen
+ insert_break(head,start,10000,10000)
+ end
+ return head, start
+end
+
+methods[4] = function(head,start) -- - => - - -
+ if start.prev and start.next then
+ local tmp
+ head, start, tmp = remove_node(head,start)
+ head, start = insert_node_before(head,start,new_disc())
+ start.attr = copy_nodelist(tmp.attr) -- todo: critical only
+ start.pre, start.post, start.replace = copy_node(tmp), copy_node(tmp), tmp
+ insert_break(head,start,10000,10000)
+ end
+ return head, start
+end
+
+methods[5] = function(head,start,settings) -- x => p q r
+ if start.prev and start.next then
+ local tmp
+ head, start, tmp = remove_node(head,start)
+ head, start = insert_node_before(head,start,new_disc())
+ local attr = tmp.attr
+ local font = tmp.font
+ start.attr = copy_nodelist(attr) -- todo: critical only
+ local left, right, middle = settings.left, settings.right, settings.middle
+ if left then
+ start.pre = tonodes(tostring(left),font,attr) -- was right
+ end
+ if right then
+ start.post = tonodes(tostring(right),font,attr) -- was left
+ end
+ if middle then
+ start.replace = tonodes(tostring(middle),font,attr)
+ end
+ free_node(tmp)
+ insert_break(head,start,10000,10000)
+ end
+ return head, start
+end
+
+local function process(namespace,attribute,head)
+ local done, numbers = false, languages.numbers
+ local start, n = head, 0
+ while start do
+ local id = start.id
+ if id == glyph_code then
+ local attr = start[a_breakpoints]
+ if attr and attr > 0 then
+ start[a_breakpoints] = unsetvalue -- maybe test for subtype > 256 (faster)
+ -- look ahead and back n chars
+ local data = mapping[attr]
+ if data then
+ local map = data.characters
+ local cmap = map[start.char]
+ if cmap then
+ local lang = start.lang
+ -- we do a sanity check for language
+ local smap = lang and lang >= 0 and lang < 0x7FFF and (cmap[numbers[lang]] or cmap[""])
+ if smap then
+ if n >= smap.nleft then
+ local m = smap.nright
+ local next = start.next
+ while next do -- gamble on same attribute (not that important actually)
+ local id = next.id
+ if id == glyph_code then -- gamble on same attribute (not that important actually)
+ if map[next.char] then
+ break
+ elseif m == 1 then
+ local method = methods[smap.type]
+ if method then
+ head, start = method(head,start,smap)
+ done = true
+ end
+ break
+ else
+ m = m - 1
+ next = next.next
+ end
+ elseif id == kern_code and next.subtype == kerning_code then
+ next = next.next
+ -- ignore intercharacter kerning, will go way
+ else
+ -- we can do clever and set n and jump ahead but ... not now
+ break
+ end
+ end
+ end
+ n = 0
+ else
+ n = n + 1
+ end
+ else
+ n = n + 1
+ end
+ else
+ n = 0
+ end
+ else
+ -- n = n + 1 -- if we want single char handling (|-|) then we will use grouping and then we need this
+ end
+ elseif id == kern_code and start.subtype == kerning_code then
+ -- ignore intercharacter kerning, will go way
+ else
+ n = 0
+ end
+ start = start.next
+ end
+ return head, done
+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,
+ } -- 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
+ tasks.enableaction("processors","typesetters.breakpoints.handler")
+ end
+ n = n.number
+ end
+ end
+ texattribute[a_breakpoints] = n
+end
+
+breakpoints.handler = nodes.installattributehandler {
+ name = "breakpoint",
+ namespace = breakpoints,
+ processor = process,
+}
+
+-- function breakpoints.enable()
+-- tasks.enableaction("processors","typesetters.breakpoints.handler")
+-- end
+
+-- interface
+
+commands.definebreakpoints = breakpoints.define
+commands.definebreakpoint = breakpoints.setreplacement
+commands.setbreakpoints = breakpoints.set
diff --git a/tex/context/base/typo-cap.lua b/tex/context/base/typo-cap.lua
index 304d133c9..fdbf2e353 100644
--- a/tex/context/base/typo-cap.lua
+++ b/tex/context/base/typo-cap.lua
@@ -1,331 +1,331 @@
-if not modules then modules = { } end modules ['typo-cap'] = {
- version = 1.001,
- 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"
- }
-
-local next, type = next, type
-local format, insert = string.format, table.insert
-local div = math.div
-
-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 traverse_id = node.traverse_id
-local copy_node = node.copy
-local end_of_math = node.end_of_math
-
-local texattribute = tex.attribute
-local unsetvalue = attributes.unsetvalue
-
-local nodecodes = nodes.nodecodes
-local skipcodes = nodes.skipcodes
-local kerncodes = nodes.kerncodes
-
-local glyph_code = nodecodes.glyph
-local kern_code = nodecodes.kern
-local math_code = nodecodes.math
-
-local kerning_code = kerncodes.kerning
-local userskip_code = skipcodes.userskip
-
-local tasks = nodes.tasks
-
-local fonthashes = fonts.hashes
-local fontdata = fonthashes.identifiers
-local fontchar = fonthashes.characters
-
-local variables = interfaces.variables
-local v_reset = variables.reset
-
-local chardata = characters.data
-
-typesetters = typesetters or { }
-local typesetters = typesetters
-
-typesetters.cases = typesetters.cases or { }
-local cases = typesetters.cases
-
-cases.actions = { }
-local actions = cases.actions
-cases.attribute = c_cases -- no longer needed
-local a_cases = attributes.private("case")
-
-local lastfont = nil
-
--- we use char(0) as placeholder for the larger font, so we need to remove it
--- before it can do further harm
---
--- 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 function helper(start, codes, special, attribute, once)
- local char = start.char
- local dc = codes[char]
- if dc then
- local fnt = start.font
- if special then
- -- will become function
- if start.char == 0 then
- lastfont = fnt
- local prev, next = start.prev, start.next
- prev.next = next
- if next then
- next.prev = prev
- end
- return prev, true
- elseif lastfont and start.prev.id ~= glyph_code then
- fnt = lastfont
- start.font = lastfont
- end
- end
- local ifc = fontchar[fnt]
- if type(dc) == "table" then
- local ok = true
- for i=1,#dc do
- ok = ok and ifc[dc[i]]
- end
- if ok then
- -- tood; use generic injector
- local prev, original = start, start
- for i=1,#dc do
- local chr = dc[i]
- prev = start
- if i == 1 then
- start.char = chr
- else
- local g = copy_node(original)
- g.char = chr
- local next = start.next
- g.prev = start
- if next then
- g.next = next
- start.next = g
- next.prev = g
- end
- start = g
- end
- end
- if once then lastfont = nil end
- return prev, true
- end
- if once then lastfont = nil end
- return start, false
- elseif ifc[dc] then
- start.char = dc
- if once then lastfont = nil end
- return start, true
- end
- end
- if once then lastfont = nil end
- return start, false
-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,attribute)
- lastfont = nil
- return helper(start,uccodes)
-end
-
-local function word(start,attribute)
- lastfont = nil
- return helper(start,lccodes)
-end
-
-local function Word(start,attribute,attr)
- lastfont = nil
- local prev = start.prev
- if prev and prev.id == kern_code and prev.subtype == kerning_code then
- prev = prev.prev
- end
- if not prev or prev.id ~= glyph_code then
- --- only the first character is treated
- for n in traverse_id(glyph_code,start.next) do
- if n[attribute] == attr then
- n[attribute] = unsetvalue
- else
- -- break -- we can have nested mess
- end
- end
- -- we could return the last in the range and save some scanning
- -- but why bother
- return helper(start,uccodes)
- else
- return start, false
- end
-end
-
-local function Words(start,attribute)
- lastfont = nil
- local prev = start.prev
- if prev and prev.id == kern_code and prev.subtype == kerning_code then
- prev = prev.prev
- end
- if not prev or prev.id ~= glyph_code then
- return helper(start,uccodes)
- else
- return start, false
- end
-end
-
-local function capital(start,attribute) -- 3
- return helper(start,uccodes,true,attribute,true)
-end
-
-local function Capital(start,attribute) -- 4
- return helper(start,uccodes,true,attribute,false)
-end
-
-local function none(start)
- return start, false
-end
-
-local function random(start)
- lastfont = nil
- local ch = start.char
- local mr = math.random
- -- local tfm = fontdata[start.font].characters
- local tfm = fontchar[start.font]
- if lccodes[ch] then
- while true do
- local d = chardata[mr(1,0xFFFF)]
- if d then
- local uc = uccodes[d]
- if uc and tfm[uc] then -- this also intercepts tables
- start.char = uc
- return start, true
- end
- end
- end
- elseif uccodes[ch] then
- while true do
- local d = chardata[mr(1,0xFFFF)]
- if d then
- local lc = lccodes[d]
- if lc and tfm[lc] then -- this also intercepts tables
- start.char = lc
- return start, true
- end
- end
- end
- end
- return start, false
-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, random) -- 8
-
-register(variables.cap, variables.capital) -- clone
-register(variables.Cap, variables.Capital) -- clone
-
--- node.traverse_id_attr
-
-local function process(namespace,attribute,head) -- not real fast but also not used on much data
- lastfont = nil
- local lastattr = nil
- local done = false
- local start = head
- while start do -- while because start can jump ahead
- local id = start.id
- if id == glyph_code then
- local attr = start[attribute]
- if attr and attr > 0 then
- if attr ~= lastattr then
- lastfont = nil
- lastattr = attr
- end
- start[attribute] = unsetvalue
- local action = actions[attr%100] -- map back to low number
- if action then
- start, ok = action(start,attribute,attr)
- done = done and ok
- if trace_casing then
- report_casing("case trigger %a, instance %a, result %a",attr%100,div(attr,100),ok)
- end
- elseif trace_casing then
- report_casing("unknown case trigger %a",attr)
- end
- end
- elseif id == math_code then
- start = end_of_math(start)
- end
- if start then -- why test
- start = start.next
- end
- end
- lastfont = nil
- return head, done
-end
-
-local m, enabled = 0, false -- a trick to make neighbouring ranges work
-
-function cases.set(n)
- if n == v_reset then
- n = unsetvalue
- else
- n = registered[n] or tonumber(n)
- if n then
- if not enabled then
- tasks.enableaction("processors","typesetters.cases.handler")
- if trace_casing then
- report_casing("enabling case 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
- texattribute[a_cases] = n
- -- return n -- bonus
-end
-
-cases.handler = nodes.installattributehandler {
- name = "case",
- namespace = cases,
- processor = process,
-}
-
--- interface
-
-commands.setcharactercasing = cases.set
+if not modules then modules = { } end modules ['typo-cap'] = {
+ version = 1.001,
+ 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"
+ }
+
+local next, type = next, type
+local format, insert = string.format, table.insert
+local div = math.div
+
+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 traverse_id = node.traverse_id
+local copy_node = node.copy
+local end_of_math = node.end_of_math
+
+local texattribute = tex.attribute
+local unsetvalue = attributes.unsetvalue
+
+local nodecodes = nodes.nodecodes
+local skipcodes = nodes.skipcodes
+local kerncodes = nodes.kerncodes
+
+local glyph_code = nodecodes.glyph
+local kern_code = nodecodes.kern
+local math_code = nodecodes.math
+
+local kerning_code = kerncodes.kerning
+local userskip_code = skipcodes.userskip
+
+local tasks = nodes.tasks
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+local fontchar = fonthashes.characters
+
+local variables = interfaces.variables
+local v_reset = variables.reset
+
+local chardata = characters.data
+
+typesetters = typesetters or { }
+local typesetters = typesetters
+
+typesetters.cases = typesetters.cases or { }
+local cases = typesetters.cases
+
+cases.actions = { }
+local actions = cases.actions
+cases.attribute = c_cases -- no longer needed
+local a_cases = attributes.private("case")
+
+local lastfont = nil
+
+-- we use char(0) as placeholder for the larger font, so we need to remove it
+-- before it can do further harm
+--
+-- 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 function helper(start, codes, special, attribute, once)
+ local char = start.char
+ local dc = codes[char]
+ if dc then
+ local fnt = start.font
+ if special then
+ -- will become function
+ if start.char == 0 then
+ lastfont = fnt
+ local prev, next = start.prev, start.next
+ prev.next = next
+ if next then
+ next.prev = prev
+ end
+ return prev, true
+ elseif lastfont and start.prev.id ~= glyph_code then
+ fnt = lastfont
+ start.font = lastfont
+ end
+ end
+ local ifc = fontchar[fnt]
+ if type(dc) == "table" then
+ local ok = true
+ for i=1,#dc do
+ ok = ok and ifc[dc[i]]
+ end
+ if ok then
+ -- tood; use generic injector
+ local prev, original = start, start
+ for i=1,#dc do
+ local chr = dc[i]
+ prev = start
+ if i == 1 then
+ start.char = chr
+ else
+ local g = copy_node(original)
+ g.char = chr
+ local next = start.next
+ g.prev = start
+ if next then
+ g.next = next
+ start.next = g
+ next.prev = g
+ end
+ start = g
+ end
+ end
+ if once then lastfont = nil end
+ return prev, true
+ end
+ if once then lastfont = nil end
+ return start, false
+ elseif ifc[dc] then
+ start.char = dc
+ if once then lastfont = nil end
+ return start, true
+ end
+ end
+ if once then lastfont = nil end
+ return start, false
+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,attribute)
+ lastfont = nil
+ return helper(start,uccodes)
+end
+
+local function word(start,attribute)
+ lastfont = nil
+ return helper(start,lccodes)
+end
+
+local function Word(start,attribute,attr)
+ lastfont = nil
+ local prev = start.prev
+ if prev and prev.id == kern_code and prev.subtype == kerning_code then
+ prev = prev.prev
+ end
+ if not prev or prev.id ~= glyph_code then
+ --- only the first character is treated
+ for n in traverse_id(glyph_code,start.next) do
+ if n[attribute] == attr then
+ n[attribute] = unsetvalue
+ else
+ -- break -- we can have nested mess
+ end
+ end
+ -- we could return the last in the range and save some scanning
+ -- but why bother
+ return helper(start,uccodes)
+ else
+ return start, false
+ end
+end
+
+local function Words(start,attribute)
+ lastfont = nil
+ local prev = start.prev
+ if prev and prev.id == kern_code and prev.subtype == kerning_code then
+ prev = prev.prev
+ end
+ if not prev or prev.id ~= glyph_code then
+ return helper(start,uccodes)
+ else
+ return start, false
+ end
+end
+
+local function capital(start,attribute) -- 3
+ return helper(start,uccodes,true,attribute,true)
+end
+
+local function Capital(start,attribute) -- 4
+ return helper(start,uccodes,true,attribute,false)
+end
+
+local function none(start)
+ return start, false
+end
+
+local function random(start)
+ lastfont = nil
+ local ch = start.char
+ local mr = math.random
+ -- local tfm = fontdata[start.font].characters
+ local tfm = fontchar[start.font]
+ if lccodes[ch] then
+ while true do
+ local d = chardata[mr(1,0xFFFF)]
+ if d then
+ local uc = uccodes[d]
+ if uc and tfm[uc] then -- this also intercepts tables
+ start.char = uc
+ return start, true
+ end
+ end
+ end
+ elseif uccodes[ch] then
+ while true do
+ local d = chardata[mr(1,0xFFFF)]
+ if d then
+ local lc = lccodes[d]
+ if lc and tfm[lc] then -- this also intercepts tables
+ start.char = lc
+ return start, true
+ end
+ end
+ end
+ end
+ return start, false
+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, random) -- 8
+
+register(variables.cap, variables.capital) -- clone
+register(variables.Cap, variables.Capital) -- clone
+
+-- node.traverse_id_attr
+
+local function process(namespace,attribute,head) -- not real fast but also not used on much data
+ lastfont = nil
+ local lastattr = nil
+ local done = false
+ local start = head
+ while start do -- while because start can jump ahead
+ local id = start.id
+ if id == glyph_code then
+ local attr = start[attribute]
+ if attr and attr > 0 then
+ if attr ~= lastattr then
+ lastfont = nil
+ lastattr = attr
+ end
+ start[attribute] = unsetvalue
+ local action = actions[attr%100] -- map back to low number
+ if action then
+ start, ok = action(start,attribute,attr)
+ done = done and ok
+ if trace_casing then
+ report_casing("case trigger %a, instance %a, result %a",attr%100,div(attr,100),ok)
+ end
+ elseif trace_casing then
+ report_casing("unknown case trigger %a",attr)
+ end
+ end
+ elseif id == math_code then
+ start = end_of_math(start)
+ end
+ if start then -- why test
+ start = start.next
+ end
+ end
+ lastfont = nil
+ return head, done
+end
+
+local m, enabled = 0, false -- a trick to make neighbouring ranges work
+
+function cases.set(n)
+ if n == v_reset then
+ n = unsetvalue
+ else
+ n = registered[n] or tonumber(n)
+ if n then
+ if not enabled then
+ tasks.enableaction("processors","typesetters.cases.handler")
+ if trace_casing then
+ report_casing("enabling case 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
+ texattribute[a_cases] = n
+ -- return n -- bonus
+end
+
+cases.handler = nodes.installattributehandler {
+ name = "case",
+ namespace = cases,
+ processor = process,
+}
+
+-- interface
+
+commands.setcharactercasing = cases.set
diff --git a/tex/context/base/typo-cln.lua b/tex/context/base/typo-cln.lua
index 70d2f7b60..be00ac10d 100644
--- a/tex/context/base/typo-cln.lua
+++ b/tex/context/base/typo-cln.lua
@@ -1,102 +1,102 @@
-if not modules then modules = { } end modules ['typo-cln'] = {
- version = 1.001,
- comment = "companion to typo-cln.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- This quick and dirty hack took less time than listening to a CD (In
--- this case Dream Theaters' Octavium. Of course extensions will take
--- more time.
-
-local utfbyte = utf.byte
-
-local trace_cleaners = false trackers.register("typesetters.cleaners", function(v) trace_cleaners = v end)
-local trace_autocase = false trackers.register("typesetters.cleaners.autocase",function(v) trace_autocase = v end)
-
-local report_cleaners = logs.reporter("nodes","cleaners")
-local report_autocase = logs.reporter("nodes","autocase")
-
-typesetters.cleaners = typesetters.cleaners or { }
-local cleaners = typesetters.cleaners
-
-local variables = interfaces.variables
-
-local nodecodes = nodes.nodecodes
-local tasks = nodes.tasks
-
-local texattribute = tex.attribute
-
-local traverse_id = node.traverse_id
-
-local unsetvalue = attributes.unsetvalue
-
-local glyph_code = nodecodes.glyph
-local uccodes = characters.uccodes
-
-local a_cleaner = attributes.private("cleaner")
-
-local resetter = { -- this will become an entry in char-def
- [utfbyte(".")] = true
-}
-
--- Contrary to the casing code we need to keep track of a state.
--- We could extend the casing code with a status tracker but on
--- the other hand we might want to apply casing afterwards. So,
--- cleaning comes first.
-
-local function process(namespace,attribute,head)
- local inline, done = false, false
- for n in traverse_id(glyph_code,head) do
- local char = n.char
- if resetter[char] then
- inline = false
- elseif not inline then
- local a = n[attribute]
- if a == 1 then -- currently only one cleaner so no need to be fancy
- local upper = uccodes[char]
- if type(upper) == "table" then
- -- some day, not much change that \SS ends up here
- else
- n.char = upper
- done = true
- if trace_autocase then
- report_autocase("")
- end
- end
- end
- inline = true
- end
- end
- return head, done
-end
-
--- see typo-cap for a more advanced settings handler .. not needed now
-
-local enabled = false
-
-function cleaners.set(n)
- if n == variables.reset or not tonumber(n) or n == 0 then
- texattribute[a_cleaner] = unsetvalue
- else
- if not enabled then
- tasks.enableaction("processors","typesetters.cleaners.handler")
- if trace_cleaners then
- report_cleaners("enabling cleaners")
- end
- enabled = true
- end
- texattribute[a_cleaner] = n
- end
-end
-
-cleaners.handler = nodes.installattributehandler {
- name = "cleaner",
- namespace = cleaners,
- processor = process,
-}
-
--- interface
-
-commands.setcharactercleaning = cleaners.set
+if not modules then modules = { } end modules ['typo-cln'] = {
+ version = 1.001,
+ comment = "companion to typo-cln.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This quick and dirty hack took less time than listening to a CD (In
+-- this case Dream Theaters' Octavium. Of course extensions will take
+-- more time.
+
+local utfbyte = utf.byte
+
+local trace_cleaners = false trackers.register("typesetters.cleaners", function(v) trace_cleaners = v end)
+local trace_autocase = false trackers.register("typesetters.cleaners.autocase",function(v) trace_autocase = v end)
+
+local report_cleaners = logs.reporter("nodes","cleaners")
+local report_autocase = logs.reporter("nodes","autocase")
+
+typesetters.cleaners = typesetters.cleaners or { }
+local cleaners = typesetters.cleaners
+
+local variables = interfaces.variables
+
+local nodecodes = nodes.nodecodes
+local tasks = nodes.tasks
+
+local texattribute = tex.attribute
+
+local traverse_id = node.traverse_id
+
+local unsetvalue = attributes.unsetvalue
+
+local glyph_code = nodecodes.glyph
+local uccodes = characters.uccodes
+
+local a_cleaner = attributes.private("cleaner")
+
+local resetter = { -- this will become an entry in char-def
+ [utfbyte(".")] = true
+}
+
+-- Contrary to the casing code we need to keep track of a state.
+-- We could extend the casing code with a status tracker but on
+-- the other hand we might want to apply casing afterwards. So,
+-- cleaning comes first.
+
+local function process(namespace,attribute,head)
+ local inline, done = false, false
+ for n in traverse_id(glyph_code,head) do
+ local char = n.char
+ if resetter[char] then
+ inline = false
+ elseif not inline then
+ local a = n[attribute]
+ if a == 1 then -- currently only one cleaner so no need to be fancy
+ local upper = uccodes[char]
+ if type(upper) == "table" then
+ -- some day, not much change that \SS ends up here
+ else
+ n.char = upper
+ done = true
+ if trace_autocase then
+ report_autocase("")
+ end
+ end
+ end
+ inline = true
+ end
+ end
+ return head, done
+end
+
+-- see typo-cap for a more advanced settings handler .. not needed now
+
+local enabled = false
+
+function cleaners.set(n)
+ if n == variables.reset or not tonumber(n) or n == 0 then
+ texattribute[a_cleaner] = unsetvalue
+ else
+ if not enabled then
+ tasks.enableaction("processors","typesetters.cleaners.handler")
+ if trace_cleaners then
+ report_cleaners("enabling cleaners")
+ end
+ enabled = true
+ end
+ texattribute[a_cleaner] = n
+ end
+end
+
+cleaners.handler = nodes.installattributehandler {
+ name = "cleaner",
+ namespace = cleaners,
+ processor = process,
+}
+
+-- interface
+
+commands.setcharactercleaning = cleaners.set
diff --git a/tex/context/base/typo-dig.lua b/tex/context/base/typo-dig.lua
index 9cf8417b8..62d17fa3b 100644
--- a/tex/context/base/typo-dig.lua
+++ b/tex/context/base/typo-dig.lua
@@ -1,162 +1,162 @@
-if not modules then modules = { } end modules ['typo-dig'] = {
- version = 1.001,
- 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 = next, type
-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 hpack_node = node.hpack
-local traverse_id = node.traverse_id
-local insert_node_before = node.insert_before
-local insert_node_after = node.insert_after
-
-local texattribute = tex.attribute
-local unsetvalue = attributes.unsetvalue
-
-local nodecodes = nodes.nodecodes
-local glyph_code = nodecodes.glyph
-
-local nodepool = nodes.pool
-local tasks = nodes.tasks
-
-local new_glue = nodepool.glue
-
-local fonthashes = fonts.hashes
-local fontdata = fonthashes.identifiers
-local chardata = fonthashes.characters
-local quaddata = fonthashes.quads
-
-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")
-digits.attribute = a_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, nxt = start.prev, stop.next
- start.prev, stop.next = nil, nil
- local packed = hpack_node(start,width,"exactly") -- no directional mess here, just lr
- if prv then
- prv.next, packed.prev = packed, prv
- end
- if nxt then
- nxt.prev, packed.next = packed, nxt
- end
- if packed.prev then
- return head, packed
- else
- return packed, packed
- end
-end
-
-actions[1] = function(head,start,attribute,attr)
- local font = start.font
- local char = start.char
- local unic = chardata[font][char].tounicode
- local what = unic and tonumber(unic,16) or char
- if charbase[what].category == "nd" then
- local oldwidth, newwidth = start.width, 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,what,newwidth-oldwidth)
- end
- head, start = nodes.aligned(head,start,start,newwidth,"middle")
- return head, start, true
- end
- end
- return head, start, false
-end
-
-local function process(namespace,attribute,head)
- local done, current, ok = false, head, false
- while current do
- if current.id == glyph_code then
- local attr = current[attribute]
- if attr and attr > 0 then
- current[attribute] = unsetvalue
- local action = actions[attr%100] -- map back to low number
- if action then
- head, current, ok = action(head,current,attribute,attr)
- done = done and ok
- elseif trace_digits then
- report_digits("unknown digit trigger %a",attr)
- end
- end
- end
- current = current and current.next
- end
- return head, done
-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
- tasks.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
- texattribute[a_digits] = n
-end
-
-digits.handler = nodes.installattributehandler { -- we could avoid this wrapper
- name = "digits",
- namespace = digits,
- processor = process,
-}
-
--- interface
-
-commands.setdigitsmanipulation = digits.set
+if not modules then modules = { } end modules ['typo-dig'] = {
+ version = 1.001,
+ 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 = next, type
+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 hpack_node = node.hpack
+local traverse_id = node.traverse_id
+local insert_node_before = node.insert_before
+local insert_node_after = node.insert_after
+
+local texattribute = tex.attribute
+local unsetvalue = attributes.unsetvalue
+
+local nodecodes = nodes.nodecodes
+local glyph_code = nodecodes.glyph
+
+local nodepool = nodes.pool
+local tasks = nodes.tasks
+
+local new_glue = nodepool.glue
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+local chardata = fonthashes.characters
+local quaddata = fonthashes.quads
+
+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")
+digits.attribute = a_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, nxt = start.prev, stop.next
+ start.prev, stop.next = nil, nil
+ local packed = hpack_node(start,width,"exactly") -- no directional mess here, just lr
+ if prv then
+ prv.next, packed.prev = packed, prv
+ end
+ if nxt then
+ nxt.prev, packed.next = packed, nxt
+ end
+ if packed.prev then
+ return head, packed
+ else
+ return packed, packed
+ end
+end
+
+actions[1] = function(head,start,attribute,attr)
+ local font = start.font
+ local char = start.char
+ local unic = chardata[font][char].tounicode
+ local what = unic and tonumber(unic,16) or char
+ if charbase[what].category == "nd" then
+ local oldwidth, newwidth = start.width, 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,what,newwidth-oldwidth)
+ end
+ head, start = nodes.aligned(head,start,start,newwidth,"middle")
+ return head, start, true
+ end
+ end
+ return head, start, false
+end
+
+local function process(namespace,attribute,head)
+ local done, current, ok = false, head, false
+ while current do
+ if current.id == glyph_code then
+ local attr = current[attribute]
+ if attr and attr > 0 then
+ current[attribute] = unsetvalue
+ local action = actions[attr%100] -- map back to low number
+ if action then
+ head, current, ok = action(head,current,attribute,attr)
+ done = done and ok
+ elseif trace_digits then
+ report_digits("unknown digit trigger %a",attr)
+ end
+ end
+ end
+ current = current and current.next
+ end
+ return head, done
+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
+ tasks.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
+ texattribute[a_digits] = n
+end
+
+digits.handler = nodes.installattributehandler { -- we could avoid this wrapper
+ name = "digits",
+ namespace = digits,
+ processor = process,
+}
+
+-- interface
+
+commands.setdigitsmanipulation = digits.set
diff --git a/tex/context/base/typo-dir.lua b/tex/context/base/typo-dir.lua
index f02395475..7e5f8c2d3 100644
--- a/tex/context/base/typo-dir.lua
+++ b/tex/context/base/typo-dir.lua
@@ -1,463 +1,463 @@
-if not modules then modules = { } end modules ['typo-dir'] = {
- 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"
-}
-
--- todo: also use end_of_math here?
-
-local next, type = next, type
-local format, insert, sub, find, match = string.format, table.insert, string.sub, string.find, string.match
-local utfchar = utf.char
-
--- vertical space handler
-
-local nodes, node = nodes, node
-
-local trace_directions = false trackers.register("typesetters.directions", function(v) trace_directions = v end)
-
-local report_directions = logs.reporter("typesetting","directions")
-
-local traverse_id = node.traverse_id
-local insert_node_before = node.insert_before
-local insert_node_after = node.insert_after
-local remove_node = nodes.remove
-
-local texattribute = tex.attribute
-local unsetvalue = attributes.unsetvalue
-
-local nodecodes = nodes.nodecodes
-local whatcodes = nodes.whatcodes
-local mathcodes = nodes.mathcodes
-
-local tasks = nodes.tasks
-
-local glyph_code = nodecodes.glyph
-local whatsit_code = nodecodes.whatsit
-local math_code = nodecodes.math
-
-local localpar_code = whatcodes.localpar
-local dir_code = whatcodes.dir
-
-local nodepool = nodes.pool
-
-local new_textdir = nodepool.textdir
-
-local beginmath_code = mathcodes.beginmath
-local endmath_code = mathcodes.endmath
-
-local fonthashes = fonts.hashes
-local fontdata = fonthashes.identifiers
-local fontchar = fonthashes.characters
-
-local chardata = characters.data
-local chardirs = characters.directions -- maybe make a special mirror table
-
---~ Analysis by Idris:
---~
---~ 1. Assuming the reading- vs word-order distinction (bidi-char types) is governing;
---~ 2. Assuming that 'ARAB' represents an actual arabic string in raw input order, not word-order;
---~ 3. Assuming that 'BARA' represent the correct RL word order;
---~
---~ Then we have, with input: LATIN ARAB
---~
---~ \textdir TLT LATIN ARAB => LATIN BARA
---~ \textdir TRT LATIN ARAB => LATIN BARA
---~ \textdir TRT LRO LATIN ARAB => LATIN ARAB
---~ \textdir TLT LRO LATIN ARAB => LATIN ARAB
---~ \textdir TLT RLO LATIN ARAB => NITAL ARAB
---~ \textdir TRT RLO LATIN ARAB => NITAL ARAB
-
--- elseif d == "es" then -- European Number Separator
--- elseif d == "et" then -- European Number Terminator
--- elseif d == "cs" then -- Common Number Separator
--- elseif d == "nsm" then -- Non-Spacing Mark
--- elseif d == "bn" then -- Boundary Neutral
--- elseif d == "b" then -- Paragraph Separator
--- elseif d == "s" then -- Segment Separator
--- elseif d == "ws" then -- Whitespace
--- elseif d == "on" then -- Other Neutrals
-
-typesetters.directions = typesetters.directions or { }
-local directions = typesetters.directions
-
-local a_state = attributes.private('state')
-local a_directions = attributes.private('directions')
-
-local skipmath = true
-local strip = false
-
--- todo: delayed inserts here
--- todo: get rid of local functions here
-
--- beware, math adds whatsits afterwards so that will mess things up
-
-local finish, autodir, embedded, override, done = nil, 0, 0, 0, false
-local list, glyphs = nil, false
-local finished, finidir, finipos = nil, nil, 1
-local head, current, inserted = nil, nil, nil
-
-local function finish_auto_before()
- head, inserted = insert_node_before(head,current,new_textdir("-"..finish))
- finished, finidir = inserted, finish
- if trace_directions then
- insert(list,#list,format("auto finish inserted before: %s",finish))
- finipos = #list-1
- end
- finish, autodir, done = nil, 0, true
-end
-
-local function finish_auto_after()
- head, current = insert_node_after(head,current,new_textdir("-"..finish))
- finished, finidir = current, finish
- if trace_directions then
- list[#list+1] = format("auto finish inserted after: %s",finish)
- finipos = #list
- end
- finish, autodir, done = nil, 0, true
-end
-
-local function force_auto_left_before()
- if finish then
- finish_auto_before()
- end
- if embedded >= 0 then
- finish, autodir, done = "TLT", 1, true
- else
- finish, autodir, done = "TRT", -1, true
- end
- if finidir == finish then
- head = remove_node(head,finished,true)
- if trace_directions then
- list[finipos] = list[finipos] .. " (deleted afterwards)"
- insert(list,#list,format("start text dir %s (embedded: %s)",finish,embedded))
- end
- else
- head, inserted = insert_node_before(head,current,new_textdir("+"..finish))
- if trace_directions then
- insert(list,#list,format("start text dir %s (embedded: %s)",finish,embedded))
- end
- end
-end
-
-local function force_auto_right_before()
- if finish then
- finish_auto_before()
- end
- if embedded <= 0 then
- finish, autodir, done = "TRT", -1, true
- else
- finish, autodir, done = "TLT", 1, true
- end
- if finidir == finish then
- head = remove_node(head,finished,true)
- if trace_directions then
- list[finipos] = list[finipos] .. " (deleted afterwards)"
- insert(list,#list,format("start text dir %s (embedded: %s)",finish,embedded))
- end
- else
- head, inserted = insert_node_before(head,current,new_textdir("+"..finish))
- if trace_directions then
- insert(list,#list,format("start text dir %s (embedded: %s)",finish,embedded))
- end
- end
-end
-
--- todo: use new dir functions
-
-local s_isol = fonts.analyzers.states.isol
-
-function directions.process(namespace,attribute,start) -- todo: make faster
- if not start.next then
- return start, false
- end
- head, current, inserted = start, start, nil
- finish, autodir, embedded, override, done = nil, 0, 0, 0, false
- list, glyphs = trace_directions and { }, false
- finished, finidir, finipos = nil, nil, 1
- local stack, top, obsolete = { }, 0, { }
- local lro, rlo, prevattr, inmath = false, false, 0, false
- while current do
- local id = current.id
- if skipmath and id == math_code then
- local subtype = current.subtype
- if subtype == beginmath_code then
- inmath = true
- elseif subtype == endmath_code then
- inmath = false
- else
- -- todo
- end
- current = current.next
- elseif inmath then
- current = current.next
- else
- local attr = current[attribute]
- if attr and attr > 0 then
- -- current[attribute] = unsetvalue -- slow, needed?
- if attr == 1 then
- -- bidi parsing mode
- elseif attr ~= prevattr then
- -- no pop, grouped driven (2=normal,3=lro,4=rlo)
- if attr == 3 then
- if trace_directions then
- list[#list+1] = format("override right -> left (lro) (bidi=%s)",attr)
- end
- lro, rlo = true, false
- elseif attr == 4 then
- if trace_directions then
- list[#list+1] = format("override left -> right (rlo) (bidi=%s)",attr)
- end
- lro, rlo = false, true
- else
- if trace_directions and
- current ~= head then list[#list+1] = format("override reset (bidi=%s)",attr)
- end
- lro, rlo = false, false
- end
- prevattr = attr
- end
- end
- if id == glyph_code then
- glyphs = true
- if attr and attr > 0 then
- local char = current.char
- local d = chardirs[char]
- if rlo or override > 0 then
- if d == "l" then
- if trace_directions then
- list[#list+1] = format("char %s (%s / U+%04X) of class %s overidden to r (bidi=%s)",utfchar(char),char,char,d,attr)
- end
- d = "r"
- elseif trace_directions then
- if d == "lro" or d == "rlo" or d == "pdf" then -- else side effects on terminal
- list[#list+1] = format("override char of class %s (bidi=%s)",d,attr)
- else -- todo: rle lre
- list[#list+1] = format("char %s (%s / U+%04X) of class %s (bidi=%s)",utfchar(char),char,char,d,attr)
- end
- end
- elseif lro or override < 0 then
- if d == "r" or d == "al" then
- current[a_state] = s_isol -- maybe better have a special bidi attr value -> override (9) -> todo
- if trace_directions then
- list[#list+1] = format("char %s (%s / U+%04X) of class %s overidden to l (bidi=%s) (state=isol)",utfchar(char),char,char,d,attr)
- end
- d = "l"
- elseif trace_directions then
- if d == "lro" or d == "rlo" or d == "pdf" then -- else side effects on terminal
- list[#list+1] = format("override char of class %s (bidi=%s)",d,attr)
- else -- todo: rle lre
- list[#list+1] = format("char %s (%s / U+%04X) of class %s (bidi=%s)",utfchar(char),char,char,d,attr)
- end
- end
- elseif trace_directions then
- if d == "lro" or d == "rlo" or d == "pdf" then -- else side effects on terminal
- list[#list+1] = format("override char of class %s (bidi=%s)",d,attr)
- else -- todo: rle lre
- list[#list+1] = format("char %s (%s / U+%04X) of class %s (bidi=%s)",utfchar(char),char,char,d,attr)
- end
- end
- if d == "on" then
- local mirror = chardata[char].mirror -- maybe make a special mirror table
- if mirror and fontchar[current.font][mirror] then
- -- todo: set attribute
- if autodir < 0 then
- current.char = mirror
- done = true
- --~ elseif left or autodir > 0 then
- --~ if not is_right(current.prev) then
- --~ current.char = mirror
- --~ done = true
- --~ end
- end
- end
- elseif d == "l" or d == "en" then -- european number
- if autodir <= 0 then -- could be option
- force_auto_left_before()
- end
- elseif d == "r" or d == "al" then -- arabic number
- if autodir >= 0 then
- force_auto_right_before()
- end
- elseif d == "an" then -- arabic number
- -- actually this is language dependent ...
--- if autodir <= 0 then
--- force_auto_left_before()
--- end
- if autodir >= 0 then
- force_auto_right_before()
- end
- elseif d == "lro" then -- Left-to-Right Override -> right becomes left
- if trace_directions then
- list[#list+1] = "override right -> left"
- end
- top = top + 1
- stack[top] = { override, embedded }
- override = -1
- obsolete[#obsolete+1] = current
- elseif d == "rlo" then -- Right-to-Left Override -> left becomes right
- if trace_directions then
- list[#list+1] = "override left -> right"
- end
- top = top + 1
- stack[top] = { override, embedded }
- override = 1
- obsolete[#obsolete+1] = current
- elseif d == "lre" then -- Left-to-Right Embedding -> TLT
- if trace_directions then
- list[#list+1] = "embedding left -> right"
- end
- top = top + 1
- stack[top] = { override, embedded }
- embedded = 1
- obsolete[#obsolete+1] = current
- elseif d == "rle" then -- Right-to-Left Embedding -> TRT
- if trace_directions then
- list[#list+1] = "embedding right -> left"
- end
- top = top + 1
- stack[top] = { override, embedded }
- embedded = -1 -- was 1
- obsolete[#obsolete+1] = current
- elseif d == "pdf" then -- Pop Directional Format
- -- override = 0
- if top > 0 then
- local s = stack[top]
- override, embedded = s[1], s[2]
- top = top - 1
- if trace_directions then
- list[#list+1] = format("state: override: %s, embedded: %s, autodir: %s",override,embedded,autodir)
- end
- else
- if trace_directions then
- list[#list+1] = "pop (error, too many pops)"
- end
- end
- obsolete[#obsolete+1] = current
- end
- elseif trace_directions then
- local char = current.char
- local d = chardirs[char]
- list[#list+1] = format("char %s (%s / U+%04X) of class %s (no bidi)",utfchar(char),char,char,d or "?")
- end
- elseif id == whatsit_code then
- if finish then
- finish_auto_before()
- end
- local subtype = current.subtype
- if subtype == localpar_code then
- local dir = current.dir
- local d = sub(dir,2,2)
- if d == 'R' then -- find(dir,".R.") / dir == "TRT"
- autodir = -1
- else
- autodir = 1
- end
- -- embedded = autodir
- if trace_directions then
- list[#list+1] = format("pardir %s",dir)
- end
- elseif subtype == dir_code then
- local dir = current.dir
- -- local sign = sub(dir,1,1)
- -- local dire = sub(dir,3,3)
- local sign, dire = match(dir,"^(.).(.)")
- if dire == "R" then
- if sign == "+" then
- finish, autodir = "TRT", -1
- else
- finish, autodir = nil, 0
- end
- else
- if sign == "+" then
- finish, autodir = "TLT", 1
- else
- finish, autodir = nil, 0
- end
- end
- if trace_directions then
- list[#list+1] = format("textdir %s",dir)
- end
- end
- else
- if trace_directions then
- list[#list+1] = format("node %s (subtype %s)",nodecodes[id],current.subtype)
- end
- if finish then
- finish_auto_before()
- end
- end
- local cn = current.next
- if not cn then
- if finish then
- finish_auto_after()
- end
- end
- current = cn
- end
- end
- if trace_directions and glyphs then
- report_directions("start log")
- for i=1,#list do
- report_directions("%02i: %s",i,list[i])
- end
- report_directions("stop log")
- end
- if done and strip then
- local n = #obsolete
- if n > 0 then
- for i=1,n do
- remove_node(head,obsolete[i],true)
- end
- report_directions("%s character nodes removed",n)
- end
- end
- return head, done
-end
-
---~ local function is_right(n) -- keep !
---~ if n then
---~ local id = n.id
---~ if id == glyph_code then
---~ local attr = n[attribute]
---~ if attr and attr > 0 then
---~ local d = chardirs[n.char]
---~ if d == "r" or d == "al" then -- override
---~ return true
---~ end
---~ end
---~ end
---~ end
---~ return false
---~ end
-
---~ function directions.enable()
---~ tasks.enableaction("processors","directions.handler")
---~ end
-
-local enabled = false
-
-function directions.set(n) -- todo: names and numbers
- if not enabled then
- if trace_directions then
- report_breakpoints("enabling directions handler")
- end
- tasks.enableaction("processors","typesetters.directions.handler")
- enabled = true
- end
- if not n or n == 0 then
- n = unsetvalue
- -- maybe tracing
- end
- texattribute[a_directions] = n
-end
-
-commands.setdirection = directions.set
-
-directions.handler = nodes.installattributehandler {
- name = "directions",
- namespace = directions,
- processor = directions.process,
-}
+if not modules then modules = { } end modules ['typo-dir'] = {
+ 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"
+}
+
+-- todo: also use end_of_math here?
+
+local next, type = next, type
+local format, insert, sub, find, match = string.format, table.insert, string.sub, string.find, string.match
+local utfchar = utf.char
+
+-- vertical space handler
+
+local nodes, node = nodes, node
+
+local trace_directions = false trackers.register("typesetters.directions", function(v) trace_directions = v end)
+
+local report_directions = logs.reporter("typesetting","directions")
+
+local traverse_id = node.traverse_id
+local insert_node_before = node.insert_before
+local insert_node_after = node.insert_after
+local remove_node = nodes.remove
+
+local texattribute = tex.attribute
+local unsetvalue = attributes.unsetvalue
+
+local nodecodes = nodes.nodecodes
+local whatcodes = nodes.whatcodes
+local mathcodes = nodes.mathcodes
+
+local tasks = nodes.tasks
+
+local glyph_code = nodecodes.glyph
+local whatsit_code = nodecodes.whatsit
+local math_code = nodecodes.math
+
+local localpar_code = whatcodes.localpar
+local dir_code = whatcodes.dir
+
+local nodepool = nodes.pool
+
+local new_textdir = nodepool.textdir
+
+local beginmath_code = mathcodes.beginmath
+local endmath_code = mathcodes.endmath
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+local fontchar = fonthashes.characters
+
+local chardata = characters.data
+local chardirs = characters.directions -- maybe make a special mirror table
+
+--~ Analysis by Idris:
+--~
+--~ 1. Assuming the reading- vs word-order distinction (bidi-char types) is governing;
+--~ 2. Assuming that 'ARAB' represents an actual arabic string in raw input order, not word-order;
+--~ 3. Assuming that 'BARA' represent the correct RL word order;
+--~
+--~ Then we have, with input: LATIN ARAB
+--~
+--~ \textdir TLT LATIN ARAB => LATIN BARA
+--~ \textdir TRT LATIN ARAB => LATIN BARA
+--~ \textdir TRT LRO LATIN ARAB => LATIN ARAB
+--~ \textdir TLT LRO LATIN ARAB => LATIN ARAB
+--~ \textdir TLT RLO LATIN ARAB => NITAL ARAB
+--~ \textdir TRT RLO LATIN ARAB => NITAL ARAB
+
+-- elseif d == "es" then -- European Number Separator
+-- elseif d == "et" then -- European Number Terminator
+-- elseif d == "cs" then -- Common Number Separator
+-- elseif d == "nsm" then -- Non-Spacing Mark
+-- elseif d == "bn" then -- Boundary Neutral
+-- elseif d == "b" then -- Paragraph Separator
+-- elseif d == "s" then -- Segment Separator
+-- elseif d == "ws" then -- Whitespace
+-- elseif d == "on" then -- Other Neutrals
+
+typesetters.directions = typesetters.directions or { }
+local directions = typesetters.directions
+
+local a_state = attributes.private('state')
+local a_directions = attributes.private('directions')
+
+local skipmath = true
+local strip = false
+
+-- todo: delayed inserts here
+-- todo: get rid of local functions here
+
+-- beware, math adds whatsits afterwards so that will mess things up
+
+local finish, autodir, embedded, override, done = nil, 0, 0, 0, false
+local list, glyphs = nil, false
+local finished, finidir, finipos = nil, nil, 1
+local head, current, inserted = nil, nil, nil
+
+local function finish_auto_before()
+ head, inserted = insert_node_before(head,current,new_textdir("-"..finish))
+ finished, finidir = inserted, finish
+ if trace_directions then
+ insert(list,#list,format("auto finish inserted before: %s",finish))
+ finipos = #list-1
+ end
+ finish, autodir, done = nil, 0, true
+end
+
+local function finish_auto_after()
+ head, current = insert_node_after(head,current,new_textdir("-"..finish))
+ finished, finidir = current, finish
+ if trace_directions then
+ list[#list+1] = format("auto finish inserted after: %s",finish)
+ finipos = #list
+ end
+ finish, autodir, done = nil, 0, true
+end
+
+local function force_auto_left_before()
+ if finish then
+ finish_auto_before()
+ end
+ if embedded >= 0 then
+ finish, autodir, done = "TLT", 1, true
+ else
+ finish, autodir, done = "TRT", -1, true
+ end
+ if finidir == finish then
+ head = remove_node(head,finished,true)
+ if trace_directions then
+ list[finipos] = list[finipos] .. " (deleted afterwards)"
+ insert(list,#list,format("start text dir %s (embedded: %s)",finish,embedded))
+ end
+ else
+ head, inserted = insert_node_before(head,current,new_textdir("+"..finish))
+ if trace_directions then
+ insert(list,#list,format("start text dir %s (embedded: %s)",finish,embedded))
+ end
+ end
+end
+
+local function force_auto_right_before()
+ if finish then
+ finish_auto_before()
+ end
+ if embedded <= 0 then
+ finish, autodir, done = "TRT", -1, true
+ else
+ finish, autodir, done = "TLT", 1, true
+ end
+ if finidir == finish then
+ head = remove_node(head,finished,true)
+ if trace_directions then
+ list[finipos] = list[finipos] .. " (deleted afterwards)"
+ insert(list,#list,format("start text dir %s (embedded: %s)",finish,embedded))
+ end
+ else
+ head, inserted = insert_node_before(head,current,new_textdir("+"..finish))
+ if trace_directions then
+ insert(list,#list,format("start text dir %s (embedded: %s)",finish,embedded))
+ end
+ end
+end
+
+-- todo: use new dir functions
+
+local s_isol = fonts.analyzers.states.isol
+
+function directions.process(namespace,attribute,start) -- todo: make faster
+ if not start.next then
+ return start, false
+ end
+ head, current, inserted = start, start, nil
+ finish, autodir, embedded, override, done = nil, 0, 0, 0, false
+ list, glyphs = trace_directions and { }, false
+ finished, finidir, finipos = nil, nil, 1
+ local stack, top, obsolete = { }, 0, { }
+ local lro, rlo, prevattr, inmath = false, false, 0, false
+ while current do
+ local id = current.id
+ if skipmath and id == math_code then
+ local subtype = current.subtype
+ if subtype == beginmath_code then
+ inmath = true
+ elseif subtype == endmath_code then
+ inmath = false
+ else
+ -- todo
+ end
+ current = current.next
+ elseif inmath then
+ current = current.next
+ else
+ local attr = current[attribute]
+ if attr and attr > 0 then
+ -- current[attribute] = unsetvalue -- slow, needed?
+ if attr == 1 then
+ -- bidi parsing mode
+ elseif attr ~= prevattr then
+ -- no pop, grouped driven (2=normal,3=lro,4=rlo)
+ if attr == 3 then
+ if trace_directions then
+ list[#list+1] = format("override right -> left (lro) (bidi=%s)",attr)
+ end
+ lro, rlo = true, false
+ elseif attr == 4 then
+ if trace_directions then
+ list[#list+1] = format("override left -> right (rlo) (bidi=%s)",attr)
+ end
+ lro, rlo = false, true
+ else
+ if trace_directions and
+ current ~= head then list[#list+1] = format("override reset (bidi=%s)",attr)
+ end
+ lro, rlo = false, false
+ end
+ prevattr = attr
+ end
+ end
+ if id == glyph_code then
+ glyphs = true
+ if attr and attr > 0 then
+ local char = current.char
+ local d = chardirs[char]
+ if rlo or override > 0 then
+ if d == "l" then
+ if trace_directions then
+ list[#list+1] = format("char %s (%s / U+%04X) of class %s overidden to r (bidi=%s)",utfchar(char),char,char,d,attr)
+ end
+ d = "r"
+ elseif trace_directions then
+ if d == "lro" or d == "rlo" or d == "pdf" then -- else side effects on terminal
+ list[#list+1] = format("override char of class %s (bidi=%s)",d,attr)
+ else -- todo: rle lre
+ list[#list+1] = format("char %s (%s / U+%04X) of class %s (bidi=%s)",utfchar(char),char,char,d,attr)
+ end
+ end
+ elseif lro or override < 0 then
+ if d == "r" or d == "al" then
+ current[a_state] = s_isol -- maybe better have a special bidi attr value -> override (9) -> todo
+ if trace_directions then
+ list[#list+1] = format("char %s (%s / U+%04X) of class %s overidden to l (bidi=%s) (state=isol)",utfchar(char),char,char,d,attr)
+ end
+ d = "l"
+ elseif trace_directions then
+ if d == "lro" or d == "rlo" or d == "pdf" then -- else side effects on terminal
+ list[#list+1] = format("override char of class %s (bidi=%s)",d,attr)
+ else -- todo: rle lre
+ list[#list+1] = format("char %s (%s / U+%04X) of class %s (bidi=%s)",utfchar(char),char,char,d,attr)
+ end
+ end
+ elseif trace_directions then
+ if d == "lro" or d == "rlo" or d == "pdf" then -- else side effects on terminal
+ list[#list+1] = format("override char of class %s (bidi=%s)",d,attr)
+ else -- todo: rle lre
+ list[#list+1] = format("char %s (%s / U+%04X) of class %s (bidi=%s)",utfchar(char),char,char,d,attr)
+ end
+ end
+ if d == "on" then
+ local mirror = chardata[char].mirror -- maybe make a special mirror table
+ if mirror and fontchar[current.font][mirror] then
+ -- todo: set attribute
+ if autodir < 0 then
+ current.char = mirror
+ done = true
+ --~ elseif left or autodir > 0 then
+ --~ if not is_right(current.prev) then
+ --~ current.char = mirror
+ --~ done = true
+ --~ end
+ end
+ end
+ elseif d == "l" or d == "en" then -- european number
+ if autodir <= 0 then -- could be option
+ force_auto_left_before()
+ end
+ elseif d == "r" or d == "al" then -- arabic number
+ if autodir >= 0 then
+ force_auto_right_before()
+ end
+ elseif d == "an" then -- arabic number
+ -- actually this is language dependent ...
+-- if autodir <= 0 then
+-- force_auto_left_before()
+-- end
+ if autodir >= 0 then
+ force_auto_right_before()
+ end
+ elseif d == "lro" then -- Left-to-Right Override -> right becomes left
+ if trace_directions then
+ list[#list+1] = "override right -> left"
+ end
+ top = top + 1
+ stack[top] = { override, embedded }
+ override = -1
+ obsolete[#obsolete+1] = current
+ elseif d == "rlo" then -- Right-to-Left Override -> left becomes right
+ if trace_directions then
+ list[#list+1] = "override left -> right"
+ end
+ top = top + 1
+ stack[top] = { override, embedded }
+ override = 1
+ obsolete[#obsolete+1] = current
+ elseif d == "lre" then -- Left-to-Right Embedding -> TLT
+ if trace_directions then
+ list[#list+1] = "embedding left -> right"
+ end
+ top = top + 1
+ stack[top] = { override, embedded }
+ embedded = 1
+ obsolete[#obsolete+1] = current
+ elseif d == "rle" then -- Right-to-Left Embedding -> TRT
+ if trace_directions then
+ list[#list+1] = "embedding right -> left"
+ end
+ top = top + 1
+ stack[top] = { override, embedded }
+ embedded = -1 -- was 1
+ obsolete[#obsolete+1] = current
+ elseif d == "pdf" then -- Pop Directional Format
+ -- override = 0
+ if top > 0 then
+ local s = stack[top]
+ override, embedded = s[1], s[2]
+ top = top - 1
+ if trace_directions then
+ list[#list+1] = format("state: override: %s, embedded: %s, autodir: %s",override,embedded,autodir)
+ end
+ else
+ if trace_directions then
+ list[#list+1] = "pop (error, too many pops)"
+ end
+ end
+ obsolete[#obsolete+1] = current
+ end
+ elseif trace_directions then
+ local char = current.char
+ local d = chardirs[char]
+ list[#list+1] = format("char %s (%s / U+%04X) of class %s (no bidi)",utfchar(char),char,char,d or "?")
+ end
+ elseif id == whatsit_code then
+ if finish then
+ finish_auto_before()
+ end
+ local subtype = current.subtype
+ if subtype == localpar_code then
+ local dir = current.dir
+ local d = sub(dir,2,2)
+ if d == 'R' then -- find(dir,".R.") / dir == "TRT"
+ autodir = -1
+ else
+ autodir = 1
+ end
+ -- embedded = autodir
+ if trace_directions then
+ list[#list+1] = format("pardir %s",dir)
+ end
+ elseif subtype == dir_code then
+ local dir = current.dir
+ -- local sign = sub(dir,1,1)
+ -- local dire = sub(dir,3,3)
+ local sign, dire = match(dir,"^(.).(.)")
+ if dire == "R" then
+ if sign == "+" then
+ finish, autodir = "TRT", -1
+ else
+ finish, autodir = nil, 0
+ end
+ else
+ if sign == "+" then
+ finish, autodir = "TLT", 1
+ else
+ finish, autodir = nil, 0
+ end
+ end
+ if trace_directions then
+ list[#list+1] = format("textdir %s",dir)
+ end
+ end
+ else
+ if trace_directions then
+ list[#list+1] = format("node %s (subtype %s)",nodecodes[id],current.subtype)
+ end
+ if finish then
+ finish_auto_before()
+ end
+ end
+ local cn = current.next
+ if not cn then
+ if finish then
+ finish_auto_after()
+ end
+ end
+ current = cn
+ end
+ end
+ if trace_directions and glyphs then
+ report_directions("start log")
+ for i=1,#list do
+ report_directions("%02i: %s",i,list[i])
+ end
+ report_directions("stop log")
+ end
+ if done and strip then
+ local n = #obsolete
+ if n > 0 then
+ for i=1,n do
+ remove_node(head,obsolete[i],true)
+ end
+ report_directions("%s character nodes removed",n)
+ end
+ end
+ return head, done
+end
+
+--~ local function is_right(n) -- keep !
+--~ if n then
+--~ local id = n.id
+--~ if id == glyph_code then
+--~ local attr = n[attribute]
+--~ if attr and attr > 0 then
+--~ local d = chardirs[n.char]
+--~ if d == "r" or d == "al" then -- override
+--~ return true
+--~ end
+--~ end
+--~ end
+--~ end
+--~ return false
+--~ end
+
+--~ function directions.enable()
+--~ tasks.enableaction("processors","directions.handler")
+--~ end
+
+local enabled = false
+
+function directions.set(n) -- todo: names and numbers
+ if not enabled then
+ if trace_directions then
+ report_breakpoints("enabling directions handler")
+ end
+ tasks.enableaction("processors","typesetters.directions.handler")
+ enabled = true
+ end
+ if not n or n == 0 then
+ n = unsetvalue
+ -- maybe tracing
+ end
+ texattribute[a_directions] = n
+end
+
+commands.setdirection = directions.set
+
+directions.handler = nodes.installattributehandler {
+ name = "directions",
+ namespace = directions,
+ processor = directions.process,
+}
diff --git a/tex/context/base/typo-ini.lua b/tex/context/base/typo-ini.lua
index 42c752c31..c45d29664 100644
--- a/tex/context/base/typo-ini.lua
+++ b/tex/context/base/typo-ini.lua
@@ -1,11 +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 { }
+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/tex/context/base/typo-itc.lua b/tex/context/base/typo-itc.lua
index bee2cf41e..b39ea2f23 100644
--- a/tex/context/base/typo-itc.lua
+++ b/tex/context/base/typo-itc.lua
@@ -1,256 +1,256 @@
-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 utfchar = utf.char
-
-local trace_italics = false trackers.register("typesetters.italics", function(v) trace_italics = v end)
-
-local report_italics = logs.reporter("nodes","italics")
-
-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 tasks = nodes.tasks
-
-local insert_node_after = node.insert_after
-local delete_node = nodes.delete
-local end_of_math = node.end_of_math
-
-local texattribute = tex.attribute
-local a_italics = attributes.private("italics")
-local unsetvalue = attributes.unsetvalue
-
-local new_correction_kern = nodes.pool.fontkern
-local new_correction_glue = nodes.pool.glue
-
-local fonthashes = fonts.hashes
-local fontdata = fonthashes.identifiers
-local italicsdata = fonthashes.italics
-
-local forcedvariant = false
-
-function typesetters.italics.forcevariant(variant)
- forcedvariant = variant
-end
-
-local function setitalicinfont(font,char)
- local tfmdata = fontdata[font]
- local character = tfmdata.characters[char]
- if character then
- local italic = character.italic_correction
- 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
- character.italic_correction = italic or 0
- end
- return italic
- else
- return 0
- end
-end
-
--- todo: clear attribute
-
-local function process(namespace,attribute,head)
- local done = false
- local italic = 0
- local lastfont = nil
- local lastattr = nil
- local previous = nil
- local prevchar = nil
- local current = head
- local inserted = nil
- while current do
- local id = current.id
- if id == glyph_code then
- local font = current.font
- local char = current.char
- local data = italicsdata[font]
- if font ~= lastfont then
- if italic ~= 0 then
- if data then
- if trace_italics then
- report_italics("ignoring %p between italic %C and italic %C",italic,prevchar,char)
- end
- else
- if trace_italics then
- report_italics("inserting %p between italic %C and regular %C",italic,prevchar,char)
- end
- insert_node_after(head,previous,new_correction_kern(italic))
- done = true
- end
- elseif inserted and data then
- if trace_italics then
- report_italics("deleting last correction before %C",char)
- end
- delete_node(head,inserted)
- else
- -- nothing
- end
- lastfont = font
- end
- if data then
- local attr = forcedvariant or current[attribute]
- if attr and attr > 0 then
- local cd = data[char]
- if not cd then
- -- this really can happen
- italic = 0
- else
- italic = cd.italic or cd.italic_correction
- if not italic then
- italic = setitalicinfont(font,char) -- calculated once
- -- italic = 0
- end
- if italic ~= 0 then
- lastfont = font
- lastattr = attr
- previous = current
- prevchar = char
- end
- end
- else
- italic = 0
- end
- else
- italic = 0
- end
- inserted = nil
- elseif id == disc_code then
- -- skip
- elseif id == kern_code then
- inserted = nil
- italic = 0
- elseif id == glue_code then
- if italic ~= 0 then
- if trace_italics then
- report_italics("inserting %p between italic %C and glue",italic,prevchar)
- end
- inserted = new_correction_glue(italic) -- maybe just add ? else problem with penalties
- insert_node_after(head,previous,inserted)
- italic = 0
- done = true
- end
- elseif id == math_code then
- current = end_of_math(current)
- elseif italic ~= 0 then
- if trace_italics then
- report_italics("inserting %p between italic %C and whatever",italic,prevchar)
- end
- inserted = nil
- insert_node_after(head,previous,new_correction_kern(italic))
- italic = 0
- done = true
- end
- current = current.next
- end
- if italic ~= 0 and lastattr > 1 then -- more control is needed here
- if trace_italics then
- report_italics("inserting %p between italic %C and end of list",italic,prevchar)
- end
- insert_node_after(head,previous,new_correction_kern(italic))
- done = true
- end
- return head, done
-end
-
-local enable
-
-enable = function()
- tasks.enableaction("processors","typesetters.italics.handler")
- if trace_italics then
- report_italics("enabling text italics")
- end
- enable = false
-end
-
-function italics.set(n)
- if enable then
- enable()
- end
- if n == variables.reset then
- texattribute[a_italics] = unsetvalue
- else
- texattribute[a_italics] = tonumber(n) or unsetvalue
- end
-end
-
-function italics.reset()
- texattribute[a_italics] = unsetvalue
-end
-
-italics.handler = nodes.installattributehandler {
- name = "italics",
- namespace = italics,
- processor = process,
-}
-
-local variables = interfaces.variables
-local settings_to_hash = utilities.parsers.settings_to_hash
-
-function commands.setupitaliccorrection(option) -- no grouping !
- if enable then
- enable()
- 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
- if options[variables.global] then
- forcedvariant = variant
- texattribute[a_italics] = unsetvalue
- else
- forcedvariant = false
- texattribute[a_italics] = variant
- end
- if trace_italics then
- report_italics("forcing %a, variant %a",forcedvariant,variant ~= unsetvalue and variant)
- end
-end
-
--- for manuals:
-
-local stack = { }
-
-function commands.pushitaliccorrection()
- table.insert(stack,{forcedvariant, texattribute[a_italics] })
-end
-
-function commands.popitaliccorrection()
- local top = table.remove(stack)
- forcedvariant = top[1]
- texattribute[a_italics] = top[2]
-end
+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 utfchar = utf.char
+
+local trace_italics = false trackers.register("typesetters.italics", function(v) trace_italics = v end)
+
+local report_italics = logs.reporter("nodes","italics")
+
+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 tasks = nodes.tasks
+
+local insert_node_after = node.insert_after
+local delete_node = nodes.delete
+local end_of_math = node.end_of_math
+
+local texattribute = tex.attribute
+local a_italics = attributes.private("italics")
+local unsetvalue = attributes.unsetvalue
+
+local new_correction_kern = nodes.pool.fontkern
+local new_correction_glue = nodes.pool.glue
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+local italicsdata = fonthashes.italics
+
+local forcedvariant = false
+
+function typesetters.italics.forcevariant(variant)
+ forcedvariant = variant
+end
+
+local function setitalicinfont(font,char)
+ local tfmdata = fontdata[font]
+ local character = tfmdata.characters[char]
+ if character then
+ local italic = character.italic_correction
+ 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
+ character.italic_correction = italic or 0
+ end
+ return italic
+ else
+ return 0
+ end
+end
+
+-- todo: clear attribute
+
+local function process(namespace,attribute,head)
+ local done = false
+ local italic = 0
+ local lastfont = nil
+ local lastattr = nil
+ local previous = nil
+ local prevchar = nil
+ local current = head
+ local inserted = nil
+ while current do
+ local id = current.id
+ if id == glyph_code then
+ local font = current.font
+ local char = current.char
+ local data = italicsdata[font]
+ if font ~= lastfont then
+ if italic ~= 0 then
+ if data then
+ if trace_italics then
+ report_italics("ignoring %p between italic %C and italic %C",italic,prevchar,char)
+ end
+ else
+ if trace_italics then
+ report_italics("inserting %p between italic %C and regular %C",italic,prevchar,char)
+ end
+ insert_node_after(head,previous,new_correction_kern(italic))
+ done = true
+ end
+ elseif inserted and data then
+ if trace_italics then
+ report_italics("deleting last correction before %C",char)
+ end
+ delete_node(head,inserted)
+ else
+ -- nothing
+ end
+ lastfont = font
+ end
+ if data then
+ local attr = forcedvariant or current[attribute]
+ if attr and attr > 0 then
+ local cd = data[char]
+ if not cd then
+ -- this really can happen
+ italic = 0
+ else
+ italic = cd.italic or cd.italic_correction
+ if not italic then
+ italic = setitalicinfont(font,char) -- calculated once
+ -- italic = 0
+ end
+ if italic ~= 0 then
+ lastfont = font
+ lastattr = attr
+ previous = current
+ prevchar = char
+ end
+ end
+ else
+ italic = 0
+ end
+ else
+ italic = 0
+ end
+ inserted = nil
+ elseif id == disc_code then
+ -- skip
+ elseif id == kern_code then
+ inserted = nil
+ italic = 0
+ elseif id == glue_code then
+ if italic ~= 0 then
+ if trace_italics then
+ report_italics("inserting %p between italic %C and glue",italic,prevchar)
+ end
+ inserted = new_correction_glue(italic) -- maybe just add ? else problem with penalties
+ insert_node_after(head,previous,inserted)
+ italic = 0
+ done = true
+ end
+ elseif id == math_code then
+ current = end_of_math(current)
+ elseif italic ~= 0 then
+ if trace_italics then
+ report_italics("inserting %p between italic %C and whatever",italic,prevchar)
+ end
+ inserted = nil
+ insert_node_after(head,previous,new_correction_kern(italic))
+ italic = 0
+ done = true
+ end
+ current = current.next
+ end
+ if italic ~= 0 and lastattr > 1 then -- more control is needed here
+ if trace_italics then
+ report_italics("inserting %p between italic %C and end of list",italic,prevchar)
+ end
+ insert_node_after(head,previous,new_correction_kern(italic))
+ done = true
+ end
+ return head, done
+end
+
+local enable
+
+enable = function()
+ tasks.enableaction("processors","typesetters.italics.handler")
+ if trace_italics then
+ report_italics("enabling text italics")
+ end
+ enable = false
+end
+
+function italics.set(n)
+ if enable then
+ enable()
+ end
+ if n == variables.reset then
+ texattribute[a_italics] = unsetvalue
+ else
+ texattribute[a_italics] = tonumber(n) or unsetvalue
+ end
+end
+
+function italics.reset()
+ texattribute[a_italics] = unsetvalue
+end
+
+italics.handler = nodes.installattributehandler {
+ name = "italics",
+ namespace = italics,
+ processor = process,
+}
+
+local variables = interfaces.variables
+local settings_to_hash = utilities.parsers.settings_to_hash
+
+function commands.setupitaliccorrection(option) -- no grouping !
+ if enable then
+ enable()
+ 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
+ if options[variables.global] then
+ forcedvariant = variant
+ texattribute[a_italics] = unsetvalue
+ else
+ forcedvariant = false
+ texattribute[a_italics] = variant
+ end
+ if trace_italics then
+ report_italics("forcing %a, variant %a",forcedvariant,variant ~= unsetvalue and variant)
+ end
+end
+
+-- for manuals:
+
+local stack = { }
+
+function commands.pushitaliccorrection()
+ table.insert(stack,{forcedvariant, texattribute[a_italics] })
+end
+
+function commands.popitaliccorrection()
+ local top = table.remove(stack)
+ forcedvariant = top[1]
+ texattribute[a_italics] = top[2]
+end
diff --git a/tex/context/base/typo-krn.lua b/tex/context/base/typo-krn.lua
index eac876262..fb28d3b2d 100644
--- a/tex/context/base/typo-krn.lua
+++ b/tex/context/base/typo-krn.lua
@@ -1,335 +1,335 @@
-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"
-}
-
-local next, type, tonumber = next, type, tonumber
-local utfchar = utf.char
-
-local nodes, node, fonts = nodes, node, fonts
-
-local find_node_tail = node.tail or node.slide
-local free_node = node.free
-local free_nodelist = node.flush_list
-local copy_node = node.copy
-local copy_nodelist = node.copy_list
-local insert_node_before = node.insert_before
-local insert_node_after = node.insert_after
-local end_of_math = node.end_of_math
-
-local texattribute = tex.attribute
-local unsetvalue = attributes.unsetvalue
-
-local nodepool = nodes.pool
-local tasks = nodes.tasks
-
-local new_gluespec = nodepool.gluespec
-local new_kern = nodepool.kern
-local new_glue = nodepool.glue
-
-local nodecodes = nodes.nodecodes
-local kerncodes = nodes.kerncodes
-local skipcodes = nodes.skipcodes
-
-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 kerning_code = kerncodes.kerning
-local userkern_code = kerncodes.userkern
-local userskip_code = skipcodes.userskip
-local spaceskip_code = skipcodes.spaceskip
-local xspaceskip_code = skipcodes.xspaceskip
-
-local fonthashes = fonts.hashes
-local fontdata = fonthashes.identifiers
-local chardata = fonthashes.characters
-local quaddata = fonthashes.quads
-local markdata = fonthashes.marks
-
-local v_max = interfaces.variables.max
-
-typesetters = typesetters or { }
-local typesetters = typesetters
-
-typesetters.kerns = typesetters.kerns or { }
-local kerns = typesetters.kerns
-
-kerns.mapping = kerns.mapping or { }
-kerns.factors = kerns.factors or { }
-local a_kerns = attributes.private("kern")
-local a_fontkern = attributes.private('fontkern')
-kerns.attribute = kerns.attribute
-
-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
-
-local gluefactor = 4 -- assumes quad = .5 enspace
-
-kerns.keepligature = false -- just for fun (todo: control setting with key/value)
-kerns.keeptogether = false -- just for fun (todo: control setting with key/value)
-
--- 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)
- local s = g.spec
- s.stretch = kern
- s.stretch_order = 1
- return g
- else
- return new_kern(kern)
- end
-end
-
-local function spec_injector(fillup,width,stretch,shrink)
- if fillup then
- local s = new_gluespec(width,2*stretch,2*shrink)
- s.stretch_order = 1
- return s
- else
- return new_gluespec(width,stretch,shrink)
- end
-end
-
--- needs checking ... base mode / node mode
-
-local function do_process(namespace,attribute,head,force) -- todo: glue so that we can fully stretch
- local start, done, lastfont = head, false, nil
- local keepligature = kerns.keepligature
- local keeptogether = kerns.keeptogether
- local fillup = false
- while start do
- -- faster to test for attr first
- local attr = force or start[attribute]
- if attr and attr > 0 then
- start[attribute] = unsetvalue
- local krn = mapping[attr]
- if krn == v_max then
- krn = .25
- fillup = true
- else
- fillup = false
- end
- if krn and krn ~= 0 then
- local id = start.id
- if id == glyph_code then
- lastfont = start.font
- local c = start.components
- if c then
- if keepligature and keepligature(start) then
- -- keep 'm
- else
- c = do_process(namespace,attribute,c,attr)
- local s = start
- local p, n = s.prev, s.next
- local tail = find_node_tail(c)
- if p then
- p.next = c
- c.prev = p
- else
- head = c
- end
- if n then
- n.prev = tail
- end
- tail.next = n
- start = c
- s.components = nil
- -- we now leak nodes !
- -- free_node(s)
- done = true
- end
- end
- local prev = start.prev
- if not prev then
- -- skip
- elseif markdata[lastfont][start.char] then
- -- skip
- else
- local pid = prev.id
- if not pid then
- -- nothing
- elseif pid == kern_code then
- if prev.subtype == kerning_code or prev[a_fontkern] then
- if keeptogether and prev.prev.id == glyph_code and keeptogether(prev.prev,start) then -- we could also pass start
- -- keep 'm
- else
- -- not yet ok, as injected kerns can be overlays (from node-inj.lua)
- prev.subtype = userkern_code
- prev.kern = prev.kern + quaddata[lastfont]*krn -- here
- done = true
- end
- end
- elseif pid == glyph_code then
- if prev.font == lastfont then
- local prevchar, lastchar = prev.char, start.char
- if keeptogether and keeptogether(prev,start) then
- -- keep 'm
- else
- local kerns = chardata[lastfont][prevchar].kerns
- local kern = kerns and kerns[lastchar] or 0
- krn = kern + quaddata[lastfont]*krn -- here
- insert_node_before(head,start,kern_injector(fillup,krn))
- done = true
- end
- else
- krn = quaddata[lastfont]*krn -- here
- insert_node_before(head,start,kern_injector(fillup,krn))
- done = true
- end
- elseif pid == disc_code then
- -- a bit too complicated, we can best not copy and just calculate
- -- but we could have multiple glyphs involved so ...
- local disc = prev -- disc
- local pre, post, replace = disc.pre, disc.post, disc.replace
- local prv, nxt = disc.prev, disc.next
- if pre and prv then -- must pair with start.prev
- -- this one happens in most cases
- local before = copy_node(prv)
- pre.prev = before
- before.next = pre
- before.prev = nil
- pre = do_process(namespace,attribute,before,attr)
- pre = pre.next
- pre.prev = nil
- disc.pre = pre
- free_node(before)
- end
- if post and nxt then -- must pair with start
- local after = copy_node(nxt)
- local tail = find_node_tail(post)
- tail.next = after
- after.prev = tail
- after.next = nil
- post = do_process(namespace,attribute,post,attr)
- tail.next = nil
- disc.post = post
- free_node(after)
- end
- if replace and prv and nxt then -- must pair with start and start.prev
- local before = copy_node(prv)
- local after = copy_node(nxt)
- local tail = find_node_tail(replace)
- replace.prev = before
- before.next = replace
- before.prev = nil
- tail.next = after
- after.prev = tail
- after.next = nil
- replace = do_process(namespace,attribute,before,attr)
- replace = replace.next
- replace.prev = nil
- after.prev.next = nil
- disc.replace = replace
- free_node(after)
- free_node(before)
- else
- if prv and prv.id == glyph_code and prv.font == lastfont then
- local prevchar, lastchar = prv.char, start.char
- local kerns = chardata[lastfont][prevchar].kerns
- local kern = kerns and kerns[lastchar] or 0
- krn = kern + quaddata[lastfont]*krn -- here
- else
- krn = quaddata[lastfont]*krn -- here
- end
- disc.replace = kern_injector(false,krn) -- only kerns permitted, no glue
- end
- end
- end
- elseif id == glue_code then
- local subtype = start.subtype
- if subtype == userskip_code or subtype == xspaceskip_code or subtype == spaceskip_code then
- local s = start.spec
- local w = s.width
- if w > 0 then
- local width, stretch, shrink = w+gluefactor*w*krn, s.stretch, s.shrink
- start.spec = spec_injector(fillup,width,stretch*width/w,shrink*width/w)
- done = true
- end
- end
- elseif id == kern_code then
- -- if start.subtype == kerning_code then -- handle with glyphs
- -- local sk = start.kern
- -- if sk > 0 then
- -- start.kern = sk*krn
- -- done = true
- -- end
- -- end
- elseif lastfont and (id == hlist_code or id == vlist_code) then -- todo: lookahead
- local p = start.prev
- if p and p.id ~= glue_code then
- insert_node_before(head,start,kern_injector(fillup,quaddata[lastfont]*krn))
- done = true
- end
- local n = start.next
- if n and n.id ~= glue_code then
- insert_node_after(head,start,kern_injector(fillup,quaddata[lastfont]*krn))
- done = true
- end
- elseif id == math_code then
- start = end_of_math(start)
- end
- end
- end
- if start then
- start = start.next
- end
- end
- return head, done
-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
- tasks.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
- texattribute[a_kerns] = factor
- return factor
-end
-
-local function process(namespace,attribute,head)
- return do_process(namespace,attribute,head) -- no direct map, because else fourth argument is tail == true
-end
-
-kerns.handler = nodes.installattributehandler {
- name = "kern",
- namespace = kerns,
- processor = process,
-}
-
--- interface
-
-commands.setcharacterkerning = kerns.set
+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"
+}
+
+local next, type, tonumber = next, type, tonumber
+local utfchar = utf.char
+
+local nodes, node, fonts = nodes, node, fonts
+
+local find_node_tail = node.tail or node.slide
+local free_node = node.free
+local free_nodelist = node.flush_list
+local copy_node = node.copy
+local copy_nodelist = node.copy_list
+local insert_node_before = node.insert_before
+local insert_node_after = node.insert_after
+local end_of_math = node.end_of_math
+
+local texattribute = tex.attribute
+local unsetvalue = attributes.unsetvalue
+
+local nodepool = nodes.pool
+local tasks = nodes.tasks
+
+local new_gluespec = nodepool.gluespec
+local new_kern = nodepool.kern
+local new_glue = nodepool.glue
+
+local nodecodes = nodes.nodecodes
+local kerncodes = nodes.kerncodes
+local skipcodes = nodes.skipcodes
+
+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 kerning_code = kerncodes.kerning
+local userkern_code = kerncodes.userkern
+local userskip_code = skipcodes.userskip
+local spaceskip_code = skipcodes.spaceskip
+local xspaceskip_code = skipcodes.xspaceskip
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+local chardata = fonthashes.characters
+local quaddata = fonthashes.quads
+local markdata = fonthashes.marks
+
+local v_max = interfaces.variables.max
+
+typesetters = typesetters or { }
+local typesetters = typesetters
+
+typesetters.kerns = typesetters.kerns or { }
+local kerns = typesetters.kerns
+
+kerns.mapping = kerns.mapping or { }
+kerns.factors = kerns.factors or { }
+local a_kerns = attributes.private("kern")
+local a_fontkern = attributes.private('fontkern')
+kerns.attribute = kerns.attribute
+
+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
+
+local gluefactor = 4 -- assumes quad = .5 enspace
+
+kerns.keepligature = false -- just for fun (todo: control setting with key/value)
+kerns.keeptogether = false -- just for fun (todo: control setting with key/value)
+
+-- 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)
+ local s = g.spec
+ s.stretch = kern
+ s.stretch_order = 1
+ return g
+ else
+ return new_kern(kern)
+ end
+end
+
+local function spec_injector(fillup,width,stretch,shrink)
+ if fillup then
+ local s = new_gluespec(width,2*stretch,2*shrink)
+ s.stretch_order = 1
+ return s
+ else
+ return new_gluespec(width,stretch,shrink)
+ end
+end
+
+-- needs checking ... base mode / node mode
+
+local function do_process(namespace,attribute,head,force) -- todo: glue so that we can fully stretch
+ local start, done, lastfont = head, false, nil
+ local keepligature = kerns.keepligature
+ local keeptogether = kerns.keeptogether
+ local fillup = false
+ while start do
+ -- faster to test for attr first
+ local attr = force or start[attribute]
+ if attr and attr > 0 then
+ start[attribute] = unsetvalue
+ local krn = mapping[attr]
+ if krn == v_max then
+ krn = .25
+ fillup = true
+ else
+ fillup = false
+ end
+ if krn and krn ~= 0 then
+ local id = start.id
+ if id == glyph_code then
+ lastfont = start.font
+ local c = start.components
+ if c then
+ if keepligature and keepligature(start) then
+ -- keep 'm
+ else
+ c = do_process(namespace,attribute,c,attr)
+ local s = start
+ local p, n = s.prev, s.next
+ local tail = find_node_tail(c)
+ if p then
+ p.next = c
+ c.prev = p
+ else
+ head = c
+ end
+ if n then
+ n.prev = tail
+ end
+ tail.next = n
+ start = c
+ s.components = nil
+ -- we now leak nodes !
+ -- free_node(s)
+ done = true
+ end
+ end
+ local prev = start.prev
+ if not prev then
+ -- skip
+ elseif markdata[lastfont][start.char] then
+ -- skip
+ else
+ local pid = prev.id
+ if not pid then
+ -- nothing
+ elseif pid == kern_code then
+ if prev.subtype == kerning_code or prev[a_fontkern] then
+ if keeptogether and prev.prev.id == glyph_code and keeptogether(prev.prev,start) then -- we could also pass start
+ -- keep 'm
+ else
+ -- not yet ok, as injected kerns can be overlays (from node-inj.lua)
+ prev.subtype = userkern_code
+ prev.kern = prev.kern + quaddata[lastfont]*krn -- here
+ done = true
+ end
+ end
+ elseif pid == glyph_code then
+ if prev.font == lastfont then
+ local prevchar, lastchar = prev.char, start.char
+ if keeptogether and keeptogether(prev,start) then
+ -- keep 'm
+ else
+ local kerns = chardata[lastfont][prevchar].kerns
+ local kern = kerns and kerns[lastchar] or 0
+ krn = kern + quaddata[lastfont]*krn -- here
+ insert_node_before(head,start,kern_injector(fillup,krn))
+ done = true
+ end
+ else
+ krn = quaddata[lastfont]*krn -- here
+ insert_node_before(head,start,kern_injector(fillup,krn))
+ done = true
+ end
+ elseif pid == disc_code then
+ -- a bit too complicated, we can best not copy and just calculate
+ -- but we could have multiple glyphs involved so ...
+ local disc = prev -- disc
+ local pre, post, replace = disc.pre, disc.post, disc.replace
+ local prv, nxt = disc.prev, disc.next
+ if pre and prv then -- must pair with start.prev
+ -- this one happens in most cases
+ local before = copy_node(prv)
+ pre.prev = before
+ before.next = pre
+ before.prev = nil
+ pre = do_process(namespace,attribute,before,attr)
+ pre = pre.next
+ pre.prev = nil
+ disc.pre = pre
+ free_node(before)
+ end
+ if post and nxt then -- must pair with start
+ local after = copy_node(nxt)
+ local tail = find_node_tail(post)
+ tail.next = after
+ after.prev = tail
+ after.next = nil
+ post = do_process(namespace,attribute,post,attr)
+ tail.next = nil
+ disc.post = post
+ free_node(after)
+ end
+ if replace and prv and nxt then -- must pair with start and start.prev
+ local before = copy_node(prv)
+ local after = copy_node(nxt)
+ local tail = find_node_tail(replace)
+ replace.prev = before
+ before.next = replace
+ before.prev = nil
+ tail.next = after
+ after.prev = tail
+ after.next = nil
+ replace = do_process(namespace,attribute,before,attr)
+ replace = replace.next
+ replace.prev = nil
+ after.prev.next = nil
+ disc.replace = replace
+ free_node(after)
+ free_node(before)
+ else
+ if prv and prv.id == glyph_code and prv.font == lastfont then
+ local prevchar, lastchar = prv.char, start.char
+ local kerns = chardata[lastfont][prevchar].kerns
+ local kern = kerns and kerns[lastchar] or 0
+ krn = kern + quaddata[lastfont]*krn -- here
+ else
+ krn = quaddata[lastfont]*krn -- here
+ end
+ disc.replace = kern_injector(false,krn) -- only kerns permitted, no glue
+ end
+ end
+ end
+ elseif id == glue_code then
+ local subtype = start.subtype
+ if subtype == userskip_code or subtype == xspaceskip_code or subtype == spaceskip_code then
+ local s = start.spec
+ local w = s.width
+ if w > 0 then
+ local width, stretch, shrink = w+gluefactor*w*krn, s.stretch, s.shrink
+ start.spec = spec_injector(fillup,width,stretch*width/w,shrink*width/w)
+ done = true
+ end
+ end
+ elseif id == kern_code then
+ -- if start.subtype == kerning_code then -- handle with glyphs
+ -- local sk = start.kern
+ -- if sk > 0 then
+ -- start.kern = sk*krn
+ -- done = true
+ -- end
+ -- end
+ elseif lastfont and (id == hlist_code or id == vlist_code) then -- todo: lookahead
+ local p = start.prev
+ if p and p.id ~= glue_code then
+ insert_node_before(head,start,kern_injector(fillup,quaddata[lastfont]*krn))
+ done = true
+ end
+ local n = start.next
+ if n and n.id ~= glue_code then
+ insert_node_after(head,start,kern_injector(fillup,quaddata[lastfont]*krn))
+ done = true
+ end
+ elseif id == math_code then
+ start = end_of_math(start)
+ end
+ end
+ end
+ if start then
+ start = start.next
+ end
+ end
+ return head, done
+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
+ tasks.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
+ texattribute[a_kerns] = factor
+ return factor
+end
+
+local function process(namespace,attribute,head)
+ return do_process(namespace,attribute,head) -- no direct map, because else fourth argument is tail == true
+end
+
+kerns.handler = nodes.installattributehandler {
+ name = "kern",
+ namespace = kerns,
+ processor = process,
+}
+
+-- interface
+
+commands.setcharacterkerning = kerns.set
diff --git a/tex/context/base/typo-lan.lua b/tex/context/base/typo-lan.lua
index a17732900..50927f744 100644
--- a/tex/context/base/typo-lan.lua
+++ b/tex/context/base/typo-lan.lua
@@ -1,72 +1,72 @@
-if not modules then modules = { } end modules ['typo-lan'] = {
- version = 1.001,
- comment = "companion to typo-lan.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 currentfont = font.current
-local setmetatableindex = table.setmetatableindex
-local utfbyte = utf.byte
-
-local hashes = fonts.hashes
-local fontdata = hashes.characters
-local emwidths = hashes.emwidths
-
-local frequencies = languages.frequencies or { }
-languages.frequencies = frequencies
-
-local frequencydata = { }
-local frequencyfile = string.formatters["lang-frq-%s.lua"]
-local frequencycache = { }
-
-setmetatableindex(frequencydata, function(t,language)
- local fullname = resolvers.findfile(frequencyfile(language))
- local v = fullname ~= "" and dofile(fullname)
- if not v or not v.frequencies then
- v = t.en
- end
- t[language] = v
- return v
-end)
-
-setmetatableindex(frequencycache, function(t,language)
- local dataset = frequencydata[language]
- local frequencies = dataset.frequencies
- if not frequencies then
- return t.en
- end
- local v = { }
- setmetatableindex(v, function(t,font)
- local average = emwidths[font] / 2
- if frequencies then
- local characters = fontdata[font]
- local sum, tot = 0, 0
- for k, v in next, frequencies do
- local character = characters[k] -- characters[type(k) == "number" and k or utfbyte(k)]
- tot = tot + v
- sum = sum + v * (character and character.width or average)
- end
- average = sum / tot -- widths
- end
- t[font] = average
- return average
- end)
- t[language] = v
- return v
-end)
-
-function frequencies.getdata(language)
- return frequencydata[language]
-end
-
-function frequencies.averagecharwidth(language,font)
- return frequencycache[language or "en"][font or currentfont()]
-end
-
-function commands.averagecharwidth(language,font)
- context(frequencycache[language or "en"][font or currentfont()])
-end
+if not modules then modules = { } end modules ['typo-lan'] = {
+ version = 1.001,
+ comment = "companion to typo-lan.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 currentfont = font.current
+local setmetatableindex = table.setmetatableindex
+local utfbyte = utf.byte
+
+local hashes = fonts.hashes
+local fontdata = hashes.characters
+local emwidths = hashes.emwidths
+
+local frequencies = languages.frequencies or { }
+languages.frequencies = frequencies
+
+local frequencydata = { }
+local frequencyfile = string.formatters["lang-frq-%s.lua"]
+local frequencycache = { }
+
+setmetatableindex(frequencydata, function(t,language)
+ local fullname = resolvers.findfile(frequencyfile(language))
+ local v = fullname ~= "" and dofile(fullname)
+ if not v or not v.frequencies then
+ v = t.en
+ end
+ t[language] = v
+ return v
+end)
+
+setmetatableindex(frequencycache, function(t,language)
+ local dataset = frequencydata[language]
+ local frequencies = dataset.frequencies
+ if not frequencies then
+ return t.en
+ end
+ local v = { }
+ setmetatableindex(v, function(t,font)
+ local average = emwidths[font] / 2
+ if frequencies then
+ local characters = fontdata[font]
+ local sum, tot = 0, 0
+ for k, v in next, frequencies do
+ local character = characters[k] -- characters[type(k) == "number" and k or utfbyte(k)]
+ tot = tot + v
+ sum = sum + v * (character and character.width or average)
+ end
+ average = sum / tot -- widths
+ end
+ t[font] = average
+ return average
+ end)
+ t[language] = v
+ return v
+end)
+
+function frequencies.getdata(language)
+ return frequencydata[language]
+end
+
+function frequencies.averagecharwidth(language,font)
+ return frequencycache[language or "en"][font or currentfont()]
+end
+
+function commands.averagecharwidth(language,font)
+ context(frequencycache[language or "en"][font or currentfont()])
+end
diff --git a/tex/context/base/typo-mar.lua b/tex/context/base/typo-mar.lua
index 65b205098..ec827883d 100644
--- a/tex/context/base/typo-mar.lua
+++ b/tex/context/base/typo-mar.lua
@@ -1,879 +1,879 @@
-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)
--- * stack across paragraphs, but that is messy and one should reconsider
--- using margin data then as also vertical spacing kicks in
--- * floating margin data, with close-to-call anchoring
-
--- -- experiment (does not work, too much interference)
---
--- local pdfprint = pdf.print
--- local format = string.format
---
--- anchors = anchors or { }
---
--- local whatever = { }
--- local factor = (7200/7227)/65536
---
--- function anchors.set(tag)
--- whatever[tag] = { pdf.h, pdf.v }
--- end
---
--- function anchors.reset(tag)
--- whatever[tag] = nil
--- end
---
--- function anchors.startmove(tag,how) -- save/restore nodes but they don't support moves
--- local w = whatever[tag]
--- if not w then
--- -- error
--- elseif how == "horizontal" or how == "h" then
--- pdfprint("page",format(" q 1 0 0 1 %f 0 cm ", (w[1] - pdf.h) * factor))
--- elseif how == "vertical" or how == "v" then
--- pdfprint("page",format(" q 1 0 0 1 0 %f cm ", (w[2] - pdf.v) * factor))
--- else
--- pdfprint("page",format(" q 1 0 0 1 %f %f cm ", (w[1] - pdf.h) * factor, (w[2] - pdf.v) * factor))
--- end
--- end
---
--- function anchors.stopmove(tag)
--- local w = whatever[tag]
--- if not w then
--- -- error
--- else
--- pdfprint("page"," Q ")
--- end
--- end
---
--- local latelua = nodes.pool.latelua
---
--- function anchors.node_set(tag)
--- return latelua(formatters["anchors.set(%q)"](tag))
--- end
---
--- function anchors.node_reset(tag)
--- return latelua(formatters["anchors.reset(%q)"](tag))
--- end
---
--- function anchors.node_start_move(tag,how)
--- return latelua(formatters["anchors.startmove(%q,%q)](tag,how))
--- end
---
--- function anchors.node_stop_move(tag)
--- return latelua(formatters["anchors.stopmove(%q)"](tag))
--- end
-
--- so far
-
-local format, validstring = string.format, string.valid
-local insert, remove = table.insert, table.remove
-local setmetatable, next = setmetatable, next
-
-local attributes, nodes, node, variables = attributes, nodes, node, variables
-
-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("typesetters","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_flushleft = variables.flushleft
-local v_flushright = variables.flushright
-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_column = variables.column
-
-local copy_node_list = node.copy_list
-local slide_nodes = node.slide
-local hpack_nodes = node.hpack -- nodes.fasthpack not really faster here
-local traverse_id = node.traverse_id
-local free_node_list = node.flush_list
-local insert_node_after = node.insert_after
-local insert_node_before = node.insert_before
-
-local concat_nodes = nodes.concat
-
-local nodecodes = nodes.nodecodes
-local listcodes = nodes.listcodes
-local gluecodes = nodes.gluecodes
-local whatsitcodes = nodes.whatsitcodes
-
-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 whatsit_code = nodecodes.whatsit
-local line_code = listcodes.line
-local cell_code = listcodes.cell
-local alignment_code = listcodes.alignment
-local leftskip_code = gluecodes.leftskip
-local rightskip_code = gluecodes.rightskip
-local userdefined_code = whatsitcodes.userdefined
-
-local dir_code = whatsitcodes.dir
-local localpar_code = whatsitcodes.localpar
-
-local nodepool = nodes.pool
-
-local new_kern = nodepool.kern
-local new_glue = nodepool.glue
-local new_penalty = nodepool.penalty
-local new_stretch = nodepool.stretch
-local new_usernumber = nodepool.usernumber
-local new_latelua = nodepool.latelua
-
-local texcount = tex.count
-local texdimen = tex.dimen
-local texbox = tex.box
-
-local points = number.points
-
-local isleftpage = layouts.status.isleftpage
-local registertogether = builders.paragraphs.registertogether
-
-local jobpositions = job.positions
-local getposition = jobpositions.position
-
-local a_margindata = attributes.private("margindata")
-
-local inline_mark = nodepool.userids["margins.inline"]
-
-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 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,
- 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,
- }
-}
-
-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(si.box.list))
- end
- else
- report_margindata("%s: nothing stored in location %a",banner,location)
- end
-end
-
-function margins.save(t)
- setmetatable(t,defaults)
- local content = texbox[t.number]
- local location = t.location
- local category = t.category
- local inline = t.inline
- local scope = t.scope or v_global
- if not content then
- report_margindata("ignoring empty margin data %a",location or "unknown")
- return
- end
- 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
- local name = t.name
- if trace_marginstack then
- showstore(store,"before",location)
- end
- if name and name ~= "" then
- if inlinestore then -- todo: inline store has to be done differently (not sparse)
- local t = table.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)
- free_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)
- free_node_list(s.box)
- end
- end
- end
- if trace_marginstack then
- showstore(store,"between",location)
- end
- end
- if t.number then
- -- better make a new table and make t entry in t
- t.box = copy_node_list(content)
- t.n = nofsaved
- -- used later (we will clean up this natural mess later)
- -- nice is to make a special status table mechanism
- local leftmargindistance = texdimen.naturalleftmargindistance
- local rightmargindistance = texdimen.naturalrightmargindistance
- t.strutdepth = texbox.strutbox.depth
- t.strutheight = texbox.strutbox.height
- t.leftskip = tex.leftskip.width -- we're not in forgetall
- t.rightskip = tex.rightskip.width -- we're not in forgetall
- t.leftmargindistance = leftmargindistance -- todo:layoutstatus table
- t.rightmargindistance = rightmargindistance
- t.leftedgedistance = texdimen.naturalleftedgedistance
- + texdimen.leftmarginwidth
- + leftmargindistance
- t.rightedgedistance = texdimen.naturalrightedgedistance
- + texdimen.rightmarginwidth
- + rightmargindistance
- t.lineheight = texdimen.lineheight
- --
- -- t.realpageno = texcount.realpageno
- if inline then
- context(new_usernumber(inline_mark,nofsaved))
- 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 status, nofstatus = { }, 0
-
-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 realpageno = candidate.realpageno
- local leftpage = isleftpage(false,true)
- local delta = 0
- local leftdelta = 0
- local rightdelta = 0
- local leftdistance = distance
- local rightdistance = distance
- 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_left then
- delta = hoffset + width + leftdistance + leftdelta
- elseif location == v_right then
- delta = -hoffset - hsize - rightdistance + rightdelta
- elseif location == v_inner then
- if leftpage then
- delta = -hoffset - hsize - rightdistance + rightdelta
- else
- delta = hoffset + width + leftdistance + leftdelta
- end
- elseif location == v_outer then
- if leftpage then
- delta = hoffset + width + leftdistance + leftdelta
- else
- delta = -hoffset - hsize - rightdistance + rightdelta
- end
- end
-
- -- we assume that list is a hbox, otherwise we had to take the whole current
- -- in order to get it right
-
- current.width = 0
- local anchornode, move_x
-
- -- this mess is needed for alignments (combinations) so we use that
- -- oportunity to add arbitrary anchoring
-
- -- always increment anchor is nicer for multipass when we add new ..
-
- local inline = candidate.inline
- local anchor = candidate.anchor
- if not anchor or anchor == "" then
- anchor = v_text
- end
- if inline or anchor ~= v_text or candidate.psubtype == alignment_code then
- -- the alignment_code check catches margintexts ste before a tabulate
- h_anchors = h_anchors + 1
- anchornode = new_latelua(format("_plib_.set('md:h',%i,{x=true,c=true})",h_anchors))
- local blob = jobpositions.get('md:h', h_anchors)
- if blob then
- local reference = jobpositions.getreserved(anchor,blob.c)
- if reference then
- if location == v_left then
- move_x = (reference.x or 0) - (blob.x or 0)
- elseif location == v_right then
- move_x = (reference.x or 0) - (blob.x or 0) + (reference.w or 0) - hsize
- else
- -- not yet done
- end
- end
- end
- end
-
- if move_x then
- delta = delta - move_x
- if trace_margindata then
- report_margindata("realigned %a, location %a, margin %a, move %p",candidate.n,location,margin,move_x)
- end
- else
- if trace_margindata then
- report_margindata("realigned %a, location %a, margin %a",candidate.n,location,margin)
- end
- end
-
- current.list = hpack_nodes(concat_nodes{anchornode,new_kern(-delta),current.list,new_kern(delta)})
- current.width = 0
-end
-
-local function realigned(current,a)
- local candidate = status[a]
- realign(current,candidate)
- nofdelayed = nofdelayed - 1
- status[a] = nil
- 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 stacked = { } -- left/right keys depending on location
-local cache = { }
-
-local function resetstacked()
- stacked = { }
-end
-
--- resetstacked()
-
-function margins.ha(tag) -- maybe l/r keys ipv left/right keys
- local p = cache[tag]
- p.p = true
- p.y = true
- jobpositions.set('md:v',tag,p)
- cache[tag] = nil
-end
-
-local function markovershoot(current)
- v_anchors = v_anchors + 1
- cache[v_anchors] = stacked
- local anchor = new_latelua(format("typesetters.margins.ha(%s)",v_anchors)) -- todo: alleen als offset > line
- current.list = hpack_nodes(concat_nodes{anchor,current.list})
-end
-
-local function getovershoot(location)
- local p = jobpositions.get("md:v",v_anchors)
- local c = jobpositions.get("md:v",v_anchors+1)
- if p and c and p.p and p.p == c.p then
- local distance = p.y - c.y
- local offset = p[location] or 0
- local overshoot = offset - distance
- if trace_marginstack then
- report_margindata("location %a, distance %p, offset %p, overshoot %p",location,distance,offset,overshoot)
- end
- if overshoot > 0 then
- return overshoot
- end
- end
- return 0
-end
-
-local function inject(parent,head,candidate)
- local box = candidate.box
- local width = box.width
- local height = box.height
- local depth = box.depth
- local shift = box.shift
- local stack = candidate.stack
- 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 psubtype = parent.subtype
- local offset = stacked[location]
- local firstonstack = offset == false or offset == nil
- nofstatus = nofstatus + 1
- nofdelayed = nofdelayed + 1
- status[nofstatus] = candidate
- -- yet untested
- if baseline == true then
- baseline = false
- -- hbox vtop
---~ for h in traverse_id(hlist_code,box.list.list) do
---~ baseline = h.height
---~ break
---~ end
- 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 = parent.width -- we can also pass textwidth
- candidate.psubtype = psubtype
- if trace_margindata then
- report_margindata("processing, index %s, height %p, depth %p, parent %s",candidate.n,height,depth,listcodes[psubtype])
- end
- if firstonstack then
- offset = 0
- else
--- offset = offset + height
- end
- if stack == v_yes then
- offset = offset + candidate.dy
- shift = shift + offset
- elseif stack == v_continue then
- offset = offset + candidate.dy
- if firstonstack then
- offset = offset + getovershoot(location)
- end
- 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 - parent.height
- if trace_margindata then
- report_margindata("top aligned by %p",delta)
- end
- if delta < candidate.threshold then
- shift = shift + voffset + delta
- 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
- box.shift = shift
- box.width = 0
- if not head then
- head = box
- elseif head.id == whatsit_code and head.subtype == localpar_code then
- -- experimental
- if head.dir == "TRT" then
- box.list = hpack_nodes(concat_nodes{new_kern(candidate.hsize),box.list,new_kern(-candidate.hsize)})
- end
- insert_node_after(head,head,box)
- else
- head.prev = box
- box.next = head
- head = box
- end
- box[a_margindata] = nofstatus
- if trace_margindata then
- report_margindata("injected, location %a, shift %p",location,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
- }
- offset = offset + height
- stacked[location] = offset -- weird, no table ?
- -- todo: if no real depth then zero
- if trace_margindata then
- report_margindata("status, offset %s",offset)
- end
- return head, room, stack == v_continue
-end
-
-local function flushinline(parent,head)
- local current = head
- local done = false
- local continue = false
- local room, don, con
- while current and nofinlined > 0 do
- local id = current.id
- if id == whatsit_code then
- if current.subtype == userdefined_code and current.user_id == inline_mark then
- local n = current.value
- 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
- continue = continue or con
- done = true
- nofstored = nofstored - 1
- end
- end
- elseif id == hlist_code or id == vlist_code then
- -- optional (but sometimes needed)
- current.list, don, con = flushinline(current,current.list)
- continue = continue or con
- done = done or don
- end
- current = current.next
- end
- return head, done, continue
-end
-
-local a_linenumber = attributes.private('linenumber')
-
-local function flushed(scope,parent) -- current is hlist
- local head = parent.list
- 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]
- 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
- registertogether(parent,room)
- else
- break
- end
- end
- end
- end
- if nofinlined > 0 then
- if done then
- parent.list = head
- end
- head, don, con = flushinline(parent,head)
- continue = continue or con
- done = done or don
- end
- if done then
- local a = head[a_linenumber] -- hack .. we need a more decent critical attribute inheritance mechanism
- parent.list = hpack_nodes(head,parent.width,"exactly")
- if a then
- parent.list[a_linenumber] = a
- end
- -- resetstacked()
- 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
- while current do
- local id = current.id
- if (id == vlist_code or id == hlist_code) and not current[a_margindata] then
- local don, continue = flushed(scope,current)
- if don then
- current[a_margindata] = 0 -- signal to prevent duplicate processing
- if continue then
- markovershoot(current)
- end
- if nofstored <= 0 then
- break
- end
- done = true
- end
- end
- current = current.next
- end
- -- if done then
- resetstacked() -- why doesn't done work ok here?
- -- end
- return head, done
- else
- return head, false
- end
-end
-
-function margins.localhandler(head,group) -- sometimes group is "" which is weird
- 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, false
- elseif nofstored > 0 then
- return handler(v_local,head,group)
- else
- if trace_margingroup then
- report_margindata("ignored 4, group %a, stored %s, inhibit %a",group,nofstored,inhibit)
- end
- return head, false
- end
-end
-
-function margins.globalhandler(head,group) -- check group
- 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, false
- elseif group == "hmode_par" then
- return handler("global",head,group)
- elseif group == "vmode_par" then -- experiment (for alignments)
- return handler("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("global",head,group)
- elseif group == "alignment" then -- experiment (for alignments)
- return handler("global",head,group)
- else
- if trace_margingroup then
- report_margindata("ignored 2, group %a, stored %s, inhibit %a",group,nofstored,inhibit)
- end
- return head, false
- end
-end
-
-local function finalhandler(head)
- if nofdelayed > 0 then
- local current = head
- local done = false
- while current do
- local id = current.id
- if id == hlist_code then
- local a = current[a_margindata]
- if not a or a == 0 then
- finalhandler(current.list)
- elseif realigned(current,a) then
- done = true
- if nofdelayed == 0 then
- return head, true
- end
- end
- elseif id == vlist_code then
- finalhandler(current.list)
- end
- current = current.next
- end
- return head, done
- else
- return head, false
- end
-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
- return finalhandler(head)
- else
- return head, false
- end
-end
-
--- Somehow the vbox builder (in combinations) gets pretty confused and decides to
--- go horizontal. So this needs more testing.
-
-prependaction("finalizers", "lists", "typesetters.margins.localhandler")
--- ("vboxbuilders", "normalizers", "typesetters.margins.localhandler")
-prependaction("mvlbuilders", "normalizers", "typesetters.margins.globalhandler")
-prependaction("shipouts", "normalizers", "typesetters.margins.finalhandler")
-
-disableaction("finalizers", "typesetters.margins.localhandler")
--- ("vboxbuilders", "typesetters.margins.localhandler")
-disableaction("mvlbuilders", "typesetters.margins.globalhandler")
-disableaction("shipouts", "typesetters.margins.finalhandler")
-
-enablelocal = function()
- enableaction("finalizers", "typesetters.margins.localhandler")
- -- enableaction("vboxbuilders", "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)
+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)
+-- * stack across paragraphs, but that is messy and one should reconsider
+-- using margin data then as also vertical spacing kicks in
+-- * floating margin data, with close-to-call anchoring
+
+-- -- experiment (does not work, too much interference)
+--
+-- local pdfprint = pdf.print
+-- local format = string.format
+--
+-- anchors = anchors or { }
+--
+-- local whatever = { }
+-- local factor = (7200/7227)/65536
+--
+-- function anchors.set(tag)
+-- whatever[tag] = { pdf.h, pdf.v }
+-- end
+--
+-- function anchors.reset(tag)
+-- whatever[tag] = nil
+-- end
+--
+-- function anchors.startmove(tag,how) -- save/restore nodes but they don't support moves
+-- local w = whatever[tag]
+-- if not w then
+-- -- error
+-- elseif how == "horizontal" or how == "h" then
+-- pdfprint("page",format(" q 1 0 0 1 %f 0 cm ", (w[1] - pdf.h) * factor))
+-- elseif how == "vertical" or how == "v" then
+-- pdfprint("page",format(" q 1 0 0 1 0 %f cm ", (w[2] - pdf.v) * factor))
+-- else
+-- pdfprint("page",format(" q 1 0 0 1 %f %f cm ", (w[1] - pdf.h) * factor, (w[2] - pdf.v) * factor))
+-- end
+-- end
+--
+-- function anchors.stopmove(tag)
+-- local w = whatever[tag]
+-- if not w then
+-- -- error
+-- else
+-- pdfprint("page"," Q ")
+-- end
+-- end
+--
+-- local latelua = nodes.pool.latelua
+--
+-- function anchors.node_set(tag)
+-- return latelua(formatters["anchors.set(%q)"](tag))
+-- end
+--
+-- function anchors.node_reset(tag)
+-- return latelua(formatters["anchors.reset(%q)"](tag))
+-- end
+--
+-- function anchors.node_start_move(tag,how)
+-- return latelua(formatters["anchors.startmove(%q,%q)](tag,how))
+-- end
+--
+-- function anchors.node_stop_move(tag)
+-- return latelua(formatters["anchors.stopmove(%q)"](tag))
+-- end
+
+-- so far
+
+local format, validstring = string.format, string.valid
+local insert, remove = table.insert, table.remove
+local setmetatable, next = setmetatable, next
+
+local attributes, nodes, node, variables = attributes, nodes, node, variables
+
+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("typesetters","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_flushleft = variables.flushleft
+local v_flushright = variables.flushright
+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_column = variables.column
+
+local copy_node_list = node.copy_list
+local slide_nodes = node.slide
+local hpack_nodes = node.hpack -- nodes.fasthpack not really faster here
+local traverse_id = node.traverse_id
+local free_node_list = node.flush_list
+local insert_node_after = node.insert_after
+local insert_node_before = node.insert_before
+
+local concat_nodes = nodes.concat
+
+local nodecodes = nodes.nodecodes
+local listcodes = nodes.listcodes
+local gluecodes = nodes.gluecodes
+local whatsitcodes = nodes.whatsitcodes
+
+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 whatsit_code = nodecodes.whatsit
+local line_code = listcodes.line
+local cell_code = listcodes.cell
+local alignment_code = listcodes.alignment
+local leftskip_code = gluecodes.leftskip
+local rightskip_code = gluecodes.rightskip
+local userdefined_code = whatsitcodes.userdefined
+
+local dir_code = whatsitcodes.dir
+local localpar_code = whatsitcodes.localpar
+
+local nodepool = nodes.pool
+
+local new_kern = nodepool.kern
+local new_glue = nodepool.glue
+local new_penalty = nodepool.penalty
+local new_stretch = nodepool.stretch
+local new_usernumber = nodepool.usernumber
+local new_latelua = nodepool.latelua
+
+local texcount = tex.count
+local texdimen = tex.dimen
+local texbox = tex.box
+
+local points = number.points
+
+local isleftpage = layouts.status.isleftpage
+local registertogether = builders.paragraphs.registertogether
+
+local jobpositions = job.positions
+local getposition = jobpositions.position
+
+local a_margindata = attributes.private("margindata")
+
+local inline_mark = nodepool.userids["margins.inline"]
+
+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 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,
+ 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,
+ }
+}
+
+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(si.box.list))
+ end
+ else
+ report_margindata("%s: nothing stored in location %a",banner,location)
+ end
+end
+
+function margins.save(t)
+ setmetatable(t,defaults)
+ local content = texbox[t.number]
+ local location = t.location
+ local category = t.category
+ local inline = t.inline
+ local scope = t.scope or v_global
+ if not content then
+ report_margindata("ignoring empty margin data %a",location or "unknown")
+ return
+ end
+ 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
+ local name = t.name
+ if trace_marginstack then
+ showstore(store,"before",location)
+ end
+ if name and name ~= "" then
+ if inlinestore then -- todo: inline store has to be done differently (not sparse)
+ local t = table.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)
+ free_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)
+ free_node_list(s.box)
+ end
+ end
+ end
+ if trace_marginstack then
+ showstore(store,"between",location)
+ end
+ end
+ if t.number then
+ -- better make a new table and make t entry in t
+ t.box = copy_node_list(content)
+ t.n = nofsaved
+ -- used later (we will clean up this natural mess later)
+ -- nice is to make a special status table mechanism
+ local leftmargindistance = texdimen.naturalleftmargindistance
+ local rightmargindistance = texdimen.naturalrightmargindistance
+ t.strutdepth = texbox.strutbox.depth
+ t.strutheight = texbox.strutbox.height
+ t.leftskip = tex.leftskip.width -- we're not in forgetall
+ t.rightskip = tex.rightskip.width -- we're not in forgetall
+ t.leftmargindistance = leftmargindistance -- todo:layoutstatus table
+ t.rightmargindistance = rightmargindistance
+ t.leftedgedistance = texdimen.naturalleftedgedistance
+ + texdimen.leftmarginwidth
+ + leftmargindistance
+ t.rightedgedistance = texdimen.naturalrightedgedistance
+ + texdimen.rightmarginwidth
+ + rightmargindistance
+ t.lineheight = texdimen.lineheight
+ --
+ -- t.realpageno = texcount.realpageno
+ if inline then
+ context(new_usernumber(inline_mark,nofsaved))
+ 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 status, nofstatus = { }, 0
+
+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 realpageno = candidate.realpageno
+ local leftpage = isleftpage(false,true)
+ local delta = 0
+ local leftdelta = 0
+ local rightdelta = 0
+ local leftdistance = distance
+ local rightdistance = distance
+ 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_left then
+ delta = hoffset + width + leftdistance + leftdelta
+ elseif location == v_right then
+ delta = -hoffset - hsize - rightdistance + rightdelta
+ elseif location == v_inner then
+ if leftpage then
+ delta = -hoffset - hsize - rightdistance + rightdelta
+ else
+ delta = hoffset + width + leftdistance + leftdelta
+ end
+ elseif location == v_outer then
+ if leftpage then
+ delta = hoffset + width + leftdistance + leftdelta
+ else
+ delta = -hoffset - hsize - rightdistance + rightdelta
+ end
+ end
+
+ -- we assume that list is a hbox, otherwise we had to take the whole current
+ -- in order to get it right
+
+ current.width = 0
+ local anchornode, move_x
+
+ -- this mess is needed for alignments (combinations) so we use that
+ -- oportunity to add arbitrary anchoring
+
+ -- always increment anchor is nicer for multipass when we add new ..
+
+ local inline = candidate.inline
+ local anchor = candidate.anchor
+ if not anchor or anchor == "" then
+ anchor = v_text
+ end
+ if inline or anchor ~= v_text or candidate.psubtype == alignment_code then
+ -- the alignment_code check catches margintexts ste before a tabulate
+ h_anchors = h_anchors + 1
+ anchornode = new_latelua(format("_plib_.set('md:h',%i,{x=true,c=true})",h_anchors))
+ local blob = jobpositions.get('md:h', h_anchors)
+ if blob then
+ local reference = jobpositions.getreserved(anchor,blob.c)
+ if reference then
+ if location == v_left then
+ move_x = (reference.x or 0) - (blob.x or 0)
+ elseif location == v_right then
+ move_x = (reference.x or 0) - (blob.x or 0) + (reference.w or 0) - hsize
+ else
+ -- not yet done
+ end
+ end
+ end
+ end
+
+ if move_x then
+ delta = delta - move_x
+ if trace_margindata then
+ report_margindata("realigned %a, location %a, margin %a, move %p",candidate.n,location,margin,move_x)
+ end
+ else
+ if trace_margindata then
+ report_margindata("realigned %a, location %a, margin %a",candidate.n,location,margin)
+ end
+ end
+
+ current.list = hpack_nodes(concat_nodes{anchornode,new_kern(-delta),current.list,new_kern(delta)})
+ current.width = 0
+end
+
+local function realigned(current,a)
+ local candidate = status[a]
+ realign(current,candidate)
+ nofdelayed = nofdelayed - 1
+ status[a] = nil
+ 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 stacked = { } -- left/right keys depending on location
+local cache = { }
+
+local function resetstacked()
+ stacked = { }
+end
+
+-- resetstacked()
+
+function margins.ha(tag) -- maybe l/r keys ipv left/right keys
+ local p = cache[tag]
+ p.p = true
+ p.y = true
+ jobpositions.set('md:v',tag,p)
+ cache[tag] = nil
+end
+
+local function markovershoot(current)
+ v_anchors = v_anchors + 1
+ cache[v_anchors] = stacked
+ local anchor = new_latelua(format("typesetters.margins.ha(%s)",v_anchors)) -- todo: alleen als offset > line
+ current.list = hpack_nodes(concat_nodes{anchor,current.list})
+end
+
+local function getovershoot(location)
+ local p = jobpositions.get("md:v",v_anchors)
+ local c = jobpositions.get("md:v",v_anchors+1)
+ if p and c and p.p and p.p == c.p then
+ local distance = p.y - c.y
+ local offset = p[location] or 0
+ local overshoot = offset - distance
+ if trace_marginstack then
+ report_margindata("location %a, distance %p, offset %p, overshoot %p",location,distance,offset,overshoot)
+ end
+ if overshoot > 0 then
+ return overshoot
+ end
+ end
+ return 0
+end
+
+local function inject(parent,head,candidate)
+ local box = candidate.box
+ local width = box.width
+ local height = box.height
+ local depth = box.depth
+ local shift = box.shift
+ local stack = candidate.stack
+ 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 psubtype = parent.subtype
+ local offset = stacked[location]
+ local firstonstack = offset == false or offset == nil
+ nofstatus = nofstatus + 1
+ nofdelayed = nofdelayed + 1
+ status[nofstatus] = candidate
+ -- yet untested
+ if baseline == true then
+ baseline = false
+ -- hbox vtop
+--~ for h in traverse_id(hlist_code,box.list.list) do
+--~ baseline = h.height
+--~ break
+--~ end
+ 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 = parent.width -- we can also pass textwidth
+ candidate.psubtype = psubtype
+ if trace_margindata then
+ report_margindata("processing, index %s, height %p, depth %p, parent %s",candidate.n,height,depth,listcodes[psubtype])
+ end
+ if firstonstack then
+ offset = 0
+ else
+-- offset = offset + height
+ end
+ if stack == v_yes then
+ offset = offset + candidate.dy
+ shift = shift + offset
+ elseif stack == v_continue then
+ offset = offset + candidate.dy
+ if firstonstack then
+ offset = offset + getovershoot(location)
+ end
+ 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 - parent.height
+ if trace_margindata then
+ report_margindata("top aligned by %p",delta)
+ end
+ if delta < candidate.threshold then
+ shift = shift + voffset + delta
+ 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
+ box.shift = shift
+ box.width = 0
+ if not head then
+ head = box
+ elseif head.id == whatsit_code and head.subtype == localpar_code then
+ -- experimental
+ if head.dir == "TRT" then
+ box.list = hpack_nodes(concat_nodes{new_kern(candidate.hsize),box.list,new_kern(-candidate.hsize)})
+ end
+ insert_node_after(head,head,box)
+ else
+ head.prev = box
+ box.next = head
+ head = box
+ end
+ box[a_margindata] = nofstatus
+ if trace_margindata then
+ report_margindata("injected, location %a, shift %p",location,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
+ }
+ offset = offset + height
+ stacked[location] = offset -- weird, no table ?
+ -- todo: if no real depth then zero
+ if trace_margindata then
+ report_margindata("status, offset %s",offset)
+ end
+ return head, room, stack == v_continue
+end
+
+local function flushinline(parent,head)
+ local current = head
+ local done = false
+ local continue = false
+ local room, don, con
+ while current and nofinlined > 0 do
+ local id = current.id
+ if id == whatsit_code then
+ if current.subtype == userdefined_code and current.user_id == inline_mark then
+ local n = current.value
+ 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
+ continue = continue or con
+ done = true
+ nofstored = nofstored - 1
+ end
+ end
+ elseif id == hlist_code or id == vlist_code then
+ -- optional (but sometimes needed)
+ current.list, don, con = flushinline(current,current.list)
+ continue = continue or con
+ done = done or don
+ end
+ current = current.next
+ end
+ return head, done, continue
+end
+
+local a_linenumber = attributes.private('linenumber')
+
+local function flushed(scope,parent) -- current is hlist
+ local head = parent.list
+ 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]
+ 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
+ registertogether(parent,room)
+ else
+ break
+ end
+ end
+ end
+ end
+ if nofinlined > 0 then
+ if done then
+ parent.list = head
+ end
+ head, don, con = flushinline(parent,head)
+ continue = continue or con
+ done = done or don
+ end
+ if done then
+ local a = head[a_linenumber] -- hack .. we need a more decent critical attribute inheritance mechanism
+ parent.list = hpack_nodes(head,parent.width,"exactly")
+ if a then
+ parent.list[a_linenumber] = a
+ end
+ -- resetstacked()
+ 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
+ while current do
+ local id = current.id
+ if (id == vlist_code or id == hlist_code) and not current[a_margindata] then
+ local don, continue = flushed(scope,current)
+ if don then
+ current[a_margindata] = 0 -- signal to prevent duplicate processing
+ if continue then
+ markovershoot(current)
+ end
+ if nofstored <= 0 then
+ break
+ end
+ done = true
+ end
+ end
+ current = current.next
+ end
+ -- if done then
+ resetstacked() -- why doesn't done work ok here?
+ -- end
+ return head, done
+ else
+ return head, false
+ end
+end
+
+function margins.localhandler(head,group) -- sometimes group is "" which is weird
+ 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, false
+ elseif nofstored > 0 then
+ return handler(v_local,head,group)
+ else
+ if trace_margingroup then
+ report_margindata("ignored 4, group %a, stored %s, inhibit %a",group,nofstored,inhibit)
+ end
+ return head, false
+ end
+end
+
+function margins.globalhandler(head,group) -- check group
+ 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, false
+ elseif group == "hmode_par" then
+ return handler("global",head,group)
+ elseif group == "vmode_par" then -- experiment (for alignments)
+ return handler("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("global",head,group)
+ elseif group == "alignment" then -- experiment (for alignments)
+ return handler("global",head,group)
+ else
+ if trace_margingroup then
+ report_margindata("ignored 2, group %a, stored %s, inhibit %a",group,nofstored,inhibit)
+ end
+ return head, false
+ end
+end
+
+local function finalhandler(head)
+ if nofdelayed > 0 then
+ local current = head
+ local done = false
+ while current do
+ local id = current.id
+ if id == hlist_code then
+ local a = current[a_margindata]
+ if not a or a == 0 then
+ finalhandler(current.list)
+ elseif realigned(current,a) then
+ done = true
+ if nofdelayed == 0 then
+ return head, true
+ end
+ end
+ elseif id == vlist_code then
+ finalhandler(current.list)
+ end
+ current = current.next
+ end
+ return head, done
+ else
+ return head, false
+ end
+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
+ return finalhandler(head)
+ else
+ return head, false
+ end
+end
+
+-- Somehow the vbox builder (in combinations) gets pretty confused and decides to
+-- go horizontal. So this needs more testing.
+
+prependaction("finalizers", "lists", "typesetters.margins.localhandler")
+-- ("vboxbuilders", "normalizers", "typesetters.margins.localhandler")
+prependaction("mvlbuilders", "normalizers", "typesetters.margins.globalhandler")
+prependaction("shipouts", "normalizers", "typesetters.margins.finalhandler")
+
+disableaction("finalizers", "typesetters.margins.localhandler")
+-- ("vboxbuilders", "typesetters.margins.localhandler")
+disableaction("mvlbuilders", "typesetters.margins.globalhandler")
+disableaction("shipouts", "typesetters.margins.finalhandler")
+
+enablelocal = function()
+ enableaction("finalizers", "typesetters.margins.localhandler")
+ -- enableaction("vboxbuilders", "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)
diff --git a/tex/context/base/typo-pag.lua b/tex/context/base/typo-pag.lua
index d39748d26..0dd75ddf9 100644
--- a/tex/context/base/typo-pag.lua
+++ b/tex/context/base/typo-pag.lua
@@ -1,179 +1,179 @@
-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"
-}
-
-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 insert_node_after = node.insert_after
-local new_penalty = nodes.pool.penalty
-
-local unsetvalue = attributes.unsetvalue
-
-local a_keeptogether = attributes.private("keeptogether")
-
-local trace_keeptogether = false
-local report_keeptogether = logs.reporter("parbuilders","keeptogether")
-
-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 ? ... maybe the local_par
-
-function builders.paragraphs.registertogether(line,specification) -- might change
- if not enabled then
- nodes.tasks.enableaction("finalizers","builders.paragraphs.keeptogether")
- end
- local a = 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
- line[a_keeptogether] = last
- end
- if trace_keeptogether then
- local a = a or last
- local c = cache[a]
- if trace_keeptogether then
- 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
-end
-
-local function keeptogether(start,a)
- if start then
- local specification = cache[a]
- if a then
- local current = start.next
- local previous = start
- local total = previous.depth
- 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 = current.id
- if id == vlist_code or id == hlist_code then
- total = total + current.height + current.depth
- if trace_keeptogether then
- report_keeptogether("%s, index %s, total %p, threshold %p","list",a,total,threshold)
- end
- if total <= threshold then
- if previous.id == penalty_code then
- previous.penalty = 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 + current.spec.width
- if trace_keeptogether then
- report_keeptogether("%s, index %s, total %p, threshold %p","glue",a,total,threshold)
- end
- if total <= threshold then
- if previous.id == penalty_code then
- previous.penalty = 10000
- else
- insert_node_after(head,previous,new_penalty(10000))
- end
- else
- break
- end
- elseif id == kern_code then
- total = total + current.kern
- if trace_keeptogether then
- report_keeptogether("%s, index %s, total %s, threshold %s","kern",a,total,threshold)
- end
- if total <= threshold then
- if previous.id == penalty_code then
- previous.penalty = 10000
- else
- insert_node_after(head,previous,new_penalty(10000))
- end
- else
- break
- end
- elseif id == penalty_code then
- if total <= threshold then
- if previous.id == penalty_code then
- previous.penalty = 10000
- end
- current.penalty = 10000
- else
- break
- end
- end
- previous = current
- current = current.next
- end
- end
- end
-end
-
--- also look at first non glue/kern node e.g for a dropped caps
-
-function builders.paragraphs.keeptogether(head)
- local done = false
- local current = head
- while current do
- if current.id == hlist_code then
- local a = current[a_keeptogether]
- if a and a > 0 then
- keeptogether(current,a)
- current[a_keeptogether] = unsetvalue
- cache[a] = nil
- done = true
- end
- end
- current = current.next
- end
- return head, done
-end
+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"
+}
+
+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 insert_node_after = node.insert_after
+local new_penalty = nodes.pool.penalty
+
+local unsetvalue = attributes.unsetvalue
+
+local a_keeptogether = attributes.private("keeptogether")
+
+local trace_keeptogether = false
+local report_keeptogether = logs.reporter("parbuilders","keeptogether")
+
+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 ? ... maybe the local_par
+
+function builders.paragraphs.registertogether(line,specification) -- might change
+ if not enabled then
+ nodes.tasks.enableaction("finalizers","builders.paragraphs.keeptogether")
+ end
+ local a = 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
+ line[a_keeptogether] = last
+ end
+ if trace_keeptogether then
+ local a = a or last
+ local c = cache[a]
+ if trace_keeptogether then
+ 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
+end
+
+local function keeptogether(start,a)
+ if start then
+ local specification = cache[a]
+ if a then
+ local current = start.next
+ local previous = start
+ local total = previous.depth
+ 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 = current.id
+ if id == vlist_code or id == hlist_code then
+ total = total + current.height + current.depth
+ if trace_keeptogether then
+ report_keeptogether("%s, index %s, total %p, threshold %p","list",a,total,threshold)
+ end
+ if total <= threshold then
+ if previous.id == penalty_code then
+ previous.penalty = 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 + current.spec.width
+ if trace_keeptogether then
+ report_keeptogether("%s, index %s, total %p, threshold %p","glue",a,total,threshold)
+ end
+ if total <= threshold then
+ if previous.id == penalty_code then
+ previous.penalty = 10000
+ else
+ insert_node_after(head,previous,new_penalty(10000))
+ end
+ else
+ break
+ end
+ elseif id == kern_code then
+ total = total + current.kern
+ if trace_keeptogether then
+ report_keeptogether("%s, index %s, total %s, threshold %s","kern",a,total,threshold)
+ end
+ if total <= threshold then
+ if previous.id == penalty_code then
+ previous.penalty = 10000
+ else
+ insert_node_after(head,previous,new_penalty(10000))
+ end
+ else
+ break
+ end
+ elseif id == penalty_code then
+ if total <= threshold then
+ if previous.id == penalty_code then
+ previous.penalty = 10000
+ end
+ current.penalty = 10000
+ else
+ break
+ end
+ end
+ previous = current
+ current = current.next
+ end
+ end
+ end
+end
+
+-- also look at first non glue/kern node e.g for a dropped caps
+
+function builders.paragraphs.keeptogether(head)
+ local done = false
+ local current = head
+ while current do
+ if current.id == hlist_code then
+ local a = current[a_keeptogether]
+ if a and a > 0 then
+ keeptogether(current,a)
+ current[a_keeptogether] = unsetvalue
+ cache[a] = nil
+ done = true
+ end
+ end
+ current = current.next
+ end
+ return head, done
+end
diff --git a/tex/context/base/typo-par.lua b/tex/context/base/typo-par.lua
index 0449becbf..b25ae4a5b 100644
--- a/tex/context/base/typo-par.lua
+++ b/tex/context/base/typo-par.lua
@@ -1,181 +1,181 @@
-if not modules then modules = { } end modules ['typo-par'] = {
- version = 1.001,
- comment = "companion to typo-par.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- A playground for experiments.
-
-local utfbyte = utf.byte
-local utfchar = utf.char
-
-local trace_paragraphs = false trackers.register("typesetters.paragraphs", function(v) trace_paragraphs = v end)
-local trace_dropper = false trackers.register("typesetters.paragraphs.dropper",function(v) trace_dropper = v end)
-
-local report_paragraphs = logs.reporter("nodes","paragraphs")
-local report_dropper = logs.reporter("nodes","dropped")
-
-typesetters.paragraphs = typesetters.paragraphs or { }
-local paragraphs = typesetters.paragraphs
-
-local nodecodes = nodes.nodecodes
-local whatsitcodes = nodes.whatsitcodes
-local tasks = nodes.tasks
-
-local variables = interfaces.variables
-
-local texattribute = tex.attribute
-local unsetvalue = attributes.unsetvalue
-
-local glyph_code = nodecodes.glyph
-local hlist_code = nodecodes.hlist
-local kern_node = nodecodes.kern
-local whatsit_code = nodecodes.whatsit
-local localpar_code = whatsitcodes.localpar
-
-local a_paragraph = attributes.private("paragraphspecial")
-local a_color = attributes.private('color')
-local a_transparency = attributes.private('transparency')
-local a_colorspace = attributes.private('colormodel')
-
-local dropper = {
- enabled = false,
- -- font = 0,
- -- n = 0,
- -- distance = 0,
- -- hoffset = 0,
- -- voffset = 0,
-}
-
-local droppers = { }
-
-typesetters.paragraphs.droppers = droppers
-
-function droppers.set(specification)
- dropper = specification or { }
-end
-
-function droppers.freeze()
- if dropper.enabled then
- dropper.font = font.current()
- end
-end
-
--- dropped caps experiment (will be done properly when luatex
--- stores the state in the local par node) .. btw, search still
--- works with dropped caps, as does an export
-
--- we need a 'par' attribute and in fact for dropped caps we don't need
--- need an attribute ... dropit will become s state counter (or end up
--- in the localpar user data
-
--- for the moment, each paragraph gets a number as id (attribute) ..problem
--- with nesting .. or anyhow, needed for tagging anyway
-
--- todo: prevent linebreak .. but normally a dropper ends up atthe top of
--- a page so this has a low priority
-
-local function process(namespace,attribute,head)
- local done = false
- if head.id == whatsit_code and head.subtype == localpar_code then
- -- begin of par
- local a = head[attribute]
- if a and a > 0 then
- if dropper.enabled then
- dropper.enabled = false -- dangerous for e.g. nested || in tufte
- local first = head.next
- if first and first.id == hlist_code then
- -- parbox .. needs to be set at 0
- first = first.next
- end
- if first and first.id == glyph_code then
--- if texattribute[a_paragraph] >= 0 then
--- texattribute[a_paragraph] = unsetvalue
--- end
- local char = first.char
- local prev = first.prev
- local next = first.next
- -- if prev.id == hlist_code then
- -- -- set the width to 0
- -- end
- if next and next.id == kern_node then
- next.kern = 0
- end
- first.font = dropper.font or first.font
- -- can be a helper
- local ma = dropper.ma or 0
- local ca = dropper.ca
- local ta = dropper.ta
- if ca and ca > 0 then
- first[a_colorspace] = ma == 0 and 1 or ma
- first[a_color] = ca
- end
- if ta and ta > 0 then
- first[a_transparency] = ta
- end
- --
- local width = first.width
- local height = first.height
- local depth = first.depth
- local distance = dropper.distance or 0
- local voffset = dropper.voffset or 0
- local hoffset = dropper.hoffset or 0
- first.xoffset = - width - hoffset - distance
- first.yoffset = - height - voffset
- if true then
- -- needed till we can store parindent with localpar
- first.prev = nil
- first.next = nil
- local h = node.hpack(first)
- h.width = 0
- h.height = 0
- h.depth = 0
- prev.next = h
- next.prev = h
- h.next = next
- h.prev = prev
- end
- if dropper.location == variables.margin then
- -- okay
- else
- local lines = tonumber(dropper.n) or 0
- if lines == 0 then -- safeguard, not too precise
- lines = math.ceil((height+voffset) / tex.baselineskip.width)
- end
- tex.hangafter = - lines
- tex.hangindent = width + distance
- end
- done = true
- end
- end
- end
- end
- return head, done
-end
-
-local enabled = false
-
-function paragraphs.set(n)
- if n == variables.reset or not tonumber(n) or n == 0 then
- texattribute[a_paragraph] = unsetvalue
- else
- if not enabled then
- tasks.enableaction("processors","typesetters.paragraphs.handler")
- if trace_paragraphs then
- report_paragraphs("enabling paragraphs")
- end
- enabled = true
- end
- texattribute[a_paragraph] = n
- end
-end
-
-paragraphs.attribute = a_paragraph
-
-paragraphs.handler = nodes.installattributehandler {
- name = "paragraphs",
- namespace = paragraphs,
- processor = process,
-}
+if not modules then modules = { } end modules ['typo-par'] = {
+ version = 1.001,
+ comment = "companion to typo-par.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- A playground for experiments.
+
+local utfbyte = utf.byte
+local utfchar = utf.char
+
+local trace_paragraphs = false trackers.register("typesetters.paragraphs", function(v) trace_paragraphs = v end)
+local trace_dropper = false trackers.register("typesetters.paragraphs.dropper",function(v) trace_dropper = v end)
+
+local report_paragraphs = logs.reporter("nodes","paragraphs")
+local report_dropper = logs.reporter("nodes","dropped")
+
+typesetters.paragraphs = typesetters.paragraphs or { }
+local paragraphs = typesetters.paragraphs
+
+local nodecodes = nodes.nodecodes
+local whatsitcodes = nodes.whatsitcodes
+local tasks = nodes.tasks
+
+local variables = interfaces.variables
+
+local texattribute = tex.attribute
+local unsetvalue = attributes.unsetvalue
+
+local glyph_code = nodecodes.glyph
+local hlist_code = nodecodes.hlist
+local kern_node = nodecodes.kern
+local whatsit_code = nodecodes.whatsit
+local localpar_code = whatsitcodes.localpar
+
+local a_paragraph = attributes.private("paragraphspecial")
+local a_color = attributes.private('color')
+local a_transparency = attributes.private('transparency')
+local a_colorspace = attributes.private('colormodel')
+
+local dropper = {
+ enabled = false,
+ -- font = 0,
+ -- n = 0,
+ -- distance = 0,
+ -- hoffset = 0,
+ -- voffset = 0,
+}
+
+local droppers = { }
+
+typesetters.paragraphs.droppers = droppers
+
+function droppers.set(specification)
+ dropper = specification or { }
+end
+
+function droppers.freeze()
+ if dropper.enabled then
+ dropper.font = font.current()
+ end
+end
+
+-- dropped caps experiment (will be done properly when luatex
+-- stores the state in the local par node) .. btw, search still
+-- works with dropped caps, as does an export
+
+-- we need a 'par' attribute and in fact for dropped caps we don't need
+-- need an attribute ... dropit will become s state counter (or end up
+-- in the localpar user data
+
+-- for the moment, each paragraph gets a number as id (attribute) ..problem
+-- with nesting .. or anyhow, needed for tagging anyway
+
+-- todo: prevent linebreak .. but normally a dropper ends up atthe top of
+-- a page so this has a low priority
+
+local function process(namespace,attribute,head)
+ local done = false
+ if head.id == whatsit_code and head.subtype == localpar_code then
+ -- begin of par
+ local a = head[attribute]
+ if a and a > 0 then
+ if dropper.enabled then
+ dropper.enabled = false -- dangerous for e.g. nested || in tufte
+ local first = head.next
+ if first and first.id == hlist_code then
+ -- parbox .. needs to be set at 0
+ first = first.next
+ end
+ if first and first.id == glyph_code then
+-- if texattribute[a_paragraph] >= 0 then
+-- texattribute[a_paragraph] = unsetvalue
+-- end
+ local char = first.char
+ local prev = first.prev
+ local next = first.next
+ -- if prev.id == hlist_code then
+ -- -- set the width to 0
+ -- end
+ if next and next.id == kern_node then
+ next.kern = 0
+ end
+ first.font = dropper.font or first.font
+ -- can be a helper
+ local ma = dropper.ma or 0
+ local ca = dropper.ca
+ local ta = dropper.ta
+ if ca and ca > 0 then
+ first[a_colorspace] = ma == 0 and 1 or ma
+ first[a_color] = ca
+ end
+ if ta and ta > 0 then
+ first[a_transparency] = ta
+ end
+ --
+ local width = first.width
+ local height = first.height
+ local depth = first.depth
+ local distance = dropper.distance or 0
+ local voffset = dropper.voffset or 0
+ local hoffset = dropper.hoffset or 0
+ first.xoffset = - width - hoffset - distance
+ first.yoffset = - height - voffset
+ if true then
+ -- needed till we can store parindent with localpar
+ first.prev = nil
+ first.next = nil
+ local h = node.hpack(first)
+ h.width = 0
+ h.height = 0
+ h.depth = 0
+ prev.next = h
+ next.prev = h
+ h.next = next
+ h.prev = prev
+ end
+ if dropper.location == variables.margin then
+ -- okay
+ else
+ local lines = tonumber(dropper.n) or 0
+ if lines == 0 then -- safeguard, not too precise
+ lines = math.ceil((height+voffset) / tex.baselineskip.width)
+ end
+ tex.hangafter = - lines
+ tex.hangindent = width + distance
+ end
+ done = true
+ end
+ end
+ end
+ end
+ return head, done
+end
+
+local enabled = false
+
+function paragraphs.set(n)
+ if n == variables.reset or not tonumber(n) or n == 0 then
+ texattribute[a_paragraph] = unsetvalue
+ else
+ if not enabled then
+ tasks.enableaction("processors","typesetters.paragraphs.handler")
+ if trace_paragraphs then
+ report_paragraphs("enabling paragraphs")
+ end
+ enabled = true
+ end
+ texattribute[a_paragraph] = n
+ end
+end
+
+paragraphs.attribute = a_paragraph
+
+paragraphs.handler = nodes.installattributehandler {
+ name = "paragraphs",
+ namespace = paragraphs,
+ processor = process,
+}
diff --git a/tex/context/base/typo-prc.lua b/tex/context/base/typo-prc.lua
index 4fb64d0f5..5b74abd0b 100644
--- a/tex/context/base/typo-prc.lua
+++ b/tex/context/base/typo-prc.lua
@@ -1,125 +1,125 @@
-if not modules then modules = { } end modules ['typo-prc'] = {
- version = 1.001,
- comment = "companion to typo-prc.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- moved from strc-ini.lua
-
-
-local formatters = string.formatters
-local lpegmatch, patterns, P, C, Cs = lpeg.match, lpeg.patterns, lpeg.P, lpeg.C, lpeg.Cs
-
--- processors: syntax: processor->data ... not ok yet
-
-typesetters.processors = typesetters.processors or { }
-local processors = typesetters.processors
-
-local trace_processors = false
-local report_processors = logs.reporter("processors")
-local registered = { }
-
-trackers.register("typesetters.processors", function(v) trace_processors = v end)
-
-function processors.register(p)
- registered[p] = true
-end
-
-function processors.reset(p)
- registered[p] = nil
-end
-
---~ local splitter = lpeg.splitat("->",true) -- also support =>
-
-local becomes = P('->')
-local processor = (1-becomes)^1
-local splitter = C(processor) * becomes * Cs(patterns.argument + patterns.content)
-
-function processors.split(str)
- local p, s = lpegmatch(splitter,str)
- if registered[p] then
- return p, s
- else
- return false, str
- end
-end
-
-function processors.apply(p,s)
- local str = p
- if s == nil then
- p, s = lpegmatch(splitter,p)
- end
- if p and registered[p] then
- if trace_processors then
- report_processors("applying %s processor %a, argument: %s","known",p,s)
- end
- context.applyprocessor(p,s)
- elseif s then
- if trace_processors then
- report_processors("applying %s processor %a, argument: %s","unknown",p,s)
- end
- context(s)
- elseif str then
- if trace_processors then
- report_processors("applying %s processor, data: %s","ignored",str)
- end
- context(str)
- end
-end
-
-function processors.startapply(p,s)
- local str = p
- if s == nil then
- p, s = lpegmatch(splitter,p)
- end
- if p and registered[p] then
- if trace_processors then
- report_processors("start applying %s processor %a","known",p)
- end
- context.applyprocessor(p)
- context("{")
- return s
- elseif p then
- if trace_processors then
- report_processors("start applying %s processor %a","unknown",p)
- end
- context.firstofoneargument()
- context("{")
- return s
- else
- if trace_processors then
- report_processors("start applying %s processor","ignored")
- end
- context.firstofoneargument()
- context("{")
- return str
- end
-end
-
-function processors.stopapply()
- context("}")
- if trace_processors then
- report_processors("stop applying processor")
- end
-end
-
-function processors.tostring(str)
- local p, s = lpegmatch(splitter,str)
- if registered[p] then
- return formatters["\\applyprocessor{%s}{%s}"](p,s)
- else
- return str
- end
-end
-
-function processors.stripped(str)
- local p, s = lpegmatch(splitter,str)
- return s or str
-end
-
--- interface
-
-commands.registerstructureprocessor = processors.register
-commands.resetstructureprocessor = processors.reset
+if not modules then modules = { } end modules ['typo-prc'] = {
+ version = 1.001,
+ comment = "companion to typo-prc.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- moved from strc-ini.lua
+
+
+local formatters = string.formatters
+local lpegmatch, patterns, P, C, Cs = lpeg.match, lpeg.patterns, lpeg.P, lpeg.C, lpeg.Cs
+
+-- processors: syntax: processor->data ... not ok yet
+
+typesetters.processors = typesetters.processors or { }
+local processors = typesetters.processors
+
+local trace_processors = false
+local report_processors = logs.reporter("processors")
+local registered = { }
+
+trackers.register("typesetters.processors", function(v) trace_processors = v end)
+
+function processors.register(p)
+ registered[p] = true
+end
+
+function processors.reset(p)
+ registered[p] = nil
+end
+
+--~ local splitter = lpeg.splitat("->",true) -- also support =>
+
+local becomes = P('->')
+local processor = (1-becomes)^1
+local splitter = C(processor) * becomes * Cs(patterns.argument + patterns.content)
+
+function processors.split(str)
+ local p, s = lpegmatch(splitter,str)
+ if registered[p] then
+ return p, s
+ else
+ return false, str
+ end
+end
+
+function processors.apply(p,s)
+ local str = p
+ if s == nil then
+ p, s = lpegmatch(splitter,p)
+ end
+ if p and registered[p] then
+ if trace_processors then
+ report_processors("applying %s processor %a, argument: %s","known",p,s)
+ end
+ context.applyprocessor(p,s)
+ elseif s then
+ if trace_processors then
+ report_processors("applying %s processor %a, argument: %s","unknown",p,s)
+ end
+ context(s)
+ elseif str then
+ if trace_processors then
+ report_processors("applying %s processor, data: %s","ignored",str)
+ end
+ context(str)
+ end
+end
+
+function processors.startapply(p,s)
+ local str = p
+ if s == nil then
+ p, s = lpegmatch(splitter,p)
+ end
+ if p and registered[p] then
+ if trace_processors then
+ report_processors("start applying %s processor %a","known",p)
+ end
+ context.applyprocessor(p)
+ context("{")
+ return s
+ elseif p then
+ if trace_processors then
+ report_processors("start applying %s processor %a","unknown",p)
+ end
+ context.firstofoneargument()
+ context("{")
+ return s
+ else
+ if trace_processors then
+ report_processors("start applying %s processor","ignored")
+ end
+ context.firstofoneargument()
+ context("{")
+ return str
+ end
+end
+
+function processors.stopapply()
+ context("}")
+ if trace_processors then
+ report_processors("stop applying processor")
+ end
+end
+
+function processors.tostring(str)
+ local p, s = lpegmatch(splitter,str)
+ if registered[p] then
+ return formatters["\\applyprocessor{%s}{%s}"](p,s)
+ else
+ return str
+ end
+end
+
+function processors.stripped(str)
+ local p, s = lpegmatch(splitter,str)
+ return s or str
+end
+
+-- interface
+
+commands.registerstructureprocessor = processors.register
+commands.resetstructureprocessor = processors.reset
diff --git a/tex/context/base/typo-rep.lua b/tex/context/base/typo-rep.lua
index e7e11bbf0..8451ce52b 100644
--- a/tex/context/base/typo-rep.lua
+++ b/tex/context/base/typo-rep.lua
@@ -1,128 +1,128 @@
-if not modules then modules = { } end modules ['typo-rep'] = {
- 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 was rather boring to program (more of the same) but I could
--- endure it by listening to a couple cd's by The Scene and The Lau
--- on the squeezebox on my desk.
-
-local trace_stripping = false trackers.register("nodes.stripping", function(v) trace_stripping = v end)
- trackers.register("fonts.stripping", function(v) trace_stripping = v end)
-
-local report_stripping = logs.reporter("fonts","stripping")
-
-local nodes, node = nodes, node
-
-local delete_node = nodes.delete
-local replace_node = nodes.replace
-local copy_node = node.copy
-
-local chardata = characters.data
-local collected = false
-local a_stripping = attributes.private("stripping")
-local fontdata = fonts.hashes.identifiers
-local tasks = nodes.tasks
-
-local texattribute = tex.attribute
-local unsetvalue = attributes.unsetvalue
-
-local v_reset = interfaces.variables.reset
-
-local nodecodes = nodes.nodecodes
-local glyph_code = nodecodes.glyph
-
--- todo: other namespace -> typesetters
-
-nodes.stripping = nodes.stripping or { } local stripping = nodes.stripping
-stripping.glyphs = stripping.glyphs or { } local glyphs = stripping.glyphs
-
-local function initialize()
- for k,v in next, chardata do
- if v.category == "cf" and v.visible ~= "yes" then
- if not glyphs[k] then
- glyphs[k] = true
- end
- end
- end
- initialize = nil
-end
-
-local function process(what,head,current,char)
- if what == true then
- if trace_stripping then
- report_stripping("deleting %C from text",char)
- end
- head, current = delete_node(head,current)
- elseif type(what) == "function" then
- head, current = what(head,current)
- current = current.next
- if trace_stripping then
- report_stripping("processing %C in text",char)
- end
- elseif what then -- assume node
- head, current = replace_node(head,current,copy_node(what))
- current = current.next
- if trace_stripping then
- report_stripping("replacing %C in text",char)
- end
- end
- return head, current
-end
-
-function nodes.handlers.stripping(head)
- local current, done = head, false
- while current do
- if current.id == glyph_code then
- -- it's more efficient to keep track of what needs to be kept
- local todo = current[a_stripping]
- if todo == 1 then
- local char = current.char
- local what = glyphs[char]
- if what then
- head, current = process(what,head,current,char)
- done = true
- else -- handling of spacing etc has to be done elsewhere
- current = current.next
- end
- else
- current = current.next
- end
- else
- current = current.next
- end
- end
- return head, done
-end
-
-local enabled = false
-
-function stripping.set(n) -- number or 'reset'
- if n == v_reset then
- n = unsetvalue
- else
- n = tonumber(n)
- if n then
- if not enabled then
- if initialize then initialize() end
- tasks.enableaction("processors","nodes.handlers.stripping")
- enabled = true
- end
- else
- n = unsetvalue
- end
- end
- texattribute[a_stripping] = n
-end
-
--- why not in task-ini?
-
-tasks.appendaction("processors","fonts","nodes.handlers.stripping",nil,"nodes.handlers.characters")
-tasks.disableaction("processors","nodes.handlers.stripping")
-
--- interface
-
-commands.setcharacterstripping = stripping.set
+if not modules then modules = { } end modules ['typo-rep'] = {
+ 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 was rather boring to program (more of the same) but I could
+-- endure it by listening to a couple cd's by The Scene and The Lau
+-- on the squeezebox on my desk.
+
+local trace_stripping = false trackers.register("nodes.stripping", function(v) trace_stripping = v end)
+ trackers.register("fonts.stripping", function(v) trace_stripping = v end)
+
+local report_stripping = logs.reporter("fonts","stripping")
+
+local nodes, node = nodes, node
+
+local delete_node = nodes.delete
+local replace_node = nodes.replace
+local copy_node = node.copy
+
+local chardata = characters.data
+local collected = false
+local a_stripping = attributes.private("stripping")
+local fontdata = fonts.hashes.identifiers
+local tasks = nodes.tasks
+
+local texattribute = tex.attribute
+local unsetvalue = attributes.unsetvalue
+
+local v_reset = interfaces.variables.reset
+
+local nodecodes = nodes.nodecodes
+local glyph_code = nodecodes.glyph
+
+-- todo: other namespace -> typesetters
+
+nodes.stripping = nodes.stripping or { } local stripping = nodes.stripping
+stripping.glyphs = stripping.glyphs or { } local glyphs = stripping.glyphs
+
+local function initialize()
+ for k,v in next, chardata do
+ if v.category == "cf" and v.visible ~= "yes" then
+ if not glyphs[k] then
+ glyphs[k] = true
+ end
+ end
+ end
+ initialize = nil
+end
+
+local function process(what,head,current,char)
+ if what == true then
+ if trace_stripping then
+ report_stripping("deleting %C from text",char)
+ end
+ head, current = delete_node(head,current)
+ elseif type(what) == "function" then
+ head, current = what(head,current)
+ current = current.next
+ if trace_stripping then
+ report_stripping("processing %C in text",char)
+ end
+ elseif what then -- assume node
+ head, current = replace_node(head,current,copy_node(what))
+ current = current.next
+ if trace_stripping then
+ report_stripping("replacing %C in text",char)
+ end
+ end
+ return head, current
+end
+
+function nodes.handlers.stripping(head)
+ local current, done = head, false
+ while current do
+ if current.id == glyph_code then
+ -- it's more efficient to keep track of what needs to be kept
+ local todo = current[a_stripping]
+ if todo == 1 then
+ local char = current.char
+ local what = glyphs[char]
+ if what then
+ head, current = process(what,head,current,char)
+ done = true
+ else -- handling of spacing etc has to be done elsewhere
+ current = current.next
+ end
+ else
+ current = current.next
+ end
+ else
+ current = current.next
+ end
+ end
+ return head, done
+end
+
+local enabled = false
+
+function stripping.set(n) -- number or 'reset'
+ if n == v_reset then
+ n = unsetvalue
+ else
+ n = tonumber(n)
+ if n then
+ if not enabled then
+ if initialize then initialize() end
+ tasks.enableaction("processors","nodes.handlers.stripping")
+ enabled = true
+ end
+ else
+ n = unsetvalue
+ end
+ end
+ texattribute[a_stripping] = n
+end
+
+-- why not in task-ini?
+
+tasks.appendaction("processors","fonts","nodes.handlers.stripping",nil,"nodes.handlers.characters")
+tasks.disableaction("processors","nodes.handlers.stripping")
+
+-- interface
+
+commands.setcharacterstripping = stripping.set
diff --git a/tex/context/base/typo-spa.lua b/tex/context/base/typo-spa.lua
index 11de65f7b..5eba22889 100644
--- a/tex/context/base/typo-spa.lua
+++ b/tex/context/base/typo-spa.lua
@@ -1,229 +1,229 @@
-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 utfchar = utf.char
-
-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 insert_node_before = node.insert_before
-local insert_node_after = node.insert_after
-local remove_node = nodes.remove
-local end_of_math = node.end_of_math
-
-local fonthashes = fonts.hashes
-local fontdata = fonthashes.identifiers
-local quaddata = fonthashes.quads
-
-local texattribute = tex.attribute
-local unsetvalue = attributes.unsetvalue
-
-local v_reset = interfaces.variables.reset
-
-local nodecodes = nodes.nodecodes
-local glyph_code = nodecodes.glyph
-local math_code = nodecodes.math
-
-local somespace = nodes.somespace
-local somepenalty = nodes.somepenalty
-
-local nodepool = nodes.pool
-local tasks = nodes.tasks
-
-local new_penalty = nodepool.penalty
-local new_glue = nodepool.glue
-
-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")
-spacings.attribute = a_spacings
-
-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
-
-local function process(namespace,attribute,head)
- local done = false
- local start = head
- -- head is always begin of par (whatsit), so we have at least two prev nodes
- -- penalty followed by glue
- while start do
- local id = start.id
- if id == glyph_code then
- local attr = start[attribute]
- if attr and attr > 0 then
- local data = mapping[attr]
- if data then
- local char = start.char
- local map = data.characters[char]
- start[attribute] = unsetvalue -- needed?
- if map then
- local left = map.left
- local right = map.right
- local alternative = map.alternative
- local quad = quaddata[start.font]
- local prev = start.prev
- if left and left ~= 0 and prev then
- local ok = false
- local prevprev = prev.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))
- done = true
- end
- end
- local next = start.next
- if right and right ~= 0 and next then
- local ok = false
- local nextnext = next.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))
- done = true
- end
- end
- end
- end
- end
- elseif id == math_code then
- start = end_of_math(start) -- weird, can return nil .. no math end?
- end
- if start then
- start = start.next
- end
- end
- return head, done
-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
- tasks.enableaction("processors","typesetters.spacings.handler")
- enabled = true
- end
- n = data.number or unsetvalue
- end
- end
- texattribute[a_spacings] = n
-end
-
-function spacings.reset()
- texattribute[a_spacings] = unsetvalue
-end
-
-spacings.handler = nodes.installattributehandler {
- name = "spacing",
- namespace = spacings,
- processor = process,
-}
-
--- interface
-
-commands.definecharacterspacing = spacings.define
-commands.setupcharacterspacing = spacings.setup
-commands.setcharacterspacing = spacings.set
+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 utfchar = utf.char
+
+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 insert_node_before = node.insert_before
+local insert_node_after = node.insert_after
+local remove_node = nodes.remove
+local end_of_math = node.end_of_math
+
+local fonthashes = fonts.hashes
+local fontdata = fonthashes.identifiers
+local quaddata = fonthashes.quads
+
+local texattribute = tex.attribute
+local unsetvalue = attributes.unsetvalue
+
+local v_reset = interfaces.variables.reset
+
+local nodecodes = nodes.nodecodes
+local glyph_code = nodecodes.glyph
+local math_code = nodecodes.math
+
+local somespace = nodes.somespace
+local somepenalty = nodes.somepenalty
+
+local nodepool = nodes.pool
+local tasks = nodes.tasks
+
+local new_penalty = nodepool.penalty
+local new_glue = nodepool.glue
+
+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")
+spacings.attribute = a_spacings
+
+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
+
+local function process(namespace,attribute,head)
+ local done = false
+ local start = head
+ -- head is always begin of par (whatsit), so we have at least two prev nodes
+ -- penalty followed by glue
+ while start do
+ local id = start.id
+ if id == glyph_code then
+ local attr = start[attribute]
+ if attr and attr > 0 then
+ local data = mapping[attr]
+ if data then
+ local char = start.char
+ local map = data.characters[char]
+ start[attribute] = unsetvalue -- needed?
+ if map then
+ local left = map.left
+ local right = map.right
+ local alternative = map.alternative
+ local quad = quaddata[start.font]
+ local prev = start.prev
+ if left and left ~= 0 and prev then
+ local ok = false
+ local prevprev = prev.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))
+ done = true
+ end
+ end
+ local next = start.next
+ if right and right ~= 0 and next then
+ local ok = false
+ local nextnext = next.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))
+ done = true
+ end
+ end
+ end
+ end
+ end
+ elseif id == math_code then
+ start = end_of_math(start) -- weird, can return nil .. no math end?
+ end
+ if start then
+ start = start.next
+ end
+ end
+ return head, done
+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
+ tasks.enableaction("processors","typesetters.spacings.handler")
+ enabled = true
+ end
+ n = data.number or unsetvalue
+ end
+ end
+ texattribute[a_spacings] = n
+end
+
+function spacings.reset()
+ texattribute[a_spacings] = unsetvalue
+end
+
+spacings.handler = nodes.installattributehandler {
+ name = "spacing",
+ namespace = spacings,
+ processor = process,
+}
+
+-- interface
+
+commands.definecharacterspacing = spacings.define
+commands.setupcharacterspacing = spacings.setup
+commands.setcharacterspacing = spacings.set
diff --git a/tex/context/base/unic-ini.lua b/tex/context/base/unic-ini.lua
index 6a0c387d3..cca1f0617 100644
--- a/tex/context/base/unic-ini.lua
+++ b/tex/context/base/unic-ini.lua
@@ -1,19 +1,19 @@
-if not modules then modules = { } end modules ['unic-ini'] = {
- version = 1.001,
- comment = "companion to unic-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local utfchar = utf.char
-
--- Beware, initializing unicodechar happens at first usage and takes
--- 0.05 -- 0.1 second (lots of function calls).
-
-function commands.unicodechar(asked)
- local n = characters.unicodechar(asked)
- if n then
- context(utfchar(n))
- end
-end
+if not modules then modules = { } end modules ['unic-ini'] = {
+ version = 1.001,
+ comment = "companion to unic-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local utfchar = utf.char
+
+-- Beware, initializing unicodechar happens at first usage and takes
+-- 0.05 -- 0.1 second (lots of function calls).
+
+function commands.unicodechar(asked)
+ local n = characters.unicodechar(asked)
+ if n then
+ context(utfchar(n))
+ end
+end
diff --git a/tex/context/base/util-deb.lua b/tex/context/base/util-deb.lua
index 9e5233774..785373f86 100644
--- a/tex/context/base/util-deb.lua
+++ b/tex/context/base/util-deb.lua
@@ -1,128 +1,128 @@
-if not modules then modules = { } end modules ['util-deb'] = {
- 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"
-}
-
--- the <anonymous> tag is kind of generic and used for functions that are not
--- bound to a variable, like node.new, node.copy etc (contrary to for instance
--- node.has_attribute which is bound to a has_attribute local variable in mkiv)
-
-local debug = require "debug"
-
-local getinfo = debug.getinfo
-local type, next, tostring = type, next, tostring
-local format, find = string.format, string.find
-local is_boolean = string.is_boolean
-
-utilities = utilities or { }
-local debugger = utilities.debugger or { }
-utilities.debugger = debugger
-
-local counters = { }
-local names = { }
-
-local report = logs.reporter("debugger")
-
--- one
-
-local function hook()
- local f = getinfo(2) -- "nS"
- if f then
- local n = "unknown"
- if f.what == "C" then
- n = f.name or '<anonymous>'
- if not names[n] then
- names[n] = format("%42s",n)
- end
- else
- -- source short_src linedefined what name namewhat nups func
- n = f.name or f.namewhat or f.what
- if not n or n == "" then
- n = "?"
- end
- if not names[n] then
- names[n] = format("%42s : % 5i : %s",n,f.linedefined or 0,f.short_src or "unknown source")
- end
- end
- counters[n] = (counters[n] or 0) + 1
- end
-end
-
-function debugger.showstats(printer,threshold) -- hm, something has changed, rubish now
- printer = printer or report
- threshold = threshold or 0
- local total, grandtotal, functions = 0, 0, 0
- local dataset = { }
- for name, count in next, counters do
- dataset[#dataset+1] = { name, count }
- end
- table.sort(dataset,function(a,b) return a[2] == b[2] and b[1] > a[1] or a[2] > b[2] end)
- for i=1,#dataset do
- local d = dataset[i]
- local name = d[1]
- local count = d[2]
- if count > threshold and not find(name,"for generator") then -- move up
- printer(format("%8i %s\n", count, names[name]))
- total = total + count
- end
- grandtotal = grandtotal + count
- functions = functions + 1
- end
- printer("\n")
- printer(format("functions : % 10i\n", functions))
- printer(format("total : % 10i\n", total))
- printer(format("grand total: % 10i\n", grandtotal))
- printer(format("threshold : % 10i\n", threshold))
-end
-
-function debugger.savestats(filename,threshold)
- local f = io.open(filename,'w')
- if f then
- debugger.showstats(function(str) f:write(str) end,threshold)
- f:close()
- end
-end
-
-function debugger.enable()
- debug.sethook(hook,"c")
-end
-
-function debugger.disable()
- debug.sethook()
---~ counters[debug.getinfo(2,"f").func] = nil
-end
-
---~ debugger.enable()
-
---~ print(math.sin(1*.5))
---~ print(math.sin(1*.5))
---~ print(math.sin(1*.5))
---~ print(math.sin(1*.5))
---~ print(math.sin(1*.5))
-
---~ debugger.disable()
-
---~ print("")
---~ debugger.showstats()
---~ print("")
---~ debugger.showstats(print,3)
-
--- from the lua book:
-
-function traceback()
- local level = 1
- while true do
- local info = debug.getinfo(level, "Sl")
- if not info then
- break
- elseif info.what == "C" then
- print(format("%3i : C function",level))
- else
- print(format("%3i : [%s]:%d",level,info.short_src,info.currentline))
- end
- level = level + 1
- end
-end
+if not modules then modules = { } end modules ['util-deb'] = {
+ 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"
+}
+
+-- the <anonymous> tag is kind of generic and used for functions that are not
+-- bound to a variable, like node.new, node.copy etc (contrary to for instance
+-- node.has_attribute which is bound to a has_attribute local variable in mkiv)
+
+local debug = require "debug"
+
+local getinfo = debug.getinfo
+local type, next, tostring = type, next, tostring
+local format, find = string.format, string.find
+local is_boolean = string.is_boolean
+
+utilities = utilities or { }
+local debugger = utilities.debugger or { }
+utilities.debugger = debugger
+
+local counters = { }
+local names = { }
+
+local report = logs.reporter("debugger")
+
+-- one
+
+local function hook()
+ local f = getinfo(2) -- "nS"
+ if f then
+ local n = "unknown"
+ if f.what == "C" then
+ n = f.name or '<anonymous>'
+ if not names[n] then
+ names[n] = format("%42s",n)
+ end
+ else
+ -- source short_src linedefined what name namewhat nups func
+ n = f.name or f.namewhat or f.what
+ if not n or n == "" then
+ n = "?"
+ end
+ if not names[n] then
+ names[n] = format("%42s : % 5i : %s",n,f.linedefined or 0,f.short_src or "unknown source")
+ end
+ end
+ counters[n] = (counters[n] or 0) + 1
+ end
+end
+
+function debugger.showstats(printer,threshold) -- hm, something has changed, rubish now
+ printer = printer or report
+ threshold = threshold or 0
+ local total, grandtotal, functions = 0, 0, 0
+ local dataset = { }
+ for name, count in next, counters do
+ dataset[#dataset+1] = { name, count }
+ end
+ table.sort(dataset,function(a,b) return a[2] == b[2] and b[1] > a[1] or a[2] > b[2] end)
+ for i=1,#dataset do
+ local d = dataset[i]
+ local name = d[1]
+ local count = d[2]
+ if count > threshold and not find(name,"for generator") then -- move up
+ printer(format("%8i %s\n", count, names[name]))
+ total = total + count
+ end
+ grandtotal = grandtotal + count
+ functions = functions + 1
+ end
+ printer("\n")
+ printer(format("functions : % 10i\n", functions))
+ printer(format("total : % 10i\n", total))
+ printer(format("grand total: % 10i\n", grandtotal))
+ printer(format("threshold : % 10i\n", threshold))
+end
+
+function debugger.savestats(filename,threshold)
+ local f = io.open(filename,'w')
+ if f then
+ debugger.showstats(function(str) f:write(str) end,threshold)
+ f:close()
+ end
+end
+
+function debugger.enable()
+ debug.sethook(hook,"c")
+end
+
+function debugger.disable()
+ debug.sethook()
+--~ counters[debug.getinfo(2,"f").func] = nil
+end
+
+--~ debugger.enable()
+
+--~ print(math.sin(1*.5))
+--~ print(math.sin(1*.5))
+--~ print(math.sin(1*.5))
+--~ print(math.sin(1*.5))
+--~ print(math.sin(1*.5))
+
+--~ debugger.disable()
+
+--~ print("")
+--~ debugger.showstats()
+--~ print("")
+--~ debugger.showstats(print,3)
+
+-- from the lua book:
+
+function traceback()
+ local level = 1
+ while true do
+ local info = debug.getinfo(level, "Sl")
+ if not info then
+ break
+ elseif info.what == "C" then
+ print(format("%3i : C function",level))
+ else
+ print(format("%3i : [%s]:%d",level,info.short_src,info.currentline))
+ end
+ level = level + 1
+ end
+end
diff --git a/tex/context/base/util-dim.lua b/tex/context/base/util-dim.lua
index bbfeae7d4..47b2706b7 100644
--- a/tex/context/base/util-dim.lua
+++ b/tex/context/base/util-dim.lua
@@ -1,449 +1,449 @@
-if not modules then modules = { } end modules ['util-dim'] = {
- version = 1.001,
- comment = "support for dimensions",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
---[[ldx--
-<p>Internally <l n='luatex'/> work with scaled point, which are
-represented by integers. However, in practice, at east at the
-<l n='tex'/> end we work with more generic units like points (pt). Going
-from scaled points (numbers) to one of those units can be
-done by using the conversion factors collected in the following
-table.</p>
---ldx]]--
-
-local format, match, gsub, type, setmetatable = string.format, string.match, string.gsub, type, setmetatable
-local P, S, R, Cc, C, lpegmatch = lpeg.P, lpeg.S, lpeg.R, lpeg.Cc, lpeg.C, lpeg.match
-
-local allocate = utilities.storage.allocate
-local setmetatableindex = table.setmetatableindex
-local formatters = string.formatters
-
---this might become another namespace
-
-number = number or { }
-local number = number
-
-number.tonumberf = function(n) return match(format("%.20f",n),"(.-0?)0*$") end -- one zero too much but alas
-number.tonumberg = function(n) return format("%.20g",n) end
-
-local dimenfactors = allocate {
- ["pt"] = 1/65536,
- ["in"] = ( 100/ 7227)/65536,
- ["cm"] = ( 254/ 7227)/65536,
- ["mm"] = ( 2540/ 7227)/65536,
- ["sp"] = 1, -- 65536 sp in 1pt
- ["bp"] = ( 7200/ 7227)/65536,
- ["pc"] = ( 1/ 12)/65536,
- ["dd"] = ( 1157/ 1238)/65536,
- ["cc"] = ( 1157/14856)/65536,
- ["nd"] = (20320/21681)/65536,
- ["nc"] = ( 5080/65043)/65536
-}
-
---~ print(table.serialize(dimenfactors))
---~
---~ %.99g:
---~
---~ t={
---~ ["bp"]=1.5201782378580324e-005,
---~ ["cc"]=1.1883696112892098e-006,
---~ ["cm"]=5.3628510057769479e-007,
---~ ["dd"]=1.4260435335470516e-005,
---~ ["em"]=0.000152587890625,
---~ ["ex"]=6.103515625e-005,
---~ ["in"]=2.1113586636917117e-007,
---~ ["mm"]=5.3628510057769473e-008,
---~ ["nc"]=1.1917446679504327e-006,
---~ ["nd"]=1.4300936015405194e-005,
---~ ["pc"]=1.2715657552083333e-006,
---~ ["pt"]=1.52587890625e-005,
---~ ["sp"]=1,
---~ }
---~
---~ patched %s and tonumber
---~
---~ t={
---~ ["bp"]=0.00001520178238,
---~ ["cc"]=0.00000118836961,
---~ ["cm"]=0.0000005362851,
---~ ["dd"]=0.00001426043534,
---~ ["em"]=0.00015258789063,
---~ ["ex"]=0.00006103515625,
---~ ["in"]=0.00000021113587,
---~ ["mm"]=0.00000005362851,
---~ ["nc"]=0.00000119174467,
---~ ["nd"]=0.00001430093602,
---~ ["pc"]=0.00000127156576,
---~ ["pt"]=0.00001525878906,
---~ ["sp"]=1,
---~ }
-
---[[ldx--
-<p>A conversion function that takes a number, unit (string) and optional
-format (string) is implemented using this table.</p>
---ldx]]--
-
-
-local function numbertodimen(n,unit,fmt)
- if type(n) == 'string' then
- return n
- else
- unit = unit or 'pt'
- if not fmt then
- fmt = "%s%s"
- elseif fmt == true then
- fmt = "%0.5f%s"
- end
- return format(fmt,n*dimenfactors[unit],unit)
- -- if fmt then
- -- return format(fmt,n*dimenfactors[unit],unit)
- -- else
- -- return match(format("%.20f",n*dimenfactors[unit]),"(.-0?)0*$") .. unit
- -- end
- end
-end
-
---[[ldx--
-<p>We collect a bunch of converters in the <type>number</type> namespace.</p>
---ldx]]--
-
-number.maxdimen = 1073741823
-number.todimen = numbertodimen
-number.dimenfactors = dimenfactors
-
-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
-function number.tonewdidots (n,fmt) return numbertodimen(n,"nd",fmt) end
-function number.tonewciceros (n,fmt) return numbertodimen(n,"nc",fmt) end
-
---[[ldx--
-<p>More interesting it to implement a (sort of) dimen datatype, one
-that permits calculations too. First we define a function that
-converts a string to scaledpoints. We use <l n='lpeg'/>. We capture
-a number and optionally a unit. When no unit is given a constant
-capture takes place.</p>
---ldx]]--
-
-local amount = (S("+-")^0 * R("09")^0 * P(".")^0 * R("09")^0) + Cc("0")
-local unit = R("az")^1
-
-local dimenpair = amount/tonumber * (unit^1/dimenfactors + Cc(1)) -- tonumber is new
-
-lpeg.patterns.dimenpair = dimenpair
-
-local splitter = amount/tonumber * C(unit^1)
-
-function number.splitdimen(str)
- return lpegmatch(splitter,str)
-end
-
---[[ldx--
-<p>We use a metatable to intercept errors. When no key is found in
-the table with factors, the metatable will be consulted for an
-alternative index function.</p>
---ldx]]--
-
-setmetatableindex(dimenfactors, function(t,s)
- -- error("wrong dimension: " .. (s or "?")) -- better a message
- return false
-end)
-
---[[ldx--
-<p>We redefine the following function later on, so we comment it
-here (which saves us bytecodes.</p>
---ldx]]--
-
--- function string.todimen(str)
--- if type(str) == "number" then
--- return str
--- else
--- local value, unit = lpegmatch(dimenpair,str)
--- return value/unit
--- end
--- end
---
--- local stringtodimen = string.todimen
-
-local stringtodimen -- assigned later (commenting saves bytecode)
-
-local amount = S("+-")^0 * R("09")^0 * S(".,")^0 * R("09")^0
-local unit = P("pt") + P("cm") + P("mm") + P("sp") + P("bp") + P("in") +
- P("pc") + P("dd") + P("cc") + P("nd") + P("nc")
-
-local validdimen = amount * unit
-
-lpeg.patterns.validdimen = validdimen
-
---[[ldx--
-<p>This converter accepts calls like:</p>
-
-<typing>
-string.todimen("10")
-string.todimen(".10")
-string.todimen("10.0")
-string.todimen("10.0pt")
-string.todimen("10pt")
-string.todimen("10.0pt")
-</typing>
-
-<p>With this in place, we can now implement a proper datatype for dimensions, one
-that permits us to do this:</p>
-
-<typing>
-s = dimen "10pt" + dimen "20pt" + dimen "200pt"
- - dimen "100sp" / 10 + "20pt" + "0pt"
-</typing>
-
-<p>We create a local metatable for this new type:</p>
---ldx]]--
-
-local dimensions = { }
-
---[[ldx--
-<p>The main (and globally) visible representation of a dimen is defined next: it is
-a one-element table. The unit that is returned from the match is normally a number
-(one of the previously defined factors) but we also accept functions. Later we will
-see why. This function is redefined later.</p>
---ldx]]--
-
--- function dimen(a)
--- if a then
--- local ta= type(a)
--- if ta == "string" then
--- local value, unit = lpegmatch(pattern,a)
--- if type(unit) == "function" then
--- k = value/unit()
--- else
--- k = value/unit
--- end
--- a = k
--- elseif ta == "table" then
--- a = a[1]
--- end
--- return setmetatable({ a }, dimensions)
--- else
--- return setmetatable({ 0 }, dimensions)
--- end
--- end
-
---[[ldx--
-<p>This function return a small hash with a metatable attached. It is
-through this metatable that we can do the calculations. We could have
-shared some of the code but for reasons of speed we don't.</p>
---ldx]]--
-
-function dimensions.__add(a, b)
- local ta, tb = type(a), type(b)
- if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end
- if tb == "string" then b = stringtodimen(b) elseif tb == "table" then b = b[1] end
- return setmetatable({ a + b }, dimensions)
-end
-
-function dimensions.__sub(a, b)
- local ta, tb = type(a), type(b)
- if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end
- if tb == "string" then b = stringtodimen(b) elseif tb == "table" then b = b[1] end
- return setmetatable({ a - b }, dimensions)
-end
-
-function dimensions.__mul(a, b)
- local ta, tb = type(a), type(b)
- if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end
- if tb == "string" then b = stringtodimen(b) elseif tb == "table" then b = b[1] end
- return setmetatable({ a * b }, dimensions)
-end
-
-function dimensions.__div(a, b)
- local ta, tb = type(a), type(b)
- if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end
- if tb == "string" then b = stringtodimen(b) elseif tb == "table" then b = b[1] end
- return setmetatable({ a / b }, dimensions)
-end
-
-function dimensions.__unm(a)
- local ta = type(a)
- if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end
- return setmetatable({ - a }, dimensions)
-end
-
---[[ldx--
-<p>It makes no sense to implement the power and modulo function but
-the next two do make sense because they permits is code like:</p>
-
-<typing>
-local a, b = dimen "10pt", dimen "11pt"
-...
-if a > b then
- ...
-end
-</typing>
---ldx]]--
-
--- makes no sense: dimensions.__pow and dimensions.__mod
-
-function dimensions.__lt(a, b)
- return a[1] < b[1]
-end
-
-function dimensions.__eq(a, b)
- return a[1] == b[1]
-end
-
---[[ldx--
-<p>We also need to provide a function for conversion to string (so that
-we can print dimensions). We print them as points, just like <l n='tex'/>.</p>
---ldx]]--
-
-function dimensions.__tostring(a)
- return a[1]/65536 .. "pt" -- instead of todimen(a[1])
-end
-
---[[ldx--
-<p>Since it does not take much code, we also provide a way to access
-a few accessors</p>
-
-<typing>
-print(dimen().pt)
-print(dimen().sp)
-</typing>
---ldx]]--
-
-function dimensions.__index(tab,key)
- local d = dimenfactors[key]
- if not d then
- error("illegal property of dimen: " .. key)
- d = 1
- end
- return 1/d
-end
-
---[[ldx--
-<p>In the converter from string to dimension we support functions as
-factors. This is because in <l n='tex'/> we have a few more units:
-<type>ex</type> and <type>em</type>. These are not constant factors but
-depend on the current font. They are not defined by default, but need
-an explicit function call. This is because at the moment that this code
-is loaded, the relevant tables that hold the functions needed may not
-yet be available.</p>
---ldx]]--
-
- dimenfactors["ex"] = 4 * 1/65536 -- 4pt
- dimenfactors["em"] = 10 * 1/65536 -- 10pt
--- dimenfactors["%"] = 4 * 1/65536 -- 400pt/100
-
---[[ldx--
-<p>The previous code is rather efficient (also thanks to <l n='lpeg'/>) but we
-can speed it up by caching converted dimensions. On my machine (2008) the following
-loop takes about 25.5 seconds.</p>
-
-<typing>
-for i=1,1000000 do
- local s = dimen "10pt" + dimen "20pt" + dimen "200pt"
- - dimen "100sp" / 10 + "20pt" + "0pt"
-end
-</typing>
-
-<p>When we cache converted strings this becomes 16.3 seconds. In order not
-to waste too much memory on it, we tag the values of the cache as being
-week which mean that the garbage collector will collect them in a next
-sweep. This means that in most cases the speed up is mostly affecting the
-current couple of calculations and as such the speed penalty is small.</p>
-
-<p>We redefine two previous defined functions that can benefit from
-this:</p>
---ldx]]--
-
-local known = { } setmetatable(known, { __mode = "v" })
-
-function dimen(a)
- if a then
- local ta= type(a)
- if ta == "string" then
- local k = known[a]
- if k then
- a = k
- else
- local value, unit = lpegmatch(dimenpair,a)
- if type(unit) == "function" then
- k = value/unit()
- else
- k = value/unit
- end
- known[a] = k
- a = k
- end
- elseif ta == "table" then
- a = a[1]
- end
- return setmetatable({ a }, dimensions)
- else
- return setmetatable({ 0 }, dimensions)
- end
-end
-
-function string.todimen(str) -- maybe use tex.sp when available
- if type(str) == "number" then
- return str
- else
- local k = known[str]
- if not k then
- local value, unit = lpegmatch(dimenpair,str)
- if value and unit then
- k = value/unit -- to be considered: round
- else
- k = 0
- end
- -- print(str,value,unit)
- known[str] = k
- end
- return k
- end
-end
-
---~ local known = { }
-
---~ function string.todimen(str) -- maybe use tex.sp
---~ local k = known[str]
---~ if not k then
---~ k = tex.sp(str)
---~ known[str] = k
---~ end
---~ return k
---~ end
-
-stringtodimen = string.todimen -- local variable defined earlier
-
-function number.toscaled(d)
- return format("%0.5f",d/2^16)
-end
-
---[[ldx--
-<p>In a similar fashion we can define a glue datatype. In that case we
-probably use a hash instead of a one-element table.</p>
---ldx]]--
-
---[[ldx--
-<p>Goodie:s</p>
---ldx]]--
-
-function number.percent(n,d) -- will be cleaned up once luatex 0.30 is out
- d = d or tex.hsize
- if type(d) == "string" then
- d = stringtodimen(d)
- end
- return (n/100) * d
-end
-
-number["%"] = number.percent
+if not modules then modules = { } end modules ['util-dim'] = {
+ version = 1.001,
+ comment = "support for dimensions",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[ldx--
+<p>Internally <l n='luatex'/> work with scaled point, which are
+represented by integers. However, in practice, at east at the
+<l n='tex'/> end we work with more generic units like points (pt). Going
+from scaled points (numbers) to one of those units can be
+done by using the conversion factors collected in the following
+table.</p>
+--ldx]]--
+
+local format, match, gsub, type, setmetatable = string.format, string.match, string.gsub, type, setmetatable
+local P, S, R, Cc, C, lpegmatch = lpeg.P, lpeg.S, lpeg.R, lpeg.Cc, lpeg.C, lpeg.match
+
+local allocate = utilities.storage.allocate
+local setmetatableindex = table.setmetatableindex
+local formatters = string.formatters
+
+--this might become another namespace
+
+number = number or { }
+local number = number
+
+number.tonumberf = function(n) return match(format("%.20f",n),"(.-0?)0*$") end -- one zero too much but alas
+number.tonumberg = function(n) return format("%.20g",n) end
+
+local dimenfactors = allocate {
+ ["pt"] = 1/65536,
+ ["in"] = ( 100/ 7227)/65536,
+ ["cm"] = ( 254/ 7227)/65536,
+ ["mm"] = ( 2540/ 7227)/65536,
+ ["sp"] = 1, -- 65536 sp in 1pt
+ ["bp"] = ( 7200/ 7227)/65536,
+ ["pc"] = ( 1/ 12)/65536,
+ ["dd"] = ( 1157/ 1238)/65536,
+ ["cc"] = ( 1157/14856)/65536,
+ ["nd"] = (20320/21681)/65536,
+ ["nc"] = ( 5080/65043)/65536
+}
+
+--~ print(table.serialize(dimenfactors))
+--~
+--~ %.99g:
+--~
+--~ t={
+--~ ["bp"]=1.5201782378580324e-005,
+--~ ["cc"]=1.1883696112892098e-006,
+--~ ["cm"]=5.3628510057769479e-007,
+--~ ["dd"]=1.4260435335470516e-005,
+--~ ["em"]=0.000152587890625,
+--~ ["ex"]=6.103515625e-005,
+--~ ["in"]=2.1113586636917117e-007,
+--~ ["mm"]=5.3628510057769473e-008,
+--~ ["nc"]=1.1917446679504327e-006,
+--~ ["nd"]=1.4300936015405194e-005,
+--~ ["pc"]=1.2715657552083333e-006,
+--~ ["pt"]=1.52587890625e-005,
+--~ ["sp"]=1,
+--~ }
+--~
+--~ patched %s and tonumber
+--~
+--~ t={
+--~ ["bp"]=0.00001520178238,
+--~ ["cc"]=0.00000118836961,
+--~ ["cm"]=0.0000005362851,
+--~ ["dd"]=0.00001426043534,
+--~ ["em"]=0.00015258789063,
+--~ ["ex"]=0.00006103515625,
+--~ ["in"]=0.00000021113587,
+--~ ["mm"]=0.00000005362851,
+--~ ["nc"]=0.00000119174467,
+--~ ["nd"]=0.00001430093602,
+--~ ["pc"]=0.00000127156576,
+--~ ["pt"]=0.00001525878906,
+--~ ["sp"]=1,
+--~ }
+
+--[[ldx--
+<p>A conversion function that takes a number, unit (string) and optional
+format (string) is implemented using this table.</p>
+--ldx]]--
+
+
+local function numbertodimen(n,unit,fmt)
+ if type(n) == 'string' then
+ return n
+ else
+ unit = unit or 'pt'
+ if not fmt then
+ fmt = "%s%s"
+ elseif fmt == true then
+ fmt = "%0.5f%s"
+ end
+ return format(fmt,n*dimenfactors[unit],unit)
+ -- if fmt then
+ -- return format(fmt,n*dimenfactors[unit],unit)
+ -- else
+ -- return match(format("%.20f",n*dimenfactors[unit]),"(.-0?)0*$") .. unit
+ -- end
+ end
+end
+
+--[[ldx--
+<p>We collect a bunch of converters in the <type>number</type> namespace.</p>
+--ldx]]--
+
+number.maxdimen = 1073741823
+number.todimen = numbertodimen
+number.dimenfactors = dimenfactors
+
+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
+function number.tonewdidots (n,fmt) return numbertodimen(n,"nd",fmt) end
+function number.tonewciceros (n,fmt) return numbertodimen(n,"nc",fmt) end
+
+--[[ldx--
+<p>More interesting it to implement a (sort of) dimen datatype, one
+that permits calculations too. First we define a function that
+converts a string to scaledpoints. We use <l n='lpeg'/>. We capture
+a number and optionally a unit. When no unit is given a constant
+capture takes place.</p>
+--ldx]]--
+
+local amount = (S("+-")^0 * R("09")^0 * P(".")^0 * R("09")^0) + Cc("0")
+local unit = R("az")^1
+
+local dimenpair = amount/tonumber * (unit^1/dimenfactors + Cc(1)) -- tonumber is new
+
+lpeg.patterns.dimenpair = dimenpair
+
+local splitter = amount/tonumber * C(unit^1)
+
+function number.splitdimen(str)
+ return lpegmatch(splitter,str)
+end
+
+--[[ldx--
+<p>We use a metatable to intercept errors. When no key is found in
+the table with factors, the metatable will be consulted for an
+alternative index function.</p>
+--ldx]]--
+
+setmetatableindex(dimenfactors, function(t,s)
+ -- error("wrong dimension: " .. (s or "?")) -- better a message
+ return false
+end)
+
+--[[ldx--
+<p>We redefine the following function later on, so we comment it
+here (which saves us bytecodes.</p>
+--ldx]]--
+
+-- function string.todimen(str)
+-- if type(str) == "number" then
+-- return str
+-- else
+-- local value, unit = lpegmatch(dimenpair,str)
+-- return value/unit
+-- end
+-- end
+--
+-- local stringtodimen = string.todimen
+
+local stringtodimen -- assigned later (commenting saves bytecode)
+
+local amount = S("+-")^0 * R("09")^0 * S(".,")^0 * R("09")^0
+local unit = P("pt") + P("cm") + P("mm") + P("sp") + P("bp") + P("in") +
+ P("pc") + P("dd") + P("cc") + P("nd") + P("nc")
+
+local validdimen = amount * unit
+
+lpeg.patterns.validdimen = validdimen
+
+--[[ldx--
+<p>This converter accepts calls like:</p>
+
+<typing>
+string.todimen("10")
+string.todimen(".10")
+string.todimen("10.0")
+string.todimen("10.0pt")
+string.todimen("10pt")
+string.todimen("10.0pt")
+</typing>
+
+<p>With this in place, we can now implement a proper datatype for dimensions, one
+that permits us to do this:</p>
+
+<typing>
+s = dimen "10pt" + dimen "20pt" + dimen "200pt"
+ - dimen "100sp" / 10 + "20pt" + "0pt"
+</typing>
+
+<p>We create a local metatable for this new type:</p>
+--ldx]]--
+
+local dimensions = { }
+
+--[[ldx--
+<p>The main (and globally) visible representation of a dimen is defined next: it is
+a one-element table. The unit that is returned from the match is normally a number
+(one of the previously defined factors) but we also accept functions. Later we will
+see why. This function is redefined later.</p>
+--ldx]]--
+
+-- function dimen(a)
+-- if a then
+-- local ta= type(a)
+-- if ta == "string" then
+-- local value, unit = lpegmatch(pattern,a)
+-- if type(unit) == "function" then
+-- k = value/unit()
+-- else
+-- k = value/unit
+-- end
+-- a = k
+-- elseif ta == "table" then
+-- a = a[1]
+-- end
+-- return setmetatable({ a }, dimensions)
+-- else
+-- return setmetatable({ 0 }, dimensions)
+-- end
+-- end
+
+--[[ldx--
+<p>This function return a small hash with a metatable attached. It is
+through this metatable that we can do the calculations. We could have
+shared some of the code but for reasons of speed we don't.</p>
+--ldx]]--
+
+function dimensions.__add(a, b)
+ local ta, tb = type(a), type(b)
+ if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end
+ if tb == "string" then b = stringtodimen(b) elseif tb == "table" then b = b[1] end
+ return setmetatable({ a + b }, dimensions)
+end
+
+function dimensions.__sub(a, b)
+ local ta, tb = type(a), type(b)
+ if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end
+ if tb == "string" then b = stringtodimen(b) elseif tb == "table" then b = b[1] end
+ return setmetatable({ a - b }, dimensions)
+end
+
+function dimensions.__mul(a, b)
+ local ta, tb = type(a), type(b)
+ if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end
+ if tb == "string" then b = stringtodimen(b) elseif tb == "table" then b = b[1] end
+ return setmetatable({ a * b }, dimensions)
+end
+
+function dimensions.__div(a, b)
+ local ta, tb = type(a), type(b)
+ if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end
+ if tb == "string" then b = stringtodimen(b) elseif tb == "table" then b = b[1] end
+ return setmetatable({ a / b }, dimensions)
+end
+
+function dimensions.__unm(a)
+ local ta = type(a)
+ if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end
+ return setmetatable({ - a }, dimensions)
+end
+
+--[[ldx--
+<p>It makes no sense to implement the power and modulo function but
+the next two do make sense because they permits is code like:</p>
+
+<typing>
+local a, b = dimen "10pt", dimen "11pt"
+...
+if a > b then
+ ...
+end
+</typing>
+--ldx]]--
+
+-- makes no sense: dimensions.__pow and dimensions.__mod
+
+function dimensions.__lt(a, b)
+ return a[1] < b[1]
+end
+
+function dimensions.__eq(a, b)
+ return a[1] == b[1]
+end
+
+--[[ldx--
+<p>We also need to provide a function for conversion to string (so that
+we can print dimensions). We print them as points, just like <l n='tex'/>.</p>
+--ldx]]--
+
+function dimensions.__tostring(a)
+ return a[1]/65536 .. "pt" -- instead of todimen(a[1])
+end
+
+--[[ldx--
+<p>Since it does not take much code, we also provide a way to access
+a few accessors</p>
+
+<typing>
+print(dimen().pt)
+print(dimen().sp)
+</typing>
+--ldx]]--
+
+function dimensions.__index(tab,key)
+ local d = dimenfactors[key]
+ if not d then
+ error("illegal property of dimen: " .. key)
+ d = 1
+ end
+ return 1/d
+end
+
+--[[ldx--
+<p>In the converter from string to dimension we support functions as
+factors. This is because in <l n='tex'/> we have a few more units:
+<type>ex</type> and <type>em</type>. These are not constant factors but
+depend on the current font. They are not defined by default, but need
+an explicit function call. This is because at the moment that this code
+is loaded, the relevant tables that hold the functions needed may not
+yet be available.</p>
+--ldx]]--
+
+ dimenfactors["ex"] = 4 * 1/65536 -- 4pt
+ dimenfactors["em"] = 10 * 1/65536 -- 10pt
+-- dimenfactors["%"] = 4 * 1/65536 -- 400pt/100
+
+--[[ldx--
+<p>The previous code is rather efficient (also thanks to <l n='lpeg'/>) but we
+can speed it up by caching converted dimensions. On my machine (2008) the following
+loop takes about 25.5 seconds.</p>
+
+<typing>
+for i=1,1000000 do
+ local s = dimen "10pt" + dimen "20pt" + dimen "200pt"
+ - dimen "100sp" / 10 + "20pt" + "0pt"
+end
+</typing>
+
+<p>When we cache converted strings this becomes 16.3 seconds. In order not
+to waste too much memory on it, we tag the values of the cache as being
+week which mean that the garbage collector will collect them in a next
+sweep. This means that in most cases the speed up is mostly affecting the
+current couple of calculations and as such the speed penalty is small.</p>
+
+<p>We redefine two previous defined functions that can benefit from
+this:</p>
+--ldx]]--
+
+local known = { } setmetatable(known, { __mode = "v" })
+
+function dimen(a)
+ if a then
+ local ta= type(a)
+ if ta == "string" then
+ local k = known[a]
+ if k then
+ a = k
+ else
+ local value, unit = lpegmatch(dimenpair,a)
+ if type(unit) == "function" then
+ k = value/unit()
+ else
+ k = value/unit
+ end
+ known[a] = k
+ a = k
+ end
+ elseif ta == "table" then
+ a = a[1]
+ end
+ return setmetatable({ a }, dimensions)
+ else
+ return setmetatable({ 0 }, dimensions)
+ end
+end
+
+function string.todimen(str) -- maybe use tex.sp when available
+ if type(str) == "number" then
+ return str
+ else
+ local k = known[str]
+ if not k then
+ local value, unit = lpegmatch(dimenpair,str)
+ if value and unit then
+ k = value/unit -- to be considered: round
+ else
+ k = 0
+ end
+ -- print(str,value,unit)
+ known[str] = k
+ end
+ return k
+ end
+end
+
+--~ local known = { }
+
+--~ function string.todimen(str) -- maybe use tex.sp
+--~ local k = known[str]
+--~ if not k then
+--~ k = tex.sp(str)
+--~ known[str] = k
+--~ end
+--~ return k
+--~ end
+
+stringtodimen = string.todimen -- local variable defined earlier
+
+function number.toscaled(d)
+ return format("%0.5f",d/2^16)
+end
+
+--[[ldx--
+<p>In a similar fashion we can define a glue datatype. In that case we
+probably use a hash instead of a one-element table.</p>
+--ldx]]--
+
+--[[ldx--
+<p>Goodie:s</p>
+--ldx]]--
+
+function number.percent(n,d) -- will be cleaned up once luatex 0.30 is out
+ d = d or tex.hsize
+ if type(d) == "string" then
+ d = stringtodimen(d)
+ end
+ return (n/100) * d
+end
+
+number["%"] = number.percent
diff --git a/tex/context/base/util-env.lua b/tex/context/base/util-env.lua
index 1b1157931..f4f3ef69f 100644
--- a/tex/context/base/util-env.lua
+++ b/tex/context/base/util-env.lua
@@ -1,287 +1,287 @@
-if not modules then modules = { } end modules ['util-env'] = {
- 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 allocate, mark = utilities.storage.allocate, utilities.storage.mark
-
-local format, sub, match, gsub, find = string.format, string.sub, string.match, string.gsub, string.find
-local unquoted, quoted = string.unquoted, string.quoted
-local concat, insert, remove = table.concat, table.insert, table.remove
-
-environment = environment or { }
-local environment = environment
-
--- precautions
-
-os.setlocale(nil,nil) -- useless feature and even dangerous in luatex
-
-function os.setlocale()
- -- no way you can mess with it
-end
-
--- dirty tricks (we will replace the texlua call by luatex --luaonly)
-
-local validengines = allocate {
- ["luatex"] = true,
- ["luajittex"] = true,
- -- ["luatex.exe"] = true,
- -- ["luajittex.exe"] = true,
-}
-
-local basicengines = allocate {
- ["luatex"] = "luatex",
- ["texlua"] = "luatex",
- ["texluac"] = "luatex",
- ["luajittex"] = "luajittex",
- ["texluajit"] = "luajittex",
- -- ["texlua.exe"] = "luatex",
- -- ["texluajit.exe"] = "luajittex",
-}
-
-local luaengines=allocate {
- ["lua"] = true,
- ["luajit"] = true,
-}
-
-environment.validengines = validengines
-environment.basicengines = basicengines
-
--- [-1] = binary
--- [ 0] = self
--- [ 1] = argument 1 ...
-
--- instead we could set ranges
-
-if not arg then
- -- used as library
-elseif luaengines[file.removesuffix(arg[-1])] then
--- arg[-1] = arg[0]
--- arg[ 0] = arg[1]
--- for k=2,#arg do
--- arg[k-1] = arg[k]
--- end
--- remove(arg) -- last
-elseif validengines[file.removesuffix(arg[0])] then
- if arg[1] == "--luaonly" then
- arg[-1] = arg[0]
- arg[ 0] = arg[2]
- for k=3,#arg do
- arg[k-2] = arg[k]
- end
- remove(arg) -- last
- remove(arg) -- pre-last
- else
- -- tex run
- end
-
- -- This is an ugly hack but it permits symlinking a script (say 'context') to 'mtxrun' as in:
- --
- -- ln -s /opt/minimals/tex/texmf-linux-64/bin/mtxrun context
- --
- -- The special mapping hack is needed because 'luatools' boils down to 'mtxrun --script base'
- -- but it's unlikely that there will be more of this
-
- local originalzero = file.basename(arg[0])
- local specialmapping = { luatools == "base" }
-
- if originalzero ~= "mtxrun" and originalzero ~= "mtxrun.lua" then
- arg[0] = specialmapping[originalzero] or originalzero
- insert(arg,0,"--script")
- insert(arg,0,"mtxrun")
- end
-
-end
-
--- environment
-
-environment.arguments = allocate()
-environment.files = allocate()
-environment.sortedflags = nil
-
--- context specific arguments (in order not to confuse the engine)
-
-function environment.initializearguments(arg)
- local arguments, files = { }, { }
- environment.arguments, environment.files, environment.sortedflags = arguments, files, nil
- for index=1,#arg do
- local argument = arg[index]
- if index > 0 then
- local flag, value = match(argument,"^%-+(.-)=(.-)$")
- if flag then
- flag = gsub(flag,"^c:","")
- arguments[flag] = unquoted(value or "")
- else
- flag = match(argument,"^%-+(.+)")
- if flag then
- flag = gsub(flag,"^c:","")
- arguments[flag] = true
- else
- files[#files+1] = argument
- end
- end
- end
- end
- environment.ownname = file.reslash(environment.ownname or arg[0] or 'unknown.lua')
-end
-
-function environment.setargument(name,value)
- environment.arguments[name] = value
-end
-
--- todo: defaults, better checks e.g on type (boolean versus string)
---
--- tricky: too many hits when we support partials unless we add
--- a registration of arguments so from now on we have 'partial'
-
-function environment.getargument(name,partial)
- local arguments, sortedflags = environment.arguments, environment.sortedflags
- if arguments[name] then
- return arguments[name]
- elseif partial then
- if not sortedflags then
- sortedflags = allocate(table.sortedkeys(arguments))
- for k=1,#sortedflags do
- sortedflags[k] = "^" .. sortedflags[k]
- end
- environment.sortedflags = sortedflags
- end
- -- example of potential clash: ^mode ^modefile
- for k=1,#sortedflags do
- local v = sortedflags[k]
- if find(name,v) then
- return arguments[sub(v,2,#v)]
- end
- end
- end
- return nil
-end
-
-environment.argument = environment.getargument
-
-function environment.splitarguments(separator) -- rather special, cut-off before separator
- local done, before, after = false, { }, { }
- local originalarguments = environment.originalarguments
- for k=1,#originalarguments do
- local v = originalarguments[k]
- if not done and v == separator then
- done = true
- elseif done then
- after[#after+1] = v
- else
- before[#before+1] = v
- end
- end
- return before, after
-end
-
-function environment.reconstructcommandline(arg,noquote)
- arg = arg or environment.originalarguments
- if noquote and #arg == 1 then
- -- we could just do: return unquoted(resolvers.resolve(arg[i]))
- local a = arg[1]
- a = resolvers.resolve(a)
- a = unquoted(a)
- return a
- elseif #arg > 0 then
- local result = { }
- for i=1,#arg do
- -- we could just do: result[#result+1] = format("%q",unquoted(resolvers.resolve(arg[i])))
- local a = arg[i]
- a = resolvers.resolve(a)
- a = unquoted(a)
- a = gsub(a,'"','\\"') -- tricky
- if find(a," ") then
- result[#result+1] = quoted(a)
- else
- result[#result+1] = a
- end
- end
- return concat(result," ")
- else
- return ""
- end
-end
-
--- handy in e.g. package.addluapath(environment.relativepath("scripts"))
-
-function environment.relativepath(path,root)
- if not path then
- path = ""
- end
- if not file.is_rootbased_path(path) then
- if not root then
- root = file.pathpart(environment.ownscript or environment.ownname or ".")
- end
- if root == "" then
- root = "."
- end
- path = root .. "/" .. path
- end
- return file.collapsepath(path,true)
-end
-
--- -- when script lives on e:/tmp we get this:
---
--- print(environment.relativepath("x/y/z","c:/w")) -- c:/w/x/y/z
--- print(environment.relativepath("x")) -- e:/tmp/x
--- print(environment.relativepath("../x")) -- e:/x
--- print(environment.relativepath("./x")) -- e:/tmp/x
--- print(environment.relativepath("/x")) -- /x
--- print(environment.relativepath("c:/x")) -- c:/x
--- print(environment.relativepath("//x")) -- //x
--- print(environment.relativepath()) -- e:/tmp
-
--- -- to be tested:
---
--- function environment.reconstructcommandline(arg,noquote)
--- arg = arg or environment.originalarguments
--- if noquote and #arg == 1 then
--- return unquoted(resolvers.resolve(arg[1]))
--- elseif #arg > 0 then
--- local result = { }
--- for i=1,#arg do
--- result[#result+1] = format("%q",unquoted(resolvers.resolve(arg[i]))) -- always quote
--- end
--- return concat(result," ")
--- else
--- return ""
--- end
--- end
-
-if arg then
-
- -- new, reconstruct quoted snippets (maybe better just remove the " then and add them later)
- local newarg, instring = { }, false
-
- for index=1,#arg do
- local argument = arg[index]
- if find(argument,"^\"") then
- newarg[#newarg+1] = gsub(argument,"^\"","")
- if not find(argument,"\"$") then
- instring = true
- end
- elseif find(argument,"\"$") then
- newarg[#newarg] = newarg[#newarg] .. " " .. gsub(argument,"\"$","")
- instring = false
- elseif instring then
- newarg[#newarg] = newarg[#newarg] .. " " .. argument
- else
- newarg[#newarg+1] = argument
- end
- end
- for i=1,-5,-1 do
- newarg[i] = arg[i]
- end
-
- environment.initializearguments(newarg)
-
- environment.originalarguments = mark(newarg)
- environment.rawarguments = mark(arg)
-
- arg = { } -- prevent duplicate handling
-
-end
+if not modules then modules = { } end modules ['util-env'] = {
+ 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 allocate, mark = utilities.storage.allocate, utilities.storage.mark
+
+local format, sub, match, gsub, find = string.format, string.sub, string.match, string.gsub, string.find
+local unquoted, quoted = string.unquoted, string.quoted
+local concat, insert, remove = table.concat, table.insert, table.remove
+
+environment = environment or { }
+local environment = environment
+
+-- precautions
+
+os.setlocale(nil,nil) -- useless feature and even dangerous in luatex
+
+function os.setlocale()
+ -- no way you can mess with it
+end
+
+-- dirty tricks (we will replace the texlua call by luatex --luaonly)
+
+local validengines = allocate {
+ ["luatex"] = true,
+ ["luajittex"] = true,
+ -- ["luatex.exe"] = true,
+ -- ["luajittex.exe"] = true,
+}
+
+local basicengines = allocate {
+ ["luatex"] = "luatex",
+ ["texlua"] = "luatex",
+ ["texluac"] = "luatex",
+ ["luajittex"] = "luajittex",
+ ["texluajit"] = "luajittex",
+ -- ["texlua.exe"] = "luatex",
+ -- ["texluajit.exe"] = "luajittex",
+}
+
+local luaengines=allocate {
+ ["lua"] = true,
+ ["luajit"] = true,
+}
+
+environment.validengines = validengines
+environment.basicengines = basicengines
+
+-- [-1] = binary
+-- [ 0] = self
+-- [ 1] = argument 1 ...
+
+-- instead we could set ranges
+
+if not arg then
+ -- used as library
+elseif luaengines[file.removesuffix(arg[-1])] then
+-- arg[-1] = arg[0]
+-- arg[ 0] = arg[1]
+-- for k=2,#arg do
+-- arg[k-1] = arg[k]
+-- end
+-- remove(arg) -- last
+elseif validengines[file.removesuffix(arg[0])] then
+ if arg[1] == "--luaonly" then
+ arg[-1] = arg[0]
+ arg[ 0] = arg[2]
+ for k=3,#arg do
+ arg[k-2] = arg[k]
+ end
+ remove(arg) -- last
+ remove(arg) -- pre-last
+ else
+ -- tex run
+ end
+
+ -- This is an ugly hack but it permits symlinking a script (say 'context') to 'mtxrun' as in:
+ --
+ -- ln -s /opt/minimals/tex/texmf-linux-64/bin/mtxrun context
+ --
+ -- The special mapping hack is needed because 'luatools' boils down to 'mtxrun --script base'
+ -- but it's unlikely that there will be more of this
+
+ local originalzero = file.basename(arg[0])
+ local specialmapping = { luatools == "base" }
+
+ if originalzero ~= "mtxrun" and originalzero ~= "mtxrun.lua" then
+ arg[0] = specialmapping[originalzero] or originalzero
+ insert(arg,0,"--script")
+ insert(arg,0,"mtxrun")
+ end
+
+end
+
+-- environment
+
+environment.arguments = allocate()
+environment.files = allocate()
+environment.sortedflags = nil
+
+-- context specific arguments (in order not to confuse the engine)
+
+function environment.initializearguments(arg)
+ local arguments, files = { }, { }
+ environment.arguments, environment.files, environment.sortedflags = arguments, files, nil
+ for index=1,#arg do
+ local argument = arg[index]
+ if index > 0 then
+ local flag, value = match(argument,"^%-+(.-)=(.-)$")
+ if flag then
+ flag = gsub(flag,"^c:","")
+ arguments[flag] = unquoted(value or "")
+ else
+ flag = match(argument,"^%-+(.+)")
+ if flag then
+ flag = gsub(flag,"^c:","")
+ arguments[flag] = true
+ else
+ files[#files+1] = argument
+ end
+ end
+ end
+ end
+ environment.ownname = file.reslash(environment.ownname or arg[0] or 'unknown.lua')
+end
+
+function environment.setargument(name,value)
+ environment.arguments[name] = value
+end
+
+-- todo: defaults, better checks e.g on type (boolean versus string)
+--
+-- tricky: too many hits when we support partials unless we add
+-- a registration of arguments so from now on we have 'partial'
+
+function environment.getargument(name,partial)
+ local arguments, sortedflags = environment.arguments, environment.sortedflags
+ if arguments[name] then
+ return arguments[name]
+ elseif partial then
+ if not sortedflags then
+ sortedflags = allocate(table.sortedkeys(arguments))
+ for k=1,#sortedflags do
+ sortedflags[k] = "^" .. sortedflags[k]
+ end
+ environment.sortedflags = sortedflags
+ end
+ -- example of potential clash: ^mode ^modefile
+ for k=1,#sortedflags do
+ local v = sortedflags[k]
+ if find(name,v) then
+ return arguments[sub(v,2,#v)]
+ end
+ end
+ end
+ return nil
+end
+
+environment.argument = environment.getargument
+
+function environment.splitarguments(separator) -- rather special, cut-off before separator
+ local done, before, after = false, { }, { }
+ local originalarguments = environment.originalarguments
+ for k=1,#originalarguments do
+ local v = originalarguments[k]
+ if not done and v == separator then
+ done = true
+ elseif done then
+ after[#after+1] = v
+ else
+ before[#before+1] = v
+ end
+ end
+ return before, after
+end
+
+function environment.reconstructcommandline(arg,noquote)
+ arg = arg or environment.originalarguments
+ if noquote and #arg == 1 then
+ -- we could just do: return unquoted(resolvers.resolve(arg[i]))
+ local a = arg[1]
+ a = resolvers.resolve(a)
+ a = unquoted(a)
+ return a
+ elseif #arg > 0 then
+ local result = { }
+ for i=1,#arg do
+ -- we could just do: result[#result+1] = format("%q",unquoted(resolvers.resolve(arg[i])))
+ local a = arg[i]
+ a = resolvers.resolve(a)
+ a = unquoted(a)
+ a = gsub(a,'"','\\"') -- tricky
+ if find(a," ") then
+ result[#result+1] = quoted(a)
+ else
+ result[#result+1] = a
+ end
+ end
+ return concat(result," ")
+ else
+ return ""
+ end
+end
+
+-- handy in e.g. package.addluapath(environment.relativepath("scripts"))
+
+function environment.relativepath(path,root)
+ if not path then
+ path = ""
+ end
+ if not file.is_rootbased_path(path) then
+ if not root then
+ root = file.pathpart(environment.ownscript or environment.ownname or ".")
+ end
+ if root == "" then
+ root = "."
+ end
+ path = root .. "/" .. path
+ end
+ return file.collapsepath(path,true)
+end
+
+-- -- when script lives on e:/tmp we get this:
+--
+-- print(environment.relativepath("x/y/z","c:/w")) -- c:/w/x/y/z
+-- print(environment.relativepath("x")) -- e:/tmp/x
+-- print(environment.relativepath("../x")) -- e:/x
+-- print(environment.relativepath("./x")) -- e:/tmp/x
+-- print(environment.relativepath("/x")) -- /x
+-- print(environment.relativepath("c:/x")) -- c:/x
+-- print(environment.relativepath("//x")) -- //x
+-- print(environment.relativepath()) -- e:/tmp
+
+-- -- to be tested:
+--
+-- function environment.reconstructcommandline(arg,noquote)
+-- arg = arg or environment.originalarguments
+-- if noquote and #arg == 1 then
+-- return unquoted(resolvers.resolve(arg[1]))
+-- elseif #arg > 0 then
+-- local result = { }
+-- for i=1,#arg do
+-- result[#result+1] = format("%q",unquoted(resolvers.resolve(arg[i]))) -- always quote
+-- end
+-- return concat(result," ")
+-- else
+-- return ""
+-- end
+-- end
+
+if arg then
+
+ -- new, reconstruct quoted snippets (maybe better just remove the " then and add them later)
+ local newarg, instring = { }, false
+
+ for index=1,#arg do
+ local argument = arg[index]
+ if find(argument,"^\"") then
+ newarg[#newarg+1] = gsub(argument,"^\"","")
+ if not find(argument,"\"$") then
+ instring = true
+ end
+ elseif find(argument,"\"$") then
+ newarg[#newarg] = newarg[#newarg] .. " " .. gsub(argument,"\"$","")
+ instring = false
+ elseif instring then
+ newarg[#newarg] = newarg[#newarg] .. " " .. argument
+ else
+ newarg[#newarg+1] = argument
+ end
+ end
+ for i=1,-5,-1 do
+ newarg[i] = arg[i]
+ end
+
+ environment.initializearguments(newarg)
+
+ environment.originalarguments = mark(newarg)
+ environment.rawarguments = mark(arg)
+
+ arg = { } -- prevent duplicate handling
+
+end
diff --git a/tex/context/base/util-fmt.lua b/tex/context/base/util-fmt.lua
index 8ec7236a9..371a5dfce 100644
--- a/tex/context/base/util-fmt.lua
+++ b/tex/context/base/util-fmt.lua
@@ -1,76 +1,76 @@
-if not modules then modules = { } end modules ['util-fmt'] = {
- 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"
-}
-
-utilities = utilities or { }
-utilities.formatters = utilities.formatters or { }
-local formatters = utilities.formatters
-
-local concat, format = table.concat, string.format
-local tostring, type = tostring, type
-local strip = string.strip
-
-local lpegmatch = lpeg.match
-local stripper = lpeg.patterns.stripzeros
-
-function formatters.stripzeros(str)
- return lpegmatch(stripper,str)
-end
-
-function formatters.formatcolumns(result,between)
- if result and #result > 0 then
- between = between or " "
- local widths, numbers = { }, { }
- local first = result[1]
- local n = #first
- for i=1,n do
- widths[i] = 0
- end
- for i=1,#result do
- local r = result[i]
- for j=1,n do
- local rj = r[j]
- local tj = type(rj)
- if tj == "number" then
- numbers[j] = true
- end
- if tj ~= "string" then
- rj = tostring(rj)
- r[j] = rj
- end
- local w = #rj
- if w > widths[j] then
- widths[j] = w
- end
- end
- end
- for i=1,n do
- local w = widths[i]
- if numbers[i] then
- if w > 80 then
- widths[i] = "%s" .. between
- else
- widths[i] = "%0" .. w .. "i" .. between
- end
- else
- if w > 80 then
- widths[i] = "%s" .. between
- elseif w > 0 then
- widths[i] = "%-" .. w .. "s" .. between
- else
- widths[i] = "%s"
- end
- end
- end
- local template = strip(concat(widths))
- for i=1,#result do
- local str = format(template,unpack(result[i]))
- result[i] = strip(str)
- end
- end
- return result
-end
+if not modules then modules = { } end modules ['util-fmt'] = {
+ 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"
+}
+
+utilities = utilities or { }
+utilities.formatters = utilities.formatters or { }
+local formatters = utilities.formatters
+
+local concat, format = table.concat, string.format
+local tostring, type = tostring, type
+local strip = string.strip
+
+local lpegmatch = lpeg.match
+local stripper = lpeg.patterns.stripzeros
+
+function formatters.stripzeros(str)
+ return lpegmatch(stripper,str)
+end
+
+function formatters.formatcolumns(result,between)
+ if result and #result > 0 then
+ between = between or " "
+ local widths, numbers = { }, { }
+ local first = result[1]
+ local n = #first
+ for i=1,n do
+ widths[i] = 0
+ end
+ for i=1,#result do
+ local r = result[i]
+ for j=1,n do
+ local rj = r[j]
+ local tj = type(rj)
+ if tj == "number" then
+ numbers[j] = true
+ end
+ if tj ~= "string" then
+ rj = tostring(rj)
+ r[j] = rj
+ end
+ local w = #rj
+ if w > widths[j] then
+ widths[j] = w
+ end
+ end
+ end
+ for i=1,n do
+ local w = widths[i]
+ if numbers[i] then
+ if w > 80 then
+ widths[i] = "%s" .. between
+ else
+ widths[i] = "%0" .. w .. "i" .. between
+ end
+ else
+ if w > 80 then
+ widths[i] = "%s" .. between
+ elseif w > 0 then
+ widths[i] = "%-" .. w .. "s" .. between
+ else
+ widths[i] = "%s"
+ end
+ end
+ end
+ local template = strip(concat(widths))
+ for i=1,#result do
+ local str = format(template,unpack(result[i]))
+ result[i] = strip(str)
+ end
+ end
+ return result
+end
diff --git a/tex/context/base/util-jsn.lua b/tex/context/base/util-jsn.lua
index 9870d0896..29587cd38 100644
--- a/tex/context/base/util-jsn.lua
+++ b/tex/context/base/util-jsn.lua
@@ -1,146 +1,146 @@
-if not modules then modules = { } end modules ['util-jsn'] = {
- version = 1.001,
- comment = "companion to m-json.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- Of course we could make a nice complete parser with proper error messages but
--- as json is generated programmatically errors are systematic and we can assume
--- a correct stream. If not, we have some fatal error anyway. So, we can just rely
--- on strings being strings (apart from the unicode escape which is not in 5.1) and
--- as we first catch known types we just assume that anything else is a number.
---
--- Reminder for me: check usage in framework and extend when needed. Also document
--- it in the cld lib documentation.
-
-local P, V, R, S, C, Cc, Cs, Ct, Cf, Cg = lpeg.P, lpeg.V, lpeg.R, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cf, lpeg.Cg
-local lpegmatch = lpeg.match
-local format = string.format
-local utfchar = utf.char
-local concat = table.concat
-
-local tonumber, tostring, rawset, type = tonumber, tostring, rawset, type
-
-local json = utilities.json or { }
-utilities.json = json
-
--- moduledata = moduledata or { }
--- moduledata.json = json
-
--- \\ \/ \b \f \n \r \t \uHHHH
-
-local lbrace = P("{")
-local rbrace = P("}")
-local lparent = P("[")
-local rparent = P("]")
-local comma = P(",")
-local colon = P(":")
-local dquote = P('"')
-
-local whitespace = lpeg.patterns.whitespace
-local optionalws = whitespace^0
-
-local escape = C(P("\\u") / "0x" * S("09","AF","af")) / function(s) return utfchar(tonumber(s)) end
-local jstring = dquote * Cs((escape + (1-dquote))^0) * dquote
-local jtrue = P("true") * Cc(true)
-local jfalse = P("false") * Cc(false)
-local jnull = P("null") * Cc(nil)
-local jnumber = (1-whitespace-rparent-rbrace-comma)^1 / tonumber
-
-local key = jstring
-
-local jsonconverter = { "value",
- object = lbrace * Cf(Ct("") * V("pair") * (comma * V("pair"))^0,rawset) * rbrace,
- pair = Cg(optionalws * key * optionalws * colon * V("value")),
- array = Ct(lparent * V("value") * (comma * V("value"))^0 * rparent),
- value = optionalws * (jstring + V("object") + V("array") + jtrue + jfalse + jnull + jnumber + #rparent) * optionalws,
-}
-
--- local jsonconverter = { "value",
--- object = lbrace * Cf(Ct("") * V("pair") * (comma * V("pair"))^0,rawset) * rbrace,
--- pair = Cg(optionalws * V("string") * optionalws * colon * V("value")),
--- array = Ct(lparent * V("value") * (comma * V("value"))^0 * rparent),
--- string = jstring,
--- value = optionalws * (V("string") + V("object") + V("array") + jtrue + jfalse + jnull + jnumber) * optionalws,
--- }
-
--- lpeg.print(jsonconverter) -- size 181
-
-function json.tolua(str)
- return lpegmatch(jsonconverter,str)
-end
-
-local function tojson(value,t) -- we could optimize #t
- local kind = type(value)
- if kind == "table" then
- local done = false
- local size = #value
- if size == 0 then
- for k, v in next, value do
- if done then
- t[#t+1] = ","
- else
- t[#t+1] = "{"
- done = true
- end
- t[#t+1] = format("%q:",k)
- tojson(v,t)
- end
- if done then
- t[#t+1] = "}"
- else
- t[#t+1] = "{}"
- end
- elseif size == 1 then
- -- we can optimize for non tables
- t[#t+1] = "["
- tojson(value[1],t)
- t[#t+1] = "]"
- else
- for i=1,size do
- if done then
- t[#t+1] = ","
- else
- t[#t+1] = "["
- done = true
- end
- tojson(value[i],t)
- end
- t[#t+1] = "]"
- end
- elseif kind == "string" then
- t[#t+1] = format("%q",value)
- elseif kind == "number" then
- t[#t+1] = value
- elseif kind == "boolean" then
- t[#t+1] = tostring(value)
- end
- return t
-end
-
-function json.tostring(value)
- -- todo optimize for non table
- local kind = type(value)
- if kind == "table" then
- return concat(tojson(value,{}),"")
- elseif kind == "string" or kind == "number" then
- return value
- else
- return tostring(value)
- end
-end
-
--- local tmp = [[ { "a" : true, "b" : [ 123 , 456E-10, { "a" : true, "b" : [ 123 , 456 ] } ] } ]]
-
--- tmp = json.tolua(tmp)
--- inspect(tmp)
--- tmp = json.tostring(tmp)
--- inspect(tmp)
--- tmp = json.tolua(tmp)
--- inspect(tmp)
--- tmp = json.tostring(tmp)
--- inspect(tmp)
-
--- inspect(json.tostring(true))
+if not modules then modules = { } end modules ['util-jsn'] = {
+ version = 1.001,
+ comment = "companion to m-json.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- Of course we could make a nice complete parser with proper error messages but
+-- as json is generated programmatically errors are systematic and we can assume
+-- a correct stream. If not, we have some fatal error anyway. So, we can just rely
+-- on strings being strings (apart from the unicode escape which is not in 5.1) and
+-- as we first catch known types we just assume that anything else is a number.
+--
+-- Reminder for me: check usage in framework and extend when needed. Also document
+-- it in the cld lib documentation.
+
+local P, V, R, S, C, Cc, Cs, Ct, Cf, Cg = lpeg.P, lpeg.V, lpeg.R, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cf, lpeg.Cg
+local lpegmatch = lpeg.match
+local format = string.format
+local utfchar = utf.char
+local concat = table.concat
+
+local tonumber, tostring, rawset, type = tonumber, tostring, rawset, type
+
+local json = utilities.json or { }
+utilities.json = json
+
+-- moduledata = moduledata or { }
+-- moduledata.json = json
+
+-- \\ \/ \b \f \n \r \t \uHHHH
+
+local lbrace = P("{")
+local rbrace = P("}")
+local lparent = P("[")
+local rparent = P("]")
+local comma = P(",")
+local colon = P(":")
+local dquote = P('"')
+
+local whitespace = lpeg.patterns.whitespace
+local optionalws = whitespace^0
+
+local escape = C(P("\\u") / "0x" * S("09","AF","af")) / function(s) return utfchar(tonumber(s)) end
+local jstring = dquote * Cs((escape + (1-dquote))^0) * dquote
+local jtrue = P("true") * Cc(true)
+local jfalse = P("false") * Cc(false)
+local jnull = P("null") * Cc(nil)
+local jnumber = (1-whitespace-rparent-rbrace-comma)^1 / tonumber
+
+local key = jstring
+
+local jsonconverter = { "value",
+ object = lbrace * Cf(Ct("") * V("pair") * (comma * V("pair"))^0,rawset) * rbrace,
+ pair = Cg(optionalws * key * optionalws * colon * V("value")),
+ array = Ct(lparent * V("value") * (comma * V("value"))^0 * rparent),
+ value = optionalws * (jstring + V("object") + V("array") + jtrue + jfalse + jnull + jnumber + #rparent) * optionalws,
+}
+
+-- local jsonconverter = { "value",
+-- object = lbrace * Cf(Ct("") * V("pair") * (comma * V("pair"))^0,rawset) * rbrace,
+-- pair = Cg(optionalws * V("string") * optionalws * colon * V("value")),
+-- array = Ct(lparent * V("value") * (comma * V("value"))^0 * rparent),
+-- string = jstring,
+-- value = optionalws * (V("string") + V("object") + V("array") + jtrue + jfalse + jnull + jnumber) * optionalws,
+-- }
+
+-- lpeg.print(jsonconverter) -- size 181
+
+function json.tolua(str)
+ return lpegmatch(jsonconverter,str)
+end
+
+local function tojson(value,t) -- we could optimize #t
+ local kind = type(value)
+ if kind == "table" then
+ local done = false
+ local size = #value
+ if size == 0 then
+ for k, v in next, value do
+ if done then
+ t[#t+1] = ","
+ else
+ t[#t+1] = "{"
+ done = true
+ end
+ t[#t+1] = format("%q:",k)
+ tojson(v,t)
+ end
+ if done then
+ t[#t+1] = "}"
+ else
+ t[#t+1] = "{}"
+ end
+ elseif size == 1 then
+ -- we can optimize for non tables
+ t[#t+1] = "["
+ tojson(value[1],t)
+ t[#t+1] = "]"
+ else
+ for i=1,size do
+ if done then
+ t[#t+1] = ","
+ else
+ t[#t+1] = "["
+ done = true
+ end
+ tojson(value[i],t)
+ end
+ t[#t+1] = "]"
+ end
+ elseif kind == "string" then
+ t[#t+1] = format("%q",value)
+ elseif kind == "number" then
+ t[#t+1] = value
+ elseif kind == "boolean" then
+ t[#t+1] = tostring(value)
+ end
+ return t
+end
+
+function json.tostring(value)
+ -- todo optimize for non table
+ local kind = type(value)
+ if kind == "table" then
+ return concat(tojson(value,{}),"")
+ elseif kind == "string" or kind == "number" then
+ return value
+ else
+ return tostring(value)
+ end
+end
+
+-- local tmp = [[ { "a" : true, "b" : [ 123 , 456E-10, { "a" : true, "b" : [ 123 , 456 ] } ] } ]]
+
+-- tmp = json.tolua(tmp)
+-- inspect(tmp)
+-- tmp = json.tostring(tmp)
+-- inspect(tmp)
+-- tmp = json.tolua(tmp)
+-- inspect(tmp)
+-- tmp = json.tostring(tmp)
+-- inspect(tmp)
+
+-- inspect(json.tostring(true))
diff --git a/tex/context/base/util-lib.lua b/tex/context/base/util-lib.lua
index 065f91091..c5c999113 100644
--- a/tex/context/base/util-lib.lua
+++ b/tex/context/base/util-lib.lua
@@ -1,288 +1,288 @@
-if not modules then modules = { } end modules ['util-lib'] = {
- 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",
-}
-
--- This is experimental code for Hans and Luigi. Don't depend on it! There
--- will be a plain variant.
-
---[[
-
-The problem with library bindings is manyfold. They are of course platform
-dependent and while a binary with its directly related libraries are often
-easy to maintain and load, additional libraries can each have their demands.
-
-One important aspect is that loading additional libraries from within the
-loaded one is also operating system dependent. There can be shared libraries
-elsewhere on the system and as there can be multiple libraries with the same
-name but different usage and versioning there can be clashes. So there has to
-be some logic in where to look for these sublibraries.
-
-We found out that for instance on windows libraries are by default sought on
-the parents path and then on the binary paths and these of course can be in
-an out of our control, thereby enlarging the changes on a clash. A rather
-safe solution for that to load the library on the path where it sits.
-
-Another aspect is initialization. When you ask for a library t.e.x it will
-try to initialize luaopen_t_e_x no matter if such an inializer is present.
-However, because loading is configurable and in the case of luatex is already
-partly under out control, this is easy to deal with. We only have to make
-sure that we inform the loader that the library has been loaded so that
-it won't load it twice.
-
-In swiglib we have chosen for a clear organization and although one can use
-variants normally in the tex directory structure predictability is more or
-less the standard. For instance:
-
-.../tex/texmf-mswin/bin/lib/luatex/lua/swiglib/mysql/core.dll
-.../tex/texmf-mswin/bin/lib/luajittex/lua/swiglib/mysql/core.dll
-.../tex/texmf-mswin/bin/lib/luatex/context/lua/swiglib/mysql/core.dll
-.../tex/texmf-mswin/bin/lib/swiglib/lua/mysql/core.dll
-.../tex/texmf-mswin/bin/lib/swiglib/lua/mysql/5.6/core.dll
-
-The lookups are determined via an entry in texmfcnf.lua:
-
-CLUAINPUTS = ".;$SELFAUTOLOC/lib/{$engine,luatex}/lua//",
-
-A request for t.e.x is converted to t/e/x.dll or t/e/x.so depending on the
-platform. Then we use the regular finder to locate the file in the tex
-directory structure. Once located we goto the path where it sits, load the
-file and return to the original path. We register as t.e.x in order to
-prevent reloading and also because the base name is seldom unique.
-
-The main function is a big one and evolved out of experiments that Luigi
-Scarso and I conducted when playing with variants of SwigLib. The function
-locates the library using the context mkiv resolver that operates on the
-tds tree and if that doesn't work out well, the normal clib path is used.
-
-The lookups is somewhat clever in the sense that it can deal with (optional)
-versions and can fall back on non versioned alternatives if needed, either
-or not using a wildcard lookup.
-
-This code is experimental and by providing a special abstract loader (called
-swiglib) we can start using the libraries.
-
-A complication is that we might end up with a luajittex path matching before a
-luatex path due to the path spec. One solution is to first check with the engine
-prefixed. This could be prevented by a more strict lib pattern but that is not
-always under our control. So, we first check for paths with engine in their name
-and then without.
-
-]]--
-
--- seems to be clua in recent texlive
-
-local gsub, find = string.gsub, string.find
-local pathpart, nameonly, joinfile = file.pathpart, file.nameonly, file.join
-local findfile, findfiles = resolvers and resolvers.findfile, resolvers and resolvers.findfiles
-
-local loaded = package.loaded
-
-local report_swiglib = logs.reporter("swiglib")
-local trace_swiglib = false trackers.register("resolvers.swiglib", function(v) trace_swiglib = v end)
-
--- We can check if there are more that one component, and if not, we can
--- append 'core'.
-
-local done = false
-
-local function requireswiglib(required,version)
- local trace_swiglib = trace_swiglib or package.helpers.trace
- local library = loaded[required]
- if library == nil then
- -- initialize a few variables
- local required_full = gsub(required,"%.","/") -- package.helpers.lualibfile
- local required_path = pathpart(required_full)
- local required_base = nameonly(required_full)
- local required_name = required_base .. "." .. os.libsuffix
- local version = type(version) == "string" and version ~= "" and version or false
- local engine = environment.ownmain or false
- --
- if trace_swiglib and not done then
- local list = resolvers.expandedpathlistfromvariable("lib") -- fresh, no reuse
- for i=1,#list do
- report_swiglib("tds path %i: %s",i,list[i])
- end
- end
- -- helpers
- local function found(locate,asked_library,how,...)
- if trace_swiglib then
- report_swiglib("checking %s: %a",how,asked_library)
- end
- return locate(asked_library,...)
- end
- local function check(locate,...)
- local found = nil
- if version then
- local asked_library = joinfile(required_path,version,required_name)
- if trace_swiglib then
- report_swiglib("checking %s: %a","with version",asked_library)
- end
- found = locate(asked_library,...)
- end
- if not found or found == "" then
- local asked_library = joinfile(required_path,required_name)
- if trace_swiglib then
- report_swiglib("checking %s: %a","with version",asked_library)
- end
- found = locate(asked_library,...)
- end
- return found and found ~= "" and found or false
- end
- -- Alternatively we could first collect the locations and then do the two attempts
- -- on this list but in practice this is not more efficient as we might have a fast
- -- match anyway.
- local function attempt(checkpattern)
- -- check cnf spec using name and version
- if trace_swiglib then
- report_swiglib("checking tds lib paths strictly")
- end
- local found = findfile and check(findfile,"lib")
- if found and (not checkpattern or find(found,checkpattern)) then
- return found
- end
- -- check cnf spec using wildcard
- if trace_swiglib then
- report_swiglib("checking tds lib paths with wildcard")
- end
- local asked_library = joinfile(required_path,".*",required_name)
- if trace_swiglib then
- report_swiglib("checking %s: %a","latest version",asked_library)
- end
- local list = findfiles(asked_library,"lib",true)
- if list and #list > 0 then
- table.sort(list)
- local found = list[#list]
- if found and (not checkpattern or find(found,checkpattern)) then
- return found
- end
- end
- -- Check lib paths using name and version.
- if trace_swiglib then
- report_swiglib("checking lib paths")
- end
- package.extralibpath(environment.ownpath)
- local paths = package.libpaths()
- for i=1,#paths do
- local found = check(lfs.isfile)
- if found and (not checkpattern or find(found,checkpattern)) then
- return found
- end
- end
- return false
- end
- local found_library = nil
- if engine then
- if trace_swiglib then
- report_swiglib("attemp 1, engine %a",engine)
- end
- found_library = attempt("/"..engine.."/")
- if not found_library then
- if trace_swiglib then
- report_swiglib("attemp 2, no engine",asked_library)
- end
- found_library = attempt()
- end
- else
- found_library = attempt()
- end
- -- load and initialize when found
- if not found_library then
- if trace_swiglib then
- report_swiglib("not found: %a",required)
- end
- library = false
- else
- local path = pathpart(found_library)
- local base = nameonly(found_library)
- dir.push(path)
- if trace_swiglib then
- report_swiglib("found: %a",found_library)
- end
- local message = nil
- local opener = "luaopen_" .. required_base
- library, message = package.loadlib(found_library,opener)
- local libtype = type(library)
- if libtype == "function" then
- library = library()
- else
- report_swiglib("load error: %a returns %a, message %a",opener,libtype,message or "no message")
- library = false
- end
- dir.pop()
- end
- -- cache result
- if not library then
- report_swiglib("unknown: %a",required)
- elseif trace_swiglib then
- report_swiglib("stored: %a",required)
- end
- loaded[required] = library
- else
- report_swiglib("reused: %a",required)
- end
- return library
-end
-
---[[
-
-For convenience we make the require loader function swiglib aware. Alternatively
-we could put the specific loader in the global namespace.
-
-]]--
-
-local savedrequire = require
-
-function require(name,version)
- if find(name,"^swiglib%.") then
- return requireswiglib(name,version)
- else
- return savedrequire(name)
- end
-end
-
---[[
-
-At the cost of some overhead we provide a specific loader so that we can keep
-track of swiglib usage which is handy for development. In context this is the
-recommended loader.
-
-]]--
-
-local swiglibs = { }
-
-function swiglib(name,version)
- local library = swiglibs[name]
- if not library then
- statistics.starttiming(swiglibs)
- if trace_swiglib then
- report_swiglib("loading %a",name)
- end
- library = requireswiglib("swiglib." .. name,version)
- swiglibs[name] = library
- statistics.stoptiming(swiglibs)
- end
- return library
-end
-
-statistics.register("used swiglibs", function()
- if next(swiglibs) then
- return string.format("%s, initial load time %s seconds",table.concat(table.sortedkeys(swiglibs)," "),statistics.elapsedtime(swiglibs))
- end
-end)
-
---[[
-
-So, we now have:
-
-local gm = require("swiglib.gmwand.core")
-local gm = swiglib("gmwand.core")
-local sq = swiglib("mysql.core")
-local sq = swiglib("mysql.core","5.6")
-
-Watch out, the last one is less explicit and lacks the swiglib prefix.
-
-]]--
+if not modules then modules = { } end modules ['util-lib'] = {
+ 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",
+}
+
+-- This is experimental code for Hans and Luigi. Don't depend on it! There
+-- will be a plain variant.
+
+--[[
+
+The problem with library bindings is manyfold. They are of course platform
+dependent and while a binary with its directly related libraries are often
+easy to maintain and load, additional libraries can each have their demands.
+
+One important aspect is that loading additional libraries from within the
+loaded one is also operating system dependent. There can be shared libraries
+elsewhere on the system and as there can be multiple libraries with the same
+name but different usage and versioning there can be clashes. So there has to
+be some logic in where to look for these sublibraries.
+
+We found out that for instance on windows libraries are by default sought on
+the parents path and then on the binary paths and these of course can be in
+an out of our control, thereby enlarging the changes on a clash. A rather
+safe solution for that to load the library on the path where it sits.
+
+Another aspect is initialization. When you ask for a library t.e.x it will
+try to initialize luaopen_t_e_x no matter if such an inializer is present.
+However, because loading is configurable and in the case of luatex is already
+partly under out control, this is easy to deal with. We only have to make
+sure that we inform the loader that the library has been loaded so that
+it won't load it twice.
+
+In swiglib we have chosen for a clear organization and although one can use
+variants normally in the tex directory structure predictability is more or
+less the standard. For instance:
+
+.../tex/texmf-mswin/bin/lib/luatex/lua/swiglib/mysql/core.dll
+.../tex/texmf-mswin/bin/lib/luajittex/lua/swiglib/mysql/core.dll
+.../tex/texmf-mswin/bin/lib/luatex/context/lua/swiglib/mysql/core.dll
+.../tex/texmf-mswin/bin/lib/swiglib/lua/mysql/core.dll
+.../tex/texmf-mswin/bin/lib/swiglib/lua/mysql/5.6/core.dll
+
+The lookups are determined via an entry in texmfcnf.lua:
+
+CLUAINPUTS = ".;$SELFAUTOLOC/lib/{$engine,luatex}/lua//",
+
+A request for t.e.x is converted to t/e/x.dll or t/e/x.so depending on the
+platform. Then we use the regular finder to locate the file in the tex
+directory structure. Once located we goto the path where it sits, load the
+file and return to the original path. We register as t.e.x in order to
+prevent reloading and also because the base name is seldom unique.
+
+The main function is a big one and evolved out of experiments that Luigi
+Scarso and I conducted when playing with variants of SwigLib. The function
+locates the library using the context mkiv resolver that operates on the
+tds tree and if that doesn't work out well, the normal clib path is used.
+
+The lookups is somewhat clever in the sense that it can deal with (optional)
+versions and can fall back on non versioned alternatives if needed, either
+or not using a wildcard lookup.
+
+This code is experimental and by providing a special abstract loader (called
+swiglib) we can start using the libraries.
+
+A complication is that we might end up with a luajittex path matching before a
+luatex path due to the path spec. One solution is to first check with the engine
+prefixed. This could be prevented by a more strict lib pattern but that is not
+always under our control. So, we first check for paths with engine in their name
+and then without.
+
+]]--
+
+-- seems to be clua in recent texlive
+
+local gsub, find = string.gsub, string.find
+local pathpart, nameonly, joinfile = file.pathpart, file.nameonly, file.join
+local findfile, findfiles = resolvers and resolvers.findfile, resolvers and resolvers.findfiles
+
+local loaded = package.loaded
+
+local report_swiglib = logs.reporter("swiglib")
+local trace_swiglib = false trackers.register("resolvers.swiglib", function(v) trace_swiglib = v end)
+
+-- We can check if there are more that one component, and if not, we can
+-- append 'core'.
+
+local done = false
+
+local function requireswiglib(required,version)
+ local trace_swiglib = trace_swiglib or package.helpers.trace
+ local library = loaded[required]
+ if library == nil then
+ -- initialize a few variables
+ local required_full = gsub(required,"%.","/") -- package.helpers.lualibfile
+ local required_path = pathpart(required_full)
+ local required_base = nameonly(required_full)
+ local required_name = required_base .. "." .. os.libsuffix
+ local version = type(version) == "string" and version ~= "" and version or false
+ local engine = environment.ownmain or false
+ --
+ if trace_swiglib and not done then
+ local list = resolvers.expandedpathlistfromvariable("lib") -- fresh, no reuse
+ for i=1,#list do
+ report_swiglib("tds path %i: %s",i,list[i])
+ end
+ end
+ -- helpers
+ local function found(locate,asked_library,how,...)
+ if trace_swiglib then
+ report_swiglib("checking %s: %a",how,asked_library)
+ end
+ return locate(asked_library,...)
+ end
+ local function check(locate,...)
+ local found = nil
+ if version then
+ local asked_library = joinfile(required_path,version,required_name)
+ if trace_swiglib then
+ report_swiglib("checking %s: %a","with version",asked_library)
+ end
+ found = locate(asked_library,...)
+ end
+ if not found or found == "" then
+ local asked_library = joinfile(required_path,required_name)
+ if trace_swiglib then
+ report_swiglib("checking %s: %a","with version",asked_library)
+ end
+ found = locate(asked_library,...)
+ end
+ return found and found ~= "" and found or false
+ end
+ -- Alternatively we could first collect the locations and then do the two attempts
+ -- on this list but in practice this is not more efficient as we might have a fast
+ -- match anyway.
+ local function attempt(checkpattern)
+ -- check cnf spec using name and version
+ if trace_swiglib then
+ report_swiglib("checking tds lib paths strictly")
+ end
+ local found = findfile and check(findfile,"lib")
+ if found and (not checkpattern or find(found,checkpattern)) then
+ return found
+ end
+ -- check cnf spec using wildcard
+ if trace_swiglib then
+ report_swiglib("checking tds lib paths with wildcard")
+ end
+ local asked_library = joinfile(required_path,".*",required_name)
+ if trace_swiglib then
+ report_swiglib("checking %s: %a","latest version",asked_library)
+ end
+ local list = findfiles(asked_library,"lib",true)
+ if list and #list > 0 then
+ table.sort(list)
+ local found = list[#list]
+ if found and (not checkpattern or find(found,checkpattern)) then
+ return found
+ end
+ end
+ -- Check lib paths using name and version.
+ if trace_swiglib then
+ report_swiglib("checking lib paths")
+ end
+ package.extralibpath(environment.ownpath)
+ local paths = package.libpaths()
+ for i=1,#paths do
+ local found = check(lfs.isfile)
+ if found and (not checkpattern or find(found,checkpattern)) then
+ return found
+ end
+ end
+ return false
+ end
+ local found_library = nil
+ if engine then
+ if trace_swiglib then
+ report_swiglib("attemp 1, engine %a",engine)
+ end
+ found_library = attempt("/"..engine.."/")
+ if not found_library then
+ if trace_swiglib then
+ report_swiglib("attemp 2, no engine",asked_library)
+ end
+ found_library = attempt()
+ end
+ else
+ found_library = attempt()
+ end
+ -- load and initialize when found
+ if not found_library then
+ if trace_swiglib then
+ report_swiglib("not found: %a",required)
+ end
+ library = false
+ else
+ local path = pathpart(found_library)
+ local base = nameonly(found_library)
+ dir.push(path)
+ if trace_swiglib then
+ report_swiglib("found: %a",found_library)
+ end
+ local message = nil
+ local opener = "luaopen_" .. required_base
+ library, message = package.loadlib(found_library,opener)
+ local libtype = type(library)
+ if libtype == "function" then
+ library = library()
+ else
+ report_swiglib("load error: %a returns %a, message %a",opener,libtype,message or "no message")
+ library = false
+ end
+ dir.pop()
+ end
+ -- cache result
+ if not library then
+ report_swiglib("unknown: %a",required)
+ elseif trace_swiglib then
+ report_swiglib("stored: %a",required)
+ end
+ loaded[required] = library
+ else
+ report_swiglib("reused: %a",required)
+ end
+ return library
+end
+
+--[[
+
+For convenience we make the require loader function swiglib aware. Alternatively
+we could put the specific loader in the global namespace.
+
+]]--
+
+local savedrequire = require
+
+function require(name,version)
+ if find(name,"^swiglib%.") then
+ return requireswiglib(name,version)
+ else
+ return savedrequire(name)
+ end
+end
+
+--[[
+
+At the cost of some overhead we provide a specific loader so that we can keep
+track of swiglib usage which is handy for development. In context this is the
+recommended loader.
+
+]]--
+
+local swiglibs = { }
+
+function swiglib(name,version)
+ local library = swiglibs[name]
+ if not library then
+ statistics.starttiming(swiglibs)
+ if trace_swiglib then
+ report_swiglib("loading %a",name)
+ end
+ library = requireswiglib("swiglib." .. name,version)
+ swiglibs[name] = library
+ statistics.stoptiming(swiglibs)
+ end
+ return library
+end
+
+statistics.register("used swiglibs", function()
+ if next(swiglibs) then
+ return string.format("%s, initial load time %s seconds",table.concat(table.sortedkeys(swiglibs)," "),statistics.elapsedtime(swiglibs))
+ end
+end)
+
+--[[
+
+So, we now have:
+
+local gm = require("swiglib.gmwand.core")
+local gm = swiglib("gmwand.core")
+local sq = swiglib("mysql.core")
+local sq = swiglib("mysql.core","5.6")
+
+Watch out, the last one is less explicit and lacks the swiglib prefix.
+
+]]--
diff --git a/tex/context/base/util-lua.lua b/tex/context/base/util-lua.lua
index a69fa9cdd..f3be9dcd2 100644
--- a/tex/context/base/util-lua.lua
+++ b/tex/context/base/util-lua.lua
@@ -1,351 +1,351 @@
-if not modules then modules = { } end modules ['util-lua'] = {
- version = 1.001,
- comment = "companion to luat-lib.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- comment = "the strip code is written by Peter Cawley",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- we will remove the 5.1 code some day soon
-
-local rep, sub, byte, dump, format = string.rep, string.sub, string.byte, string.dump, string.format
-local load, loadfile, type = load, loadfile, type
-
-utilities = utilities or {}
-utilities.lua = utilities.lua or { }
-local luautilities = utilities.lua
-
-local report_lua = logs.reporter("system","lua")
-
-local tracestripping = false
-local forcestupidcompile = true -- use internal bytecode compiler
-luautilities.stripcode = true -- support stripping when asked for
-luautilities.alwaysstripcode = false -- saves 1 meg on 7 meg compressed format file (2012.08.12)
-luautilities.nofstrippedchunks = 0
-luautilities.nofstrippedbytes = 0
-local strippedchunks = { } -- allocate()
-luautilities.strippedchunks = strippedchunks
-
-luautilities.suffixes = {
- tma = "tma",
- tmc = jit and "tmb" or "tmc",
- lua = "lua",
- luc = jit and "lub" or "luc",
- lui = "lui",
- luv = "luv",
- luj = "luj",
- tua = "tua",
- tuc = "tuc",
-}
-
--- environment.loadpreprocessedfile can be set to a preprocessor
-
-if jit or status.luatex_version >= 74 then
-
- local function register(name)
- if tracestripping then
- report_lua("stripped bytecode from %a",name or "unknown")
- end
- strippedchunks[#strippedchunks+1] = name
- luautilities.nofstrippedchunks = luautilities.nofstrippedchunks + 1
- end
-
- local function stupidcompile(luafile,lucfile,strip)
- local code = io.loaddata(luafile)
- if code and code ~= "" then
- code = load(code)
- if code then
- code = dump(code,strip and luautilities.stripcode or luautilities.alwaysstripcode)
- if code and code ~= "" then
- register(name)
- io.savedata(lucfile,code)
- return true, 0
- end
- else
- report_lua("fatal error %a in file %a",1,luafile)
- end
- else
- report_lua("fatal error %a in file %a",2,luafile)
- end
- return false, 0
- end
-
- -- quite subtle ... doing this wrong incidentally can give more bytes
-
- function luautilities.loadedluacode(fullname,forcestrip,name)
- -- quite subtle ... doing this wrong incidentally can give more bytes
- name = name or fullname
- local code = environment.loadpreprocessedfile and environment.loadpreprocessedfile(fullname) or loadfile(fullname)
- if code then
- code()
- end
- if forcestrip and luautilities.stripcode then
- if type(forcestrip) == "function" then
- forcestrip = forcestrip(fullname)
- end
- if forcestrip or luautilities.alwaysstripcode then
- register(name)
- return load(dump(code,true)), 0
- else
- return code, 0
- end
- elseif luautilities.alwaysstripcode then
- register(name)
- return load(dump(code,true)), 0
- else
- return code, 0
- end
- end
-
- function luautilities.strippedloadstring(code,forcestrip,name) -- not executed
- if forcestrip and luautilities.stripcode or luautilities.alwaysstripcode then
- code = load(code)
- if not code then
- report_lua("fatal error %a in file %a",3,name)
- end
- register(name)
- code = dump(code,true)
- end
- return load(code), 0
- end
-
- function luautilities.compile(luafile,lucfile,cleanup,strip,fallback) -- defaults: cleanup=false strip=true
- report_lua("compiling %a into %a",luafile,lucfile)
- os.remove(lucfile)
- local done = stupidcompile(luafile,lucfile,strip ~= false)
- if done then
- report_lua("dumping %a into %a stripped",luafile,lucfile)
- if cleanup == true and lfs.isfile(lucfile) and lfs.isfile(luafile) then
- report_lua("removing %a",luafile)
- os.remove(luafile)
- end
- end
- return done
- end
-
- function luautilities.loadstripped(...)
- local l = load(...)
- if l then
- return load(dump(l,true))
- end
- end
-
-else
-
- -- The next function was posted by Peter Cawley on the lua list and strips line
- -- number information etc. from the bytecode data blob. We only apply this trick
- -- when we store data tables. Stripping makes the compressed format file about
- -- 1MB smaller (and uncompressed we save at least 6MB).
- --
- -- You can consider this feature an experiment, so it might disappear. There is
- -- no noticeable gain in runtime although the memory footprint should be somewhat
- -- smaller (and the file system has a bit less to deal with).
- --
- -- Begin of borrowed code ... works for Lua 5.1 which LuaTeX currently uses ...
-
- local function register(name,before,after)
- local delta = before - after
- if tracestripping then
- report_lua("bytecodes stripped from %a, # before %s, # after %s, delta %s",name,before,after,delta)
- end
- strippedchunks[#strippedchunks+1] = name
- luautilities.nofstrippedchunks = luautilities.nofstrippedchunks + 1
- luautilities.nofstrippedbytes = luautilities.nofstrippedbytes + delta
- return delta
- end
-
- local strip_code_pc
-
- if _MAJORVERSION == 5 and _MINORVERSION == 1 then
-
- strip_code_pc = function(dump,name)
- local before = #dump
- local version, format, endian, int, size, ins, num = byte(dump,5,11)
- local subint
- if endian == 1 then
- subint = function(dump, i, l)
- local val = 0
- for n = l, 1, -1 do
- val = val * 256 + byte(dump,i + n - 1)
- end
- return val, i + l
- end
- else
- subint = function(dump, i, l)
- local val = 0
- for n = 1, l, 1 do
- val = val * 256 + byte(dump,i + n - 1)
- end
- return val, i + l
- end
- end
- local strip_function
- strip_function = function(dump)
- local count, offset = subint(dump, 1, size)
- local stripped, dirty = rep("\0", size), offset + count
- offset = offset + count + int * 2 + 4
- offset = offset + int + subint(dump, offset, int) * ins
- count, offset = subint(dump, offset, int)
- for n = 1, count do
- local t
- t, offset = subint(dump, offset, 1)
- if t == 1 then
- offset = offset + 1
- elseif t == 4 then
- offset = offset + size + subint(dump, offset, size)
- elseif t == 3 then
- offset = offset + num
- end
- end
- count, offset = subint(dump, offset, int)
- stripped = stripped .. sub(dump,dirty, offset - 1)
- for n = 1, count do
- local proto, off = strip_function(sub(dump,offset, -1))
- stripped, offset = stripped .. proto, offset + off - 1
- end
- offset = offset + subint(dump, offset, int) * int + int
- count, offset = subint(dump, offset, int)
- for n = 1, count do
- offset = offset + subint(dump, offset, size) + size + int * 2
- end
- count, offset = subint(dump, offset, int)
- for n = 1, count do
- offset = offset + subint(dump, offset, size) + size
- end
- stripped = stripped .. rep("\0", int * 3)
- return stripped, offset
- end
- dump = sub(dump,1,12) .. strip_function(sub(dump,13,-1))
- local after = #dump
- local delta = register(name,before,after)
- return dump, delta
- end
-
- else
-
- strip_code_pc = function(dump,name)
- return dump, 0
- end
-
- end
-
- -- ... end of borrowed code.
-
- -- quite subtle ... doing this wrong incidentally can give more bytes
-
- function luautilities.loadedluacode(fullname,forcestrip,name)
- -- quite subtle ... doing this wrong incidentally can give more bytes
- local code = environment.loadpreprocessedfile and environment.preprocessedloadfile(fullname) or loadfile(fullname)
- if code then
- code()
- end
- if forcestrip and luautilities.stripcode then
- if type(forcestrip) == "function" then
- forcestrip = forcestrip(fullname)
- end
- if forcestrip then
- local code, n = strip_code_pc(dump(code),name)
- return load(code), n
- elseif luautilities.alwaysstripcode then
- return load(strip_code_pc(dump(code),name))
- else
- return code, 0
- end
- elseif luautilities.alwaysstripcode then
- return load(strip_code_pc(dump(code),name))
- else
- return code, 0
- end
- end
-
- function luautilities.strippedloadstring(code,forcestrip,name) -- not executed
- local n = 0
- if (forcestrip and luautilities.stripcode) or luautilities.alwaysstripcode then
- code = load(code)
- if not code then
- report_lua("fatal error in file %a",name)
- end
- code, n = strip_code_pc(dump(code),name)
- end
- return load(code), n
- end
-
- local function stupidcompile(luafile,lucfile,strip)
- local code = io.loaddata(luafile)
- local n = 0
- if code and code ~= "" then
- code = load(code)
- if not code then
- report_lua("fatal error in file %a",luafile)
- end
- code = dump(code)
- if strip then
- code, n = strip_code_pc(code,luautilities.stripcode or luautilities.alwaysstripcode,luafile) -- last one is reported
- end
- if code and code ~= "" then
- io.savedata(lucfile,code)
- end
- end
- return n
- end
-
- local luac_normal = "texluac -o %q %q"
- local luac_strip = "texluac -s -o %q %q"
-
- function luautilities.compile(luafile,lucfile,cleanup,strip,fallback) -- defaults: cleanup=false strip=true
- report_lua("compiling %a into %a",luafile,lucfile)
- os.remove(lucfile)
- local done = false
- if strip ~= false then
- strip = true
- end
- if forcestupidcompile then
- fallback = true
- elseif strip then
- done = os.spawn(format(luac_strip, lucfile,luafile)) == 0
- else
- done = os.spawn(format(luac_normal,lucfile,luafile)) == 0
- end
- if not done and fallback then
- local n = stupidcompile(luafile,lucfile,strip)
- if n > 0 then
- report_lua("%a dumped into %a (%i bytes stripped)",luafile,lucfile,n)
- else
- report_lua("%a dumped into %a (unstripped)",luafile,lucfile)
- end
- cleanup = false -- better see how bad it is
- done = true -- hm
- end
- if done and cleanup == true and lfs.isfile(lucfile) and lfs.isfile(luafile) then
- report_lua("removing %a",luafile)
- os.remove(luafile)
- end
- return done
- end
-
- luautilities.loadstripped = loadstring
-
-end
-
--- local getmetatable, type = getmetatable, type
---
--- local types = { }
---
--- function luautilities.registerdatatype(d,name)
--- types[getmetatable(d)] = name
--- end
---
--- function luautilities.datatype(d)
--- local t = type(d)
--- if t == "userdata" then
--- local m = getmetatable(d)
--- return m and types[m] or "userdata"
--- else
--- return t
--- end
--- end
---
--- luautilities.registerdatatype(lpeg.P("!"),"lpeg")
---
--- print(luautilities.datatype(lpeg.P("oeps")))
+if not modules then modules = { } end modules ['util-lua'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ comment = "the strip code is written by Peter Cawley",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- we will remove the 5.1 code some day soon
+
+local rep, sub, byte, dump, format = string.rep, string.sub, string.byte, string.dump, string.format
+local load, loadfile, type = load, loadfile, type
+
+utilities = utilities or {}
+utilities.lua = utilities.lua or { }
+local luautilities = utilities.lua
+
+local report_lua = logs.reporter("system","lua")
+
+local tracestripping = false
+local forcestupidcompile = true -- use internal bytecode compiler
+luautilities.stripcode = true -- support stripping when asked for
+luautilities.alwaysstripcode = false -- saves 1 meg on 7 meg compressed format file (2012.08.12)
+luautilities.nofstrippedchunks = 0
+luautilities.nofstrippedbytes = 0
+local strippedchunks = { } -- allocate()
+luautilities.strippedchunks = strippedchunks
+
+luautilities.suffixes = {
+ tma = "tma",
+ tmc = jit and "tmb" or "tmc",
+ lua = "lua",
+ luc = jit and "lub" or "luc",
+ lui = "lui",
+ luv = "luv",
+ luj = "luj",
+ tua = "tua",
+ tuc = "tuc",
+}
+
+-- environment.loadpreprocessedfile can be set to a preprocessor
+
+if jit or status.luatex_version >= 74 then
+
+ local function register(name)
+ if tracestripping then
+ report_lua("stripped bytecode from %a",name or "unknown")
+ end
+ strippedchunks[#strippedchunks+1] = name
+ luautilities.nofstrippedchunks = luautilities.nofstrippedchunks + 1
+ end
+
+ local function stupidcompile(luafile,lucfile,strip)
+ local code = io.loaddata(luafile)
+ if code and code ~= "" then
+ code = load(code)
+ if code then
+ code = dump(code,strip and luautilities.stripcode or luautilities.alwaysstripcode)
+ if code and code ~= "" then
+ register(name)
+ io.savedata(lucfile,code)
+ return true, 0
+ end
+ else
+ report_lua("fatal error %a in file %a",1,luafile)
+ end
+ else
+ report_lua("fatal error %a in file %a",2,luafile)
+ end
+ return false, 0
+ end
+
+ -- quite subtle ... doing this wrong incidentally can give more bytes
+
+ function luautilities.loadedluacode(fullname,forcestrip,name)
+ -- quite subtle ... doing this wrong incidentally can give more bytes
+ name = name or fullname
+ local code = environment.loadpreprocessedfile and environment.loadpreprocessedfile(fullname) or loadfile(fullname)
+ if code then
+ code()
+ end
+ if forcestrip and luautilities.stripcode then
+ if type(forcestrip) == "function" then
+ forcestrip = forcestrip(fullname)
+ end
+ if forcestrip or luautilities.alwaysstripcode then
+ register(name)
+ return load(dump(code,true)), 0
+ else
+ return code, 0
+ end
+ elseif luautilities.alwaysstripcode then
+ register(name)
+ return load(dump(code,true)), 0
+ else
+ return code, 0
+ end
+ end
+
+ function luautilities.strippedloadstring(code,forcestrip,name) -- not executed
+ if forcestrip and luautilities.stripcode or luautilities.alwaysstripcode then
+ code = load(code)
+ if not code then
+ report_lua("fatal error %a in file %a",3,name)
+ end
+ register(name)
+ code = dump(code,true)
+ end
+ return load(code), 0
+ end
+
+ function luautilities.compile(luafile,lucfile,cleanup,strip,fallback) -- defaults: cleanup=false strip=true
+ report_lua("compiling %a into %a",luafile,lucfile)
+ os.remove(lucfile)
+ local done = stupidcompile(luafile,lucfile,strip ~= false)
+ if done then
+ report_lua("dumping %a into %a stripped",luafile,lucfile)
+ if cleanup == true and lfs.isfile(lucfile) and lfs.isfile(luafile) then
+ report_lua("removing %a",luafile)
+ os.remove(luafile)
+ end
+ end
+ return done
+ end
+
+ function luautilities.loadstripped(...)
+ local l = load(...)
+ if l then
+ return load(dump(l,true))
+ end
+ end
+
+else
+
+ -- The next function was posted by Peter Cawley on the lua list and strips line
+ -- number information etc. from the bytecode data blob. We only apply this trick
+ -- when we store data tables. Stripping makes the compressed format file about
+ -- 1MB smaller (and uncompressed we save at least 6MB).
+ --
+ -- You can consider this feature an experiment, so it might disappear. There is
+ -- no noticeable gain in runtime although the memory footprint should be somewhat
+ -- smaller (and the file system has a bit less to deal with).
+ --
+ -- Begin of borrowed code ... works for Lua 5.1 which LuaTeX currently uses ...
+
+ local function register(name,before,after)
+ local delta = before - after
+ if tracestripping then
+ report_lua("bytecodes stripped from %a, # before %s, # after %s, delta %s",name,before,after,delta)
+ end
+ strippedchunks[#strippedchunks+1] = name
+ luautilities.nofstrippedchunks = luautilities.nofstrippedchunks + 1
+ luautilities.nofstrippedbytes = luautilities.nofstrippedbytes + delta
+ return delta
+ end
+
+ local strip_code_pc
+
+ if _MAJORVERSION == 5 and _MINORVERSION == 1 then
+
+ strip_code_pc = function(dump,name)
+ local before = #dump
+ local version, format, endian, int, size, ins, num = byte(dump,5,11)
+ local subint
+ if endian == 1 then
+ subint = function(dump, i, l)
+ local val = 0
+ for n = l, 1, -1 do
+ val = val * 256 + byte(dump,i + n - 1)
+ end
+ return val, i + l
+ end
+ else
+ subint = function(dump, i, l)
+ local val = 0
+ for n = 1, l, 1 do
+ val = val * 256 + byte(dump,i + n - 1)
+ end
+ return val, i + l
+ end
+ end
+ local strip_function
+ strip_function = function(dump)
+ local count, offset = subint(dump, 1, size)
+ local stripped, dirty = rep("\0", size), offset + count
+ offset = offset + count + int * 2 + 4
+ offset = offset + int + subint(dump, offset, int) * ins
+ count, offset = subint(dump, offset, int)
+ for n = 1, count do
+ local t
+ t, offset = subint(dump, offset, 1)
+ if t == 1 then
+ offset = offset + 1
+ elseif t == 4 then
+ offset = offset + size + subint(dump, offset, size)
+ elseif t == 3 then
+ offset = offset + num
+ end
+ end
+ count, offset = subint(dump, offset, int)
+ stripped = stripped .. sub(dump,dirty, offset - 1)
+ for n = 1, count do
+ local proto, off = strip_function(sub(dump,offset, -1))
+ stripped, offset = stripped .. proto, offset + off - 1
+ end
+ offset = offset + subint(dump, offset, int) * int + int
+ count, offset = subint(dump, offset, int)
+ for n = 1, count do
+ offset = offset + subint(dump, offset, size) + size + int * 2
+ end
+ count, offset = subint(dump, offset, int)
+ for n = 1, count do
+ offset = offset + subint(dump, offset, size) + size
+ end
+ stripped = stripped .. rep("\0", int * 3)
+ return stripped, offset
+ end
+ dump = sub(dump,1,12) .. strip_function(sub(dump,13,-1))
+ local after = #dump
+ local delta = register(name,before,after)
+ return dump, delta
+ end
+
+ else
+
+ strip_code_pc = function(dump,name)
+ return dump, 0
+ end
+
+ end
+
+ -- ... end of borrowed code.
+
+ -- quite subtle ... doing this wrong incidentally can give more bytes
+
+ function luautilities.loadedluacode(fullname,forcestrip,name)
+ -- quite subtle ... doing this wrong incidentally can give more bytes
+ local code = environment.loadpreprocessedfile and environment.preprocessedloadfile(fullname) or loadfile(fullname)
+ if code then
+ code()
+ end
+ if forcestrip and luautilities.stripcode then
+ if type(forcestrip) == "function" then
+ forcestrip = forcestrip(fullname)
+ end
+ if forcestrip then
+ local code, n = strip_code_pc(dump(code),name)
+ return load(code), n
+ elseif luautilities.alwaysstripcode then
+ return load(strip_code_pc(dump(code),name))
+ else
+ return code, 0
+ end
+ elseif luautilities.alwaysstripcode then
+ return load(strip_code_pc(dump(code),name))
+ else
+ return code, 0
+ end
+ end
+
+ function luautilities.strippedloadstring(code,forcestrip,name) -- not executed
+ local n = 0
+ if (forcestrip and luautilities.stripcode) or luautilities.alwaysstripcode then
+ code = load(code)
+ if not code then
+ report_lua("fatal error in file %a",name)
+ end
+ code, n = strip_code_pc(dump(code),name)
+ end
+ return load(code), n
+ end
+
+ local function stupidcompile(luafile,lucfile,strip)
+ local code = io.loaddata(luafile)
+ local n = 0
+ if code and code ~= "" then
+ code = load(code)
+ if not code then
+ report_lua("fatal error in file %a",luafile)
+ end
+ code = dump(code)
+ if strip then
+ code, n = strip_code_pc(code,luautilities.stripcode or luautilities.alwaysstripcode,luafile) -- last one is reported
+ end
+ if code and code ~= "" then
+ io.savedata(lucfile,code)
+ end
+ end
+ return n
+ end
+
+ local luac_normal = "texluac -o %q %q"
+ local luac_strip = "texluac -s -o %q %q"
+
+ function luautilities.compile(luafile,lucfile,cleanup,strip,fallback) -- defaults: cleanup=false strip=true
+ report_lua("compiling %a into %a",luafile,lucfile)
+ os.remove(lucfile)
+ local done = false
+ if strip ~= false then
+ strip = true
+ end
+ if forcestupidcompile then
+ fallback = true
+ elseif strip then
+ done = os.spawn(format(luac_strip, lucfile,luafile)) == 0
+ else
+ done = os.spawn(format(luac_normal,lucfile,luafile)) == 0
+ end
+ if not done and fallback then
+ local n = stupidcompile(luafile,lucfile,strip)
+ if n > 0 then
+ report_lua("%a dumped into %a (%i bytes stripped)",luafile,lucfile,n)
+ else
+ report_lua("%a dumped into %a (unstripped)",luafile,lucfile)
+ end
+ cleanup = false -- better see how bad it is
+ done = true -- hm
+ end
+ if done and cleanup == true and lfs.isfile(lucfile) and lfs.isfile(luafile) then
+ report_lua("removing %a",luafile)
+ os.remove(luafile)
+ end
+ return done
+ end
+
+ luautilities.loadstripped = loadstring
+
+end
+
+-- local getmetatable, type = getmetatable, type
+--
+-- local types = { }
+--
+-- function luautilities.registerdatatype(d,name)
+-- types[getmetatable(d)] = name
+-- end
+--
+-- function luautilities.datatype(d)
+-- local t = type(d)
+-- if t == "userdata" then
+-- local m = getmetatable(d)
+-- return m and types[m] or "userdata"
+-- else
+-- return t
+-- end
+-- end
+--
+-- luautilities.registerdatatype(lpeg.P("!"),"lpeg")
+--
+-- print(luautilities.datatype(lpeg.P("oeps")))
diff --git a/tex/context/base/util-mrg.lua b/tex/context/base/util-mrg.lua
index c50ae8a75..690188ef8 100644
--- a/tex/context/base/util-mrg.lua
+++ b/tex/context/base/util-mrg.lua
@@ -1,228 +1,228 @@
-if not modules then modules = { } end modules ['util-mrg'] = {
- 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"
-}
-
--- hm, quite unreadable
-
-local gsub, format = string.gsub, string.format
-local concat = table.concat
-local type, next = type, next
-
-local P, R, S, V, Ct, C, Cs, Cc, Cp, Cmt, Cb, Cg = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.Ct, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.Cp, lpeg.Cmt, lpeg.Cb, lpeg.Cg
-local lpegmatch, patterns = lpeg.match, lpeg.patterns
-
-utilities = utilities or { }
-local merger = utilities.merger or { }
-utilities.merger = merger
-merger.strip_comment = true
-
-local report = logs.reporter("system","merge")
-utilities.report = report
-
-local m_begin_merge = "begin library merge"
-local m_end_merge = "end library merge"
-local m_begin_closure = "do -- create closure to overcome 200 locals limit"
-local m_end_closure = "end -- of closure"
-
-local m_pattern =
- "%c+" ..
- "%-%-%s+" .. m_begin_merge ..
- "%c+(.-)%c+" ..
- "%-%-%s+" .. m_end_merge ..
- "%c+"
-
-local m_format =
- "\n\n-- " .. m_begin_merge ..
- "\n%s\n" ..
- "-- " .. m_end_merge .. "\n\n"
-
-local m_faked =
- "-- " .. "created merged file" .. "\n\n" ..
- "-- " .. m_begin_merge .. "\n\n" ..
- "-- " .. m_end_merge .. "\n\n"
-
-local m_report = [[
--- used libraries : %s
--- skipped libraries : %s
--- original bytes : %s
--- stripped bytes : %s
-]]
-
-local m_preloaded = [[package.loaded[%q] = package.loaded[%q] or true]]
-
-local function self_fake()
- return m_faked
-end
-
-local function self_nothing()
- return ""
-end
-
-local function self_load(name)
- local data = io.loaddata(name) or ""
- if data == "" then
- report("unknown file %a",name)
- else
- report("inserting file %a",name)
- end
- return data or ""
-end
-
--- -- saves some 20K .. scite comments
--- data = gsub(data,"%-%-~[^\n\r]*[\r\n]","")
--- -- saves some 20K .. ldx comments
--- data = gsub(data,"%-%-%[%[ldx%-%-.-%-%-ldx%]%]%-%-","")
-
-local space = patterns.space
-local eol = patterns.newline
-local equals = P("=")^0
-local open = P("[") * Cg(equals,"init") * P("[") * P("\n")^-1
-local close = P("]") * C(equals) * P("]")
-local closeeq = Cmt(close * Cb("init"), function(s,i,a,b) return a == b end)
-local longstring = open * (1 - closeeq)^0 * close
-
-local quoted = patterns.quoted
-local digit = patterns.digit
-local emptyline = space^0 * eol
-local operator1 = P("<=") + P(">=") + P("~=") + P("..") + S("/^<>=*+%%")
-local operator2 = S("*+/")
-local operator3 = S("-")
-local operator4 = P("..")
-local separator = S(",;")
-
-local ignore = (P("]") * space^1 * P("=") * space^1 * P("]")) / "]=[" +
- (P("=") * space^1 * P("{")) / "={" +
- (P("(") * space^1) / "(" +
- (P("{") * (space+eol)^1 * P("}")) / "{}"
-local strings = quoted -- / function (s) print("<<"..s..">>") return s end
-local longcmt = (emptyline^0 * P("--") * longstring * emptyline^0) / ""
-local longstr = longstring
-local comment = emptyline^0 * P("--") * P("-")^0 * (1-eol)^0 * emptyline^1 / "\n"
-local optionalspaces = space^0 / ""
-local mandatespaces = space^1 / ""
-local optionalspacing = (eol+space)^0 / ""
-local mandatespacing = (eol+space)^1 / ""
-local pack = digit * space^1 * operator4 * optionalspacing +
- optionalspacing * operator1 * optionalspacing +
- optionalspacing * operator2 * optionalspaces +
- mandatespacing * operator3 * mandatespaces +
- optionalspaces * separator * optionalspaces
-local lines = emptyline^2 / "\n"
-local spaces = (space * space) / " "
------ spaces = ((space+eol)^1 ) / " "
-
-local compact = Cs ( (
- ignore +
- strings +
- longcmt +
- longstr +
- comment +
- pack +
- lines +
- spaces +
- 1
-)^1 )
-
-local strip = Cs((emptyline^2/"\n" + 1)^0)
-local stripreturn = Cs((1-P("return") * space^1 * P(1-space-eol)^1 * (space+eol)^0 * P(-1))^1)
-
-function merger.compact(data)
- return lpegmatch(strip,lpegmatch(compact,data))
-end
-
-local function self_compact(data)
- local delta = 0
- if merger.strip_comment then
- local before = #data
- data = lpegmatch(compact,data)
- data = lpegmatch(strip,data) -- also strips in longstrings ... alas
- -- data = string.strip(data)
- local after = #data
- delta = before - after
- report("original size %s, compacted to %s, stripped %s",before,after,delta)
- data = format("-- original size: %s, stripped down to: %s\n\n%s",before,after,data)
- end
- return lpegmatch(stripreturn,data) or data, delta
-end
-
-local function self_save(name, data)
- if data ~= "" then
- io.savedata(name,data)
- report("saving %s with size %s",name,#data)
- end
-end
-
-local function self_swap(data,code)
- return data ~= "" and (gsub(data,m_pattern, function() return format(m_format,code) end, 1)) or ""
-end
-
-local function self_libs(libs,list)
- local result, f, frozen, foundpath = { }, nil, false, nil
- result[#result+1] = "\n"
- if type(libs) == 'string' then libs = { libs } end
- if type(list) == 'string' then list = { list } end
- for i=1,#libs do
- local lib = libs[i]
- for j=1,#list do
- local pth = gsub(list[j],"\\","/") -- file.clean_path
- report("checking library path %a",pth)
- local name = pth .. "/" .. lib
- if lfs.isfile(name) then
- foundpath = pth
- end
- end
- if foundpath then break end
- end
- if foundpath then
- report("using library path %a",foundpath)
- local right, wrong, original, stripped = { }, { }, 0, 0
- for i=1,#libs do
- local lib = libs[i]
- local fullname = foundpath .. "/" .. lib
- if lfs.isfile(fullname) then
- report("using library %a",fullname)
- local preloaded = file.nameonly(lib)
- local data = io.loaddata(fullname,true)
- original = original + #data
- local data, delta = self_compact(data)
- right[#right+1] = lib
- result[#result+1] = m_begin_closure
- result[#result+1] = format(m_preloaded,preloaded,preloaded)
- result[#result+1] = data
- result[#result+1] = m_end_closure
- stripped = stripped + delta
- else
- report("skipping library %a",fullname)
- wrong[#wrong+1] = lib
- end
- end
- right = #right > 0 and concat(right," ") or "-"
- wrong = #wrong > 0 and concat(wrong," ") or "-"
- report("used libraries: %a",right)
- report("skipped libraries: %a",wrong)
- report("original bytes: %a",original)
- report("stripped bytes: %a",stripped)
- result[#result+1] = format(m_report,right,wrong,original,stripped)
- else
- report("no valid library path found")
- end
- return concat(result, "\n\n")
-end
-
-function merger.selfcreate(libs,list,target)
- if target then
- self_save(target,self_swap(self_fake(),self_libs(libs,list)))
- end
-end
-
-function merger.selfmerge(name,libs,list,target)
- self_save(target or name,self_swap(self_load(name),self_libs(libs,list)))
-end
-
-function merger.selfclean(name)
- self_save(name,self_swap(self_load(name),self_nothing()))
-end
+if not modules then modules = { } end modules ['util-mrg'] = {
+ 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"
+}
+
+-- hm, quite unreadable
+
+local gsub, format = string.gsub, string.format
+local concat = table.concat
+local type, next = type, next
+
+local P, R, S, V, Ct, C, Cs, Cc, Cp, Cmt, Cb, Cg = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.Ct, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.Cp, lpeg.Cmt, lpeg.Cb, lpeg.Cg
+local lpegmatch, patterns = lpeg.match, lpeg.patterns
+
+utilities = utilities or { }
+local merger = utilities.merger or { }
+utilities.merger = merger
+merger.strip_comment = true
+
+local report = logs.reporter("system","merge")
+utilities.report = report
+
+local m_begin_merge = "begin library merge"
+local m_end_merge = "end library merge"
+local m_begin_closure = "do -- create closure to overcome 200 locals limit"
+local m_end_closure = "end -- of closure"
+
+local m_pattern =
+ "%c+" ..
+ "%-%-%s+" .. m_begin_merge ..
+ "%c+(.-)%c+" ..
+ "%-%-%s+" .. m_end_merge ..
+ "%c+"
+
+local m_format =
+ "\n\n-- " .. m_begin_merge ..
+ "\n%s\n" ..
+ "-- " .. m_end_merge .. "\n\n"
+
+local m_faked =
+ "-- " .. "created merged file" .. "\n\n" ..
+ "-- " .. m_begin_merge .. "\n\n" ..
+ "-- " .. m_end_merge .. "\n\n"
+
+local m_report = [[
+-- used libraries : %s
+-- skipped libraries : %s
+-- original bytes : %s
+-- stripped bytes : %s
+]]
+
+local m_preloaded = [[package.loaded[%q] = package.loaded[%q] or true]]
+
+local function self_fake()
+ return m_faked
+end
+
+local function self_nothing()
+ return ""
+end
+
+local function self_load(name)
+ local data = io.loaddata(name) or ""
+ if data == "" then
+ report("unknown file %a",name)
+ else
+ report("inserting file %a",name)
+ end
+ return data or ""
+end
+
+-- -- saves some 20K .. scite comments
+-- data = gsub(data,"%-%-~[^\n\r]*[\r\n]","")
+-- -- saves some 20K .. ldx comments
+-- data = gsub(data,"%-%-%[%[ldx%-%-.-%-%-ldx%]%]%-%-","")
+
+local space = patterns.space
+local eol = patterns.newline
+local equals = P("=")^0
+local open = P("[") * Cg(equals,"init") * P("[") * P("\n")^-1
+local close = P("]") * C(equals) * P("]")
+local closeeq = Cmt(close * Cb("init"), function(s,i,a,b) return a == b end)
+local longstring = open * (1 - closeeq)^0 * close
+
+local quoted = patterns.quoted
+local digit = patterns.digit
+local emptyline = space^0 * eol
+local operator1 = P("<=") + P(">=") + P("~=") + P("..") + S("/^<>=*+%%")
+local operator2 = S("*+/")
+local operator3 = S("-")
+local operator4 = P("..")
+local separator = S(",;")
+
+local ignore = (P("]") * space^1 * P("=") * space^1 * P("]")) / "]=[" +
+ (P("=") * space^1 * P("{")) / "={" +
+ (P("(") * space^1) / "(" +
+ (P("{") * (space+eol)^1 * P("}")) / "{}"
+local strings = quoted -- / function (s) print("<<"..s..">>") return s end
+local longcmt = (emptyline^0 * P("--") * longstring * emptyline^0) / ""
+local longstr = longstring
+local comment = emptyline^0 * P("--") * P("-")^0 * (1-eol)^0 * emptyline^1 / "\n"
+local optionalspaces = space^0 / ""
+local mandatespaces = space^1 / ""
+local optionalspacing = (eol+space)^0 / ""
+local mandatespacing = (eol+space)^1 / ""
+local pack = digit * space^1 * operator4 * optionalspacing +
+ optionalspacing * operator1 * optionalspacing +
+ optionalspacing * operator2 * optionalspaces +
+ mandatespacing * operator3 * mandatespaces +
+ optionalspaces * separator * optionalspaces
+local lines = emptyline^2 / "\n"
+local spaces = (space * space) / " "
+----- spaces = ((space+eol)^1 ) / " "
+
+local compact = Cs ( (
+ ignore +
+ strings +
+ longcmt +
+ longstr +
+ comment +
+ pack +
+ lines +
+ spaces +
+ 1
+)^1 )
+
+local strip = Cs((emptyline^2/"\n" + 1)^0)
+local stripreturn = Cs((1-P("return") * space^1 * P(1-space-eol)^1 * (space+eol)^0 * P(-1))^1)
+
+function merger.compact(data)
+ return lpegmatch(strip,lpegmatch(compact,data))
+end
+
+local function self_compact(data)
+ local delta = 0
+ if merger.strip_comment then
+ local before = #data
+ data = lpegmatch(compact,data)
+ data = lpegmatch(strip,data) -- also strips in longstrings ... alas
+ -- data = string.strip(data)
+ local after = #data
+ delta = before - after
+ report("original size %s, compacted to %s, stripped %s",before,after,delta)
+ data = format("-- original size: %s, stripped down to: %s\n\n%s",before,after,data)
+ end
+ return lpegmatch(stripreturn,data) or data, delta
+end
+
+local function self_save(name, data)
+ if data ~= "" then
+ io.savedata(name,data)
+ report("saving %s with size %s",name,#data)
+ end
+end
+
+local function self_swap(data,code)
+ return data ~= "" and (gsub(data,m_pattern, function() return format(m_format,code) end, 1)) or ""
+end
+
+local function self_libs(libs,list)
+ local result, f, frozen, foundpath = { }, nil, false, nil
+ result[#result+1] = "\n"
+ if type(libs) == 'string' then libs = { libs } end
+ if type(list) == 'string' then list = { list } end
+ for i=1,#libs do
+ local lib = libs[i]
+ for j=1,#list do
+ local pth = gsub(list[j],"\\","/") -- file.clean_path
+ report("checking library path %a",pth)
+ local name = pth .. "/" .. lib
+ if lfs.isfile(name) then
+ foundpath = pth
+ end
+ end
+ if foundpath then break end
+ end
+ if foundpath then
+ report("using library path %a",foundpath)
+ local right, wrong, original, stripped = { }, { }, 0, 0
+ for i=1,#libs do
+ local lib = libs[i]
+ local fullname = foundpath .. "/" .. lib
+ if lfs.isfile(fullname) then
+ report("using library %a",fullname)
+ local preloaded = file.nameonly(lib)
+ local data = io.loaddata(fullname,true)
+ original = original + #data
+ local data, delta = self_compact(data)
+ right[#right+1] = lib
+ result[#result+1] = m_begin_closure
+ result[#result+1] = format(m_preloaded,preloaded,preloaded)
+ result[#result+1] = data
+ result[#result+1] = m_end_closure
+ stripped = stripped + delta
+ else
+ report("skipping library %a",fullname)
+ wrong[#wrong+1] = lib
+ end
+ end
+ right = #right > 0 and concat(right," ") or "-"
+ wrong = #wrong > 0 and concat(wrong," ") or "-"
+ report("used libraries: %a",right)
+ report("skipped libraries: %a",wrong)
+ report("original bytes: %a",original)
+ report("stripped bytes: %a",stripped)
+ result[#result+1] = format(m_report,right,wrong,original,stripped)
+ else
+ report("no valid library path found")
+ end
+ return concat(result, "\n\n")
+end
+
+function merger.selfcreate(libs,list,target)
+ if target then
+ self_save(target,self_swap(self_fake(),self_libs(libs,list)))
+ end
+end
+
+function merger.selfmerge(name,libs,list,target)
+ self_save(target or name,self_swap(self_load(name),self_libs(libs,list)))
+end
+
+function merger.selfclean(name)
+ self_save(name,self_swap(self_load(name),self_nothing()))
+end
diff --git a/tex/context/base/util-pck.lua b/tex/context/base/util-pck.lua
index fe9911946..7be5e8f42 100644
--- a/tex/context/base/util-pck.lua
+++ b/tex/context/base/util-pck.lua
@@ -1,144 +1,144 @@
-if not modules then modules = { } end modules ['util-pck'] = {
- 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"
-}
-
--- moved from core-uti
-
-local next, tostring, type = next, tostring, type
-local sort, concat = table.sort, table.concat
-local sortedhashkeys, sortedkeys = table.sortedhashkeys, table.sortedkeys
-
-utilities = utilities or { }
-utilities.packers = utilities.packers or { }
-local packers = utilities.packers
-packers.version = 1.00
-
-local function hashed(t)
- local s, ns = { }, 0
- for k, v in next, t do
- ns = ns + 1
- if type(v) == "table" then
- s[ns] = k .. "={" .. hashed(v) .. "}"
- else
- s[ns] = k .. "=" .. tostring(v)
- end
- end
- sort(s)
- return concat(s,",")
-end
-
-local function simplehashed(t)
- local s, ns = { }, 0
- for k, v in next, t do
- ns = ns + 1
- s[ns] = k .. "=" .. v
- end
- sort(s)
- return concat(s,",")
-end
-
-packers.hashed = hashed
-packers.simplehashed = simplehashed
-
--- In luatex < 0.74 (lua 5.1) a next chain was the same for each run so no sort was needed,
--- but in the latest greatest versions (lua 5.2) we really need to sort the keys in order
--- not to get endless runs due to a difference in tuc files.
-
-local function pack(t,keys,hash,index)
- if t then
- -- for k, v in next, t do
- -- local sk = sortedkeys(t)
- local sk = sortedhashkeys(t)
- for i=1,#sk do
- local k = sk[i]
- local v = t[k]
- --
- if type(v) == "table" then
- pack(v,keys,hash,index)
- if keys[k] then
- local h = hashed(v)
- local i = hash[h]
- if not i then
- i = #index + 1
- index[i] = v
- hash[h] = i
- end
- t[k] = i
- end
- end
- end
- end
-end
-
-local function unpack(t,keys,index)
- if t then
- for k, v in next, t do
- if keys[k] and type(v) == "number" then
- local iv = index[v]
- if iv then
- v = iv
- t[k] = v
- end
- end
- if type(v) == "table" then
- unpack(v,keys,index)
- end
- end
- end
-end
-
-function packers.new(keys,version)
- return {
- version = version or packers.version,
- keys = table.tohash(keys),
- hash = { },
- index = { },
- }
-end
-
-function packers.pack(t,p,shared)
- if shared then
- pack(t,p.keys,p.hash,p.index)
- elseif not t.packer then
- pack(t,p.keys,p.hash,p.index)
- if #p.index > 0 then
- t.packer = {
- version = p.version or packers.version,
- keys = p.keys,
- index = p.index,
- }
- end
- p.hash = { }
- p.index = { }
- end
-end
-
-function packers.unpack(t,p,shared)
- if shared then
- if p then
- unpack(t,p.keys,p.index)
- end
- else
- local tp = t.packer
- if tp then
- if tp.version == (p and p.version or packers.version) then
- unpack(t,tp.keys,tp.index)
- else
- return false
- end
- t.packer = nil
- end
- end
- return true
-end
-
-function packers.strip(p)
- p.hash = nil
-end
-
--- We could have a packer.serialize where we first flush the shared table
--- and then use inline a reference . This saves an unpack.
+if not modules then modules = { } end modules ['util-pck'] = {
+ 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"
+}
+
+-- moved from core-uti
+
+local next, tostring, type = next, tostring, type
+local sort, concat = table.sort, table.concat
+local sortedhashkeys, sortedkeys = table.sortedhashkeys, table.sortedkeys
+
+utilities = utilities or { }
+utilities.packers = utilities.packers or { }
+local packers = utilities.packers
+packers.version = 1.00
+
+local function hashed(t)
+ local s, ns = { }, 0
+ for k, v in next, t do
+ ns = ns + 1
+ if type(v) == "table" then
+ s[ns] = k .. "={" .. hashed(v) .. "}"
+ else
+ s[ns] = k .. "=" .. tostring(v)
+ end
+ end
+ sort(s)
+ return concat(s,",")
+end
+
+local function simplehashed(t)
+ local s, ns = { }, 0
+ for k, v in next, t do
+ ns = ns + 1
+ s[ns] = k .. "=" .. v
+ end
+ sort(s)
+ return concat(s,",")
+end
+
+packers.hashed = hashed
+packers.simplehashed = simplehashed
+
+-- In luatex < 0.74 (lua 5.1) a next chain was the same for each run so no sort was needed,
+-- but in the latest greatest versions (lua 5.2) we really need to sort the keys in order
+-- not to get endless runs due to a difference in tuc files.
+
+local function pack(t,keys,hash,index)
+ if t then
+ -- for k, v in next, t do
+ -- local sk = sortedkeys(t)
+ local sk = sortedhashkeys(t)
+ for i=1,#sk do
+ local k = sk[i]
+ local v = t[k]
+ --
+ if type(v) == "table" then
+ pack(v,keys,hash,index)
+ if keys[k] then
+ local h = hashed(v)
+ local i = hash[h]
+ if not i then
+ i = #index + 1
+ index[i] = v
+ hash[h] = i
+ end
+ t[k] = i
+ end
+ end
+ end
+ end
+end
+
+local function unpack(t,keys,index)
+ if t then
+ for k, v in next, t do
+ if keys[k] and type(v) == "number" then
+ local iv = index[v]
+ if iv then
+ v = iv
+ t[k] = v
+ end
+ end
+ if type(v) == "table" then
+ unpack(v,keys,index)
+ end
+ end
+ end
+end
+
+function packers.new(keys,version)
+ return {
+ version = version or packers.version,
+ keys = table.tohash(keys),
+ hash = { },
+ index = { },
+ }
+end
+
+function packers.pack(t,p,shared)
+ if shared then
+ pack(t,p.keys,p.hash,p.index)
+ elseif not t.packer then
+ pack(t,p.keys,p.hash,p.index)
+ if #p.index > 0 then
+ t.packer = {
+ version = p.version or packers.version,
+ keys = p.keys,
+ index = p.index,
+ }
+ end
+ p.hash = { }
+ p.index = { }
+ end
+end
+
+function packers.unpack(t,p,shared)
+ if shared then
+ if p then
+ unpack(t,p.keys,p.index)
+ end
+ else
+ local tp = t.packer
+ if tp then
+ if tp.version == (p and p.version or packers.version) then
+ unpack(t,tp.keys,tp.index)
+ else
+ return false
+ end
+ t.packer = nil
+ end
+ end
+ return true
+end
+
+function packers.strip(p)
+ p.hash = nil
+end
+
+-- We could have a packer.serialize where we first flush the shared table
+-- and then use inline a reference . This saves an unpack.
diff --git a/tex/context/base/util-prs.lua b/tex/context/base/util-prs.lua
index ed1e32a99..cdf497588 100644
--- a/tex/context/base/util-prs.lua
+++ b/tex/context/base/util-prs.lua
@@ -1,593 +1,593 @@
-if not modules then modules = { } end modules ['util-prs'] = {
- 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 lpeg, table, string = lpeg, table, string
-local P, R, V, S, C, Ct, Cs, Carg, Cc, Cg, Cf, Cp = lpeg.P, lpeg.R, lpeg.V, lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cs, lpeg.Carg, lpeg.Cc, lpeg.Cg, lpeg.Cf, lpeg.Cp
-local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
-local concat, format, gmatch, find = table.concat, string.format, string.gmatch, string.find
-local tostring, type, next, rawset = tostring, type, next, rawset
-
-utilities = utilities or {}
-local parsers = utilities.parsers or { }
-utilities.parsers = parsers
-local patterns = parsers.patterns or { }
-parsers.patterns = patterns
-
-local setmetatableindex = table.setmetatableindex
-local sortedhash = table.sortedhash
-
--- we share some patterns
-
-local digit = R("09")
-local space = P(' ')
-local equal = P("=")
-local comma = P(",")
-local lbrace = P("{")
-local rbrace = P("}")
-local lparent = P("(")
-local rparent = P(")")
-local period = S(".")
-local punctuation = S(".,:;")
-local spacer = lpegpatterns.spacer
-local whitespace = lpegpatterns.whitespace
-local newline = lpegpatterns.newline
-local anything = lpegpatterns.anything
-local endofstring = lpegpatterns.endofstring
-
-local nobrace = 1 - ( lbrace + rbrace )
-local noparent = 1 - ( lparent + rparent)
-
--- we could use a Cf Cg construct
-
-local escape, left, right = P("\\"), P('{'), P('}')
-
-lpegpatterns.balanced = P {
- [1] = ((escape * (left+right)) + (1 - (left+right)) + V(2))^0,
- [2] = left * V(1) * right
-}
-
-local nestedbraces = P { lbrace * (nobrace + V(1))^0 * rbrace }
-local nestedparents = P { lparent * (noparent + V(1))^0 * rparent }
-local spaces = space^0
-local argument = Cs((lbrace/"") * ((nobrace + nestedbraces)^0) * (rbrace/""))
-local content = (1-endofstring)^0
-
-lpegpatterns.nestedbraces = nestedbraces -- no capture
-lpegpatterns.nestedparents = nestedparents -- no capture
-lpegpatterns.nested = nestedbraces -- no capture
-lpegpatterns.argument = argument -- argument after e.g. =
-lpegpatterns.content = content -- rest after e.g =
-
-local value = P(lbrace * C((nobrace + nestedbraces)^0) * rbrace) + C((nestedbraces + (1-comma))^0)
-
-local key = C((1-equal-comma)^1)
-local pattern_a = (space+comma)^0 * (key * equal * value + key * C(""))
-local pattern_c = (space+comma)^0 * (key * equal * value)
-
-local key = C((1-space-equal-comma)^1)
-local pattern_b = spaces * comma^0 * spaces * (key * ((spaces * equal * spaces * value) + C("")))
-
--- "a=1, b=2, c=3, d={a{b,c}d}, e=12345, f=xx{a{b,c}d}xx, g={}" : outer {} removes, leading spaces ignored
-
--- todo: rewrite to fold etc
---
--- parse = lpeg.Cf(lpeg.Carg(1) * lpeg.Cg(key * equal * value) * separator^0,rawset)^0 -- lpeg.match(parse,"...",1,hash)
-
-local hash = { }
-
-local function set(key,value)
- hash[key] = value
-end
-
-local pattern_a_s = (pattern_a/set)^1
-local pattern_b_s = (pattern_b/set)^1
-local pattern_c_s = (pattern_c/set)^1
-
-patterns.settings_to_hash_a = pattern_a_s
-patterns.settings_to_hash_b = pattern_b_s
-patterns.settings_to_hash_c = pattern_c_s
-
-function parsers.make_settings_to_hash_pattern(set,how)
- if type(str) == "table" then
- return set
- elseif how == "strict" then
- return (pattern_c/set)^1
- elseif how == "tolerant" then
- return (pattern_b/set)^1
- else
- return (pattern_a/set)^1
- end
-end
-
-function parsers.settings_to_hash(str,existing)
- if type(str) == "table" then
- if existing then
- for k, v in next, str do
- existing[k] = v
- end
- return exiting
- else
- return str
- end
- elseif str and str ~= "" then
- hash = existing or { }
- lpegmatch(pattern_a_s,str)
- return hash
- else
- return { }
- end
-end
-
-function parsers.settings_to_hash_tolerant(str,existing)
- if type(str) == "table" then
- if existing then
- for k, v in next, str do
- existing[k] = v
- end
- return exiting
- else
- return str
- end
- elseif str and str ~= "" then
- hash = existing or { }
- lpegmatch(pattern_b_s,str)
- return hash
- else
- return { }
- end
-end
-
-function parsers.settings_to_hash_strict(str,existing)
- if type(str) == "table" then
- if existing then
- for k, v in next, str do
- existing[k] = v
- end
- return exiting
- else
- return str
- end
- elseif str and str ~= "" then
- hash = existing or { }
- lpegmatch(pattern_c_s,str)
- return next(hash) and hash
- else
- return nil
- end
-end
-
-local separator = comma * space^0
-local value = P(lbrace * C((nobrace + nestedbraces)^0) * rbrace)
- + C((nestedbraces + (1-comma))^0)
-local pattern = spaces * Ct(value*(separator*value)^0)
-
--- "aap, {noot}, mies" : outer {} removes, leading spaces ignored
-
-patterns.settings_to_array = pattern
-
--- we could use a weak table as cache
-
-function parsers.settings_to_array(str,strict)
- if type(str) == "table" then
- return str
- elseif not str or str == "" then
- return { }
- elseif strict then
- if find(str,"{") then
- return lpegmatch(pattern,str)
- else
- return { str }
- end
- else
- return lpegmatch(pattern,str)
- end
-end
-
-local function set(t,v)
- t[#t+1] = v
-end
-
-local value = P(Carg(1)*value) / set
-local pattern = value*(separator*value)^0 * Carg(1)
-
-function parsers.add_settings_to_array(t,str)
- return lpegmatch(pattern,str,nil,t)
-end
-
-function parsers.hash_to_string(h,separator,yes,no,strict,omit)
- if h then
- local t, tn, s = { }, 0, table.sortedkeys(h)
- omit = omit and table.tohash(omit)
- for i=1,#s do
- local key = s[i]
- if not omit or not omit[key] then
- local value = h[key]
- if type(value) == "boolean" then
- if yes and no then
- if value then
- tn = tn + 1
- t[tn] = key .. '=' .. yes
- elseif not strict then
- tn = tn + 1
- t[tn] = key .. '=' .. no
- end
- elseif value or not strict then
- tn = tn + 1
- t[tn] = key .. '=' .. tostring(value)
- end
- else
- tn = tn + 1
- t[tn] = key .. '=' .. value
- end
- end
- end
- return concat(t,separator or ",")
- else
- return ""
- end
-end
-
-function parsers.array_to_string(a,separator)
- if a then
- return concat(a,separator or ",")
- else
- return ""
- end
-end
-
-function parsers.settings_to_set(str,t) -- tohash? -- todo: lpeg -- duplicate anyway
- t = t or { }
--- for s in gmatch(str,"%s*([^, ]+)") do -- space added
- for s in gmatch(str,"[^, ]+") do -- space added
- t[s] = true
- end
- return t
-end
-
-function parsers.simple_hash_to_string(h, separator)
- local t, tn = { }, 0
- for k, v in sortedhash(h) do
- if v then
- tn = tn + 1
- t[tn] = k
- end
- end
- return concat(t,separator or ",")
-end
-
--- for chem (currently one level)
-
-local value = P(lbrace * C((nobrace + nestedbraces)^0) * rbrace)
- + C(digit^1 * lparent * (noparent + nestedparents)^1 * rparent)
- + C((nestedbraces + (1-comma))^1)
-local pattern_a = spaces * Ct(value*(separator*value)^0)
-
-local function repeater(n,str)
- if not n then
- return str
- else
- local s = lpegmatch(pattern_a,str)
- if n == 1 then
- return unpack(s)
- else
- local t, tn = { }, 0
- for i=1,n do
- for j=1,#s do
- tn = tn + 1
- t[tn] = s[j]
- end
- end
- return unpack(t)
- end
- end
-end
-
-local value = P(lbrace * C((nobrace + nestedbraces)^0) * rbrace)
- + (C(digit^1)/tonumber * lparent * Cs((noparent + nestedparents)^1) * rparent) / repeater
- + C((nestedbraces + (1-comma))^1)
-local pattern_b = spaces * Ct(value*(separator*value)^0)
-
-function parsers.settings_to_array_with_repeat(str,expand) -- beware: "" => { }
- if expand then
- return lpegmatch(pattern_b,str) or { }
- else
- return lpegmatch(pattern_a,str) or { }
- end
-end
-
---
-
-local value = lbrace * C((nobrace + nestedbraces)^0) * rbrace
-local pattern = Ct((space + value)^0)
-
-function parsers.arguments_to_table(str)
- return lpegmatch(pattern,str)
-end
-
--- temporary here (unoptimized)
-
-function parsers.getparameters(self,class,parentclass,settings)
- local sc = self[class]
- if not sc then
- sc = { }
- self[class] = sc
- if parentclass then
- local sp = self[parentclass]
- if not sp then
- sp = { }
- self[parentclass] = sp
- end
- setmetatableindex(sc,sp)
- end
- end
- parsers.settings_to_hash(settings,sc)
-end
-
-function parsers.listitem(str)
- return gmatch(str,"[^, ]+")
-end
-
---
-
-local pattern = Cs { "start",
- start = V("one") + V("two") + V("three"),
- rest = (Cc(",") * V("thousand"))^0 * (P(".") + endofstring) * anything^0,
- thousand = digit * digit * digit,
- one = digit * V("rest"),
- two = digit * digit * V("rest"),
- three = V("thousand") * V("rest"),
-}
-
-lpegpatterns.splitthousands = pattern -- maybe better in the parsers namespace ?
-
-function parsers.splitthousands(str)
- return lpegmatch(pattern,str) or str
-end
-
--- print(parsers.splitthousands("11111111111.11"))
-
-local optionalwhitespace = whitespace^0
-
-lpegpatterns.words = Ct((Cs((1-punctuation-whitespace)^1) + anything)^1)
-lpegpatterns.sentences = Ct((optionalwhitespace * Cs((1-period)^0 * period))^1)
-lpegpatterns.paragraphs = Ct((optionalwhitespace * Cs((whitespace^1*endofstring/"" + 1 - (spacer^0*newline*newline))^1))^1)
-
--- local str = " Word1 word2. \n Word3 word4. \n\n Word5 word6.\n "
--- inspect(lpegmatch(lpegpatterns.paragraphs,str))
--- inspect(lpegmatch(lpegpatterns.sentences,str))
--- inspect(lpegmatch(lpegpatterns.words,str))
-
--- handy for k="v" [, ] k="v"
-
-local dquote = P('"')
-local equal = P('=')
-local escape = P('\\')
-local separator = S(' ,')
-
-local key = C((1-equal)^1)
-local value = dquote * C((1-dquote-escape*dquote)^0) * dquote
-
-local pattern = Cf(Ct("") * Cg(key * equal * value) * separator^0,rawset)^0 * P(-1)
-
-patterns.keq_to_hash_c = pattern
-
-function parsers.keq_to_hash(str)
- if str and str ~= "" then
- return lpegmatch(pattern,str)
- else
- return { }
- end
-end
-
--- inspect(lpeg.match(pattern,[[key="value"]]))
-
-local defaultspecification = { separator = ",", quote = '"' }
-
--- this version accepts multiple separators and quotes as used in the
--- database module
-
-function parsers.csvsplitter(specification)
- specification = specification and table.setmetatableindex(specification,defaultspecification) or defaultspecification
- local separator = specification.separator
- local quotechar = specification.quote
- local separator = S(separator ~= "" and separator or ",")
- local whatever = C((1 - separator - newline)^0)
- if quotechar and quotechar ~= "" then
- local quotedata = nil
- for chr in gmatch(quotechar,".") do
- local quotechar = P(chr)
- local quoteword = quotechar * C((1 - quotechar)^0) * quotechar
- if quotedata then
- quotedata = quotedata + quoteword
- else
- quotedata = quoteword
- end
- end
- whatever = quotedata + whatever
- end
- local parser = Ct((Ct(whatever * (separator * whatever)^0) * S("\n\r"))^0 )
- return function(data)
- return lpegmatch(parser,data)
- end
-end
-
--- and this is a slightly patched version of a version posted by Philipp Gesang
-
--- local mycsvsplitter = utilities.parsers.rfc4180splitter()
---
--- local crap = [[
--- first,second,third,fourth
--- "1","2","3","4"
--- "a","b","c","d"
--- "foo","bar""baz","boogie","xyzzy"
--- ]]
---
--- local list, names = mycsvsplitter(crap,true) inspect(list) inspect(names)
--- local list, names = mycsvsplitter(crap) inspect(list) inspect(names)
-
-function parsers.rfc4180splitter(specification)
- specification = specification and table.setmetatableindex(specification,defaultspecification) or defaultspecification
- local separator = specification.separator --> rfc: COMMA
- local quotechar = P(specification.quote) --> DQUOTE
- local dquotechar = quotechar * quotechar --> 2DQUOTE
- / specification.quote
- local separator = S(separator ~= "" and separator or ",")
- local escaped = quotechar
- * Cs((dquotechar + (1 - quotechar))^0)
- * quotechar
- local non_escaped = C((1 - quotechar - newline - separator)^1)
- local field = escaped + non_escaped
- local record = Ct((field * separator^-1)^1)
- local headerline = record * Cp()
- local wholeblob = Ct((newline^-1 * record)^0)
- return function(data,getheader)
- if getheader then
- local header, position = lpegmatch(headerline,data)
- local data = lpegmatch(wholeblob,data,position)
- return data, header
- else
- return lpegmatch(wholeblob,data)
- end
- end
-end
-
--- utilities.parsers.stepper("1,7-",9,function(i) print(">>>",i) end)
--- utilities.parsers.stepper("1-3,7,8,9")
--- utilities.parsers.stepper("1-3,6,7",function(i) print(">>>",i) end)
--- utilities.parsers.stepper(" 1 : 3, ,7 ")
--- utilities.parsers.stepper("1:4,9:13,24:*",30)
-
-local function ranger(first,last,n,action)
- if not first then
- -- forget about it
- elseif last == true then
- for i=first,n or first do
- action(i)
- end
- elseif last then
- for i=first,last do
- action(i)
- end
- else
- action(first)
- end
-end
-
-local cardinal = lpegpatterns.cardinal / tonumber
-local spacers = lpegpatterns.spacer^0
-local endofstring = lpegpatterns.endofstring
-
-local stepper = spacers * ( C(cardinal) * ( spacers * S(":-") * spacers * ( C(cardinal) + Cc(true) ) + Cc(false) )
- * Carg(1) * Carg(2) / ranger * S(", ")^0 )^1
-
-local stepper = spacers * ( C(cardinal) * ( spacers * S(":-") * spacers * ( C(cardinal) + (P("*") + endofstring) * Cc(true) ) + Cc(false) )
- * Carg(1) * Carg(2) / ranger * S(", ")^0 )^1 * endofstring -- we're sort of strict (could do without endofstring)
-
-function parsers.stepper(str,n,action)
- if type(n) == "function" then
- lpegmatch(stepper,str,1,false,n or print)
- else
- lpegmatch(stepper,str,1,n,action or print)
- end
-end
-
---
-
-local pattern_math = Cs((P("%")/"\\percent " + P("^") * Cc("{") * lpegpatterns.integer * Cc("}") + P(1))^0)
-local pattern_text = Cs((P("%")/"\\percent " + (P("^")/"\\high") * Cc("{") * lpegpatterns.integer * Cc("}") + P(1))^0)
-
-patterns.unittotex = pattern
-
-function parsers.unittotex(str,textmode)
- return lpegmatch(textmode and pattern_text or pattern_math,str)
-end
-
-local pattern = Cs((P("^") / "<sup>" * lpegpatterns.integer * Cc("</sup>") + P(1))^0)
-
-function parsers.unittoxml(str)
- return lpegmatch(pattern,str)
-end
-
--- print(utilities.parsers.unittotex("10^-32 %"),utilities.parsers.unittoxml("10^32 %"))
-
-local cache = { }
-local spaces = lpeg.patterns.space^0
-local dummy = function() end
-
-table.setmetatableindex(cache,function(t,k)
- local separator = P(k)
- local value = (1-separator)^0
- local pattern = spaces * C(value) * separator^0 * Cp()
- t[k] = pattern
- return pattern
-end)
-
-local commalistiterator = cache[","]
-
-function utilities.parsers.iterator(str,separator)
- local n = #str
- if n == 0 then
- return dummy
- else
- local pattern = separator and cache[separator] or commalistiterator
- local p = 1
- return function()
- if p <= n then
- local s, e = lpegmatch(pattern,str,p)
- if e then
- p = e
- return s
- end
- end
- end
- end
-end
-
--- for s in utilities.parsers.iterator("a b c,b,c") do
--- print(s)
--- end
-
-local function initialize(t,name)
- local source = t[name]
- if source then
- local result = { }
- for k, v in next, t[name] do
- result[k] = v
- end
- return result
- else
- return { }
- end
-end
-
-local function fetch(t,name)
- return t[name] or { }
-end
-
-function process(result,more)
- for k, v in next, more do
- result[k] = v
- end
- return result
-end
-
-local name = C((1-S(", "))^1)
-local parser = (Carg(1) * name / initialize) * (S(", ")^1 * (Carg(1) * name / fetch))^0
-local merge = Cf(parser,process)
-
-function utilities.parsers.mergehashes(hash,list)
- return lpegmatch(merge,list,1,hash)
-end
-
--- local t = {
--- aa = { alpha = 1, beta = 2, gamma = 3, },
--- bb = { alpha = 4, beta = 5, delta = 6, },
--- cc = { epsilon = 3 },
--- }
---
--- inspect(utilities.parsers.mergehashes(t,"aa, bb, cc"))
+if not modules then modules = { } end modules ['util-prs'] = {
+ 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 lpeg, table, string = lpeg, table, string
+local P, R, V, S, C, Ct, Cs, Carg, Cc, Cg, Cf, Cp = lpeg.P, lpeg.R, lpeg.V, lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cs, lpeg.Carg, lpeg.Cc, lpeg.Cg, lpeg.Cf, lpeg.Cp
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+local concat, format, gmatch, find = table.concat, string.format, string.gmatch, string.find
+local tostring, type, next, rawset = tostring, type, next, rawset
+
+utilities = utilities or {}
+local parsers = utilities.parsers or { }
+utilities.parsers = parsers
+local patterns = parsers.patterns or { }
+parsers.patterns = patterns
+
+local setmetatableindex = table.setmetatableindex
+local sortedhash = table.sortedhash
+
+-- we share some patterns
+
+local digit = R("09")
+local space = P(' ')
+local equal = P("=")
+local comma = P(",")
+local lbrace = P("{")
+local rbrace = P("}")
+local lparent = P("(")
+local rparent = P(")")
+local period = S(".")
+local punctuation = S(".,:;")
+local spacer = lpegpatterns.spacer
+local whitespace = lpegpatterns.whitespace
+local newline = lpegpatterns.newline
+local anything = lpegpatterns.anything
+local endofstring = lpegpatterns.endofstring
+
+local nobrace = 1 - ( lbrace + rbrace )
+local noparent = 1 - ( lparent + rparent)
+
+-- we could use a Cf Cg construct
+
+local escape, left, right = P("\\"), P('{'), P('}')
+
+lpegpatterns.balanced = P {
+ [1] = ((escape * (left+right)) + (1 - (left+right)) + V(2))^0,
+ [2] = left * V(1) * right
+}
+
+local nestedbraces = P { lbrace * (nobrace + V(1))^0 * rbrace }
+local nestedparents = P { lparent * (noparent + V(1))^0 * rparent }
+local spaces = space^0
+local argument = Cs((lbrace/"") * ((nobrace + nestedbraces)^0) * (rbrace/""))
+local content = (1-endofstring)^0
+
+lpegpatterns.nestedbraces = nestedbraces -- no capture
+lpegpatterns.nestedparents = nestedparents -- no capture
+lpegpatterns.nested = nestedbraces -- no capture
+lpegpatterns.argument = argument -- argument after e.g. =
+lpegpatterns.content = content -- rest after e.g =
+
+local value = P(lbrace * C((nobrace + nestedbraces)^0) * rbrace) + C((nestedbraces + (1-comma))^0)
+
+local key = C((1-equal-comma)^1)
+local pattern_a = (space+comma)^0 * (key * equal * value + key * C(""))
+local pattern_c = (space+comma)^0 * (key * equal * value)
+
+local key = C((1-space-equal-comma)^1)
+local pattern_b = spaces * comma^0 * spaces * (key * ((spaces * equal * spaces * value) + C("")))
+
+-- "a=1, b=2, c=3, d={a{b,c}d}, e=12345, f=xx{a{b,c}d}xx, g={}" : outer {} removes, leading spaces ignored
+
+-- todo: rewrite to fold etc
+--
+-- parse = lpeg.Cf(lpeg.Carg(1) * lpeg.Cg(key * equal * value) * separator^0,rawset)^0 -- lpeg.match(parse,"...",1,hash)
+
+local hash = { }
+
+local function set(key,value)
+ hash[key] = value
+end
+
+local pattern_a_s = (pattern_a/set)^1
+local pattern_b_s = (pattern_b/set)^1
+local pattern_c_s = (pattern_c/set)^1
+
+patterns.settings_to_hash_a = pattern_a_s
+patterns.settings_to_hash_b = pattern_b_s
+patterns.settings_to_hash_c = pattern_c_s
+
+function parsers.make_settings_to_hash_pattern(set,how)
+ if type(str) == "table" then
+ return set
+ elseif how == "strict" then
+ return (pattern_c/set)^1
+ elseif how == "tolerant" then
+ return (pattern_b/set)^1
+ else
+ return (pattern_a/set)^1
+ end
+end
+
+function parsers.settings_to_hash(str,existing)
+ if type(str) == "table" then
+ if existing then
+ for k, v in next, str do
+ existing[k] = v
+ end
+ return exiting
+ else
+ return str
+ end
+ elseif str and str ~= "" then
+ hash = existing or { }
+ lpegmatch(pattern_a_s,str)
+ return hash
+ else
+ return { }
+ end
+end
+
+function parsers.settings_to_hash_tolerant(str,existing)
+ if type(str) == "table" then
+ if existing then
+ for k, v in next, str do
+ existing[k] = v
+ end
+ return exiting
+ else
+ return str
+ end
+ elseif str and str ~= "" then
+ hash = existing or { }
+ lpegmatch(pattern_b_s,str)
+ return hash
+ else
+ return { }
+ end
+end
+
+function parsers.settings_to_hash_strict(str,existing)
+ if type(str) == "table" then
+ if existing then
+ for k, v in next, str do
+ existing[k] = v
+ end
+ return exiting
+ else
+ return str
+ end
+ elseif str and str ~= "" then
+ hash = existing or { }
+ lpegmatch(pattern_c_s,str)
+ return next(hash) and hash
+ else
+ return nil
+ end
+end
+
+local separator = comma * space^0
+local value = P(lbrace * C((nobrace + nestedbraces)^0) * rbrace)
+ + C((nestedbraces + (1-comma))^0)
+local pattern = spaces * Ct(value*(separator*value)^0)
+
+-- "aap, {noot}, mies" : outer {} removes, leading spaces ignored
+
+patterns.settings_to_array = pattern
+
+-- we could use a weak table as cache
+
+function parsers.settings_to_array(str,strict)
+ if type(str) == "table" then
+ return str
+ elseif not str or str == "" then
+ return { }
+ elseif strict then
+ if find(str,"{") then
+ return lpegmatch(pattern,str)
+ else
+ return { str }
+ end
+ else
+ return lpegmatch(pattern,str)
+ end
+end
+
+local function set(t,v)
+ t[#t+1] = v
+end
+
+local value = P(Carg(1)*value) / set
+local pattern = value*(separator*value)^0 * Carg(1)
+
+function parsers.add_settings_to_array(t,str)
+ return lpegmatch(pattern,str,nil,t)
+end
+
+function parsers.hash_to_string(h,separator,yes,no,strict,omit)
+ if h then
+ local t, tn, s = { }, 0, table.sortedkeys(h)
+ omit = omit and table.tohash(omit)
+ for i=1,#s do
+ local key = s[i]
+ if not omit or not omit[key] then
+ local value = h[key]
+ if type(value) == "boolean" then
+ if yes and no then
+ if value then
+ tn = tn + 1
+ t[tn] = key .. '=' .. yes
+ elseif not strict then
+ tn = tn + 1
+ t[tn] = key .. '=' .. no
+ end
+ elseif value or not strict then
+ tn = tn + 1
+ t[tn] = key .. '=' .. tostring(value)
+ end
+ else
+ tn = tn + 1
+ t[tn] = key .. '=' .. value
+ end
+ end
+ end
+ return concat(t,separator or ",")
+ else
+ return ""
+ end
+end
+
+function parsers.array_to_string(a,separator)
+ if a then
+ return concat(a,separator or ",")
+ else
+ return ""
+ end
+end
+
+function parsers.settings_to_set(str,t) -- tohash? -- todo: lpeg -- duplicate anyway
+ t = t or { }
+-- for s in gmatch(str,"%s*([^, ]+)") do -- space added
+ for s in gmatch(str,"[^, ]+") do -- space added
+ t[s] = true
+ end
+ return t
+end
+
+function parsers.simple_hash_to_string(h, separator)
+ local t, tn = { }, 0
+ for k, v in sortedhash(h) do
+ if v then
+ tn = tn + 1
+ t[tn] = k
+ end
+ end
+ return concat(t,separator or ",")
+end
+
+-- for chem (currently one level)
+
+local value = P(lbrace * C((nobrace + nestedbraces)^0) * rbrace)
+ + C(digit^1 * lparent * (noparent + nestedparents)^1 * rparent)
+ + C((nestedbraces + (1-comma))^1)
+local pattern_a = spaces * Ct(value*(separator*value)^0)
+
+local function repeater(n,str)
+ if not n then
+ return str
+ else
+ local s = lpegmatch(pattern_a,str)
+ if n == 1 then
+ return unpack(s)
+ else
+ local t, tn = { }, 0
+ for i=1,n do
+ for j=1,#s do
+ tn = tn + 1
+ t[tn] = s[j]
+ end
+ end
+ return unpack(t)
+ end
+ end
+end
+
+local value = P(lbrace * C((nobrace + nestedbraces)^0) * rbrace)
+ + (C(digit^1)/tonumber * lparent * Cs((noparent + nestedparents)^1) * rparent) / repeater
+ + C((nestedbraces + (1-comma))^1)
+local pattern_b = spaces * Ct(value*(separator*value)^0)
+
+function parsers.settings_to_array_with_repeat(str,expand) -- beware: "" => { }
+ if expand then
+ return lpegmatch(pattern_b,str) or { }
+ else
+ return lpegmatch(pattern_a,str) or { }
+ end
+end
+
+--
+
+local value = lbrace * C((nobrace + nestedbraces)^0) * rbrace
+local pattern = Ct((space + value)^0)
+
+function parsers.arguments_to_table(str)
+ return lpegmatch(pattern,str)
+end
+
+-- temporary here (unoptimized)
+
+function parsers.getparameters(self,class,parentclass,settings)
+ local sc = self[class]
+ if not sc then
+ sc = { }
+ self[class] = sc
+ if parentclass then
+ local sp = self[parentclass]
+ if not sp then
+ sp = { }
+ self[parentclass] = sp
+ end
+ setmetatableindex(sc,sp)
+ end
+ end
+ parsers.settings_to_hash(settings,sc)
+end
+
+function parsers.listitem(str)
+ return gmatch(str,"[^, ]+")
+end
+
+--
+
+local pattern = Cs { "start",
+ start = V("one") + V("two") + V("three"),
+ rest = (Cc(",") * V("thousand"))^0 * (P(".") + endofstring) * anything^0,
+ thousand = digit * digit * digit,
+ one = digit * V("rest"),
+ two = digit * digit * V("rest"),
+ three = V("thousand") * V("rest"),
+}
+
+lpegpatterns.splitthousands = pattern -- maybe better in the parsers namespace ?
+
+function parsers.splitthousands(str)
+ return lpegmatch(pattern,str) or str
+end
+
+-- print(parsers.splitthousands("11111111111.11"))
+
+local optionalwhitespace = whitespace^0
+
+lpegpatterns.words = Ct((Cs((1-punctuation-whitespace)^1) + anything)^1)
+lpegpatterns.sentences = Ct((optionalwhitespace * Cs((1-period)^0 * period))^1)
+lpegpatterns.paragraphs = Ct((optionalwhitespace * Cs((whitespace^1*endofstring/"" + 1 - (spacer^0*newline*newline))^1))^1)
+
+-- local str = " Word1 word2. \n Word3 word4. \n\n Word5 word6.\n "
+-- inspect(lpegmatch(lpegpatterns.paragraphs,str))
+-- inspect(lpegmatch(lpegpatterns.sentences,str))
+-- inspect(lpegmatch(lpegpatterns.words,str))
+
+-- handy for k="v" [, ] k="v"
+
+local dquote = P('"')
+local equal = P('=')
+local escape = P('\\')
+local separator = S(' ,')
+
+local key = C((1-equal)^1)
+local value = dquote * C((1-dquote-escape*dquote)^0) * dquote
+
+local pattern = Cf(Ct("") * Cg(key * equal * value) * separator^0,rawset)^0 * P(-1)
+
+patterns.keq_to_hash_c = pattern
+
+function parsers.keq_to_hash(str)
+ if str and str ~= "" then
+ return lpegmatch(pattern,str)
+ else
+ return { }
+ end
+end
+
+-- inspect(lpeg.match(pattern,[[key="value"]]))
+
+local defaultspecification = { separator = ",", quote = '"' }
+
+-- this version accepts multiple separators and quotes as used in the
+-- database module
+
+function parsers.csvsplitter(specification)
+ specification = specification and table.setmetatableindex(specification,defaultspecification) or defaultspecification
+ local separator = specification.separator
+ local quotechar = specification.quote
+ local separator = S(separator ~= "" and separator or ",")
+ local whatever = C((1 - separator - newline)^0)
+ if quotechar and quotechar ~= "" then
+ local quotedata = nil
+ for chr in gmatch(quotechar,".") do
+ local quotechar = P(chr)
+ local quoteword = quotechar * C((1 - quotechar)^0) * quotechar
+ if quotedata then
+ quotedata = quotedata + quoteword
+ else
+ quotedata = quoteword
+ end
+ end
+ whatever = quotedata + whatever
+ end
+ local parser = Ct((Ct(whatever * (separator * whatever)^0) * S("\n\r"))^0 )
+ return function(data)
+ return lpegmatch(parser,data)
+ end
+end
+
+-- and this is a slightly patched version of a version posted by Philipp Gesang
+
+-- local mycsvsplitter = utilities.parsers.rfc4180splitter()
+--
+-- local crap = [[
+-- first,second,third,fourth
+-- "1","2","3","4"
+-- "a","b","c","d"
+-- "foo","bar""baz","boogie","xyzzy"
+-- ]]
+--
+-- local list, names = mycsvsplitter(crap,true) inspect(list) inspect(names)
+-- local list, names = mycsvsplitter(crap) inspect(list) inspect(names)
+
+function parsers.rfc4180splitter(specification)
+ specification = specification and table.setmetatableindex(specification,defaultspecification) or defaultspecification
+ local separator = specification.separator --> rfc: COMMA
+ local quotechar = P(specification.quote) --> DQUOTE
+ local dquotechar = quotechar * quotechar --> 2DQUOTE
+ / specification.quote
+ local separator = S(separator ~= "" and separator or ",")
+ local escaped = quotechar
+ * Cs((dquotechar + (1 - quotechar))^0)
+ * quotechar
+ local non_escaped = C((1 - quotechar - newline - separator)^1)
+ local field = escaped + non_escaped
+ local record = Ct((field * separator^-1)^1)
+ local headerline = record * Cp()
+ local wholeblob = Ct((newline^-1 * record)^0)
+ return function(data,getheader)
+ if getheader then
+ local header, position = lpegmatch(headerline,data)
+ local data = lpegmatch(wholeblob,data,position)
+ return data, header
+ else
+ return lpegmatch(wholeblob,data)
+ end
+ end
+end
+
+-- utilities.parsers.stepper("1,7-",9,function(i) print(">>>",i) end)
+-- utilities.parsers.stepper("1-3,7,8,9")
+-- utilities.parsers.stepper("1-3,6,7",function(i) print(">>>",i) end)
+-- utilities.parsers.stepper(" 1 : 3, ,7 ")
+-- utilities.parsers.stepper("1:4,9:13,24:*",30)
+
+local function ranger(first,last,n,action)
+ if not first then
+ -- forget about it
+ elseif last == true then
+ for i=first,n or first do
+ action(i)
+ end
+ elseif last then
+ for i=first,last do
+ action(i)
+ end
+ else
+ action(first)
+ end
+end
+
+local cardinal = lpegpatterns.cardinal / tonumber
+local spacers = lpegpatterns.spacer^0
+local endofstring = lpegpatterns.endofstring
+
+local stepper = spacers * ( C(cardinal) * ( spacers * S(":-") * spacers * ( C(cardinal) + Cc(true) ) + Cc(false) )
+ * Carg(1) * Carg(2) / ranger * S(", ")^0 )^1
+
+local stepper = spacers * ( C(cardinal) * ( spacers * S(":-") * spacers * ( C(cardinal) + (P("*") + endofstring) * Cc(true) ) + Cc(false) )
+ * Carg(1) * Carg(2) / ranger * S(", ")^0 )^1 * endofstring -- we're sort of strict (could do without endofstring)
+
+function parsers.stepper(str,n,action)
+ if type(n) == "function" then
+ lpegmatch(stepper,str,1,false,n or print)
+ else
+ lpegmatch(stepper,str,1,n,action or print)
+ end
+end
+
+--
+
+local pattern_math = Cs((P("%")/"\\percent " + P("^") * Cc("{") * lpegpatterns.integer * Cc("}") + P(1))^0)
+local pattern_text = Cs((P("%")/"\\percent " + (P("^")/"\\high") * Cc("{") * lpegpatterns.integer * Cc("}") + P(1))^0)
+
+patterns.unittotex = pattern
+
+function parsers.unittotex(str,textmode)
+ return lpegmatch(textmode and pattern_text or pattern_math,str)
+end
+
+local pattern = Cs((P("^") / "<sup>" * lpegpatterns.integer * Cc("</sup>") + P(1))^0)
+
+function parsers.unittoxml(str)
+ return lpegmatch(pattern,str)
+end
+
+-- print(utilities.parsers.unittotex("10^-32 %"),utilities.parsers.unittoxml("10^32 %"))
+
+local cache = { }
+local spaces = lpeg.patterns.space^0
+local dummy = function() end
+
+table.setmetatableindex(cache,function(t,k)
+ local separator = P(k)
+ local value = (1-separator)^0
+ local pattern = spaces * C(value) * separator^0 * Cp()
+ t[k] = pattern
+ return pattern
+end)
+
+local commalistiterator = cache[","]
+
+function utilities.parsers.iterator(str,separator)
+ local n = #str
+ if n == 0 then
+ return dummy
+ else
+ local pattern = separator and cache[separator] or commalistiterator
+ local p = 1
+ return function()
+ if p <= n then
+ local s, e = lpegmatch(pattern,str,p)
+ if e then
+ p = e
+ return s
+ end
+ end
+ end
+ end
+end
+
+-- for s in utilities.parsers.iterator("a b c,b,c") do
+-- print(s)
+-- end
+
+local function initialize(t,name)
+ local source = t[name]
+ if source then
+ local result = { }
+ for k, v in next, t[name] do
+ result[k] = v
+ end
+ return result
+ else
+ return { }
+ end
+end
+
+local function fetch(t,name)
+ return t[name] or { }
+end
+
+function process(result,more)
+ for k, v in next, more do
+ result[k] = v
+ end
+ return result
+end
+
+local name = C((1-S(", "))^1)
+local parser = (Carg(1) * name / initialize) * (S(", ")^1 * (Carg(1) * name / fetch))^0
+local merge = Cf(parser,process)
+
+function utilities.parsers.mergehashes(hash,list)
+ return lpegmatch(merge,list,1,hash)
+end
+
+-- local t = {
+-- aa = { alpha = 1, beta = 2, gamma = 3, },
+-- bb = { alpha = 4, beta = 5, delta = 6, },
+-- cc = { epsilon = 3 },
+-- }
+--
+-- inspect(utilities.parsers.mergehashes(t,"aa, bb, cc"))
diff --git a/tex/context/base/util-ran.lua b/tex/context/base/util-ran.lua
index 7e97be2e6..50d0a7082 100644
--- a/tex/context/base/util-ran.lua
+++ b/tex/context/base/util-ran.lua
@@ -1,107 +1,107 @@
-if not modules then modules = { } end modules ['util-ran'] = {
- 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 random = math.random
-local concat = table.concat
-local sub, upper = string.sub, string.upper
-
-local randomizers = utilities.randomizers or { }
-utilities.randomizers = randomizers
-
-local l_one = "bcdfghjklmnpqrstvwxz"
-local l_two = "aeiouy"
-
-local u_one = upper(l_one)
-local u_two = upper(l_two)
-
-local n_one = #l_one
-local n_two = #l_two
-
-function randomizers.word(min,max,separator)
- local t = { }
- for i=1,random(min,max) do
- if i % 2 == 0 then
- local r = random(1,n_one)
- t[i] = sub(l_one,r,r)
- else
- local r = random(1,n_two)
- t[i] = sub(l_two,r,r)
- end
- end
- return concat(t,separator)
-end
-
-function randomizers.initials(min,max)
- if not min then
- if not max then
- min, max = 1, 3
- else
- min, max = 1, min
- end
- elseif not max then
- max = min
- end
- local t = { }
- local n = random(min or 1,max or 3)
- local m = 0
- for i=1,n do
- m = m + 1
- if i % 2 == 0 then
- local r = random(1,n_one)
- t[m] = sub(u_one,r,r)
- else
- local r = random(1,n_two)
- t[m] = sub(u_two,r,r)
- end
- m = m + 1
- t[m] = "."
- end
- return concat(t)
-end
-
-function randomizers.firstname(min,max)
- if not min then
- if not max then
- min, max = 3, 10
- else
- min, max = 1, min
- end
- elseif not max then
- max = min
- end
- local t = { }
- local n = random(min,max)
- local b = true
- if n % 2 == 0 then
- local r = random(1,n_two)
- t[1] = sub(u_two,r,r)
- b = true
- else
- local r = random(1,n_one)
- t[1] = sub(u_one,r,r)
- b = false
- end
- for i=2,n do
- if b then
- local r = random(1,n_one)
- t[i] = sub(l_one,r,r)
- b = false
- else
- local r = random(1,n_two)
- t[i] = sub(l_two,r,r)
- b = true
- end
- end
- return concat(t,separator)
-end
-
-randomizers.surname = randomizers.firstname
-
--- for i=1,10 do
--- print(randomizers.initials(1,3),randomizers.firstname(5,10),randomizers.surname(5,15))
--- end
+if not modules then modules = { } end modules ['util-ran'] = {
+ 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 random = math.random
+local concat = table.concat
+local sub, upper = string.sub, string.upper
+
+local randomizers = utilities.randomizers or { }
+utilities.randomizers = randomizers
+
+local l_one = "bcdfghjklmnpqrstvwxz"
+local l_two = "aeiouy"
+
+local u_one = upper(l_one)
+local u_two = upper(l_two)
+
+local n_one = #l_one
+local n_two = #l_two
+
+function randomizers.word(min,max,separator)
+ local t = { }
+ for i=1,random(min,max) do
+ if i % 2 == 0 then
+ local r = random(1,n_one)
+ t[i] = sub(l_one,r,r)
+ else
+ local r = random(1,n_two)
+ t[i] = sub(l_two,r,r)
+ end
+ end
+ return concat(t,separator)
+end
+
+function randomizers.initials(min,max)
+ if not min then
+ if not max then
+ min, max = 1, 3
+ else
+ min, max = 1, min
+ end
+ elseif not max then
+ max = min
+ end
+ local t = { }
+ local n = random(min or 1,max or 3)
+ local m = 0
+ for i=1,n do
+ m = m + 1
+ if i % 2 == 0 then
+ local r = random(1,n_one)
+ t[m] = sub(u_one,r,r)
+ else
+ local r = random(1,n_two)
+ t[m] = sub(u_two,r,r)
+ end
+ m = m + 1
+ t[m] = "."
+ end
+ return concat(t)
+end
+
+function randomizers.firstname(min,max)
+ if not min then
+ if not max then
+ min, max = 3, 10
+ else
+ min, max = 1, min
+ end
+ elseif not max then
+ max = min
+ end
+ local t = { }
+ local n = random(min,max)
+ local b = true
+ if n % 2 == 0 then
+ local r = random(1,n_two)
+ t[1] = sub(u_two,r,r)
+ b = true
+ else
+ local r = random(1,n_one)
+ t[1] = sub(u_one,r,r)
+ b = false
+ end
+ for i=2,n do
+ if b then
+ local r = random(1,n_one)
+ t[i] = sub(l_one,r,r)
+ b = false
+ else
+ local r = random(1,n_two)
+ t[i] = sub(l_two,r,r)
+ b = true
+ end
+ end
+ return concat(t,separator)
+end
+
+randomizers.surname = randomizers.firstname
+
+-- for i=1,10 do
+-- print(randomizers.initials(1,3),randomizers.firstname(5,10),randomizers.surname(5,15))
+-- end
diff --git a/tex/context/base/util-seq.lua b/tex/context/base/util-seq.lua
index 1b56bbdba..27f95f0ee 100644
--- a/tex/context/base/util-seq.lua
+++ b/tex/context/base/util-seq.lua
@@ -1,330 +1,330 @@
-if not modules then modules = { } end modules ['util-seq'] = {
- 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>Here we implement a mechanism for chaining the special functions
-that we use in <l n="context"> to deal with mode list processing. We
-assume that namespaces for the functions are used, but for speed we
-use locals to refer to them when compiling the chain.</p>
---ldx]]--
-
--- todo: delayed: i.e. we register them in the right order already but delay usage
-
--- todo: protect groups (as in tasks)
-
-local format, gsub, concat, gmatch = string.format, string.gsub, table.concat, string.gmatch
-local type, load = type, load
-
-utilities = utilities or { }
-local tables = utilities.tables
-local allocate = utilities.storage.allocate
-
-local sequencers = { }
-utilities.sequencers = sequencers
-
-local functions = allocate()
-sequencers.functions = functions
-
-local removevalue = tables.removevalue
-local insertaftervalue = tables.insertaftervalue
-local insertbeforevalue = tables.insertbeforevalue
-
-local function validaction(action)
- if type(action) == "string" then
- local g = _G
- for str in gmatch(action,"[^%.]+") do
- g = g[str]
- if not g then
- return false
- end
- end
- end
- return true
-end
-
-local compile
-
-local known = { } -- just a convenience, in case we want public access (only to a few methods)
-
-function sequencers.new(t) -- was reset
- local s = {
- list = { },
- order = { },
- kind = { },
- askip = { },
- gskip = { },
- dirty = true,
- runner = nil,
- }
- if t then
- s.arguments = t.arguments
- s.returnvalues = t.returnvalues
- s.results = t.results
- local name = t.name
- if name and name ~= "" then
- s.name = name
- known[name] = s
- end
- end
- table.setmetatableindex(s,function(t,k)
- -- this will automake a dirty runner
- if k == "runner" then
- local v = compile(t,t.compiler)
- return v
- end
- end)
- known[s] = s -- saves test for string later on
- return s
-end
-
-function sequencers.prependgroup(t,group,where)
- t = known[t]
- if t then
- local order = t.order
- removevalue(order,group)
- insertbeforevalue(order,where,group)
- t.list[group] = { }
- t.dirty = true
- t.runner = nil
- end
-end
-
-function sequencers.appendgroup(t,group,where)
- t = known[t]
- if t then
- local order = t.order
- removevalue(order,group)
- insertaftervalue(order,where,group)
- t.list[group] = { }
- t.dirty = true
- t.runner = nil
- end
-end
-
-function sequencers.prependaction(t,group,action,where,kind,force)
- t = known[t]
- if t then
- local g = t.list[group]
- if g and (force or validaction(action)) then
- removevalue(g,action)
- insertbeforevalue(g,where,action)
- t.kind[action] = kind
- t.dirty = true
- t.runner = nil
- end
- end
-end
-
-function sequencers.appendaction(t,group,action,where,kind,force)
- t = known[t]
- if t then
- local g = t.list[group]
- if g and (force or validaction(action)) then
- removevalue(g,action)
- insertaftervalue(g,where,action)
- t.kind[action] = kind
- t.dirty = true
- t.runner = nil
- end
- end
-end
-
-function sequencers.enableaction(t,action)
- t = known[t]
- if t then
- t.askip[action] = false
- t.dirty = true
- t.runner = nil
- end
-end
-
-function sequencers.disableaction(t,action)
- t = known[t]
- if t then
- t.askip[action] = true
- t.dirty = true
- t.runner = nil
- end
-end
-
-function sequencers.enablegroup(t,group)
- t = known[t]
- if t then
- t.gskip[action] = false
- t.dirty = true
- t.runner = nil
- end
-end
-
-function sequencers.disablegroup(t,group)
- t = known[t]
- if t then
- t.gskip[action] = true
- t.dirty = true
- t.runner = nil
- end
-end
-
-function sequencers.setkind(t,action,kind)
- t = known[t]
- if t then
- t.kind[action] = kind
- t.dirty = true
- t.runner = nil
- end
-end
-
-function sequencers.removeaction(t,group,action,force)
- t = known[t]
- local g = t and t.list[group]
- if g and (force or validaction(action)) then
- removevalue(g,action)
- t.dirty = true
- t.runner = nil
- end
-end
-
-local function localize(str)
- return (gsub(str,"[%.: ]+","_"))
-end
-
-local function construct(t)
- local list, order, kind, gskip, askip = t.list, t.order, t.kind, t.gskip, t.askip
- local arguments, returnvalues, results = t.arguments or "...", t.returnvalues, t.results
- local variables, calls, n = { }, { }, 0
- for i=1,#order do
- local group = order[i]
- if not gskip[group] then
- local actions = list[group]
- for i=1,#actions do
- local action = actions[i]
- if not askip[action] then
- if type(action) == "function" then
- local name = localize(tostring(action))
- functions[name] = action
- action = format("utilities.sequencers.functions.%s",name)
- end
- local localized = localize(action)
- n = n + 1
- variables[n] = format("local %s = %s",localized,action)
- if not returnvalues then
- calls[n] = format("%s(%s)",localized,arguments)
- elseif n == 1 then
- calls[n] = format("local %s = %s(%s)",returnvalues,localized,arguments)
- else
- calls[n] = format("%s = %s(%s)",returnvalues,localized,arguments)
- end
- end
- end
- end
- end
- t.dirty = false
- if n == 0 then
- t.compiled = ""
- else
- variables = concat(variables,"\n")
- calls = concat(calls,"\n")
- if results then
- t.compiled = format("%s\nreturn function(%s)\n%s\nreturn %s\nend",variables,arguments,calls,results)
- else
- t.compiled = format("%s\nreturn function(%s)\n%s\nend",variables,arguments,calls)
- end
- end
--- print(t.compiled)
- return t.compiled -- also stored so that we can trace
-end
-
-sequencers.tostring = construct
-sequencers.localize = localize
-
-compile = function(t,compiler,n) -- already referred to in sequencers.new
- local compiled
- if not t or type(t) == "string" then
- -- weird ... t.compiled = t .. so
- return false
- end
- if compiler then
- compiled = compiler(t,n)
- t.compiled = compiled
- else
- compiled = construct(t,n)
- end
- local runner
- if compiled == "" then
- runner = false
- else
- runner = compiled and load(compiled)() -- we can use loadstripped here
- end
- t.runner = runner
- return runner
-end
-
-sequencers.compile = compile
-
--- we used to deal with tail as well but now that the lists are always
--- double linked and the kernel function no longer expect tail as
--- argument we stick to head and done (done can probably also go
--- as luatex deals with return values efficiently now .. in the
--- past there was some copying involved, but no longer)
-
--- todo: use sequencer (can have arguments and returnvalues etc now)
-
-local template_yes = [[
-%s
-return function(head%s)
- local ok, done = false, false
-%s
- return head, done
-end]]
-
-local template_nop = [[
-return function()
- return false, false
-end]]
-
-function sequencers.nodeprocessor(t,nofarguments) -- todo: handle 'kind' in plug into tostring
- local list, order, kind, gskip, askip = t.list, t.order, t.kind, t.gskip, t.askip
- local vars, calls, args, n = { }, { }, nil, 0
- if nofarguments == 0 then
- args = ""
- elseif nofarguments == 1 then
- args = ",one"
- elseif nofarguments == 2 then
- args = ",one,two"
- elseif nofarguments == 3 then
- args = ",one,two,three"
- elseif nofarguments == 4 then
- args = ",one,two,three,four"
- elseif nofarguments == 5 then
- args = ",one,two,three,four,five"
- else
- args = ",..."
- end
- for i=1,#order do
- local group = order[i]
- if not gskip[group] then
- local actions = list[group]
- for i=1,#actions do
- local action = actions[i]
- if not askip[action] then
- local localized = localize(action)
- n = n + 1
- vars[n] = format("local %s = %s",localized,action)
- -- only difference with tostring is kind and rets (why no return)
- if kind[action] == "nohead" then
- calls[n] = format(" ok = %s(head%s) done = done or ok",localized,args)
- else
- calls[n] = format(" head, ok = %s(head%s) done = done or ok",localized,args)
- end
- end
- end
- end
- end
- local processor = #calls > 0 and format(template_yes,concat(vars,"\n"),args,concat(calls,"\n")) or template_nop
- return processor
-end
+if not modules then modules = { } end modules ['util-seq'] = {
+ 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>Here we implement a mechanism for chaining the special functions
+that we use in <l n="context"> to deal with mode list processing. We
+assume that namespaces for the functions are used, but for speed we
+use locals to refer to them when compiling the chain.</p>
+--ldx]]--
+
+-- todo: delayed: i.e. we register them in the right order already but delay usage
+
+-- todo: protect groups (as in tasks)
+
+local format, gsub, concat, gmatch = string.format, string.gsub, table.concat, string.gmatch
+local type, load = type, load
+
+utilities = utilities or { }
+local tables = utilities.tables
+local allocate = utilities.storage.allocate
+
+local sequencers = { }
+utilities.sequencers = sequencers
+
+local functions = allocate()
+sequencers.functions = functions
+
+local removevalue = tables.removevalue
+local insertaftervalue = tables.insertaftervalue
+local insertbeforevalue = tables.insertbeforevalue
+
+local function validaction(action)
+ if type(action) == "string" then
+ local g = _G
+ for str in gmatch(action,"[^%.]+") do
+ g = g[str]
+ if not g then
+ return false
+ end
+ end
+ end
+ return true
+end
+
+local compile
+
+local known = { } -- just a convenience, in case we want public access (only to a few methods)
+
+function sequencers.new(t) -- was reset
+ local s = {
+ list = { },
+ order = { },
+ kind = { },
+ askip = { },
+ gskip = { },
+ dirty = true,
+ runner = nil,
+ }
+ if t then
+ s.arguments = t.arguments
+ s.returnvalues = t.returnvalues
+ s.results = t.results
+ local name = t.name
+ if name and name ~= "" then
+ s.name = name
+ known[name] = s
+ end
+ end
+ table.setmetatableindex(s,function(t,k)
+ -- this will automake a dirty runner
+ if k == "runner" then
+ local v = compile(t,t.compiler)
+ return v
+ end
+ end)
+ known[s] = s -- saves test for string later on
+ return s
+end
+
+function sequencers.prependgroup(t,group,where)
+ t = known[t]
+ if t then
+ local order = t.order
+ removevalue(order,group)
+ insertbeforevalue(order,where,group)
+ t.list[group] = { }
+ t.dirty = true
+ t.runner = nil
+ end
+end
+
+function sequencers.appendgroup(t,group,where)
+ t = known[t]
+ if t then
+ local order = t.order
+ removevalue(order,group)
+ insertaftervalue(order,where,group)
+ t.list[group] = { }
+ t.dirty = true
+ t.runner = nil
+ end
+end
+
+function sequencers.prependaction(t,group,action,where,kind,force)
+ t = known[t]
+ if t then
+ local g = t.list[group]
+ if g and (force or validaction(action)) then
+ removevalue(g,action)
+ insertbeforevalue(g,where,action)
+ t.kind[action] = kind
+ t.dirty = true
+ t.runner = nil
+ end
+ end
+end
+
+function sequencers.appendaction(t,group,action,where,kind,force)
+ t = known[t]
+ if t then
+ local g = t.list[group]
+ if g and (force or validaction(action)) then
+ removevalue(g,action)
+ insertaftervalue(g,where,action)
+ t.kind[action] = kind
+ t.dirty = true
+ t.runner = nil
+ end
+ end
+end
+
+function sequencers.enableaction(t,action)
+ t = known[t]
+ if t then
+ t.askip[action] = false
+ t.dirty = true
+ t.runner = nil
+ end
+end
+
+function sequencers.disableaction(t,action)
+ t = known[t]
+ if t then
+ t.askip[action] = true
+ t.dirty = true
+ t.runner = nil
+ end
+end
+
+function sequencers.enablegroup(t,group)
+ t = known[t]
+ if t then
+ t.gskip[action] = false
+ t.dirty = true
+ t.runner = nil
+ end
+end
+
+function sequencers.disablegroup(t,group)
+ t = known[t]
+ if t then
+ t.gskip[action] = true
+ t.dirty = true
+ t.runner = nil
+ end
+end
+
+function sequencers.setkind(t,action,kind)
+ t = known[t]
+ if t then
+ t.kind[action] = kind
+ t.dirty = true
+ t.runner = nil
+ end
+end
+
+function sequencers.removeaction(t,group,action,force)
+ t = known[t]
+ local g = t and t.list[group]
+ if g and (force or validaction(action)) then
+ removevalue(g,action)
+ t.dirty = true
+ t.runner = nil
+ end
+end
+
+local function localize(str)
+ return (gsub(str,"[%.: ]+","_"))
+end
+
+local function construct(t)
+ local list, order, kind, gskip, askip = t.list, t.order, t.kind, t.gskip, t.askip
+ local arguments, returnvalues, results = t.arguments or "...", t.returnvalues, t.results
+ local variables, calls, n = { }, { }, 0
+ for i=1,#order do
+ local group = order[i]
+ if not gskip[group] then
+ local actions = list[group]
+ for i=1,#actions do
+ local action = actions[i]
+ if not askip[action] then
+ if type(action) == "function" then
+ local name = localize(tostring(action))
+ functions[name] = action
+ action = format("utilities.sequencers.functions.%s",name)
+ end
+ local localized = localize(action)
+ n = n + 1
+ variables[n] = format("local %s = %s",localized,action)
+ if not returnvalues then
+ calls[n] = format("%s(%s)",localized,arguments)
+ elseif n == 1 then
+ calls[n] = format("local %s = %s(%s)",returnvalues,localized,arguments)
+ else
+ calls[n] = format("%s = %s(%s)",returnvalues,localized,arguments)
+ end
+ end
+ end
+ end
+ end
+ t.dirty = false
+ if n == 0 then
+ t.compiled = ""
+ else
+ variables = concat(variables,"\n")
+ calls = concat(calls,"\n")
+ if results then
+ t.compiled = format("%s\nreturn function(%s)\n%s\nreturn %s\nend",variables,arguments,calls,results)
+ else
+ t.compiled = format("%s\nreturn function(%s)\n%s\nend",variables,arguments,calls)
+ end
+ end
+-- print(t.compiled)
+ return t.compiled -- also stored so that we can trace
+end
+
+sequencers.tostring = construct
+sequencers.localize = localize
+
+compile = function(t,compiler,n) -- already referred to in sequencers.new
+ local compiled
+ if not t or type(t) == "string" then
+ -- weird ... t.compiled = t .. so
+ return false
+ end
+ if compiler then
+ compiled = compiler(t,n)
+ t.compiled = compiled
+ else
+ compiled = construct(t,n)
+ end
+ local runner
+ if compiled == "" then
+ runner = false
+ else
+ runner = compiled and load(compiled)() -- we can use loadstripped here
+ end
+ t.runner = runner
+ return runner
+end
+
+sequencers.compile = compile
+
+-- we used to deal with tail as well but now that the lists are always
+-- double linked and the kernel function no longer expect tail as
+-- argument we stick to head and done (done can probably also go
+-- as luatex deals with return values efficiently now .. in the
+-- past there was some copying involved, but no longer)
+
+-- todo: use sequencer (can have arguments and returnvalues etc now)
+
+local template_yes = [[
+%s
+return function(head%s)
+ local ok, done = false, false
+%s
+ return head, done
+end]]
+
+local template_nop = [[
+return function()
+ return false, false
+end]]
+
+function sequencers.nodeprocessor(t,nofarguments) -- todo: handle 'kind' in plug into tostring
+ local list, order, kind, gskip, askip = t.list, t.order, t.kind, t.gskip, t.askip
+ local vars, calls, args, n = { }, { }, nil, 0
+ if nofarguments == 0 then
+ args = ""
+ elseif nofarguments == 1 then
+ args = ",one"
+ elseif nofarguments == 2 then
+ args = ",one,two"
+ elseif nofarguments == 3 then
+ args = ",one,two,three"
+ elseif nofarguments == 4 then
+ args = ",one,two,three,four"
+ elseif nofarguments == 5 then
+ args = ",one,two,three,four,five"
+ else
+ args = ",..."
+ end
+ for i=1,#order do
+ local group = order[i]
+ if not gskip[group] then
+ local actions = list[group]
+ for i=1,#actions do
+ local action = actions[i]
+ if not askip[action] then
+ local localized = localize(action)
+ n = n + 1
+ vars[n] = format("local %s = %s",localized,action)
+ -- only difference with tostring is kind and rets (why no return)
+ if kind[action] == "nohead" then
+ calls[n] = format(" ok = %s(head%s) done = done or ok",localized,args)
+ else
+ calls[n] = format(" head, ok = %s(head%s) done = done or ok",localized,args)
+ end
+ end
+ end
+ end
+ end
+ local processor = #calls > 0 and format(template_yes,concat(vars,"\n"),args,concat(calls,"\n")) or template_nop
+ return processor
+end
diff --git a/tex/context/base/util-soc.lua b/tex/context/base/util-soc.lua
index ba2f7b507..30301c510 100644
--- a/tex/context/base/util-soc.lua
+++ b/tex/context/base/util-soc.lua
@@ -1,93 +1,93 @@
-if not modules then modules = { } end modules ['util-soc'] = {
- version = 1.001,
- comment = "support for sockets / protocols",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local format = string.format
-
-local smtp = require("socket.smtp")
-local ltn12 = require("ltn12")
-local mime = require("mime")
-
-local mail = utilities.mail or { }
-utilities.mail = mail
-
-local report_mail = logs.reporter("mail")
-
-function mail.send(specification)
- local presets = specification.presets
- if presets then
- table.setmetatableindex(specification,presets)
- end
- local server = specification.server or ""
- if not server then
- report_mail("no server specified")
- return false
- end
- local to = specification.to or specification.recepient or ""
- if to == "" then
- report_mail("no recepient specified")
- return false
- end
- local from = specification.from or specification.sender or ""
- if from == "" then
- report_mail("no sender specified")
- return false
- end
- local message = { }
- local body = specification.body
- if body then
- message[#message+1] = {
- body = body
- }
- end
- local files = specification.files
- if files then
- for i=1,#files do
- local filename = files[i]
- local handle = io.open(filename, "rb")
- if handle then
- report_mail("attaching file %a",filename)
- message[#message+1] = {
- headers = {
- ["content-type"] = format('application/pdf; name="%s"',filename),
- ["content-disposition"] = format('attachment; filename="%s"',filename),
- ["content-description"] = format('file: %s',filename),
- ["content-transfer-encoding"] = "BASE64"
- },
- body = ltn12.source.chain(
- ltn12.source.file(handle),
- ltn12.filter.chain(mime.encode("base64"),mime.wrap())
- )
- }
- else
- report_mail("file %a not found",filename)
- end
- end
- end
- local result, detail = smtp.send {
- server = specification.server,
- port = specification.port,
- user = specification.user,
- password = specification.password,
- from = from,
- rcpt = to,
- source = smtp.message {
- headers = {
- to = to,
- from = from,
- cc = specification.cc,
- subject = specification.subject or "no subject",
- },
- body = message
- },
- }
- if detail then
- report_mail("error: %s",detail)
- else
- report_mail("message sent")
- end
-end
+if not modules then modules = { } end modules ['util-soc'] = {
+ version = 1.001,
+ comment = "support for sockets / protocols",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local format = string.format
+
+local smtp = require("socket.smtp")
+local ltn12 = require("ltn12")
+local mime = require("mime")
+
+local mail = utilities.mail or { }
+utilities.mail = mail
+
+local report_mail = logs.reporter("mail")
+
+function mail.send(specification)
+ local presets = specification.presets
+ if presets then
+ table.setmetatableindex(specification,presets)
+ end
+ local server = specification.server or ""
+ if not server then
+ report_mail("no server specified")
+ return false
+ end
+ local to = specification.to or specification.recepient or ""
+ if to == "" then
+ report_mail("no recepient specified")
+ return false
+ end
+ local from = specification.from or specification.sender or ""
+ if from == "" then
+ report_mail("no sender specified")
+ return false
+ end
+ local message = { }
+ local body = specification.body
+ if body then
+ message[#message+1] = {
+ body = body
+ }
+ end
+ local files = specification.files
+ if files then
+ for i=1,#files do
+ local filename = files[i]
+ local handle = io.open(filename, "rb")
+ if handle then
+ report_mail("attaching file %a",filename)
+ message[#message+1] = {
+ headers = {
+ ["content-type"] = format('application/pdf; name="%s"',filename),
+ ["content-disposition"] = format('attachment; filename="%s"',filename),
+ ["content-description"] = format('file: %s',filename),
+ ["content-transfer-encoding"] = "BASE64"
+ },
+ body = ltn12.source.chain(
+ ltn12.source.file(handle),
+ ltn12.filter.chain(mime.encode("base64"),mime.wrap())
+ )
+ }
+ else
+ report_mail("file %a not found",filename)
+ end
+ end
+ end
+ local result, detail = smtp.send {
+ server = specification.server,
+ port = specification.port,
+ user = specification.user,
+ password = specification.password,
+ from = from,
+ rcpt = to,
+ source = smtp.message {
+ headers = {
+ to = to,
+ from = from,
+ cc = specification.cc,
+ subject = specification.subject or "no subject",
+ },
+ body = message
+ },
+ }
+ if detail then
+ report_mail("error: %s",detail)
+ else
+ report_mail("message sent")
+ end
+end
diff --git a/tex/context/base/util-sql-imp-client.lua b/tex/context/base/util-sql-imp-client.lua
index 7c713a899..e09dfde94 100644
--- a/tex/context/base/util-sql-imp-client.lua
+++ b/tex/context/base/util-sql-imp-client.lua
@@ -1,256 +1,256 @@
-if not modules then modules = { } end modules ['util-sql-client'] = {
- 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"
-}
-
--- todo: make a converter
-
-local rawset, setmetatable = rawset, setmetatable
-local P, S, V, C, Cs, Ct, Cc, Cg, Cf, patterns, lpegmatch = lpeg.P, lpeg.S, lpeg.V, lpeg.C, lpeg.Cs, lpeg.Ct, lpeg.Cc, lpeg.Cg, lpeg.Cf, lpeg.patterns, lpeg.match
-
-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 report_state = logs.reporter("sql","client")
-
-local sql = utilities.sql
-local helpers = sql.helpers
-local methods = sql.methods
-local validspecification = helpers.validspecification
-local preparetemplate = helpers.preparetemplate
-local splitdata = helpers.splitdata
-local replacetemplate = utilities.templates.replace
-local serialize = sql.serialize
-local deserialize = sql.deserialize
-
--- Experiments with an p/action demonstrated that there is not much gain. We could do a runtime
--- capture but creating all the small tables is not faster and it doesn't work well anyway.
-
-local separator = P("\t")
-local newline = patterns.newline
-local empty = Cc("")
-
-local entry = C((1-separator-newline)^0) -- C 10% faster than Cs
-
-local unescaped = P("\\n") / "\n"
- + P("\\t") / "\t"
- + P("\\0") / "\000"
- + P("\\\\") / "\\"
-
-local entry = Cs((unescaped + (1-separator-newline))^0) -- C 10% faster than Cs but Cs needed due to nesting
-
-local getfirst = Ct( entry * (separator * (entry+empty))^0) + newline
-local skipfirst = (1-newline)^1 * newline
-local getfirstline = C((1-newline)^0)
-
-local cache = { }
-
-local function splitdata(data) -- todo: hash on first line ... maybe move to client module
- if data == "" then
- if trace_sql then
- report_state("no data")
- end
- return { }, { }
- end
- local first = lpegmatch(getfirstline,data)
- if not first then
- if trace_sql then
- report_state("no data")
- end
- return { }, { }
- end
- local p = cache[first]
- if p then
- -- report_state("reusing: %s",first)
- local entries = lpegmatch(p.parser,data)
- return entries or { }, p.keys
- elseif p == false then
- return { }, { }
- elseif p == nil then
- local keys = lpegmatch(getfirst,first) or { }
- if #keys == 0 then
- if trace_sql then
- report_state("no banner")
- end
- cache[first] = false
- return { }, { }
- end
- -- quite generic, could be a helper
- local n = #keys
- if n == 0 then
- report_state("no fields")
- cache[first] = false
- return { }, { }
- end
- if n == 1 then
- local key = keys[1]
- if trace_sql then
- report_state("one field with name %a",key)
- end
- p = Cg(Cc(key) * entry)
- else
- for i=1,n do
- local key = keys[i]
- if trace_sql then
- report_state("field %s has name %a",i,key)
- end
- local s = Cg(Cc(key) * entry)
- if p then
- p = p * separator * s
- else
- p = s
- end
- end
- end
- p = Cf(Ct("") * p,rawset) * newline^1
- p = skipfirst * Ct(p^0)
- cache[first] = { parser = p, keys = keys }
- local entries = lpegmatch(p,data)
- return entries or { }, keys
- end
-end
-
-local splitter = skipfirst * Ct((Ct(entry * (separator * entry)^0) * newline^1)^0)
-
-local function getdata(data)
- return lpegmatch(splitter,data)
-end
-
-helpers.splitdata = splitdata
-helpers.getdata = getdata
-
-local function dataprepared(specification)
- local query = preparetemplate(specification)
- if query then
- io.savedata(specification.queryfile,query)
- os.remove(specification.resultfile)
- if trace_queries then
- report_state("query: %s",query)
- end
- return true
- else
- -- maybe push an error
- os.remove(specification.queryfile)
- os.remove(specification.resultfile)
- end
-end
-
-local function datafetched(specification,runner)
- local command = replacetemplate(runner,specification)
- if trace_sql then
- local t = osclock()
- report_state("command: %s",command)
- local okay = os.execute(command)
- report_state("fetchtime: %.3f sec",osclock()-t) -- not okay under linux
- return okay == 0
- else
- return os.execute(command) == 0
- end
-end
-
-local function dataloaded(specification)
- if trace_sql then
- local t = osclock()
- local data = io.loaddata(specification.resultfile) or ""
- report_state("datasize: %.3f MB",#data/1024/1024)
- report_state("loadtime: %.3f sec",osclock()-t)
- return data
- else
- return io.loaddata(specification.resultfile) or ""
- end
-end
-
-local function dataconverted(data,converter)
- if converter then
- local data = getdata(data)
- if data then
- data = converter.client(data)
- end
- return data
- elseif trace_sql then
- local t = osclock()
- local data, keys = splitdata(data,target)
- report_state("converttime: %.3f",osclock()-t)
- report_state("keys: %s ",#keys)
- report_state("entries: %s ",#data)
- return data, keys
- else
- return splitdata(data)
- end
-end
-
--- todo: new, etc
-
-local function execute(specification)
- if trace_sql then
- report_state("executing client")
- end
- if not validspecification(specification) then
- report_state("error in specification")
- return
- end
- if not dataprepared(specification) then
- report_state("error in preparation")
- return
- end
- if not datafetched(specification,methods.client.runner) then
- report_state("error in fetching, query: %s",string.collapsespaces(io.loaddata(specification.queryfile)))
- return
- end
- local data = dataloaded(specification)
- if not data then
- report_state("error in loading")
- return
- end
- local data, keys = dataconverted(data,specification.converter)
- if not data then
- report_state("error in converting or no data")
- return
- end
- local one = data[1]
- if one then
- setmetatable(data,{ __index = one } )
- end
- return data, keys
-end
-
--- The following is not that (memory) efficient but normally we will use
--- the lib anyway. Of course we could make a dedicated converter and/or
--- hook into the splitter code but ... it makes not much sense because then
--- we can as well move the builder to the library modules.
---
--- Here we reuse data as the indexes are the same, unless we hash.
-
-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(data)
- local target = %s -- data or { }
- for i=1,#data do
- local cells = data[i]
- target[%s] = {
- %s
- }
- end
- return target
-end
-]]
-
-local celltemplate = "cells[%s]"
-
-methods.client = {
- runner = [[mysql --batch --user="%username%" --password="%password%" --host="%host%" --port=%port% --database="%database%" --default-character-set=utf8 < "%queryfile%" > "%resultfile%"]],
- execute = execute,
- usesfiles = true,
- wraptemplate = wraptemplate,
- celltemplate = celltemplate,
-}
+if not modules then modules = { } end modules ['util-sql-client'] = {
+ 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"
+}
+
+-- todo: make a converter
+
+local rawset, setmetatable = rawset, setmetatable
+local P, S, V, C, Cs, Ct, Cc, Cg, Cf, patterns, lpegmatch = lpeg.P, lpeg.S, lpeg.V, lpeg.C, lpeg.Cs, lpeg.Ct, lpeg.Cc, lpeg.Cg, lpeg.Cf, lpeg.patterns, lpeg.match
+
+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 report_state = logs.reporter("sql","client")
+
+local sql = utilities.sql
+local helpers = sql.helpers
+local methods = sql.methods
+local validspecification = helpers.validspecification
+local preparetemplate = helpers.preparetemplate
+local splitdata = helpers.splitdata
+local replacetemplate = utilities.templates.replace
+local serialize = sql.serialize
+local deserialize = sql.deserialize
+
+-- Experiments with an p/action demonstrated that there is not much gain. We could do a runtime
+-- capture but creating all the small tables is not faster and it doesn't work well anyway.
+
+local separator = P("\t")
+local newline = patterns.newline
+local empty = Cc("")
+
+local entry = C((1-separator-newline)^0) -- C 10% faster than Cs
+
+local unescaped = P("\\n") / "\n"
+ + P("\\t") / "\t"
+ + P("\\0") / "\000"
+ + P("\\\\") / "\\"
+
+local entry = Cs((unescaped + (1-separator-newline))^0) -- C 10% faster than Cs but Cs needed due to nesting
+
+local getfirst = Ct( entry * (separator * (entry+empty))^0) + newline
+local skipfirst = (1-newline)^1 * newline
+local getfirstline = C((1-newline)^0)
+
+local cache = { }
+
+local function splitdata(data) -- todo: hash on first line ... maybe move to client module
+ if data == "" then
+ if trace_sql then
+ report_state("no data")
+ end
+ return { }, { }
+ end
+ local first = lpegmatch(getfirstline,data)
+ if not first then
+ if trace_sql then
+ report_state("no data")
+ end
+ return { }, { }
+ end
+ local p = cache[first]
+ if p then
+ -- report_state("reusing: %s",first)
+ local entries = lpegmatch(p.parser,data)
+ return entries or { }, p.keys
+ elseif p == false then
+ return { }, { }
+ elseif p == nil then
+ local keys = lpegmatch(getfirst,first) or { }
+ if #keys == 0 then
+ if trace_sql then
+ report_state("no banner")
+ end
+ cache[first] = false
+ return { }, { }
+ end
+ -- quite generic, could be a helper
+ local n = #keys
+ if n == 0 then
+ report_state("no fields")
+ cache[first] = false
+ return { }, { }
+ end
+ if n == 1 then
+ local key = keys[1]
+ if trace_sql then
+ report_state("one field with name %a",key)
+ end
+ p = Cg(Cc(key) * entry)
+ else
+ for i=1,n do
+ local key = keys[i]
+ if trace_sql then
+ report_state("field %s has name %a",i,key)
+ end
+ local s = Cg(Cc(key) * entry)
+ if p then
+ p = p * separator * s
+ else
+ p = s
+ end
+ end
+ end
+ p = Cf(Ct("") * p,rawset) * newline^1
+ p = skipfirst * Ct(p^0)
+ cache[first] = { parser = p, keys = keys }
+ local entries = lpegmatch(p,data)
+ return entries or { }, keys
+ end
+end
+
+local splitter = skipfirst * Ct((Ct(entry * (separator * entry)^0) * newline^1)^0)
+
+local function getdata(data)
+ return lpegmatch(splitter,data)
+end
+
+helpers.splitdata = splitdata
+helpers.getdata = getdata
+
+local function dataprepared(specification)
+ local query = preparetemplate(specification)
+ if query then
+ io.savedata(specification.queryfile,query)
+ os.remove(specification.resultfile)
+ if trace_queries then
+ report_state("query: %s",query)
+ end
+ return true
+ else
+ -- maybe push an error
+ os.remove(specification.queryfile)
+ os.remove(specification.resultfile)
+ end
+end
+
+local function datafetched(specification,runner)
+ local command = replacetemplate(runner,specification)
+ if trace_sql then
+ local t = osclock()
+ report_state("command: %s",command)
+ local okay = os.execute(command)
+ report_state("fetchtime: %.3f sec",osclock()-t) -- not okay under linux
+ return okay == 0
+ else
+ return os.execute(command) == 0
+ end
+end
+
+local function dataloaded(specification)
+ if trace_sql then
+ local t = osclock()
+ local data = io.loaddata(specification.resultfile) or ""
+ report_state("datasize: %.3f MB",#data/1024/1024)
+ report_state("loadtime: %.3f sec",osclock()-t)
+ return data
+ else
+ return io.loaddata(specification.resultfile) or ""
+ end
+end
+
+local function dataconverted(data,converter)
+ if converter then
+ local data = getdata(data)
+ if data then
+ data = converter.client(data)
+ end
+ return data
+ elseif trace_sql then
+ local t = osclock()
+ local data, keys = splitdata(data,target)
+ report_state("converttime: %.3f",osclock()-t)
+ report_state("keys: %s ",#keys)
+ report_state("entries: %s ",#data)
+ return data, keys
+ else
+ return splitdata(data)
+ end
+end
+
+-- todo: new, etc
+
+local function execute(specification)
+ if trace_sql then
+ report_state("executing client")
+ end
+ if not validspecification(specification) then
+ report_state("error in specification")
+ return
+ end
+ if not dataprepared(specification) then
+ report_state("error in preparation")
+ return
+ end
+ if not datafetched(specification,methods.client.runner) then
+ report_state("error in fetching, query: %s",string.collapsespaces(io.loaddata(specification.queryfile)))
+ return
+ end
+ local data = dataloaded(specification)
+ if not data then
+ report_state("error in loading")
+ return
+ end
+ local data, keys = dataconverted(data,specification.converter)
+ if not data then
+ report_state("error in converting or no data")
+ return
+ end
+ local one = data[1]
+ if one then
+ setmetatable(data,{ __index = one } )
+ end
+ return data, keys
+end
+
+-- The following is not that (memory) efficient but normally we will use
+-- the lib anyway. Of course we could make a dedicated converter and/or
+-- hook into the splitter code but ... it makes not much sense because then
+-- we can as well move the builder to the library modules.
+--
+-- Here we reuse data as the indexes are the same, unless we hash.
+
+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(data)
+ local target = %s -- data or { }
+ for i=1,#data do
+ local cells = data[i]
+ target[%s] = {
+ %s
+ }
+ end
+ return target
+end
+]]
+
+local celltemplate = "cells[%s]"
+
+methods.client = {
+ runner = [[mysql --batch --user="%username%" --password="%password%" --host="%host%" --port=%port% --database="%database%" --default-character-set=utf8 < "%queryfile%" > "%resultfile%"]],
+ execute = execute,
+ usesfiles = true,
+ wraptemplate = wraptemplate,
+ celltemplate = celltemplate,
+}
diff --git a/tex/context/base/util-sql-imp-library.lua b/tex/context/base/util-sql-imp-library.lua
index 8a83b06d2..15754e26a 100644
--- a/tex/context/base/util-sql-imp-library.lua
+++ b/tex/context/base/util-sql-imp-library.lua
@@ -1,289 +1,289 @@
-if not modules then modules = { } end modules ['util-sql-library'] = {
- 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"
-}
-
--- local function pcall(f,...) return true, f(...) end
-
--- For some reason the sql lib partially fails in luatex when creating hashed row. So far
--- we couldn't figure it out (some issue with adapting the table that is passes as first
--- argument in the fetch routine. Apart from this it looks like the mysql binding has some
--- efficiency issues (like creating a keys and types table for each row) but that could be
--- optimized. Anyhow, fecthing results can be done as follows:
-
--- local function collect_1(r)
--- local t = { }
--- for i=1,r:numrows() do
--- t[#t+1] = r:fetch({},"a")
--- end
--- return t
--- end
---
--- local function collect_2(r)
--- local keys = r:getcolnames()
--- local n = #keys
--- local t = { }
--- for i=1,r:numrows() do
--- local v = { r:fetch() }
--- local r = { }
--- for i=1,n do
--- r[keys[i]] = v[i]
--- end
--- t[#t+1] = r
--- end
--- return t
--- end
---
--- local function collect_3(r)
--- local keys = r:getcolnames()
--- local n = #keys
--- local t = { }
--- for i=1,r:numrows() do
--- local v = r:fetch({},"n")
--- local r = { }
--- for i=1,n do
--- r[keys[i]] = v[i]
--- end
--- t[#t+1] = r
--- end
--- return t
--- end
---
--- On a large table with some 8 columns (mixed text and numbers) we get the following
--- timings (the 'a' alternative is already using the more efficient variant in the
--- binding).
---
--- collect_1 : 1.31
--- collect_2 : 1.39
--- collect_3 : 1.75
---
--- Some, as a workaround for this 'bug' the second alternative can be used.
-
-local format = string.format
-local lpegmatch = lpeg.match
-local setmetatable, type = setmetatable, type
-
-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 report_state = logs.reporter("sql","library")
-
-local sql = utilities.sql
-local mysql = require("luasql.mysql")
-local cache = { }
-local helpers = sql.helpers
-local methods = sql.methods
-local validspecification = helpers.validspecification
-local querysplitter = helpers.querysplitter
-local dataprepared = helpers.preparetemplate
-local serialize = sql.serialize
-local deserialize = sql.deserialize
-local formatters = string.formatters
-
-local initialize = mysql.mysql
-
-local function connect(session,specification)
- return session:connect(
- specification.database or "",
- specification.username or "",
- specification.password or "",
- specification.host or "",
- specification.port
- )
-end
-
-local function fetched(specification,query,converter)
- if not query or query == "" then
- report_state("no valid query")
- return false
- end
- local id = specification.id
- local session, connection
- if id then
- local c = cache[id]
- if c then
- session = c.session
- connection = c.connection
- end
- if not connection then
- session = initialize()
- if not session then
- return formatters["no session for %a"](id)
- end
- connection = connect(session,specification)
- if not connection then
- return formatters["no connection for %a"](id)
- end
- cache[id] = { session = session, connection = connection }
- end
- else
- session = initialize()
- if not session then
- return "no session"
- end
- connection = connect(session,specification)
- if not connection then
- return "no connection"
- end
- end
- if not connection then
- report_state("error in connection: %s@%s to %s:%s",
- specification.database or "no database",
- specification.username or "no username",
- specification.host or "no host",
- specification.port or "no port"
- )
- return "no connection"
- end
- query = lpegmatch(querysplitter,query)
- local result, okay
- for i=1,#query do
- local q = query[i]
- local r, m = connection:execute(q)
- if m then
- report_state("error in query to host %a: %s",specification.host,string.collapsespaces(q))
- if m then
- report_state("message: %s",m)
- end
- end
- local t = type(r)
- if t == "userdata" then
- result = r
- okay = true
- elseif t == "number" then
- okay = true
- end
- end
- if not okay then -- can go
- if session then
- session:close()
- end
- if connection then
- connection:close()
- end
- if id then
- cache[id] = nil
- end
- return "execution error"
- end
- local data, keys
- if result then
- if converter then
- data = converter.library(result)
- else
- keys = result:getcolnames()
- if keys then
- data = { }
- local n = result:numrows() or 0
- if n > 0 then
- local k = #keys
- for i=1,n do
- local v = { result:fetch() }
- local d = { }
- for i=1,k do
- d[keys[i]] = v[i]
- end
- data[#data+1] = d
- end
- end
- end
- end
- result:close()
- end
- if not id then
- if connection then
- connection:close()
- end
- if session then
- session:close()
- end
- end
- return false, data, keys
-end
-
-local function datafetched(specification,query,converter)
- local callokay, connectionerror, data, keys = pcall(fetched,specification,query,converter)
- if not callokay then
- report_state("call error, retrying")
- callokay, connectionerror, data, keys = pcall(fetched,specification,query,converter)
- elseif connectionerror then
- report_state("error: %s, retrying",connectionerror)
- callokay, connectionerror, data, keys = pcall(fetched,specification,query,converter)
- end
- if not callokay then
- report_state("persistent call error")
- elseif connectionerror then
- report_state("persistent error: %s",connectionerror)
- end
- return data or { }, keys or { }
-end
-
-local function execute(specification)
- if trace_sql then
- report_state("executing library")
- end
- if not validspecification(specification) then
- report_state("error in specification")
- return
- end
- local query = dataprepared(specification)
- if not query then
- report_state("error in preparation")
- return
- end
- local data, keys = datafetched(specification,query,specification.converter)
- if not data then
- report_state("error in fetching")
- return
- end
- local one = data[1]
- if one then
- setmetatable(data,{ __index = one } )
- end
- return data, keys
-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(result)
- if not result then
- return { }
- end
- local nofrows = result:numrows() or 0
- if nofrows == 0 then
- return { }
- end
- local target = { } -- no %s needed here
- for i=1,nofrows do
- local cells = { result:fetch() }
- target[%s] = {
- %s
- }
- end
- return target
-end
-]]
-
-local celltemplate = "cells[%s]"
-
-methods.library = {
- runner = function() end, -- never called
- execute = execute,
- initialize = initialize, -- returns session
- usesfiles = false,
- wraptemplate = wraptemplate,
- celltemplate = celltemplate,
-}
+if not modules then modules = { } end modules ['util-sql-library'] = {
+ 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"
+}
+
+-- local function pcall(f,...) return true, f(...) end
+
+-- For some reason the sql lib partially fails in luatex when creating hashed row. So far
+-- we couldn't figure it out (some issue with adapting the table that is passes as first
+-- argument in the fetch routine. Apart from this it looks like the mysql binding has some
+-- efficiency issues (like creating a keys and types table for each row) but that could be
+-- optimized. Anyhow, fecthing results can be done as follows:
+
+-- local function collect_1(r)
+-- local t = { }
+-- for i=1,r:numrows() do
+-- t[#t+1] = r:fetch({},"a")
+-- end
+-- return t
+-- end
+--
+-- local function collect_2(r)
+-- local keys = r:getcolnames()
+-- local n = #keys
+-- local t = { }
+-- for i=1,r:numrows() do
+-- local v = { r:fetch() }
+-- local r = { }
+-- for i=1,n do
+-- r[keys[i]] = v[i]
+-- end
+-- t[#t+1] = r
+-- end
+-- return t
+-- end
+--
+-- local function collect_3(r)
+-- local keys = r:getcolnames()
+-- local n = #keys
+-- local t = { }
+-- for i=1,r:numrows() do
+-- local v = r:fetch({},"n")
+-- local r = { }
+-- for i=1,n do
+-- r[keys[i]] = v[i]
+-- end
+-- t[#t+1] = r
+-- end
+-- return t
+-- end
+--
+-- On a large table with some 8 columns (mixed text and numbers) we get the following
+-- timings (the 'a' alternative is already using the more efficient variant in the
+-- binding).
+--
+-- collect_1 : 1.31
+-- collect_2 : 1.39
+-- collect_3 : 1.75
+--
+-- Some, as a workaround for this 'bug' the second alternative can be used.
+
+local format = string.format
+local lpegmatch = lpeg.match
+local setmetatable, type = setmetatable, type
+
+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 report_state = logs.reporter("sql","library")
+
+local sql = utilities.sql
+local mysql = require("luasql.mysql")
+local cache = { }
+local helpers = sql.helpers
+local methods = sql.methods
+local validspecification = helpers.validspecification
+local querysplitter = helpers.querysplitter
+local dataprepared = helpers.preparetemplate
+local serialize = sql.serialize
+local deserialize = sql.deserialize
+local formatters = string.formatters
+
+local initialize = mysql.mysql
+
+local function connect(session,specification)
+ return session:connect(
+ specification.database or "",
+ specification.username or "",
+ specification.password or "",
+ specification.host or "",
+ specification.port
+ )
+end
+
+local function fetched(specification,query,converter)
+ if not query or query == "" then
+ report_state("no valid query")
+ return false
+ end
+ local id = specification.id
+ local session, connection
+ if id then
+ local c = cache[id]
+ if c then
+ session = c.session
+ connection = c.connection
+ end
+ if not connection then
+ session = initialize()
+ if not session then
+ return formatters["no session for %a"](id)
+ end
+ connection = connect(session,specification)
+ if not connection then
+ return formatters["no connection for %a"](id)
+ end
+ cache[id] = { session = session, connection = connection }
+ end
+ else
+ session = initialize()
+ if not session then
+ return "no session"
+ end
+ connection = connect(session,specification)
+ if not connection then
+ return "no connection"
+ end
+ end
+ if not connection then
+ report_state("error in connection: %s@%s to %s:%s",
+ specification.database or "no database",
+ specification.username or "no username",
+ specification.host or "no host",
+ specification.port or "no port"
+ )
+ return "no connection"
+ end
+ query = lpegmatch(querysplitter,query)
+ local result, okay
+ for i=1,#query do
+ local q = query[i]
+ local r, m = connection:execute(q)
+ if m then
+ report_state("error in query to host %a: %s",specification.host,string.collapsespaces(q))
+ if m then
+ report_state("message: %s",m)
+ end
+ end
+ local t = type(r)
+ if t == "userdata" then
+ result = r
+ okay = true
+ elseif t == "number" then
+ okay = true
+ end
+ end
+ if not okay then -- can go
+ if session then
+ session:close()
+ end
+ if connection then
+ connection:close()
+ end
+ if id then
+ cache[id] = nil
+ end
+ return "execution error"
+ end
+ local data, keys
+ if result then
+ if converter then
+ data = converter.library(result)
+ else
+ keys = result:getcolnames()
+ if keys then
+ data = { }
+ local n = result:numrows() or 0
+ if n > 0 then
+ local k = #keys
+ for i=1,n do
+ local v = { result:fetch() }
+ local d = { }
+ for i=1,k do
+ d[keys[i]] = v[i]
+ end
+ data[#data+1] = d
+ end
+ end
+ end
+ end
+ result:close()
+ end
+ if not id then
+ if connection then
+ connection:close()
+ end
+ if session then
+ session:close()
+ end
+ end
+ return false, data, keys
+end
+
+local function datafetched(specification,query,converter)
+ local callokay, connectionerror, data, keys = pcall(fetched,specification,query,converter)
+ if not callokay then
+ report_state("call error, retrying")
+ callokay, connectionerror, data, keys = pcall(fetched,specification,query,converter)
+ elseif connectionerror then
+ report_state("error: %s, retrying",connectionerror)
+ callokay, connectionerror, data, keys = pcall(fetched,specification,query,converter)
+ end
+ if not callokay then
+ report_state("persistent call error")
+ elseif connectionerror then
+ report_state("persistent error: %s",connectionerror)
+ end
+ return data or { }, keys or { }
+end
+
+local function execute(specification)
+ if trace_sql then
+ report_state("executing library")
+ end
+ if not validspecification(specification) then
+ report_state("error in specification")
+ return
+ end
+ local query = dataprepared(specification)
+ if not query then
+ report_state("error in preparation")
+ return
+ end
+ local data, keys = datafetched(specification,query,specification.converter)
+ if not data then
+ report_state("error in fetching")
+ return
+ end
+ local one = data[1]
+ if one then
+ setmetatable(data,{ __index = one } )
+ end
+ return data, keys
+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(result)
+ if not result then
+ return { }
+ end
+ local nofrows = result:numrows() or 0
+ if nofrows == 0 then
+ return { }
+ end
+ local target = { } -- no %s needed here
+ for i=1,nofrows do
+ local cells = { result:fetch() }
+ target[%s] = {
+ %s
+ }
+ end
+ return target
+end
+]]
+
+local celltemplate = "cells[%s]"
+
+methods.library = {
+ runner = function() end, -- never called
+ execute = execute,
+ initialize = initialize, -- returns session
+ usesfiles = false,
+ wraptemplate = wraptemplate,
+ celltemplate = celltemplate,
+}
diff --git a/tex/context/base/util-sql-imp-swiglib.lua b/tex/context/base/util-sql-imp-swiglib.lua
index f456c9ccb..719620a6f 100644
--- a/tex/context/base/util-sql-imp-swiglib.lua
+++ b/tex/context/base/util-sql-imp-swiglib.lua
@@ -1,505 +1,505 @@
-if not modules then modules = { } end modules ['util-sql-swiglib'] = {
- 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"
-}
-
--- As the regular library is flawed (i.e. there are crashes in the table
--- construction code) and also not that efficient, Luigi Scarso looked into
--- a swig binding. This is a bit more low level approach but as we stay
--- closer to the original library it's also less dependant.
-
-local concat = table.concat
-local format = string.format
-local lpegmatch = lpeg.match
-local setmetatable, type = setmetatable, type
-local sleep = os.sleep
-
-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 report_state = logs.reporter("sql","swiglib")
-
-local sql = utilities.sql
-local mysql = require("swiglib.mysql.core") -- "5.6"
-
--- inspect(table.sortedkeys(mysql))
-
-local nofretries = 5
-local retrydelay = 1
-
-local cache = { }
-local helpers = sql.helpers
-local methods = sql.methods
-local validspecification = helpers.validspecification
-local querysplitter = helpers.querysplitter
-local dataprepared = helpers.preparetemplate
-local serialize = sql.serialize
-local deserialize = sql.deserialize
-
-local mysql_initialize = mysql.mysql_init
-
-local mysql_open_connection = mysql.mysql_real_connect
-local mysql_execute_query = mysql.mysql_real_query
-local mysql_close_connection = mysql.mysql_close
-
-local mysql_field_seek = mysql.mysql_field_seek
-local mysql_num_fields = mysql.mysql_num_fields
-local mysql_fetch_field = mysql.mysql_fetch_field
-local mysql_num_rows = mysql.mysql_num_rows
-local mysql_fetch_row = mysql.mysql_fetch_row
-local mysql_fetch_lengths = mysql.mysql_fetch_lengths
-local mysql_init = mysql.mysql_init
-local mysql_store_result = mysql.mysql_store_result
-local mysql_free_result = mysql.mysql_free_result
-local mysql_use_result = mysql.mysql_use_result
-
-local mysql_error_message = mysql.mysql_error
-local mysql_options_argument = mysql.mysql_options_argument
-
-local instance = mysql.MYSQL()
-
-local mysql_constant_false = false
-local mysql_constant_true = true
-
--- if mysql_options_argument then
---
--- mysql_constant_false = mysql_options_argument(false) -- 0 "\0"
--- mysql_constant_true = mysql_options_argument(true) -- 1 "\1"
---
--- -- print(swig_type(mysql_constant_false))
--- -- print(swig_type(mysql_constant_true))
---
--- mysql.mysql_options(instance,mysql.MYSQL_OPT_RECONNECT,mysql_constant_true);
---
--- else
---
--- print("")
--- print("incomplete swiglib.mysql interface")
--- print("")
---
--- end
-
-local typemap = mysql.MYSQL_TYPE_VAR_STRING and {
- [mysql.MYSQL_TYPE_VAR_STRING ] = "string",
- [mysql.MYSQL_TYPE_STRING ] = "string",
- [mysql.MYSQL_TYPE_DECIMAL ] = "number",
- [mysql.MYSQL_TYPE_SHORT ] = "number",
- [mysql.MYSQL_TYPE_LONG ] = "number",
- [mysql.MYSQL_TYPE_FLOAT ] = "number",
- [mysql.MYSQL_TYPE_DOUBLE ] = "number",
- [mysql.MYSQL_TYPE_LONGLONG ] = "number",
- [mysql.MYSQL_TYPE_INT24 ] = "number",
- [mysql.MYSQL_TYPE_YEAR ] = "number",
- [mysql.MYSQL_TYPE_TINY ] = "number",
- [mysql.MYSQL_TYPE_TINY_BLOB ] = "binary",
- [mysql.MYSQL_TYPE_MEDIUM_BLOB] = "binary",
- [mysql.MYSQL_TYPE_LONG_BLOB ] = "binary",
- [mysql.MYSQL_TYPE_BLOB ] = "binary",
- [mysql.MYSQL_TYPE_DATE ] = "date",
- [mysql.MYSQL_TYPE_NEWDATE ] = "date",
- [mysql.MYSQL_TYPE_DATETIME ] = "datetime",
- [mysql.MYSQL_TYPE_TIME ] = "time",
- [mysql.MYSQL_TYPE_TIMESTAMP ] = "time",
- [mysql.MYSQL_TYPE_ENUM ] = "set",
- [mysql.MYSQL_TYPE_SET ] = "set",
- [mysql.MYSQL_TYPE_NULL ] = "null",
-}
-
--- real_escape_string
-
-local function finish(t)
- local r = t._result_
- if r then
- mysql_free_result(r)
- end
-end
-
--- will become metatable magic
-
--- local function analyze(result)
--- mysql_field_seek(result,0)
--- local nofrows = mysql_num_rows(result) or 0
--- local noffields = mysql_num_fields(result)
--- local names = { }
--- local types = { }
--- for i=1,noffields do
--- local field = mysql_fetch_field(result)
--- names[i] = field.name
--- types[i] = field.type
--- end
--- return names, types, noffields, nofrows
--- end
-
-local function getcolnames(t)
- return t.names
-end
-
-local function getcoltypes(t)
- return t.types
-end
-
-local function numrows(t)
- return t.nofrows
-end
-
--- swig_type
-
--- local ulongArray_getitem = mysql.ulongArray_getitem
--- local util_getbytearray = mysql.util_getbytearray
-
--- local function list(t)
--- local result = t._result_
--- local row = mysql_fetch_row(result)
--- local len = mysql_fetch_lengths(result)
--- local result = { }
--- for i=1,t.noffields do
--- local r = i - 1 -- zero offset
--- result[i] = util_getbytearray(row,r,ulongArray_getitem(len,r))
--- end
--- return result
--- end
-
--- local function hash(t)
--- local list = util_mysql_fetch_fields_from_current_row(t._result_)
--- local result = t._result_
--- local fields = t.names
--- local row = mysql_fetch_row(result)
--- local len = mysql_fetch_lengths(result)
--- local result = { }
--- for i=1,t.noffields do
--- local r = i - 1 -- zero offset
--- result[fields[i]] = util_getbytearray(row,r,ulongArray_getitem(len,r))
--- end
--- return result
--- end
-
-local util_mysql_fetch_fields_from_current_row = mysql.util_mysql_fetch_fields_from_current_row
-local util_mysql_fetch_all_rows = mysql.util_mysql_fetch_all_rows
-
-local function list(t)
- return util_mysql_fetch_fields_from_current_row(t._result_)
-end
-
-local function hash(t)
- local list = util_mysql_fetch_fields_from_current_row(t._result_)
- local fields = t.names
- local data = { }
- for i=1,t.noffields do
- data[fields[i]] = list[i]
- end
- return data
-end
-
-local function wholelist(t)
- return util_mysql_fetch_all_rows(t._result_)
-end
-
-local mt = { __index = {
- -- regular
- finish = finish,
- list = list,
- hash = hash,
- wholelist = wholelist,
- -- compatibility
- numrows = numrows,
- getcolnames = getcolnames,
- getcoltypes = getcoltypes,
- -- fallback
- _result_ = nil,
- names = { },
- types = { },
- noffields = 0,
- nofrows = 0,
- }
-}
-
-local nt = setmetatable({},mt)
-
--- session
-
-local function close(t)
- mysql_close_connection(t._connection_)
-end
-
-local function execute(t,query)
- if query and query ~= "" then
- local connection = t._connection_
- local result = mysql_execute_query(connection,query,#query)
- if result == 0 then
- local result = mysql_store_result(connection)
- if result then
- mysql_field_seek(result,0)
- local nofrows = mysql_num_rows(result) or 0
- local noffields = mysql_num_fields(result)
- local names = { }
- local types = { }
- for i=1,noffields do
- local field = mysql_fetch_field(result)
- names[i] = field.name
- types[i] = field.type
- end
- local t = {
- _result_ = result,
- names = names,
- types = types,
- noffields = noffields,
- nofrows = nofrows,
- }
- return setmetatable(t,mt)
- else
- return nt
- end
- end
- end
- return false
-end
-
-local mt = { __index = {
- close = close,
- execute = execute,
- }
-}
-
-local function open(t,database,username,password,host,port)
- local connection = mysql_open_connection(t._session_,host or "localhost",username or "",password or "",database or "",port or 0,0,0)
- if connection then
- local t = {
- _connection_ = connection,
- }
- return setmetatable(t,mt)
- end
-end
-
-local function message(t)
- return mysql_error_message(t._session_)
-end
-
-local function close(t)
- -- dummy, as we have a global session
-end
-
-local mt = {
- __index = {
- connect = open,
- close = close,
- message = message,
- }
-}
-
-local function initialize()
- local session = {
- _session_ = mysql_initialize(instance) -- maybe share, single thread anyway
- }
- return setmetatable(session,mt)
-end
-
--- -- -- --
-
-local function connect(session,specification)
- return session:connect(
- specification.database or "",
- specification.username or "",
- specification.password or "",
- specification.host or "",
- specification.port
- )
-end
-
-local function error_in_connection(specification,action)
- report_state("error in connection: [%s] %s@%s to %s:%s",
- action or "unknown",
- specification.database or "no database",
- specification.username or "no username",
- specification.host or "no host",
- specification.port or "no port"
- )
-end
-
-local function datafetched(specification,query,converter)
- if not query or query == "" then
- report_state("no valid query")
- return { }, { }
- end
- local id = specification.id
- local session, connection
- if id then
- local c = cache[id]
- if c then
- session = c.session
- connection = c.connection
- end
- if not connection then
- session = initialize()
- connection = connect(session,specification)
- if not connection then
- for i=1,nofretries do
- sleep(retrydelay)
- report_state("retrying to connect: [%s.%s] %s@%s to %s:%s",
- id,i,
- specification.database or "no database",
- specification.username or "no username",
- specification.host or "no host",
- specification.port or "no port"
- )
- connection = connect(session,specification)
- if connection then
- break
- end
- end
- end
- if connection then
- cache[id] = { session = session, connection = connection }
- end
- end
- else
- session = initialize()
- connection = connect(session,specification)
- if not connection then
- for i=1,nofretries do
- sleep(retrydelay)
- report_state("retrying to connect: [%s] %s@%s to %s:%s",
- i,
- specification.database or "no database",
- specification.username or "no username",
- specification.host or "no host",
- specification.port or "no port"
- )
- connection = connect(session,specification)
- if connection then
- break
- end
- end
- end
- end
- if not connection then
- report_state("error in connection: %s@%s to %s:%s",
- specification.database or "no database",
- specification.username or "no username",
- specification.host or "no host",
- specification.port or "no port"
- )
- return { }, { }
- end
- query = lpegmatch(querysplitter,query)
- local result, message, okay
- for i=1,#query do
- local q = query[i]
- local r, m = connection:execute(q)
- if m then
- report_state("error in query, stage: %s",string.collapsespaces(q))
- message = message and format("%s\n%s",message,m) or m
- end
- if type(r) == "table" then
- result = r
- okay = true
- elseif not m then
- okay = true
- end
- end
- local data, keys
- if result then
- if converter then
- data = converter.swiglib(result)
- else
- keys = result.names
- data = { }
- for i=1,result.nofrows do
- data[i] = result:hash()
- end
- end
- result:finish() -- result:close()
- elseif message then
- report_state("message %s",message)
- end
- if not keys then
- keys = { }
- end
- if not data then
- data = { }
- end
- if not id then
- connection:close()
- session:close()
- end
- return data, keys
-end
-
-local function execute(specification)
- if trace_sql then
- report_state("executing library")
- end
- if not validspecification(specification) then
- report_state("error in specification")
- return
- end
- local query = dataprepared(specification)
- if not query then
- report_state("error in preparation")
- return
- end
- local data, keys = datafetched(specification,query,specification.converter)
- if not data then
- report_state("error in fetching")
- return
- end
- local one = data[1]
- if one then
- setmetatable(data,{ __index = one } )
- end
- return data, keys
-end
-
-local wraptemplate = [[
-local mysql = require("swigluamysql") -- will be stored in method
-
------ mysql_fetch_row = mysql.mysql_fetch_row
------ mysql_fetch_lengths = mysql.mysql_fetch_lengths
------ util_unpackbytearray = mysql.util_unpackbytearray
-local util_mysql_fetch_fields_from_current_row
- = mysql.util_mysql_fetch_fields_from_current_row
-
-local converters = utilities.sql.converters
-local deserialize = utilities.sql.deserialize
-
-local tostring = tostring
-local tonumber = tonumber
-local booleanstring = string.booleanstring
-
-%s
-
-return function(result)
- if not result then
- return { }
- end
- local nofrows = result.nofrows or 0
- if nofrows == 0 then
- return { }
- end
- local noffields = result.noffields or 0
- local target = { } -- no %s needed here
- result = result._result_
- for i=1,nofrows do
- -- local row = mysql_fetch_row(result)
- -- local len = mysql_fetch_lengths(result)
- -- local cells = util_unpackbytearray(row,noffields,len)
- local cells = util_mysql_fetch_fields_from_current_row(result)
- target[%s] = {
- %s
- }
- end
- return target
-end
-]]
-
-local celltemplate = "cells[%s]"
-
-methods.swiglib = {
- runner = function() end, -- never called
- execute = execute,
- initialize = initialize, -- returns session
- usesfiles = false,
- wraptemplate = wraptemplate,
- celltemplate = celltemplate,
-}
+if not modules then modules = { } end modules ['util-sql-swiglib'] = {
+ 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"
+}
+
+-- As the regular library is flawed (i.e. there are crashes in the table
+-- construction code) and also not that efficient, Luigi Scarso looked into
+-- a swig binding. This is a bit more low level approach but as we stay
+-- closer to the original library it's also less dependant.
+
+local concat = table.concat
+local format = string.format
+local lpegmatch = lpeg.match
+local setmetatable, type = setmetatable, type
+local sleep = os.sleep
+
+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 report_state = logs.reporter("sql","swiglib")
+
+local sql = utilities.sql
+local mysql = require("swiglib.mysql.core") -- "5.6"
+
+-- inspect(table.sortedkeys(mysql))
+
+local nofretries = 5
+local retrydelay = 1
+
+local cache = { }
+local helpers = sql.helpers
+local methods = sql.methods
+local validspecification = helpers.validspecification
+local querysplitter = helpers.querysplitter
+local dataprepared = helpers.preparetemplate
+local serialize = sql.serialize
+local deserialize = sql.deserialize
+
+local mysql_initialize = mysql.mysql_init
+
+local mysql_open_connection = mysql.mysql_real_connect
+local mysql_execute_query = mysql.mysql_real_query
+local mysql_close_connection = mysql.mysql_close
+
+local mysql_field_seek = mysql.mysql_field_seek
+local mysql_num_fields = mysql.mysql_num_fields
+local mysql_fetch_field = mysql.mysql_fetch_field
+local mysql_num_rows = mysql.mysql_num_rows
+local mysql_fetch_row = mysql.mysql_fetch_row
+local mysql_fetch_lengths = mysql.mysql_fetch_lengths
+local mysql_init = mysql.mysql_init
+local mysql_store_result = mysql.mysql_store_result
+local mysql_free_result = mysql.mysql_free_result
+local mysql_use_result = mysql.mysql_use_result
+
+local mysql_error_message = mysql.mysql_error
+local mysql_options_argument = mysql.mysql_options_argument
+
+local instance = mysql.MYSQL()
+
+local mysql_constant_false = false
+local mysql_constant_true = true
+
+-- if mysql_options_argument then
+--
+-- mysql_constant_false = mysql_options_argument(false) -- 0 "\0"
+-- mysql_constant_true = mysql_options_argument(true) -- 1 "\1"
+--
+-- -- print(swig_type(mysql_constant_false))
+-- -- print(swig_type(mysql_constant_true))
+--
+-- mysql.mysql_options(instance,mysql.MYSQL_OPT_RECONNECT,mysql_constant_true);
+--
+-- else
+--
+-- print("")
+-- print("incomplete swiglib.mysql interface")
+-- print("")
+--
+-- end
+
+local typemap = mysql.MYSQL_TYPE_VAR_STRING and {
+ [mysql.MYSQL_TYPE_VAR_STRING ] = "string",
+ [mysql.MYSQL_TYPE_STRING ] = "string",
+ [mysql.MYSQL_TYPE_DECIMAL ] = "number",
+ [mysql.MYSQL_TYPE_SHORT ] = "number",
+ [mysql.MYSQL_TYPE_LONG ] = "number",
+ [mysql.MYSQL_TYPE_FLOAT ] = "number",
+ [mysql.MYSQL_TYPE_DOUBLE ] = "number",
+ [mysql.MYSQL_TYPE_LONGLONG ] = "number",
+ [mysql.MYSQL_TYPE_INT24 ] = "number",
+ [mysql.MYSQL_TYPE_YEAR ] = "number",
+ [mysql.MYSQL_TYPE_TINY ] = "number",
+ [mysql.MYSQL_TYPE_TINY_BLOB ] = "binary",
+ [mysql.MYSQL_TYPE_MEDIUM_BLOB] = "binary",
+ [mysql.MYSQL_TYPE_LONG_BLOB ] = "binary",
+ [mysql.MYSQL_TYPE_BLOB ] = "binary",
+ [mysql.MYSQL_TYPE_DATE ] = "date",
+ [mysql.MYSQL_TYPE_NEWDATE ] = "date",
+ [mysql.MYSQL_TYPE_DATETIME ] = "datetime",
+ [mysql.MYSQL_TYPE_TIME ] = "time",
+ [mysql.MYSQL_TYPE_TIMESTAMP ] = "time",
+ [mysql.MYSQL_TYPE_ENUM ] = "set",
+ [mysql.MYSQL_TYPE_SET ] = "set",
+ [mysql.MYSQL_TYPE_NULL ] = "null",
+}
+
+-- real_escape_string
+
+local function finish(t)
+ local r = t._result_
+ if r then
+ mysql_free_result(r)
+ end
+end
+
+-- will become metatable magic
+
+-- local function analyze(result)
+-- mysql_field_seek(result,0)
+-- local nofrows = mysql_num_rows(result) or 0
+-- local noffields = mysql_num_fields(result)
+-- local names = { }
+-- local types = { }
+-- for i=1,noffields do
+-- local field = mysql_fetch_field(result)
+-- names[i] = field.name
+-- types[i] = field.type
+-- end
+-- return names, types, noffields, nofrows
+-- end
+
+local function getcolnames(t)
+ return t.names
+end
+
+local function getcoltypes(t)
+ return t.types
+end
+
+local function numrows(t)
+ return t.nofrows
+end
+
+-- swig_type
+
+-- local ulongArray_getitem = mysql.ulongArray_getitem
+-- local util_getbytearray = mysql.util_getbytearray
+
+-- local function list(t)
+-- local result = t._result_
+-- local row = mysql_fetch_row(result)
+-- local len = mysql_fetch_lengths(result)
+-- local result = { }
+-- for i=1,t.noffields do
+-- local r = i - 1 -- zero offset
+-- result[i] = util_getbytearray(row,r,ulongArray_getitem(len,r))
+-- end
+-- return result
+-- end
+
+-- local function hash(t)
+-- local list = util_mysql_fetch_fields_from_current_row(t._result_)
+-- local result = t._result_
+-- local fields = t.names
+-- local row = mysql_fetch_row(result)
+-- local len = mysql_fetch_lengths(result)
+-- local result = { }
+-- for i=1,t.noffields do
+-- local r = i - 1 -- zero offset
+-- result[fields[i]] = util_getbytearray(row,r,ulongArray_getitem(len,r))
+-- end
+-- return result
+-- end
+
+local util_mysql_fetch_fields_from_current_row = mysql.util_mysql_fetch_fields_from_current_row
+local util_mysql_fetch_all_rows = mysql.util_mysql_fetch_all_rows
+
+local function list(t)
+ return util_mysql_fetch_fields_from_current_row(t._result_)
+end
+
+local function hash(t)
+ local list = util_mysql_fetch_fields_from_current_row(t._result_)
+ local fields = t.names
+ local data = { }
+ for i=1,t.noffields do
+ data[fields[i]] = list[i]
+ end
+ return data
+end
+
+local function wholelist(t)
+ return util_mysql_fetch_all_rows(t._result_)
+end
+
+local mt = { __index = {
+ -- regular
+ finish = finish,
+ list = list,
+ hash = hash,
+ wholelist = wholelist,
+ -- compatibility
+ numrows = numrows,
+ getcolnames = getcolnames,
+ getcoltypes = getcoltypes,
+ -- fallback
+ _result_ = nil,
+ names = { },
+ types = { },
+ noffields = 0,
+ nofrows = 0,
+ }
+}
+
+local nt = setmetatable({},mt)
+
+-- session
+
+local function close(t)
+ mysql_close_connection(t._connection_)
+end
+
+local function execute(t,query)
+ if query and query ~= "" then
+ local connection = t._connection_
+ local result = mysql_execute_query(connection,query,#query)
+ if result == 0 then
+ local result = mysql_store_result(connection)
+ if result then
+ mysql_field_seek(result,0)
+ local nofrows = mysql_num_rows(result) or 0
+ local noffields = mysql_num_fields(result)
+ local names = { }
+ local types = { }
+ for i=1,noffields do
+ local field = mysql_fetch_field(result)
+ names[i] = field.name
+ types[i] = field.type
+ end
+ local t = {
+ _result_ = result,
+ names = names,
+ types = types,
+ noffields = noffields,
+ nofrows = nofrows,
+ }
+ return setmetatable(t,mt)
+ else
+ return nt
+ end
+ end
+ end
+ return false
+end
+
+local mt = { __index = {
+ close = close,
+ execute = execute,
+ }
+}
+
+local function open(t,database,username,password,host,port)
+ local connection = mysql_open_connection(t._session_,host or "localhost",username or "",password or "",database or "",port or 0,0,0)
+ if connection then
+ local t = {
+ _connection_ = connection,
+ }
+ return setmetatable(t,mt)
+ end
+end
+
+local function message(t)
+ return mysql_error_message(t._session_)
+end
+
+local function close(t)
+ -- dummy, as we have a global session
+end
+
+local mt = {
+ __index = {
+ connect = open,
+ close = close,
+ message = message,
+ }
+}
+
+local function initialize()
+ local session = {
+ _session_ = mysql_initialize(instance) -- maybe share, single thread anyway
+ }
+ return setmetatable(session,mt)
+end
+
+-- -- -- --
+
+local function connect(session,specification)
+ return session:connect(
+ specification.database or "",
+ specification.username or "",
+ specification.password or "",
+ specification.host or "",
+ specification.port
+ )
+end
+
+local function error_in_connection(specification,action)
+ report_state("error in connection: [%s] %s@%s to %s:%s",
+ action or "unknown",
+ specification.database or "no database",
+ specification.username or "no username",
+ specification.host or "no host",
+ specification.port or "no port"
+ )
+end
+
+local function datafetched(specification,query,converter)
+ if not query or query == "" then
+ report_state("no valid query")
+ return { }, { }
+ end
+ local id = specification.id
+ local session, connection
+ if id then
+ local c = cache[id]
+ if c then
+ session = c.session
+ connection = c.connection
+ end
+ if not connection then
+ session = initialize()
+ connection = connect(session,specification)
+ if not connection then
+ for i=1,nofretries do
+ sleep(retrydelay)
+ report_state("retrying to connect: [%s.%s] %s@%s to %s:%s",
+ id,i,
+ specification.database or "no database",
+ specification.username or "no username",
+ specification.host or "no host",
+ specification.port or "no port"
+ )
+ connection = connect(session,specification)
+ if connection then
+ break
+ end
+ end
+ end
+ if connection then
+ cache[id] = { session = session, connection = connection }
+ end
+ end
+ else
+ session = initialize()
+ connection = connect(session,specification)
+ if not connection then
+ for i=1,nofretries do
+ sleep(retrydelay)
+ report_state("retrying to connect: [%s] %s@%s to %s:%s",
+ i,
+ specification.database or "no database",
+ specification.username or "no username",
+ specification.host or "no host",
+ specification.port or "no port"
+ )
+ connection = connect(session,specification)
+ if connection then
+ break
+ end
+ end
+ end
+ end
+ if not connection then
+ report_state("error in connection: %s@%s to %s:%s",
+ specification.database or "no database",
+ specification.username or "no username",
+ specification.host or "no host",
+ specification.port or "no port"
+ )
+ return { }, { }
+ end
+ query = lpegmatch(querysplitter,query)
+ local result, message, okay
+ for i=1,#query do
+ local q = query[i]
+ local r, m = connection:execute(q)
+ if m then
+ report_state("error in query, stage: %s",string.collapsespaces(q))
+ message = message and format("%s\n%s",message,m) or m
+ end
+ if type(r) == "table" then
+ result = r
+ okay = true
+ elseif not m then
+ okay = true
+ end
+ end
+ local data, keys
+ if result then
+ if converter then
+ data = converter.swiglib(result)
+ else
+ keys = result.names
+ data = { }
+ for i=1,result.nofrows do
+ data[i] = result:hash()
+ end
+ end
+ result:finish() -- result:close()
+ elseif message then
+ report_state("message %s",message)
+ end
+ if not keys then
+ keys = { }
+ end
+ if not data then
+ data = { }
+ end
+ if not id then
+ connection:close()
+ session:close()
+ end
+ return data, keys
+end
+
+local function execute(specification)
+ if trace_sql then
+ report_state("executing library")
+ end
+ if not validspecification(specification) then
+ report_state("error in specification")
+ return
+ end
+ local query = dataprepared(specification)
+ if not query then
+ report_state("error in preparation")
+ return
+ end
+ local data, keys = datafetched(specification,query,specification.converter)
+ if not data then
+ report_state("error in fetching")
+ return
+ end
+ local one = data[1]
+ if one then
+ setmetatable(data,{ __index = one } )
+ end
+ return data, keys
+end
+
+local wraptemplate = [[
+local mysql = require("swigluamysql") -- will be stored in method
+
+----- mysql_fetch_row = mysql.mysql_fetch_row
+----- mysql_fetch_lengths = mysql.mysql_fetch_lengths
+----- util_unpackbytearray = mysql.util_unpackbytearray
+local util_mysql_fetch_fields_from_current_row
+ = mysql.util_mysql_fetch_fields_from_current_row
+
+local converters = utilities.sql.converters
+local deserialize = utilities.sql.deserialize
+
+local tostring = tostring
+local tonumber = tonumber
+local booleanstring = string.booleanstring
+
+%s
+
+return function(result)
+ if not result then
+ return { }
+ end
+ local nofrows = result.nofrows or 0
+ if nofrows == 0 then
+ return { }
+ end
+ local noffields = result.noffields or 0
+ local target = { } -- no %s needed here
+ result = result._result_
+ for i=1,nofrows do
+ -- local row = mysql_fetch_row(result)
+ -- local len = mysql_fetch_lengths(result)
+ -- local cells = util_unpackbytearray(row,noffields,len)
+ local cells = util_mysql_fetch_fields_from_current_row(result)
+ target[%s] = {
+ %s
+ }
+ end
+ return target
+end
+]]
+
+local celltemplate = "cells[%s]"
+
+methods.swiglib = {
+ runner = function() end, -- never called
+ execute = execute,
+ initialize = initialize, -- returns session
+ usesfiles = false,
+ wraptemplate = wraptemplate,
+ celltemplate = celltemplate,
+}
diff --git a/tex/context/base/util-sql-loggers.lua b/tex/context/base/util-sql-loggers.lua
index 33071f2e3..7fceb8032 100644
--- a/tex/context/base/util-sql-loggers.lua
+++ b/tex/context/base/util-sql-loggers.lua
@@ -1,277 +1,277 @@
-if not modules then modules = { } end modules ['util-sql-loggers'] = {
- version = 1.001,
- comment = "companion to lmx-*",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- This is experimental code and currently part of the base installation simply
--- because it's easier to dirtribute this way. Eventually it will be documented
--- and the related scripts will show up as well.
-
-local tonumber = tonumber
-local format = string.format
-local concat = table.concat
-local ostime, uuid, osfulltime = os.time, os.uuid, os.fulltime
-local random = math.random
-
-local sql = utilities.sql
-local loggers = { }
-sql.loggers = loggers
-
-local trace_sql = false trackers.register("sql.loggers.trace", function(v) trace_sql = v end)
-local report = logs.reporter("sql","loggers")
-
-loggers.newtoken = sql.tokens.new
-local makeconverter = sql.makeconverter
-
-local function checkeddb(presets,datatable)
- return sql.usedatabase(presets,datatable or presets.datatable or "loggers")
-end
-
-loggers.usedb = checkeddb
-
-local totype = {
- ["error"] = 1, [1] = 1, ["1"] = 1,
- ["warning"] = 2, [2] = 2, ["2"] = 2,
- ["debug"] = 3, [3] = 3, ["3"] = 3,
- ["info"] = 4, [4] = 4, ["4"] = 4,
-}
-
-local fromtype = {
- ["error"] = "error", [1] = "error", ["1"] = "error",
- ["warning"] = "warning", [2] = "warning", ["2"] = "warning",
- ["debug"] = "debug", [3] = "debug", ["3"] = "debug",
- ["info"] = "info", [4] = "info", ["4"] = "info",
-}
-
-table.setmetatableindex(totype, function() return 4 end)
-table.setmetatableindex(fromtype,function() return "info" end)
-
-loggers.totype = totype
-loggers.fromtype = fromtype
-
-local template =[[
- CREATE TABLE IF NOT EXISTS %basename% (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `time` int(11) NOT NULL,
- `type` int(11) NOT NULL,
- `action` varchar(15) NOT NULL,
- `data` longtext,
- PRIMARY KEY (`id`),
- UNIQUE KEY `id_unique_key` (`id`)
- )
- DEFAULT CHARSET = utf8 ;
-]]
-
-function loggers.createdb(presets,datatable)
-
- local db = checkeddb(presets,datatable)
-
- db.execute {
- template = template,
- variables = {
- basename = db.basename,
- },
- }
-
- report("datatable %a created in %a",db.name,db.base)
-
- return db
-
-end
-
-local template =[[
- DROP TABLE IF EXISTS %basename% ;
-]]
-
-function loggers.deletedb(presets,datatable)
-
- local db = checkeddb(presets,datatable)
-
- db.execute {
- template = template,
- variables = {
- basename = db.basename,
- },
- }
-
- report("datatable %a removed in %a",db.name,db.base)
-
-end
-
-local template =[[
- INSERT INTO %basename% (
- `time`,
- `type`,
- `action`,
- `data`
- ) VALUES (
- %time%,
- %type%,
- '%action%',
- '%[data]%'
- ) ;
-]]
-
-function loggers.save(db,data) -- beware, we pass type and action in the data (saves a table)
-
- if data then
-
- local time = ostime()
- local kind = totype[data.type]
- local action = data.action or "unknown"
-
- data.type = nil
- data.action = nil
-
- db.execute {
- template = template,
- variables = {
- basename = db.basename,
- time = ostime(),
- type = kind,
- action = action,
- data = data and db.serialize(data,"return") or "",
- },
- }
-
- end
-
-end
-
--- local template =[[
--- REMOVE FROM
--- %basename%
--- WHERE
--- `token` = '%token%' ;
--- ]]
---
--- function loggers.remove(db,token)
---
--- db.execute {
--- template = template,
--- variables = {
--- basename = db.basename,
--- token = token,
--- },
--- }
---
--- if trace_sql then
--- report("removed: %s",token)
--- end
---
--- end
-
-local template_nop =[[
- SELECT
- `time`,
- `type`,
- `action`,
- `data`
- FROM
- %basename%
- ORDER BY
- `time`, `type`, `action`
- DESC LIMIT
- %limit% ;
-]]
-
-local template_yes =[[
- SELECT
- `time`,
- `type`,
- `action`,
- `data`
- FROM
- %basename%
- %WHERE%
- ORDER BY
- `time`, `type`, `action`
- DESC LIMIT
- %limit% ;
-]]
-
-local converter = makeconverter {
- -- { name = "time", type = os.localtime },
- { name = "time", type = "number" },
- { name = "type", type = fromtype },
- { name = "action", type = "string" },
- { name = "data", type = "deserialize" },
-}
-
-function loggers.collect(db,specification)
-
- specification = specification or { }
-
- local start = specification.start
- local stop = specification.stop
- local limit = specification.limit or 100
- local kind = specification.type
- local action = specification.action
-
- local filtered = start or stop
-
- local where = { }
-
- if filtered then
- local today = os.date("*t")
-
- if type(start) ~= "table" then
- start = { }
- end
- start = os.time {
- day = start.day or today.day,
- month = start.month or today.month,
- year = start.year or today.year,
- hour = start.hour or 0,
- minute = start.minute or 0,
- second = start.second or 0,
- isdst = true,
- }
-
- if type(stop) ~= "table" then
- stop = { }
- end
- stop = os.time {
- day = stop.day or today.day,
- month = stop.month or today.month,
- year = stop.year or today.year,
- hour = stop.hour or 24,
- minute = stop.minute or 0,
- second = stop.second or 0,
- isdst = true,
- }
-
- -- report("filter: %s => %s",start,stop)
-
- where[#where+1] = format("`time` BETWEEN %s AND %s",start,stop)
-
- end
-
- if kind then
- where[#where+1] = format("`type` = %s",totype[kind])
- end
-
- if action then
- where[#where+1] = format("`action` = '%s'",action)
- end
-
- local records = db.execute {
- template = filtered and template_yes or template_nop,
- converter = converter,
- variables = {
- basename = db.basename,
- limit = limit,
- WHERE = #where > 0 and format("WHERE\n%s",concat(where," AND ")) or "",
- },
- }
-
- if trace_sql then
- report("collected: %s loggers",#records)
- end
-
- return records, keys
-
-end
+if not modules then modules = { } end modules ['util-sql-loggers'] = {
+ version = 1.001,
+ comment = "companion to lmx-*",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is experimental code and currently part of the base installation simply
+-- because it's easier to dirtribute this way. Eventually it will be documented
+-- and the related scripts will show up as well.
+
+local tonumber = tonumber
+local format = string.format
+local concat = table.concat
+local ostime, uuid, osfulltime = os.time, os.uuid, os.fulltime
+local random = math.random
+
+local sql = utilities.sql
+local loggers = { }
+sql.loggers = loggers
+
+local trace_sql = false trackers.register("sql.loggers.trace", function(v) trace_sql = v end)
+local report = logs.reporter("sql","loggers")
+
+loggers.newtoken = sql.tokens.new
+local makeconverter = sql.makeconverter
+
+local function checkeddb(presets,datatable)
+ return sql.usedatabase(presets,datatable or presets.datatable or "loggers")
+end
+
+loggers.usedb = checkeddb
+
+local totype = {
+ ["error"] = 1, [1] = 1, ["1"] = 1,
+ ["warning"] = 2, [2] = 2, ["2"] = 2,
+ ["debug"] = 3, [3] = 3, ["3"] = 3,
+ ["info"] = 4, [4] = 4, ["4"] = 4,
+}
+
+local fromtype = {
+ ["error"] = "error", [1] = "error", ["1"] = "error",
+ ["warning"] = "warning", [2] = "warning", ["2"] = "warning",
+ ["debug"] = "debug", [3] = "debug", ["3"] = "debug",
+ ["info"] = "info", [4] = "info", ["4"] = "info",
+}
+
+table.setmetatableindex(totype, function() return 4 end)
+table.setmetatableindex(fromtype,function() return "info" end)
+
+loggers.totype = totype
+loggers.fromtype = fromtype
+
+local template =[[
+ CREATE TABLE IF NOT EXISTS %basename% (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `time` int(11) NOT NULL,
+ `type` int(11) NOT NULL,
+ `action` varchar(15) NOT NULL,
+ `data` longtext,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `id_unique_key` (`id`)
+ )
+ DEFAULT CHARSET = utf8 ;
+]]
+
+function loggers.createdb(presets,datatable)
+
+ local db = checkeddb(presets,datatable)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ },
+ }
+
+ report("datatable %a created in %a",db.name,db.base)
+
+ return db
+
+end
+
+local template =[[
+ DROP TABLE IF EXISTS %basename% ;
+]]
+
+function loggers.deletedb(presets,datatable)
+
+ local db = checkeddb(presets,datatable)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ },
+ }
+
+ report("datatable %a removed in %a",db.name,db.base)
+
+end
+
+local template =[[
+ INSERT INTO %basename% (
+ `time`,
+ `type`,
+ `action`,
+ `data`
+ ) VALUES (
+ %time%,
+ %type%,
+ '%action%',
+ '%[data]%'
+ ) ;
+]]
+
+function loggers.save(db,data) -- beware, we pass type and action in the data (saves a table)
+
+ if data then
+
+ local time = ostime()
+ local kind = totype[data.type]
+ local action = data.action or "unknown"
+
+ data.type = nil
+ data.action = nil
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ time = ostime(),
+ type = kind,
+ action = action,
+ data = data and db.serialize(data,"return") or "",
+ },
+ }
+
+ end
+
+end
+
+-- local template =[[
+-- REMOVE FROM
+-- %basename%
+-- WHERE
+-- `token` = '%token%' ;
+-- ]]
+--
+-- function loggers.remove(db,token)
+--
+-- db.execute {
+-- template = template,
+-- variables = {
+-- basename = db.basename,
+-- token = token,
+-- },
+-- }
+--
+-- if trace_sql then
+-- report("removed: %s",token)
+-- end
+--
+-- end
+
+local template_nop =[[
+ SELECT
+ `time`,
+ `type`,
+ `action`,
+ `data`
+ FROM
+ %basename%
+ ORDER BY
+ `time`, `type`, `action`
+ DESC LIMIT
+ %limit% ;
+]]
+
+local template_yes =[[
+ SELECT
+ `time`,
+ `type`,
+ `action`,
+ `data`
+ FROM
+ %basename%
+ %WHERE%
+ ORDER BY
+ `time`, `type`, `action`
+ DESC LIMIT
+ %limit% ;
+]]
+
+local converter = makeconverter {
+ -- { name = "time", type = os.localtime },
+ { name = "time", type = "number" },
+ { name = "type", type = fromtype },
+ { name = "action", type = "string" },
+ { name = "data", type = "deserialize" },
+}
+
+function loggers.collect(db,specification)
+
+ specification = specification or { }
+
+ local start = specification.start
+ local stop = specification.stop
+ local limit = specification.limit or 100
+ local kind = specification.type
+ local action = specification.action
+
+ local filtered = start or stop
+
+ local where = { }
+
+ if filtered then
+ local today = os.date("*t")
+
+ if type(start) ~= "table" then
+ start = { }
+ end
+ start = os.time {
+ day = start.day or today.day,
+ month = start.month or today.month,
+ year = start.year or today.year,
+ hour = start.hour or 0,
+ minute = start.minute or 0,
+ second = start.second or 0,
+ isdst = true,
+ }
+
+ if type(stop) ~= "table" then
+ stop = { }
+ end
+ stop = os.time {
+ day = stop.day or today.day,
+ month = stop.month or today.month,
+ year = stop.year or today.year,
+ hour = stop.hour or 24,
+ minute = stop.minute or 0,
+ second = stop.second or 0,
+ isdst = true,
+ }
+
+ -- report("filter: %s => %s",start,stop)
+
+ where[#where+1] = format("`time` BETWEEN %s AND %s",start,stop)
+
+ end
+
+ if kind then
+ where[#where+1] = format("`type` = %s",totype[kind])
+ end
+
+ if action then
+ where[#where+1] = format("`action` = '%s'",action)
+ end
+
+ local records = db.execute {
+ template = filtered and template_yes or template_nop,
+ converter = converter,
+ variables = {
+ basename = db.basename,
+ limit = limit,
+ WHERE = #where > 0 and format("WHERE\n%s",concat(where," AND ")) or "",
+ },
+ }
+
+ if trace_sql then
+ report("collected: %s loggers",#records)
+ end
+
+ return records, keys
+
+end
diff --git a/tex/context/base/util-sql-sessions.lua b/tex/context/base/util-sql-sessions.lua
index d13293691..76bb91962 100644
--- a/tex/context/base/util-sql-sessions.lua
+++ b/tex/context/base/util-sql-sessions.lua
@@ -1,349 +1,349 @@
-if not modules then modules = { } end modules ['util-sql-sessions'] = {
- version = 1.001,
- comment = "companion to lmx-*",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- This is experimental code and currently part of the base installation simply
--- because it's easier to dirtribute this way. Eventually it will be documented
--- and the related scripts will show up as well.
-
--- maybe store threshold in session (in seconds)
-
-local tonumber = tonumber
-local format = string.format
-local ostime, uuid, osfulltime = os.time, os.uuid, os.fulltime
-local random = math.random
-
--- In older frameworks we kept a session table in memory. This time we
--- follow a route where we store session data in a sql table. Each session
--- has a token (similar to what we do on q2p and pod services), a data
--- blob which is just a serialized lua table (we could consider a dump instead)
--- and two times: the creation and last accessed time. The first one is handy
--- for statistics and the second one for cleanup. Both are just numbers so that
--- we don't have to waste code on conversions. Anyhow, we provide variants so that
--- we can always choose what is best.
-
-local sql = utilities.sql
-local sessions = { }
-sql.sessions = sessions
-
-local trace_sql = false trackers.register("sql.sessions.trace", function(v) trace_sql = v end)
-local report = logs.reporter("sql","sessions")
-
-sessions.newtoken = sql.tokens.new
-
-local function checkeddb(presets,datatable)
- return sql.usedatabase(presets,datatable or presets.datatable or "sessions")
-end
-
-sessions.usedb = checkeddb
-
-local template =[[
- CREATE TABLE IF NOT EXISTS %basename% (
- `token` varchar(50) NOT NULL,
- `data` longtext NOT NULL,
- `created` int(11) NOT NULL,
- `accessed` int(11) NOT NULL,
- UNIQUE KEY `token_unique_key` (`token`)
- )
- DEFAULT CHARSET = utf8 ;
-]]
-
-function sessions.createdb(presets,datatable)
-
- local db = checkeddb(presets,datatable)
-
- db.execute {
- template = template,
- variables = {
- basename = db.basename,
- },
- }
-
- report("datatable %a created in %a",db.name,db.base)
-
- return db
-
-end
-
-local template =[[
- DROP TABLE IF EXISTS %basename% ;
-]]
-
-function sessions.deletedb(presets,datatable)
-
- local db = checkeddb(presets,datatable)
-
- db.execute {
- template = template,
- variables = {
- basename = db.basename,
- },
- }
-
- report("datatable %a removed in %a",db.name,db.base)
-
-end
-
-local template =[[
- INSERT INTO %basename% (
- `token`,
- `created`,
- `accessed`,
- `data`
- ) VALUES (
- '%token%',
- %time%,
- %time%,
- '%[data]%'
- ) ;
-]]
-
-function sessions.create(db,data)
-
- local token = sessions.newtoken()
- local time = ostime()
-
- db.execute {
- template = template,
- variables = {
- basename = db.basename,
- token = token,
- time = time,
- data = db.serialize(data or { },"return")
- },
- }
-
- if trace_sql then
- report("created: %s at %s",token,osfulltime(time))
- end
-
- return {
- token = token,
- created = time,
- accessed = time,
- data = data,
- }
-end
-
-local template =[[
- UPDATE
- %basename%
- SET
- `data` = '%[data]%',
- `accessed` = %time%
- WHERE
- `token` = '%token%' ;
-]]
-
-function sessions.save(db,session)
-
- local time = ostime()
- local data = db.serialize(session.data or { },"return")
- local token = session.token
-
- session.accessed = time
-
- db.execute {
- template = template,
- variables = {
- basename = db.basename,
- token = token,
- time = ostime(),
- data = data,
- },
- }
-
- if trace_sql then
- report("saved: %s at %s",token,osfulltime(time))
- end
-
- return session
-end
-
-local template = [[
- UPDATE
- %basename%
- SET
- `accessed` = %time%
- WHERE
- `token` = '%token%' ;
-]]
-
-function sessions.touch(db,token)
-
- db.execute {
- template = template,
- variables = {
- basename = db.basename,
- token = token,
- time = ostime(),
- },
- }
-
-end
-
-local template = [[
- UPDATE
- %basename%
- SET
- `accessed` = %time%
- WHERE
- `token` = '%token%' ;
- SELECT
- *
- FROM
- %basename%
- WHERE
- `token` = '%token%' ;
-]]
-
-function sessions.restore(db,token)
-
- local records, keys = db.execute {
- template = template,
- variables = {
- basename = db.basename,
- token = token,
- time = ostime(),
- },
- }
-
- local record = records and records[1]
-
- if record then
- if trace_sql then
- report("restored: %s",token)
- end
- record.data = db.deserialize(record.data or "")
- return record, keys
- elseif trace_sql then
- report("unknown: %s",token)
- end
-
-end
-
-local template =[[
- DELETE FROM
- %basename%
- WHERE
- `token` = '%token%' ;
-]]
-
-function sessions.remove(db,token)
-
- db.execute {
- template = template,
- variables = {
- basename = db.basename,
- token = token,
- },
- }
-
- if trace_sql then
- report("removed: %s",token)
- end
-
-end
-
-local template_collect_yes =[[
- SELECT
- *
- FROM
- %basename%
- ORDER BY
- `created` ;
-]]
-
-local template_collect_nop =[[
- SELECT
- `accessed`,
- `created`,
- `accessed`,
- `token`
- FROM
- %basename%
- ORDER BY
- `created` ;
-]]
-
-function sessions.collect(db,nodata)
-
- local records, keys = db.execute {
- template = nodata and template_collect_nop or template_collect_yes,
- variables = {
- basename = db.basename,
- },
- }
-
- if not nodata then
- db.unpackdata(records)
- end
-
- if trace_sql then
- report("collected: %s sessions",#records)
- end
-
- return records, keys
-
-end
-
-local template_cleanup_yes =[[
- SELECT
- *
- FROM
- %basename%
- WHERE
- `accessed` < %time%
- ORDER BY
- `created` ;
- DELETE FROM
- %basename%
- WHERE
- `accessed` < %time% ;
-]]
-
-local template_cleanup_nop =[[
- SELECT
- `accessed`,
- `created`,
- `accessed`,
- `token`
- FROM
- %basename%
- WHERE
- `accessed` < %time%
- ORDER BY
- `created` ;
- DELETE FROM
- %basename%
- WHERE
- `accessed` < %time% ;
-]]
-
-function sessions.cleanupdb(db,delta,nodata)
-
- local time = ostime()
-
- local records, keys = db.execute {
- template = nodata and template_cleanup_nop or template_cleanup_yes,
- variables = {
- basename = db.basename,
- time = time - delta
- },
- }
-
- if not nodata then
- db.unpackdata(records)
- end
-
- if trace_sql then
- report("cleaned: %s seconds before %s",delta,osfulltime(time))
- end
-
- return records, keys
-
-end
+if not modules then modules = { } end modules ['util-sql-sessions'] = {
+ version = 1.001,
+ comment = "companion to lmx-*",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is experimental code and currently part of the base installation simply
+-- because it's easier to dirtribute this way. Eventually it will be documented
+-- and the related scripts will show up as well.
+
+-- maybe store threshold in session (in seconds)
+
+local tonumber = tonumber
+local format = string.format
+local ostime, uuid, osfulltime = os.time, os.uuid, os.fulltime
+local random = math.random
+
+-- In older frameworks we kept a session table in memory. This time we
+-- follow a route where we store session data in a sql table. Each session
+-- has a token (similar to what we do on q2p and pod services), a data
+-- blob which is just a serialized lua table (we could consider a dump instead)
+-- and two times: the creation and last accessed time. The first one is handy
+-- for statistics and the second one for cleanup. Both are just numbers so that
+-- we don't have to waste code on conversions. Anyhow, we provide variants so that
+-- we can always choose what is best.
+
+local sql = utilities.sql
+local sessions = { }
+sql.sessions = sessions
+
+local trace_sql = false trackers.register("sql.sessions.trace", function(v) trace_sql = v end)
+local report = logs.reporter("sql","sessions")
+
+sessions.newtoken = sql.tokens.new
+
+local function checkeddb(presets,datatable)
+ return sql.usedatabase(presets,datatable or presets.datatable or "sessions")
+end
+
+sessions.usedb = checkeddb
+
+local template =[[
+ CREATE TABLE IF NOT EXISTS %basename% (
+ `token` varchar(50) NOT NULL,
+ `data` longtext NOT NULL,
+ `created` int(11) NOT NULL,
+ `accessed` int(11) NOT NULL,
+ UNIQUE KEY `token_unique_key` (`token`)
+ )
+ DEFAULT CHARSET = utf8 ;
+]]
+
+function sessions.createdb(presets,datatable)
+
+ local db = checkeddb(presets,datatable)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ },
+ }
+
+ report("datatable %a created in %a",db.name,db.base)
+
+ return db
+
+end
+
+local template =[[
+ DROP TABLE IF EXISTS %basename% ;
+]]
+
+function sessions.deletedb(presets,datatable)
+
+ local db = checkeddb(presets,datatable)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ },
+ }
+
+ report("datatable %a removed in %a",db.name,db.base)
+
+end
+
+local template =[[
+ INSERT INTO %basename% (
+ `token`,
+ `created`,
+ `accessed`,
+ `data`
+ ) VALUES (
+ '%token%',
+ %time%,
+ %time%,
+ '%[data]%'
+ ) ;
+]]
+
+function sessions.create(db,data)
+
+ local token = sessions.newtoken()
+ local time = ostime()
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ token = token,
+ time = time,
+ data = db.serialize(data or { },"return")
+ },
+ }
+
+ if trace_sql then
+ report("created: %s at %s",token,osfulltime(time))
+ end
+
+ return {
+ token = token,
+ created = time,
+ accessed = time,
+ data = data,
+ }
+end
+
+local template =[[
+ UPDATE
+ %basename%
+ SET
+ `data` = '%[data]%',
+ `accessed` = %time%
+ WHERE
+ `token` = '%token%' ;
+]]
+
+function sessions.save(db,session)
+
+ local time = ostime()
+ local data = db.serialize(session.data or { },"return")
+ local token = session.token
+
+ session.accessed = time
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ token = token,
+ time = ostime(),
+ data = data,
+ },
+ }
+
+ if trace_sql then
+ report("saved: %s at %s",token,osfulltime(time))
+ end
+
+ return session
+end
+
+local template = [[
+ UPDATE
+ %basename%
+ SET
+ `accessed` = %time%
+ WHERE
+ `token` = '%token%' ;
+]]
+
+function sessions.touch(db,token)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ token = token,
+ time = ostime(),
+ },
+ }
+
+end
+
+local template = [[
+ UPDATE
+ %basename%
+ SET
+ `accessed` = %time%
+ WHERE
+ `token` = '%token%' ;
+ SELECT
+ *
+ FROM
+ %basename%
+ WHERE
+ `token` = '%token%' ;
+]]
+
+function sessions.restore(db,token)
+
+ local records, keys = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ token = token,
+ time = ostime(),
+ },
+ }
+
+ local record = records and records[1]
+
+ if record then
+ if trace_sql then
+ report("restored: %s",token)
+ end
+ record.data = db.deserialize(record.data or "")
+ return record, keys
+ elseif trace_sql then
+ report("unknown: %s",token)
+ end
+
+end
+
+local template =[[
+ DELETE FROM
+ %basename%
+ WHERE
+ `token` = '%token%' ;
+]]
+
+function sessions.remove(db,token)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ token = token,
+ },
+ }
+
+ if trace_sql then
+ report("removed: %s",token)
+ end
+
+end
+
+local template_collect_yes =[[
+ SELECT
+ *
+ FROM
+ %basename%
+ ORDER BY
+ `created` ;
+]]
+
+local template_collect_nop =[[
+ SELECT
+ `accessed`,
+ `created`,
+ `accessed`,
+ `token`
+ FROM
+ %basename%
+ ORDER BY
+ `created` ;
+]]
+
+function sessions.collect(db,nodata)
+
+ local records, keys = db.execute {
+ template = nodata and template_collect_nop or template_collect_yes,
+ variables = {
+ basename = db.basename,
+ },
+ }
+
+ if not nodata then
+ db.unpackdata(records)
+ end
+
+ if trace_sql then
+ report("collected: %s sessions",#records)
+ end
+
+ return records, keys
+
+end
+
+local template_cleanup_yes =[[
+ SELECT
+ *
+ FROM
+ %basename%
+ WHERE
+ `accessed` < %time%
+ ORDER BY
+ `created` ;
+ DELETE FROM
+ %basename%
+ WHERE
+ `accessed` < %time% ;
+]]
+
+local template_cleanup_nop =[[
+ SELECT
+ `accessed`,
+ `created`,
+ `accessed`,
+ `token`
+ FROM
+ %basename%
+ WHERE
+ `accessed` < %time%
+ ORDER BY
+ `created` ;
+ DELETE FROM
+ %basename%
+ WHERE
+ `accessed` < %time% ;
+]]
+
+function sessions.cleanupdb(db,delta,nodata)
+
+ local time = ostime()
+
+ local records, keys = db.execute {
+ template = nodata and template_cleanup_nop or template_cleanup_yes,
+ variables = {
+ basename = db.basename,
+ time = time - delta
+ },
+ }
+
+ if not nodata then
+ db.unpackdata(records)
+ end
+
+ if trace_sql then
+ report("cleaned: %s seconds before %s",delta,osfulltime(time))
+ end
+
+ return records, keys
+
+end
diff --git a/tex/context/base/util-sql-tickets.lua b/tex/context/base/util-sql-tickets.lua
index 65eb69bae..5e958299d 100644
--- a/tex/context/base/util-sql-tickets.lua
+++ b/tex/context/base/util-sql-tickets.lua
@@ -1,772 +1,772 @@
-if not modules then modules = { } end modules ['util-sql-tickets'] = {
- version = 1.001,
- comment = "companion to lmx-*",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- TODO: MAKE SOME INTO STORED PROCUDURES
-
--- This is experimental code and currently part of the base installation simply
--- because it's easier to distribute this way. Eventually it will be documented
--- and the related scripts will show up as well.
-
-local tonumber = tonumber
-local format = string.format
-local ostime, uuid, osfulltime = os.time, os.uuid, os.fulltime
-local random = math.random
-local concat = table.concat
-
-local sql = utilities.sql
-local tickets = { }
-sql.tickets = tickets
-
-local trace_sql = false trackers.register("sql.tickets.trace", function(v) trace_sql = v end)
-local report = logs.reporter("sql","tickets")
-
-local serialize = sql.serialize
-local deserialize = sql.deserialize
-local execute = sql.execute
-
-tickets.newtoken = sql.tokens.new
-
--- Beware as an index can be a string or a number, we will create
--- a combination of hash and index.
-
-local statustags = { [0] =
- "unknown",
- "pending",
- "busy",
- "finished",
- "dependent", -- same token but different subtoken (so we only need to find the first)
- "reserved-1",
- "reserved-2",
- "error",
- "deleted",
-}
-
-local status = table.swapped(statustags)
-tickets.status = status
-tickets.statustags = statustags
-
-local s_unknown = status.unknown
-local s_pending = status.pending
-local s_busy = status.busy
-local s_finished = status.finished
-local s_dependent = status.dependent
-local s_error = status.error
-local s_deleted = status.deleted
-
-local s_rubish = s_error -- and higher
-
-local function checkeddb(presets,datatable)
- return sql.usedatabase(presets,datatable or presets.datatable or "tickets")
-end
-
-tickets.usedb = checkeddb
-
-local template =[[
- CREATE TABLE IF NOT EXISTS %basename% (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `token` varchar(50) NOT NULL,
- `subtoken` INT(11) NOT NULL,
- `created` int(11) NOT NULL,
- `accessed` int(11) NOT NULL,
- `category` int(11) NOT NULL,
- `status` int(11) NOT NULL,
- `usertoken` varchar(50) NOT NULL,
- `data` longtext NOT NULL,
- `comment` longtext NOT NULL,
-
- PRIMARY KEY (`id`),
- UNIQUE INDEX `id_unique_index` (`id` ASC),
- KEY `token_unique_key` (`token`)
- )
- DEFAULT CHARSET = utf8 ;
-]]
-
-function tickets.createdb(presets,datatable)
- local db = checkeddb(presets,datatable)
- local data, keys = db.execute {
- template = template,
- variables = {
- basename = db.basename,
- },
- }
-
- report("datatable %a created in %a",db.name,db.base)
-
- return db
-
-end
-
-local template =[[
- DROP TABLE IF EXISTS %basename% ;
-]]
-
-function tickets.deletedb(presets,datatable)
-
- local db = checkeddb(presets,datatable)
-
- local data, keys = db.execute {
- template = template,
- variables = {
- basename = db.basename,
- },
- }
-
- report("datatable %a removed in %a",db.name,db.base)
-
-end
-
-local template_push =[[
- INSERT INTO %basename% (
- `token`,
- `subtoken`,
- `created`,
- `accessed`,
- `status`,
- `category`,
- `usertoken`,
- `data`,
- `comment`
- ) VALUES (
- '%token%',
- %subtoken%,
- %time%,
- %time%,
- %status%,
- %category%,
- '%usertoken%',
- '%[data]%',
- '%[comment]%'
- ) ;
-]]
-
-local template_fetch =[[
- SELECT
- *
- FROM
- %basename%
- WHERE
- `token` = '%token%'
- AND
- `subtoken` = '%subtoken%'
- ;
-]]
-
-function tickets.create(db,ticket)
-
- -- We assume a unique token .. if not we're toast anyway. We used to lock and
- -- get the last id etc etc but there is no real need for that.
-
- -- we could check for dependent here but we don't want the lookup
-
- local token = ticket.token or tickets.newtoken()
- local time = ostime()
- local status = ticket.status
- local category = ticket.category or 0
- local subtoken = ticket.subtoken or 0
- local usertoken = ticket.usertoken or ""
- local comment = ticket.comment or ""
-
- status = not status and subtoken > 1 and s_dependent or s_pending
-
- local result, message = db.execute {
- template = template_push,
- variables = {
- basename = db.basename,
- token = token,
- subtoken = subtoken,
- time = time,
- status = status,
- category = category,
- usertoken = usertoken,
- data = db.serialize(ticket.data or { },"return"),
- comment = comment,
- },
- }
-
- -- We could stick to only fetching the id and make the table here
- -- but we're not pushing that many tickets so we can as well follow
- -- the lazy approach and fetch the whole.
-
- local result, message = db.execute {
- template = template_fetch,
- variables = {
- basename = db.basename,
- token = token,
- subtoken = subtoken,
- },
- }
-
- if result and #result > 0 then
- if trace_sql then
- report("created: %s at %s",token,osfulltime(time))
- end
- return result[1]
- else
- report("failed: %s at %s",token,osfulltime(time))
- end
-
-end
-
-local template =[[
- UPDATE
- %basename%
- SET
- `data` = '%[data]%',
- `status` = %status%,
- `accessed` = %time%
- WHERE
- `id` = %id% ;
-]]
-
-function tickets.save(db,ticket)
-
- local time = ostime()
- local data = db.serialize(ticket.data or { },"return")
- local status = ticket.status or s_error
-
--- print("SETTING")
--- inspect(data)
-
- ticket.status = status
- ticket.accessed = time
-
- db.execute {
- template = template,
- variables = {
- basename = db.basename,
- id = ticket.id,
- time = ostime(),
- status = status,
- data = data,
- },
- }
-
- if trace_sql then
- report("saved: id %s, time %s",id,osfulltime(time))
- end
-
- return ticket
-end
-
-local template =[[
- UPDATE
- %basename%
- SET
- `accessed` = %time%
- WHERE
- `token` = '%token%' ;
-
- SELECT
- *
- FROM
- %basename%
- WHERE
- `id` = %id% ;
-]]
-
-function tickets.restore(db,id)
-
- local record, keys = db.execute {
- template = template,
- variables = {
- basename = db.basename,
- id = id,
- time = ostime(),
- },
- }
-
- local record = record and record[1]
-
- if record then
- if trace_sql then
- report("restored: id %s",id)
- end
- record.data = db.deserialize(record.data or "")
- return record
- elseif trace_sql then
- report("unknown: id %s",id)
- end
-
-end
-
-local template =[[
- DELETE FROM
- %basename%
- WHERE
- `id` = %id% ;
-]]
-
-function tickets.remove(db,id)
-
- db.execute {
- template = template,
- variables = {
- basename = db.basename,
- id = id,
- },
- }
-
- if trace_sql then
- report("removed: id %s",id)
- end
-
-end
-
-local template_yes =[[
- SELECT
- *
- FROM
- %basename%
- ORDER BY
- `id` ;
-]]
-
-local template_nop =[[
- SELECT
- `created`,
- `usertoken`,
- `accessed`,
- `status`
- FROM
- %basename%
- ORDER BY
- `id` ;
-]]
-
-function tickets.collect(db,nodata)
-
- local records, keys = db.execute {
- template = nodata and template_nop or template_yes,
- variables = {
- basename = db.basename,
- token = token,
- },
- }
-
- if not nodata then
- db.unpackdata(records)
- end
-
- if trace_sql then
- report("collected: %s tickets",#records)
- end
-
- return records, keys
-
-end
-
--- We aleays keep the last select in the execute so one can have
--- an update afterwards.
-
-local template =[[
- DELETE FROM
- %basename%
- WHERE
- `accessed` < %time% OR `status` >= %rubish% ;
-]]
-
-local template_cleanup_yes =[[
- SELECT
- *
- FROM
- %basename%
- WHERE
- `accessed` < %time%
- ORDER BY
- `id` ;
-]] .. template
-
-local template_cleanup_nop =[[
- SELECT
- `accessed`,
- `created`,
- `accessed`,
- `token`
- `usertoken`
- FROM
- %basename%
- WHERE
- `accessed` < %time%
- ORDER BY
- `id` ;
-]] .. template
-
-function tickets.cleanupdb(db,delta,nodata) -- maybe delta in db
-
- local time = delta and (ostime() - delta) or 0
-
- local records, keys = db.execute {
- template = nodata and template_cleanup_nop or template_cleanup_yes,
- variables = {
- basename = db.basename,
- time = time,
- rubish = s_rubish,
- },
- }
-
- if not nodata then
- db.unpackdata(records)
- end
-
- if trace_sql then
- report("cleaned: %s seconds before %s",delta,osfulltime(time))
- end
-
- return records, keys
-
-end
-
--- status related functions
-
-local template =[[
- SELECT
- `status`
- FROM
- %basename%
- WHERE
- `token` = '%token%'
- ORDER BY
- `id`
- ;
-]]
-
-function tickets.getstatus(db,token)
-
- local record, keys = db.execute {
- template = template,
- variables = {
- basename = db.basename,
- token = token,
- },
- }
-
- local record = record and record[1]
-
- return record and record.status or s_unknown
-
-end
-
-local template =[[
- SELECT
- `status`
- FROM
- %basename%
- WHERE
- `status` >= %rubish% OR `accessed` < %time%
- ORDER BY
- `id`
- ;
-]]
-
-function tickets.getobsolete(db,delta)
-
- local time = delta and (ostime() - delta) or 0
-
- local records = db.execute {
- template = template,
- variables = {
- basename = db.basename,
- time = time,
- rubish = s_rubish,
- },
- }
-
- db.unpackdata(records)
-
- return records
-
-end
-
-local template =[[
- SELECT
- `id`
- FROM
- %basename%
- WHERE
- `status` = %status%
- LIMIT
- 1 ;
-]]
-
-function tickets.hasstatus(db,status)
-
- local records = db.execute {
- template = template,
- variables = {
- basename = db.basename,
- status = status or s_unknown,
- },
- }
-
- return records and #records > 0 or false
-
-end
-
-local template =[[
- UPDATE
- %basename%
- SET
- `status` = %status%,
- `accessed` = %time%
- WHERE
- `id` = %id% ;
-]]
-
-function tickets.setstatus(db,id,status)
-
- db.execute {
- template = template,
- variables = {
- basename = db.basename,
- id = id,
- time = ostime(),
- status = status or s_error,
- },
- }
-
-end
-
-local template =[[
- DELETE FROM
- %basename%
- WHERE
- `status` IN (%status%) ;
-]]
-
-function tickets.prunedb(db,status)
-
- if type(status) == "table" then
- status = concat(status,",")
- end
-
- local data, keys = db.execute {
- template = template,
- variables = {
- basename = db.basename,
- status = status or s_unknown,
- },
- }
-
- if trace_sql then
- report("pruned: status %s removed",status)
- end
-
-end
-
--- START TRANSACTION ; ... COMMIT ;
--- LOCK TABLES %basename% WRITE ; ... UNLOCK TABLES ;
-
-local template_a = [[
- SET
- @last_ticket_token = '' ;
- UPDATE
- %basename%
- SET
- `token` = (@last_ticket_token := `token`),
- `status` = %newstatus%,
- `accessed` = %time%
- WHERE
- `status` = %status%
- ORDER BY
- `id`
- LIMIT
- 1
- ;
- SELECT
- *
- FROM
- %basename%
- WHERE
- `token` = @last_ticket_token
- ORDER BY
- `id`
- ;
-]]
-
-local template_b = [[
- SELECT
- *
- FROM
- tickets
- WHERE
- `status` = %status%
- ORDER BY
- `id`
- LIMIT
- 1
- ;
-]]
-
-function tickets.getfirstwithstatus(db,status,newstatus)
-
- local records
-
- if type(newstatus) == "number" then -- todo: also accept string
-
- records = db.execute {
- template = template_a,
- variables = {
- basename = db.basename,
- status = status or s_pending,
- newstatus = newstatus,
- time = ostime(),
- },
- }
-
-
- else
-
- records = db.execute {
- template = template_b,
- variables = {
- basename = db.basename,
- status = status or s_pending,
- },
- }
-
- end
-
- if type(records) == "table" and #records > 0 then
-
- for i=1,#records do
- local record = records[i]
- record.data = db.deserialize(record.data or "")
- record.status = newstatus or s_busy
- end
-
- return records
-
- end
-end
-
--- The next getter assumes that we have a sheduler running so that there is
--- one process in charge of changing the status.
-
-local template = [[
- SET
- @last_ticket_token = '' ;
- UPDATE
- %basename%
- SET
- `token` = (@last_ticket_token := `token`),
- `status` = %newstatus%,
- `accessed` = %time%
- WHERE
- `status` = %status%
- ORDER BY
- `id`
- LIMIT
- 1
- ;
- SELECT
- @last_ticket_token AS `token`
- ;
-]]
-
-function tickets.getfirstinqueue(db,status,newstatus)
-
- local records = db.execute {
- template = template,
- variables = {
- basename = db.basename,
- status = status or s_pending,
- newstatus = newstatus or s_busy,
- time = ostime(),
- },
- }
-
- local token = type(records) == "table" and #records > 0 and records[1].token
-
- return token ~= "" and token
-
-end
-
-local template =[[
- SELECT
- *
- FROM
- %basename%
- WHERE
- `token` = '%token%'
- ORDER BY
- `id` ;
-]]
-
-function tickets.getticketsbytoken(db,token)
-
- local records, keys = db.execute {
- template = template,
- variables = {
- basename = db.basename,
- token = token,
- },
- }
-
- db.unpackdata(records)
-
- return records
-
-end
-
-local template =[[
- SELECT
- *
- FROM
- %basename%
- WHERE
- `usertoken` = '%usertoken%' AND `status` < %rubish%
- ORDER BY
- `id` ;
-]]
-
-function tickets.getusertickets(db,usertoken)
-
- -- todo: update accessed
- -- todo: get less fields
- -- maybe only data for status changed (hard to check)
-
- local records, keys = db.execute {
- template = template,
- variables = {
- basename = db.basename,
- usertoken = usertoken,
- rubish = s_rubish,
- },
- }
-
- db.unpackdata(records)
-
- return records
-
-end
-
-local template =[[
- UPDATE
- %basename%
- SET
- `status` = %deleted%
- WHERE
- `usertoken` = '%usertoken%' ;
-]]
-
-function tickets.removeusertickets(db,usertoken)
-
- db.execute {
- template = template,
- variables = {
- basename = db.basename,
- usertoken = usertoken,
- deleted = s_deleted,
- },
- }
-
- if trace_sql then
- report("removed: usertoken %s",usertoken)
- end
-
-end
+if not modules then modules = { } end modules ['util-sql-tickets'] = {
+ version = 1.001,
+ comment = "companion to lmx-*",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- TODO: MAKE SOME INTO STORED PROCUDURES
+
+-- This is experimental code and currently part of the base installation simply
+-- because it's easier to distribute this way. Eventually it will be documented
+-- and the related scripts will show up as well.
+
+local tonumber = tonumber
+local format = string.format
+local ostime, uuid, osfulltime = os.time, os.uuid, os.fulltime
+local random = math.random
+local concat = table.concat
+
+local sql = utilities.sql
+local tickets = { }
+sql.tickets = tickets
+
+local trace_sql = false trackers.register("sql.tickets.trace", function(v) trace_sql = v end)
+local report = logs.reporter("sql","tickets")
+
+local serialize = sql.serialize
+local deserialize = sql.deserialize
+local execute = sql.execute
+
+tickets.newtoken = sql.tokens.new
+
+-- Beware as an index can be a string or a number, we will create
+-- a combination of hash and index.
+
+local statustags = { [0] =
+ "unknown",
+ "pending",
+ "busy",
+ "finished",
+ "dependent", -- same token but different subtoken (so we only need to find the first)
+ "reserved-1",
+ "reserved-2",
+ "error",
+ "deleted",
+}
+
+local status = table.swapped(statustags)
+tickets.status = status
+tickets.statustags = statustags
+
+local s_unknown = status.unknown
+local s_pending = status.pending
+local s_busy = status.busy
+local s_finished = status.finished
+local s_dependent = status.dependent
+local s_error = status.error
+local s_deleted = status.deleted
+
+local s_rubish = s_error -- and higher
+
+local function checkeddb(presets,datatable)
+ return sql.usedatabase(presets,datatable or presets.datatable or "tickets")
+end
+
+tickets.usedb = checkeddb
+
+local template =[[
+ CREATE TABLE IF NOT EXISTS %basename% (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `token` varchar(50) NOT NULL,
+ `subtoken` INT(11) NOT NULL,
+ `created` int(11) NOT NULL,
+ `accessed` int(11) NOT NULL,
+ `category` int(11) NOT NULL,
+ `status` int(11) NOT NULL,
+ `usertoken` varchar(50) NOT NULL,
+ `data` longtext NOT NULL,
+ `comment` longtext NOT NULL,
+
+ PRIMARY KEY (`id`),
+ UNIQUE INDEX `id_unique_index` (`id` ASC),
+ KEY `token_unique_key` (`token`)
+ )
+ DEFAULT CHARSET = utf8 ;
+]]
+
+function tickets.createdb(presets,datatable)
+ local db = checkeddb(presets,datatable)
+ local data, keys = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ },
+ }
+
+ report("datatable %a created in %a",db.name,db.base)
+
+ return db
+
+end
+
+local template =[[
+ DROP TABLE IF EXISTS %basename% ;
+]]
+
+function tickets.deletedb(presets,datatable)
+
+ local db = checkeddb(presets,datatable)
+
+ local data, keys = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ },
+ }
+
+ report("datatable %a removed in %a",db.name,db.base)
+
+end
+
+local template_push =[[
+ INSERT INTO %basename% (
+ `token`,
+ `subtoken`,
+ `created`,
+ `accessed`,
+ `status`,
+ `category`,
+ `usertoken`,
+ `data`,
+ `comment`
+ ) VALUES (
+ '%token%',
+ %subtoken%,
+ %time%,
+ %time%,
+ %status%,
+ %category%,
+ '%usertoken%',
+ '%[data]%',
+ '%[comment]%'
+ ) ;
+]]
+
+local template_fetch =[[
+ SELECT
+ *
+ FROM
+ %basename%
+ WHERE
+ `token` = '%token%'
+ AND
+ `subtoken` = '%subtoken%'
+ ;
+]]
+
+function tickets.create(db,ticket)
+
+ -- We assume a unique token .. if not we're toast anyway. We used to lock and
+ -- get the last id etc etc but there is no real need for that.
+
+ -- we could check for dependent here but we don't want the lookup
+
+ local token = ticket.token or tickets.newtoken()
+ local time = ostime()
+ local status = ticket.status
+ local category = ticket.category or 0
+ local subtoken = ticket.subtoken or 0
+ local usertoken = ticket.usertoken or ""
+ local comment = ticket.comment or ""
+
+ status = not status and subtoken > 1 and s_dependent or s_pending
+
+ local result, message = db.execute {
+ template = template_push,
+ variables = {
+ basename = db.basename,
+ token = token,
+ subtoken = subtoken,
+ time = time,
+ status = status,
+ category = category,
+ usertoken = usertoken,
+ data = db.serialize(ticket.data or { },"return"),
+ comment = comment,
+ },
+ }
+
+ -- We could stick to only fetching the id and make the table here
+ -- but we're not pushing that many tickets so we can as well follow
+ -- the lazy approach and fetch the whole.
+
+ local result, message = db.execute {
+ template = template_fetch,
+ variables = {
+ basename = db.basename,
+ token = token,
+ subtoken = subtoken,
+ },
+ }
+
+ if result and #result > 0 then
+ if trace_sql then
+ report("created: %s at %s",token,osfulltime(time))
+ end
+ return result[1]
+ else
+ report("failed: %s at %s",token,osfulltime(time))
+ end
+
+end
+
+local template =[[
+ UPDATE
+ %basename%
+ SET
+ `data` = '%[data]%',
+ `status` = %status%,
+ `accessed` = %time%
+ WHERE
+ `id` = %id% ;
+]]
+
+function tickets.save(db,ticket)
+
+ local time = ostime()
+ local data = db.serialize(ticket.data or { },"return")
+ local status = ticket.status or s_error
+
+-- print("SETTING")
+-- inspect(data)
+
+ ticket.status = status
+ ticket.accessed = time
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ id = ticket.id,
+ time = ostime(),
+ status = status,
+ data = data,
+ },
+ }
+
+ if trace_sql then
+ report("saved: id %s, time %s",id,osfulltime(time))
+ end
+
+ return ticket
+end
+
+local template =[[
+ UPDATE
+ %basename%
+ SET
+ `accessed` = %time%
+ WHERE
+ `token` = '%token%' ;
+
+ SELECT
+ *
+ FROM
+ %basename%
+ WHERE
+ `id` = %id% ;
+]]
+
+function tickets.restore(db,id)
+
+ local record, keys = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ id = id,
+ time = ostime(),
+ },
+ }
+
+ local record = record and record[1]
+
+ if record then
+ if trace_sql then
+ report("restored: id %s",id)
+ end
+ record.data = db.deserialize(record.data or "")
+ return record
+ elseif trace_sql then
+ report("unknown: id %s",id)
+ end
+
+end
+
+local template =[[
+ DELETE FROM
+ %basename%
+ WHERE
+ `id` = %id% ;
+]]
+
+function tickets.remove(db,id)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ id = id,
+ },
+ }
+
+ if trace_sql then
+ report("removed: id %s",id)
+ end
+
+end
+
+local template_yes =[[
+ SELECT
+ *
+ FROM
+ %basename%
+ ORDER BY
+ `id` ;
+]]
+
+local template_nop =[[
+ SELECT
+ `created`,
+ `usertoken`,
+ `accessed`,
+ `status`
+ FROM
+ %basename%
+ ORDER BY
+ `id` ;
+]]
+
+function tickets.collect(db,nodata)
+
+ local records, keys = db.execute {
+ template = nodata and template_nop or template_yes,
+ variables = {
+ basename = db.basename,
+ token = token,
+ },
+ }
+
+ if not nodata then
+ db.unpackdata(records)
+ end
+
+ if trace_sql then
+ report("collected: %s tickets",#records)
+ end
+
+ return records, keys
+
+end
+
+-- We aleays keep the last select in the execute so one can have
+-- an update afterwards.
+
+local template =[[
+ DELETE FROM
+ %basename%
+ WHERE
+ `accessed` < %time% OR `status` >= %rubish% ;
+]]
+
+local template_cleanup_yes =[[
+ SELECT
+ *
+ FROM
+ %basename%
+ WHERE
+ `accessed` < %time%
+ ORDER BY
+ `id` ;
+]] .. template
+
+local template_cleanup_nop =[[
+ SELECT
+ `accessed`,
+ `created`,
+ `accessed`,
+ `token`
+ `usertoken`
+ FROM
+ %basename%
+ WHERE
+ `accessed` < %time%
+ ORDER BY
+ `id` ;
+]] .. template
+
+function tickets.cleanupdb(db,delta,nodata) -- maybe delta in db
+
+ local time = delta and (ostime() - delta) or 0
+
+ local records, keys = db.execute {
+ template = nodata and template_cleanup_nop or template_cleanup_yes,
+ variables = {
+ basename = db.basename,
+ time = time,
+ rubish = s_rubish,
+ },
+ }
+
+ if not nodata then
+ db.unpackdata(records)
+ end
+
+ if trace_sql then
+ report("cleaned: %s seconds before %s",delta,osfulltime(time))
+ end
+
+ return records, keys
+
+end
+
+-- status related functions
+
+local template =[[
+ SELECT
+ `status`
+ FROM
+ %basename%
+ WHERE
+ `token` = '%token%'
+ ORDER BY
+ `id`
+ ;
+]]
+
+function tickets.getstatus(db,token)
+
+ local record, keys = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ token = token,
+ },
+ }
+
+ local record = record and record[1]
+
+ return record and record.status or s_unknown
+
+end
+
+local template =[[
+ SELECT
+ `status`
+ FROM
+ %basename%
+ WHERE
+ `status` >= %rubish% OR `accessed` < %time%
+ ORDER BY
+ `id`
+ ;
+]]
+
+function tickets.getobsolete(db,delta)
+
+ local time = delta and (ostime() - delta) or 0
+
+ local records = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ time = time,
+ rubish = s_rubish,
+ },
+ }
+
+ db.unpackdata(records)
+
+ return records
+
+end
+
+local template =[[
+ SELECT
+ `id`
+ FROM
+ %basename%
+ WHERE
+ `status` = %status%
+ LIMIT
+ 1 ;
+]]
+
+function tickets.hasstatus(db,status)
+
+ local records = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ status = status or s_unknown,
+ },
+ }
+
+ return records and #records > 0 or false
+
+end
+
+local template =[[
+ UPDATE
+ %basename%
+ SET
+ `status` = %status%,
+ `accessed` = %time%
+ WHERE
+ `id` = %id% ;
+]]
+
+function tickets.setstatus(db,id,status)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ id = id,
+ time = ostime(),
+ status = status or s_error,
+ },
+ }
+
+end
+
+local template =[[
+ DELETE FROM
+ %basename%
+ WHERE
+ `status` IN (%status%) ;
+]]
+
+function tickets.prunedb(db,status)
+
+ if type(status) == "table" then
+ status = concat(status,",")
+ end
+
+ local data, keys = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ status = status or s_unknown,
+ },
+ }
+
+ if trace_sql then
+ report("pruned: status %s removed",status)
+ end
+
+end
+
+-- START TRANSACTION ; ... COMMIT ;
+-- LOCK TABLES %basename% WRITE ; ... UNLOCK TABLES ;
+
+local template_a = [[
+ SET
+ @last_ticket_token = '' ;
+ UPDATE
+ %basename%
+ SET
+ `token` = (@last_ticket_token := `token`),
+ `status` = %newstatus%,
+ `accessed` = %time%
+ WHERE
+ `status` = %status%
+ ORDER BY
+ `id`
+ LIMIT
+ 1
+ ;
+ SELECT
+ *
+ FROM
+ %basename%
+ WHERE
+ `token` = @last_ticket_token
+ ORDER BY
+ `id`
+ ;
+]]
+
+local template_b = [[
+ SELECT
+ *
+ FROM
+ tickets
+ WHERE
+ `status` = %status%
+ ORDER BY
+ `id`
+ LIMIT
+ 1
+ ;
+]]
+
+function tickets.getfirstwithstatus(db,status,newstatus)
+
+ local records
+
+ if type(newstatus) == "number" then -- todo: also accept string
+
+ records = db.execute {
+ template = template_a,
+ variables = {
+ basename = db.basename,
+ status = status or s_pending,
+ newstatus = newstatus,
+ time = ostime(),
+ },
+ }
+
+
+ else
+
+ records = db.execute {
+ template = template_b,
+ variables = {
+ basename = db.basename,
+ status = status or s_pending,
+ },
+ }
+
+ end
+
+ if type(records) == "table" and #records > 0 then
+
+ for i=1,#records do
+ local record = records[i]
+ record.data = db.deserialize(record.data or "")
+ record.status = newstatus or s_busy
+ end
+
+ return records
+
+ end
+end
+
+-- The next getter assumes that we have a sheduler running so that there is
+-- one process in charge of changing the status.
+
+local template = [[
+ SET
+ @last_ticket_token = '' ;
+ UPDATE
+ %basename%
+ SET
+ `token` = (@last_ticket_token := `token`),
+ `status` = %newstatus%,
+ `accessed` = %time%
+ WHERE
+ `status` = %status%
+ ORDER BY
+ `id`
+ LIMIT
+ 1
+ ;
+ SELECT
+ @last_ticket_token AS `token`
+ ;
+]]
+
+function tickets.getfirstinqueue(db,status,newstatus)
+
+ local records = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ status = status or s_pending,
+ newstatus = newstatus or s_busy,
+ time = ostime(),
+ },
+ }
+
+ local token = type(records) == "table" and #records > 0 and records[1].token
+
+ return token ~= "" and token
+
+end
+
+local template =[[
+ SELECT
+ *
+ FROM
+ %basename%
+ WHERE
+ `token` = '%token%'
+ ORDER BY
+ `id` ;
+]]
+
+function tickets.getticketsbytoken(db,token)
+
+ local records, keys = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ token = token,
+ },
+ }
+
+ db.unpackdata(records)
+
+ return records
+
+end
+
+local template =[[
+ SELECT
+ *
+ FROM
+ %basename%
+ WHERE
+ `usertoken` = '%usertoken%' AND `status` < %rubish%
+ ORDER BY
+ `id` ;
+]]
+
+function tickets.getusertickets(db,usertoken)
+
+ -- todo: update accessed
+ -- todo: get less fields
+ -- maybe only data for status changed (hard to check)
+
+ local records, keys = db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ usertoken = usertoken,
+ rubish = s_rubish,
+ },
+ }
+
+ db.unpackdata(records)
+
+ return records
+
+end
+
+local template =[[
+ UPDATE
+ %basename%
+ SET
+ `status` = %deleted%
+ WHERE
+ `usertoken` = '%usertoken%' ;
+]]
+
+function tickets.removeusertickets(db,usertoken)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ usertoken = usertoken,
+ deleted = s_deleted,
+ },
+ }
+
+ if trace_sql then
+ report("removed: usertoken %s",usertoken)
+ end
+
+end
diff --git a/tex/context/base/util-sql-users.lua b/tex/context/base/util-sql-users.lua
index b99bfa58a..ea8fb4e07 100644
--- a/tex/context/base/util-sql-users.lua
+++ b/tex/context/base/util-sql-users.lua
@@ -1,410 +1,410 @@
-if not modules then modules = { } end modules ['util-sql-users'] = {
- version = 1.001,
- comment = "companion to lmx-*",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- This is experimental code and currently part of the base installation simply
--- because it's easier to dirtribute this way. Eventually it will be documented
--- and the related scripts will show up as well.
-
--- local sql = sql or (utilities and utilities.sql) or require("util-sql")
--- local md5 = md5 or require("md5")
-
-local sql = utilities.sql
-
-local format, upper, find, gsub, topattern = string.format, string.upper, string.find, string.gsub, string.topattern
-local sumhexa = md5.sumhexa
-local booleanstring = string.booleanstring
-
-local sql = utilities.sql
-local users = { }
-sql.users = users
-
-local trace_sql = false trackers.register("sql.users.trace", function(v) trace_sql = v end)
-local report = logs.reporter("sql","users")
-
-local function encryptpassword(str)
- if not str or str == "" then
- return ""
- elseif find(str,"^MD5:") then
- return str
- else
- return upper(format("MD5:%s",sumhexa(str)))
- end
-end
-
-local function cleanuppassword(str)
- return (gsub(str,"^MD5:",""))
-end
-
-local function samepasswords(one,two)
- if not one or not two then
- return false
- end
- if not find(one,"^MD5:") then
- one = encryptpassword(one)
- end
- if not find(two,"^MD5:") then
- two = encryptpassword(two)
- end
- return one == two
-end
-
-local function validaddress(address,addresses)
- if address and addresses and address ~= "" and addresses ~= "" then
- if find(address,topattern(addresses,true,true)) then
- return true, "valid remote address"
- end
- return false, "invalid remote address"
- else
- return true, "no remote address check"
- end
-end
-
-
-users.encryptpassword = encryptpassword
-users.cleanuppassword = cleanuppassword
-users.samepasswords = samepasswords
-users.validaddress = validaddress
-
--- print(users.encryptpassword("test")) -- MD5:098F6BCD4621D373CADE4E832627B4F6
-
-local function checkeddb(presets,datatable)
- return sql.usedatabase(presets,datatable or presets.datatable or "users")
-end
-
-users.usedb = checkeddb
-
-local groupnames = { }
-local groupnumbers = { }
-
-local function registergroup(name)
- local n = #groupnames + 1
- groupnames [n] = name
- groupnames [tostring(n)] = name
- groupnames [name] = name
- groupnumbers[n] = n
- groupnumbers[tostring(n)] = n
- groupnumbers[name] = n
- return n
-end
-
-registergroup("superuser")
-registergroup("administrator")
-registergroup("user")
-registergroup("guest")
-
-users.groupnames = groupnames
-users.groupnumbers = groupnumbers
-
--- password 'test':
---
--- INSERT insert into users (`name`,`password`,`group`,`enabled`) values ('...','MD5:098F6BCD4621D373CADE4E832627B4F6',1,1) ;
-
-local template =[[
- CREATE TABLE `users` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `name` varchar(80) NOT NULL,
- `fullname` varchar(80) NOT NULL,
- `password` varchar(50) DEFAULT NULL,
- `group` int(11) NOT NULL,
- `enabled` int(11) DEFAULT '1',
- `email` varchar(80) DEFAULT NULL,
- `address` varchar(256) DEFAULT NULL,
- `theme` varchar(50) DEFAULT NULL,
- `data` longtext,
- PRIMARY KEY (`id`),
- UNIQUE KEY `name_unique` (`name`)
- ) DEFAULT CHARSET = utf8 ;
-]]
-
-local converter, fields = sql.makeconverter {
- { name = "id", type = "number" },
- { name = "name", type = "string" },
- { name = "fullname", type = "string" },
- { name = "password", type = "string" },
- { name = "group", type = groupnames },
- { name = "enabled", type = "boolean" },
- { name = "email", type = "string" },
- { name = "address", type = "string" },
- { name = "theme", type = "string" },
- { name = "data", type = "deserialize" },
-}
-
-function users.createdb(presets,datatable)
-
- local db = checkeddb(presets,datatable)
-
- db.execute {
- template = template,
- variables = {
- basename = db.basename,
- },
- }
-
- report("datatable %a created in %a",db.name,db.base)
-
- return db
-
-end
-
-local template =[[
- SELECT
- %fields%
- FROM
- %basename%
- WHERE
- `name` = '%[name]%'
- AND
- `password` = '%[password]%'
- ;
-]]
-
-local template =[[
- SELECT
- %fields%
- FROM
- %basename%
- WHERE
- `name` = '%[name]%'
- ;
-]]
-
-function users.valid(db,username,password,address)
-
- local data = db.execute {
- template = template,
- converter = converter,
- variables = {
- basename = db.basename,
- fields = fields,
- name = username,
- },
- }
-
- local data = data and data[1]
-
- if not data then
- return false, "unknown user"
- elseif not data.enabled then
- return false, "disabled user"
- elseif data.password ~= encryptpassword(password) then
- return false, "wrong password"
- elseif not validaddress(address,data.address) then
- return false, "invalid address"
- else
- data.password = nil
- return data, "okay"
- end
-
-end
-
-local template =[[
- INSERT INTO %basename% (
- `name`,
- `fullname`,
- `password`,
- `group`,
- `enabled`,
- `email`,
- `address`,
- `theme`,
- `data`
- ) VALUES (
- '%[name]%',
- '%[fullname]%',
- '%[password]%',
- '%[group]%',
- '%[enabled]%',
- '%[email]%',
- '%[address]%',
- '%[theme]%',
- '%[data]%'
- ) ;
-]]
-
-function users.add(db,specification)
-
- local name = specification.username or specification.name
-
- if not name or name == "" then
- return
- end
-
- local data = specification.data
-
- db.execute {
- template = template,
- variables = {
- basename = db.basename,
- name = name,
- fullname = name or fullname,
- password = encryptpassword(specification.password or ""),
- group = groupnumbers[specification.group] or groupnumbers.guest,
- enabled = booleanstring(specification.enabled) and "1" or "0",
- email = specification.email,
- address = specification.address,
- theme = specification.theme,
- data = type(data) == "table" and db.serialize(data,"return") or "",
- },
- }
-
-end
-
-local template =[[
- SELECT
- %fields%
- FROM
- %basename%
- WHERE
- `name` = '%[name]%' ;
-]]
-
-function users.getbyname(db,name)
-
- local data = db.execute {
- template = template,
- converter = converter,
- variables = {
- basename = db.basename,
- fields = fields,
- name = name,
- },
- }
-
- return data and data[1] or nil
-
-end
-
-local template =[[
- SELECT
- %fields%
- FROM
- %basename%
- WHERE
- `id` = '%id%' ;
-]]
-
-local function getbyid(db,id)
-
- local data = db.execute {
- template = template,
- converter = converter,
- variables = {
- basename = db.basename,
- fields = fields,
- id = id,
- },
- }
-
- return data and data[1] or nil
-
-end
-
-users.getbyid = getbyid
-
-local template =[[
- UPDATE
- %basename%
- SET
- `fullname` = '%[fullname]%',
- `password` = '%[password]%',
- `group` = '%[group]%',
- `enabled` = '%[enabled]%',
- `email` = '%[email]%',
- `address` = '%[address]%',
- `theme` = '%[theme]%',
- `data` = '%[data]%'
- WHERE
- `id` = '%id%'
- ;
-]]
-
-function users.save(db,id,specification)
-
- id = tonumber(id)
-
- if not id then
- return
- end
-
- local user = getbyid(db,id)
-
- if tonumber(user.id) ~= id then
- return
- end
-
- local fullname = specification.fullname == nil and user.fulname or specification.fullname
- local password = specification.password == nil and user.password or specification.password
- local group = specification.group == nil and user.group or specification.group
- local enabled = specification.enabled == nil and user.enabled or specification.enabled
- local email = specification.email == nil and user.email or specification.email
- local address = specification.address == nil and user.address or specification.address
- local theme = specification.theme == nil and user.theme or specification.theme
- local data = specification.data == nil and user.data or specification.data
-
- db.execute {
- template = template,
- variables = {
- basename = db.basename,
- id = id,
- fullname = fullname,
- password = encryptpassword(password),
- group = groupnumbers[group],
- enabled = booleanstring(enabled) and "1" or "0",
- email = email,
- address = address,
- theme = theme,
- data = type(data) == "table" and db.serialize(data,"return") or "",
- },
- }
-
- return getbyid(db,id)
-
-end
-
-local template =[[
- DELETE FROM
- %basename%
- WHERE
- `id` = '%id%' ;
-]]
-
-function users.remove(db,id)
-
- db.execute {
- template = template,
- variables = {
- basename = db.basename,
- id = id,
- },
- }
-
-end
-
-local template =[[
- SELECT
- %fields%
- FROM
- %basename%
- ORDER BY
- `name` ;
-]]
-
-function users.collect(db) -- maybe also an id/name only variant
-
- local records, keys = db.execute {
- template = template,
- converter = converter,
- variables = {
- basename = db.basename,
- fields = fields,
- },
- }
-
- return records, keys
-
-end
+if not modules then modules = { } end modules ['util-sql-users'] = {
+ version = 1.001,
+ comment = "companion to lmx-*",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This is experimental code and currently part of the base installation simply
+-- because it's easier to dirtribute this way. Eventually it will be documented
+-- and the related scripts will show up as well.
+
+-- local sql = sql or (utilities and utilities.sql) or require("util-sql")
+-- local md5 = md5 or require("md5")
+
+local sql = utilities.sql
+
+local format, upper, find, gsub, topattern = string.format, string.upper, string.find, string.gsub, string.topattern
+local sumhexa = md5.sumhexa
+local booleanstring = string.booleanstring
+
+local sql = utilities.sql
+local users = { }
+sql.users = users
+
+local trace_sql = false trackers.register("sql.users.trace", function(v) trace_sql = v end)
+local report = logs.reporter("sql","users")
+
+local function encryptpassword(str)
+ if not str or str == "" then
+ return ""
+ elseif find(str,"^MD5:") then
+ return str
+ else
+ return upper(format("MD5:%s",sumhexa(str)))
+ end
+end
+
+local function cleanuppassword(str)
+ return (gsub(str,"^MD5:",""))
+end
+
+local function samepasswords(one,two)
+ if not one or not two then
+ return false
+ end
+ if not find(one,"^MD5:") then
+ one = encryptpassword(one)
+ end
+ if not find(two,"^MD5:") then
+ two = encryptpassword(two)
+ end
+ return one == two
+end
+
+local function validaddress(address,addresses)
+ if address and addresses and address ~= "" and addresses ~= "" then
+ if find(address,topattern(addresses,true,true)) then
+ return true, "valid remote address"
+ end
+ return false, "invalid remote address"
+ else
+ return true, "no remote address check"
+ end
+end
+
+
+users.encryptpassword = encryptpassword
+users.cleanuppassword = cleanuppassword
+users.samepasswords = samepasswords
+users.validaddress = validaddress
+
+-- print(users.encryptpassword("test")) -- MD5:098F6BCD4621D373CADE4E832627B4F6
+
+local function checkeddb(presets,datatable)
+ return sql.usedatabase(presets,datatable or presets.datatable or "users")
+end
+
+users.usedb = checkeddb
+
+local groupnames = { }
+local groupnumbers = { }
+
+local function registergroup(name)
+ local n = #groupnames + 1
+ groupnames [n] = name
+ groupnames [tostring(n)] = name
+ groupnames [name] = name
+ groupnumbers[n] = n
+ groupnumbers[tostring(n)] = n
+ groupnumbers[name] = n
+ return n
+end
+
+registergroup("superuser")
+registergroup("administrator")
+registergroup("user")
+registergroup("guest")
+
+users.groupnames = groupnames
+users.groupnumbers = groupnumbers
+
+-- password 'test':
+--
+-- INSERT insert into users (`name`,`password`,`group`,`enabled`) values ('...','MD5:098F6BCD4621D373CADE4E832627B4F6',1,1) ;
+
+local template =[[
+ CREATE TABLE `users` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` varchar(80) NOT NULL,
+ `fullname` varchar(80) NOT NULL,
+ `password` varchar(50) DEFAULT NULL,
+ `group` int(11) NOT NULL,
+ `enabled` int(11) DEFAULT '1',
+ `email` varchar(80) DEFAULT NULL,
+ `address` varchar(256) DEFAULT NULL,
+ `theme` varchar(50) DEFAULT NULL,
+ `data` longtext,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `name_unique` (`name`)
+ ) DEFAULT CHARSET = utf8 ;
+]]
+
+local converter, fields = sql.makeconverter {
+ { name = "id", type = "number" },
+ { name = "name", type = "string" },
+ { name = "fullname", type = "string" },
+ { name = "password", type = "string" },
+ { name = "group", type = groupnames },
+ { name = "enabled", type = "boolean" },
+ { name = "email", type = "string" },
+ { name = "address", type = "string" },
+ { name = "theme", type = "string" },
+ { name = "data", type = "deserialize" },
+}
+
+function users.createdb(presets,datatable)
+
+ local db = checkeddb(presets,datatable)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ },
+ }
+
+ report("datatable %a created in %a",db.name,db.base)
+
+ return db
+
+end
+
+local template =[[
+ SELECT
+ %fields%
+ FROM
+ %basename%
+ WHERE
+ `name` = '%[name]%'
+ AND
+ `password` = '%[password]%'
+ ;
+]]
+
+local template =[[
+ SELECT
+ %fields%
+ FROM
+ %basename%
+ WHERE
+ `name` = '%[name]%'
+ ;
+]]
+
+function users.valid(db,username,password,address)
+
+ local data = db.execute {
+ template = template,
+ converter = converter,
+ variables = {
+ basename = db.basename,
+ fields = fields,
+ name = username,
+ },
+ }
+
+ local data = data and data[1]
+
+ if not data then
+ return false, "unknown user"
+ elseif not data.enabled then
+ return false, "disabled user"
+ elseif data.password ~= encryptpassword(password) then
+ return false, "wrong password"
+ elseif not validaddress(address,data.address) then
+ return false, "invalid address"
+ else
+ data.password = nil
+ return data, "okay"
+ end
+
+end
+
+local template =[[
+ INSERT INTO %basename% (
+ `name`,
+ `fullname`,
+ `password`,
+ `group`,
+ `enabled`,
+ `email`,
+ `address`,
+ `theme`,
+ `data`
+ ) VALUES (
+ '%[name]%',
+ '%[fullname]%',
+ '%[password]%',
+ '%[group]%',
+ '%[enabled]%',
+ '%[email]%',
+ '%[address]%',
+ '%[theme]%',
+ '%[data]%'
+ ) ;
+]]
+
+function users.add(db,specification)
+
+ local name = specification.username or specification.name
+
+ if not name or name == "" then
+ return
+ end
+
+ local data = specification.data
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ name = name,
+ fullname = name or fullname,
+ password = encryptpassword(specification.password or ""),
+ group = groupnumbers[specification.group] or groupnumbers.guest,
+ enabled = booleanstring(specification.enabled) and "1" or "0",
+ email = specification.email,
+ address = specification.address,
+ theme = specification.theme,
+ data = type(data) == "table" and db.serialize(data,"return") or "",
+ },
+ }
+
+end
+
+local template =[[
+ SELECT
+ %fields%
+ FROM
+ %basename%
+ WHERE
+ `name` = '%[name]%' ;
+]]
+
+function users.getbyname(db,name)
+
+ local data = db.execute {
+ template = template,
+ converter = converter,
+ variables = {
+ basename = db.basename,
+ fields = fields,
+ name = name,
+ },
+ }
+
+ return data and data[1] or nil
+
+end
+
+local template =[[
+ SELECT
+ %fields%
+ FROM
+ %basename%
+ WHERE
+ `id` = '%id%' ;
+]]
+
+local function getbyid(db,id)
+
+ local data = db.execute {
+ template = template,
+ converter = converter,
+ variables = {
+ basename = db.basename,
+ fields = fields,
+ id = id,
+ },
+ }
+
+ return data and data[1] or nil
+
+end
+
+users.getbyid = getbyid
+
+local template =[[
+ UPDATE
+ %basename%
+ SET
+ `fullname` = '%[fullname]%',
+ `password` = '%[password]%',
+ `group` = '%[group]%',
+ `enabled` = '%[enabled]%',
+ `email` = '%[email]%',
+ `address` = '%[address]%',
+ `theme` = '%[theme]%',
+ `data` = '%[data]%'
+ WHERE
+ `id` = '%id%'
+ ;
+]]
+
+function users.save(db,id,specification)
+
+ id = tonumber(id)
+
+ if not id then
+ return
+ end
+
+ local user = getbyid(db,id)
+
+ if tonumber(user.id) ~= id then
+ return
+ end
+
+ local fullname = specification.fullname == nil and user.fulname or specification.fullname
+ local password = specification.password == nil and user.password or specification.password
+ local group = specification.group == nil and user.group or specification.group
+ local enabled = specification.enabled == nil and user.enabled or specification.enabled
+ local email = specification.email == nil and user.email or specification.email
+ local address = specification.address == nil and user.address or specification.address
+ local theme = specification.theme == nil and user.theme or specification.theme
+ local data = specification.data == nil and user.data or specification.data
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ id = id,
+ fullname = fullname,
+ password = encryptpassword(password),
+ group = groupnumbers[group],
+ enabled = booleanstring(enabled) and "1" or "0",
+ email = email,
+ address = address,
+ theme = theme,
+ data = type(data) == "table" and db.serialize(data,"return") or "",
+ },
+ }
+
+ return getbyid(db,id)
+
+end
+
+local template =[[
+ DELETE FROM
+ %basename%
+ WHERE
+ `id` = '%id%' ;
+]]
+
+function users.remove(db,id)
+
+ db.execute {
+ template = template,
+ variables = {
+ basename = db.basename,
+ id = id,
+ },
+ }
+
+end
+
+local template =[[
+ SELECT
+ %fields%
+ FROM
+ %basename%
+ ORDER BY
+ `name` ;
+]]
+
+function users.collect(db) -- maybe also an id/name only variant
+
+ local records, keys = db.execute {
+ template = template,
+ converter = converter,
+ variables = {
+ basename = db.basename,
+ fields = fields,
+ },
+ }
+
+ return records, keys
+
+end
diff --git a/tex/context/base/util-sql.lua b/tex/context/base/util-sql.lua
index cd2c4c2e2..1c1766edf 100644
--- a/tex/context/base/util-sql.lua
+++ b/tex/context/base/util-sql.lua
@@ -1,443 +1,443 @@
-if not modules then modules = { } end modules ['util-sql'] = {
- version = 1.001,
- comment = "companion to m-sql.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- todo: templates as table (saves splitting)
-
--- Of course we could use a library but we don't want another depedency and there is
--- a bit of flux in these libraries. Also, we want the data back in a way that we
--- like.
---
--- This is the first of set of sql related modules that are providing functionality
--- for a web based framework that we use for typesetting (related) services. We're
--- talking of session management, job ticket processing, storage, (xml) file processing
--- and dealing with data from databases (often ambitiously called database publishing).
---
--- There is no generic solution for such services, but from our perspective, as we use
--- context in a regular tds tree (the standard distribution) it makes sense to put shared
--- code in the context distribution. That way we don't need to reinvent wheels every time.
-
--- We use the template mechanism from util-tpl which inturn is just using the dos cq
--- windows convention of %whatever% variables that I've used for ages.
-
--- util-sql-imp-client.lua
--- util-sql-imp-library.lua
--- util-sql-imp-swiglib.lua
--- util-sql-imp-lmxsql.lua
-
--- local sql = require("util-sql")
---
--- local converter = sql.makeconverter {
--- { name = "id", type = "number" },
--- { name = "data",type = "string" },
--- }
---
--- local execute = sql.methods.swiglib.execute
--- -- local execute = sql.methods.library.execute
--- -- local execute = sql.methods.client.execute
--- -- local execute = sql.methods.lmxsql.execute
---
--- result = execute {
--- presets = {
--- host = "localhost",
--- username = "root",
--- password = "test",
--- database = "test",
--- id = "test", -- forces persistent session
--- },
--- template = "select * from `test` where `id` > %criterium% ;",
--- variables = {
--- criterium = 2,
--- },
--- converter = converter
--- }
---
--- inspect(result)
-
-local format, match = string.format, string.match
-local random = math.random
-local rawset, setmetatable, getmetatable, load, type = rawset, setmetatable, getmetatable, load, type
-local P, S, V, C, Cs, Ct, Cc, Cg, Cf, patterns, lpegmatch = lpeg.P, lpeg.S, lpeg.V, lpeg.C, lpeg.Cs, lpeg.Ct, lpeg.Cc, lpeg.Cg, lpeg.Cf, lpeg.patterns, lpeg.match
-local concat = table.concat
-
-local osuuid = os.uuid
-local osclock = os.clock or os.time
-local ostime = os.time
-local setmetatableindex = table.setmetatableindex
-
-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 report_state = logs.reporter("sql")
-
--- trace_sql = true
--- trace_queries = true
-
-utilities.sql = utilities.sql or { }
-local sql = utilities.sql
-
-local replacetemplate = utilities.templates.replace
-local loadtemplate = utilities.templates.load
-
-local methods = { }
-sql.methods = methods
-
-local helpers = { }
-sql.helpers = helpers
-
-local serialize = table.fastserialize
-local deserialize = table.deserialize
-
-sql.serialize = serialize
-sql.deserialize = deserialize
-
-helpers.serialize = serialize -- bonus
-helpers.deserialize = deserialize -- bonus
-
-local defaults = { __index =
- {
- resultfile = "result.dat",
- templatefile = "template.sql",
- queryfile = "query.sql",
- variables = { },
- username = "default",
- password = "default",
- host = "localhost",
- port = 3306,
- database = "default",
- },
-}
-
-setmetatableindex(sql.methods,function(t,k)
- report_state("start loading method %a",k)
- require("util-sql-imp-"..k)
- report_state("loading method %a done",k)
- return rawget(t,k)
-end)
-
--- converters
-
-local converters = { }
-sql.converters = converters
-
-local function makeconverter(entries,celltemplate,wraptemplate)
- local shortcuts = { }
- local assignments = { }
- local key = false
- for i=1,#entries do
- local entry = entries[i]
- local name = entry.name
- local kind = entry.type or entry.kind
- local value = format(celltemplate,i,i)
- if kind == "boolean" then
- assignments[#assignments+1] = format("[%q] = booleanstring(%s),",name,value)
- elseif kind == "number" then
- assignments[#assignments+1] = format("[%q] = tonumber(%s),",name,value)
- elseif type(kind) == "function" then
- local c = #converters + 1
- converters[c] = kind
- shortcuts[#shortcuts+1] = format("local fun_%s = converters[%s]",c,c)
- assignments[#assignments+1] = format("[%q] = fun_%s(%s),",name,c,value)
- elseif type(kind) == "table" then
- local c = #converters + 1
- converters[c] = kind
- shortcuts[#shortcuts+1] = format("local tab_%s = converters[%s]",c,c)
- assignments[#assignments+1] = format("[%q] = tab_%s[%s],",name,#converters,value)
- elseif kind == "deserialize" then
- assignments[#assignments+1] = format("[%q] = deserialize(%s),",name,value)
- elseif kind == "key" then
- -- hashed instead of indexed
- key = value
- elseif kind == "entry" then
- -- so we can (efficiently) extend the hashed table
- local default = entry.default or ""
- if type(default) == "string" then
- assignments[#assignments+1] = format("[%q] = %q,",name,default)
- else
- assignments[#assignments+1] = format("[%q] = %s,",name,tostring(default))
- end
- else
- assignments[#assignments+1] = format("[%q] = %s,",name,value)
- end
- end
- local code = format(wraptemplate,concat(shortcuts,"\n"),key and "{ }" or "data",key or "i",concat(assignments,"\n "))
- -- print(code)
- local func = load(code)
- return func and func()
-end
-
-function sql.makeconverter(entries)
- local fields = { }
- for i=1,#entries do
- fields[i] = format("`%s`",entries[i].name)
- end
- fields = concat(fields, ", ")
- local converter = {
- fields = fields
- }
- setmetatableindex(converter, function(t,k)
- local sqlmethod = methods[k]
- local v = makeconverter(entries,sqlmethod.celltemplate,sqlmethod.wraptemplate)
- t[k] = v
- return v
- end)
- return converter, fields
-end
-
--- helper for libraries:
-
-local function validspecification(specification)
- local presets = specification.presets
- if type(presets) == "string" then
- presets = dofile(presets)
- end
- if type(presets) == "table" then
- setmetatable(presets,defaults)
- setmetatable(specification,{ __index = presets })
- else
- setmetatable(specification,defaults)
- end
- return true
-end
-
-helpers.validspecification = validspecification
-
-local whitespace = patterns.whitespace^0
-local eol = patterns.eol
-local separator = P(";")
-local escaped = patterns.escaped
-local dquote = patterns.dquote
-local squote = patterns.squote
-local dsquote = squote * squote
----- quoted = patterns.quoted
-local quoted = dquote * (escaped + (1-dquote))^0 * dquote
- + squote * (escaped + dsquote + (1-squote))^0 * squote
-local comment = P("--") * (1-eol) / ""
-local query = whitespace
- * Cs((quoted + comment + 1 - separator)^1 * Cc(";"))
- * whitespace
-local splitter = Ct(query * (separator * query)^0)
-
-helpers.querysplitter = splitter
-
--- I will add a bit more checking.
-
-local function validspecification(specification)
- local presets = specification.presets
- if type(presets) == "string" then
- presets = dofile(presets)
- end
- if type(presets) == "table" then
- local m = getmetatable(presets)
- if m then
- setmetatable(m,defaults)
- else
- setmetatable(presets,defaults)
- end
- setmetatable(specification,{ __index = presets })
- else
- setmetatable(specification,defaults)
- end
- local templatefile = specification.templatefile or "query"
- local queryfile = specification.queryfile or presets.queryfile or file.nameonly(templatefile) .. "-temp.sql"
- local resultfile = specification.resultfile or presets.resultfile or file.nameonly(templatefile) .. "-temp.dat"
- specification.queryfile = queryfile
- specification.resultfile = resultfile
- if trace_sql then
- report_state("template file: %s",templatefile or "<none>")
- report_state("query file: %s",queryfile)
- report_state("result file: %s",resultfile)
- end
- return true
-end
-
-local function preparetemplate(specification)
- local template = specification.template
- if template then
- local query = replacetemplate(template,specification.variables,'sql')
- if not query then
- report_state("error in template: %s",template)
- elseif trace_queries then
- report_state("query from template: %s",query)
- end
- return query
- end
- local templatefile = specification.templatefile
- if templatefile then
- local query = loadtemplate(templatefile,specification.variables,'sql')
- if not query then
- report_state("error in template file %a",templatefile)
- elseif trace_queries then
- report_state("query from template file %a: %s",templatefile,query)
- end
- return query
- end
- report_state("no query template or templatefile")
-end
-
-helpers.preparetemplate = preparetemplate
-
--- -- -- we delay setting this -- -- --
-
-local currentmethod
-
-local function firstexecute(...)
- local execute = methods[currentmethod].execute
- sql.execute = execute
- return execute(...)
-end
-
-function sql.setmethod(method)
- currentmethod = method
- sql.execute = firstexecute
-end
-
-sql.setmethod("library")
-
--- helper:
-
-function sql.usedatabase(presets,datatable)
- local name = datatable or presets.datatable
- if name then
- local method = presets.method and sql.methods[presets.method] or sql.methods.client
- local base = presets.database or "test"
- local basename = format("`%s`.`%s`",base,name)
- local execute = nil
- local m_execute = method.execute
- if method.usesfiles then
- local queryfile = presets.queryfile or format("%s-temp.sql",name)
- local resultfile = presets.resultfile or format("%s-temp.dat",name)
- execute = function(specification) -- variables template
- if not specification.presets then specification.presets = presets end
- if not specification.queryfile then specification.queryfile = queryfile end
- if not specification.resultfile then specification.resultfile = queryfile end
- return m_execute(specification)
- end
- else
- execute = function(specification) -- variables template
- if not specification.presets then specification.presets = presets end
- return m_execute(specification)
- end
- end
- local function unpackdata(records,name)
- if records then
- name = name or "data"
- for i=1,#records do
- local record = records[i]
- local data = record[name]
- if data then
- record[name] = deserialize(data)
- end
- end
- end
- end
- return {
- presets = preset,
- base = base,
- name = name,
- basename = basename,
- execute = execute,
- serialize = serialize,
- deserialize = deserialize,
- unpackdata = unpackdata,
- }
- else
- report_state("missing name in usedatabase specification")
- end
-end
-
--- local data = utilities.sql.prepare {
--- templatefile = "test.sql",
--- variables = { },
--- host = "...",
--- username = "...",
--- password = "...",
--- database = "...",
--- }
-
--- local presets = {
--- host = "...",
--- username = "...",
--- password = "...",
--- database = "...",
--- }
---
--- local data = utilities.sql.prepare {
--- templatefile = "test.sql",
--- variables = { },
--- presets = presets,
--- }
-
--- local data = utilities.sql.prepare {
--- templatefile = "test.sql",
--- variables = { },
--- presets = dofile(...),
--- }
-
--- local data = utilities.sql.prepare {
--- templatefile = "test.sql",
--- variables = { },
--- presets = "...",
--- }
-
--- for i=1,10 do
--- local dummy = uuid() -- else same every time, don't ask
--- end
-
-sql.tokens = {
- length = 42, -- but in practice we will reserve some 50 characters
- new = function()
- return format("%s-%x06",osuuid(),random(0xFFFFF)) -- 36 + 1 + 6 = 42
- end,
-}
-
--- -- --
-
--- local func, code = sql.makeconverter {
--- { name = "a", type = "number" },
--- { name = "b", type = "string" },
--- { name = "c", type = "boolean" },
--- { name = "d", type = { x = "1" } },
--- { name = "e", type = os.fulltime },
--- }
---
--- print(code)
-
--- -- --
-
-if tex and tex.systemmodes then
-
- local droptable = table.drop
- local threshold = 16 * 1024 -- use slower but less memory hungry variant
-
- function sql.prepare(specification,tag)
- -- could go into tuc if needed
- -- todo: serialize per column
- local tag = tag or specification.tag or "last"
- local filename = format("%s-sql-result-%s.tuc",tex.jobname,tag)
- if tex.systemmodes["first"] then
- local data, keys = sql.execute(specification)
- if not data then
- data = { }
- end
- if not keys then
- keys = { }
- end
- io.savedata(filename,droptable({ data = data, keys = keys },#keys*#data>threshold))
- return data, keys
- else
- local result = table.load(filename)
- return result.data, result.keys
- end
- end
-
-else
-
- sql.prepare = sql.execute
-
-end
-
-return sql
+if not modules then modules = { } end modules ['util-sql'] = {
+ version = 1.001,
+ comment = "companion to m-sql.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- todo: templates as table (saves splitting)
+
+-- Of course we could use a library but we don't want another depedency and there is
+-- a bit of flux in these libraries. Also, we want the data back in a way that we
+-- like.
+--
+-- This is the first of set of sql related modules that are providing functionality
+-- for a web based framework that we use for typesetting (related) services. We're
+-- talking of session management, job ticket processing, storage, (xml) file processing
+-- and dealing with data from databases (often ambitiously called database publishing).
+--
+-- There is no generic solution for such services, but from our perspective, as we use
+-- context in a regular tds tree (the standard distribution) it makes sense to put shared
+-- code in the context distribution. That way we don't need to reinvent wheels every time.
+
+-- We use the template mechanism from util-tpl which inturn is just using the dos cq
+-- windows convention of %whatever% variables that I've used for ages.
+
+-- util-sql-imp-client.lua
+-- util-sql-imp-library.lua
+-- util-sql-imp-swiglib.lua
+-- util-sql-imp-lmxsql.lua
+
+-- local sql = require("util-sql")
+--
+-- local converter = sql.makeconverter {
+-- { name = "id", type = "number" },
+-- { name = "data",type = "string" },
+-- }
+--
+-- local execute = sql.methods.swiglib.execute
+-- -- local execute = sql.methods.library.execute
+-- -- local execute = sql.methods.client.execute
+-- -- local execute = sql.methods.lmxsql.execute
+--
+-- result = execute {
+-- presets = {
+-- host = "localhost",
+-- username = "root",
+-- password = "test",
+-- database = "test",
+-- id = "test", -- forces persistent session
+-- },
+-- template = "select * from `test` where `id` > %criterium% ;",
+-- variables = {
+-- criterium = 2,
+-- },
+-- converter = converter
+-- }
+--
+-- inspect(result)
+
+local format, match = string.format, string.match
+local random = math.random
+local rawset, setmetatable, getmetatable, load, type = rawset, setmetatable, getmetatable, load, type
+local P, S, V, C, Cs, Ct, Cc, Cg, Cf, patterns, lpegmatch = lpeg.P, lpeg.S, lpeg.V, lpeg.C, lpeg.Cs, lpeg.Ct, lpeg.Cc, lpeg.Cg, lpeg.Cf, lpeg.patterns, lpeg.match
+local concat = table.concat
+
+local osuuid = os.uuid
+local osclock = os.clock or os.time
+local ostime = os.time
+local setmetatableindex = table.setmetatableindex
+
+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 report_state = logs.reporter("sql")
+
+-- trace_sql = true
+-- trace_queries = true
+
+utilities.sql = utilities.sql or { }
+local sql = utilities.sql
+
+local replacetemplate = utilities.templates.replace
+local loadtemplate = utilities.templates.load
+
+local methods = { }
+sql.methods = methods
+
+local helpers = { }
+sql.helpers = helpers
+
+local serialize = table.fastserialize
+local deserialize = table.deserialize
+
+sql.serialize = serialize
+sql.deserialize = deserialize
+
+helpers.serialize = serialize -- bonus
+helpers.deserialize = deserialize -- bonus
+
+local defaults = { __index =
+ {
+ resultfile = "result.dat",
+ templatefile = "template.sql",
+ queryfile = "query.sql",
+ variables = { },
+ username = "default",
+ password = "default",
+ host = "localhost",
+ port = 3306,
+ database = "default",
+ },
+}
+
+setmetatableindex(sql.methods,function(t,k)
+ report_state("start loading method %a",k)
+ require("util-sql-imp-"..k)
+ report_state("loading method %a done",k)
+ return rawget(t,k)
+end)
+
+-- converters
+
+local converters = { }
+sql.converters = converters
+
+local function makeconverter(entries,celltemplate,wraptemplate)
+ local shortcuts = { }
+ local assignments = { }
+ local key = false
+ for i=1,#entries do
+ local entry = entries[i]
+ local name = entry.name
+ local kind = entry.type or entry.kind
+ local value = format(celltemplate,i,i)
+ if kind == "boolean" then
+ assignments[#assignments+1] = format("[%q] = booleanstring(%s),",name,value)
+ elseif kind == "number" then
+ assignments[#assignments+1] = format("[%q] = tonumber(%s),",name,value)
+ elseif type(kind) == "function" then
+ local c = #converters + 1
+ converters[c] = kind
+ shortcuts[#shortcuts+1] = format("local fun_%s = converters[%s]",c,c)
+ assignments[#assignments+1] = format("[%q] = fun_%s(%s),",name,c,value)
+ elseif type(kind) == "table" then
+ local c = #converters + 1
+ converters[c] = kind
+ shortcuts[#shortcuts+1] = format("local tab_%s = converters[%s]",c,c)
+ assignments[#assignments+1] = format("[%q] = tab_%s[%s],",name,#converters,value)
+ elseif kind == "deserialize" then
+ assignments[#assignments+1] = format("[%q] = deserialize(%s),",name,value)
+ elseif kind == "key" then
+ -- hashed instead of indexed
+ key = value
+ elseif kind == "entry" then
+ -- so we can (efficiently) extend the hashed table
+ local default = entry.default or ""
+ if type(default) == "string" then
+ assignments[#assignments+1] = format("[%q] = %q,",name,default)
+ else
+ assignments[#assignments+1] = format("[%q] = %s,",name,tostring(default))
+ end
+ else
+ assignments[#assignments+1] = format("[%q] = %s,",name,value)
+ end
+ end
+ local code = format(wraptemplate,concat(shortcuts,"\n"),key and "{ }" or "data",key or "i",concat(assignments,"\n "))
+ -- print(code)
+ local func = load(code)
+ return func and func()
+end
+
+function sql.makeconverter(entries)
+ local fields = { }
+ for i=1,#entries do
+ fields[i] = format("`%s`",entries[i].name)
+ end
+ fields = concat(fields, ", ")
+ local converter = {
+ fields = fields
+ }
+ setmetatableindex(converter, function(t,k)
+ local sqlmethod = methods[k]
+ local v = makeconverter(entries,sqlmethod.celltemplate,sqlmethod.wraptemplate)
+ t[k] = v
+ return v
+ end)
+ return converter, fields
+end
+
+-- helper for libraries:
+
+local function validspecification(specification)
+ local presets = specification.presets
+ if type(presets) == "string" then
+ presets = dofile(presets)
+ end
+ if type(presets) == "table" then
+ setmetatable(presets,defaults)
+ setmetatable(specification,{ __index = presets })
+ else
+ setmetatable(specification,defaults)
+ end
+ return true
+end
+
+helpers.validspecification = validspecification
+
+local whitespace = patterns.whitespace^0
+local eol = patterns.eol
+local separator = P(";")
+local escaped = patterns.escaped
+local dquote = patterns.dquote
+local squote = patterns.squote
+local dsquote = squote * squote
+---- quoted = patterns.quoted
+local quoted = dquote * (escaped + (1-dquote))^0 * dquote
+ + squote * (escaped + dsquote + (1-squote))^0 * squote
+local comment = P("--") * (1-eol) / ""
+local query = whitespace
+ * Cs((quoted + comment + 1 - separator)^1 * Cc(";"))
+ * whitespace
+local splitter = Ct(query * (separator * query)^0)
+
+helpers.querysplitter = splitter
+
+-- I will add a bit more checking.
+
+local function validspecification(specification)
+ local presets = specification.presets
+ if type(presets) == "string" then
+ presets = dofile(presets)
+ end
+ if type(presets) == "table" then
+ local m = getmetatable(presets)
+ if m then
+ setmetatable(m,defaults)
+ else
+ setmetatable(presets,defaults)
+ end
+ setmetatable(specification,{ __index = presets })
+ else
+ setmetatable(specification,defaults)
+ end
+ local templatefile = specification.templatefile or "query"
+ local queryfile = specification.queryfile or presets.queryfile or file.nameonly(templatefile) .. "-temp.sql"
+ local resultfile = specification.resultfile or presets.resultfile or file.nameonly(templatefile) .. "-temp.dat"
+ specification.queryfile = queryfile
+ specification.resultfile = resultfile
+ if trace_sql then
+ report_state("template file: %s",templatefile or "<none>")
+ report_state("query file: %s",queryfile)
+ report_state("result file: %s",resultfile)
+ end
+ return true
+end
+
+local function preparetemplate(specification)
+ local template = specification.template
+ if template then
+ local query = replacetemplate(template,specification.variables,'sql')
+ if not query then
+ report_state("error in template: %s",template)
+ elseif trace_queries then
+ report_state("query from template: %s",query)
+ end
+ return query
+ end
+ local templatefile = specification.templatefile
+ if templatefile then
+ local query = loadtemplate(templatefile,specification.variables,'sql')
+ if not query then
+ report_state("error in template file %a",templatefile)
+ elseif trace_queries then
+ report_state("query from template file %a: %s",templatefile,query)
+ end
+ return query
+ end
+ report_state("no query template or templatefile")
+end
+
+helpers.preparetemplate = preparetemplate
+
+-- -- -- we delay setting this -- -- --
+
+local currentmethod
+
+local function firstexecute(...)
+ local execute = methods[currentmethod].execute
+ sql.execute = execute
+ return execute(...)
+end
+
+function sql.setmethod(method)
+ currentmethod = method
+ sql.execute = firstexecute
+end
+
+sql.setmethod("library")
+
+-- helper:
+
+function sql.usedatabase(presets,datatable)
+ local name = datatable or presets.datatable
+ if name then
+ local method = presets.method and sql.methods[presets.method] or sql.methods.client
+ local base = presets.database or "test"
+ local basename = format("`%s`.`%s`",base,name)
+ local execute = nil
+ local m_execute = method.execute
+ if method.usesfiles then
+ local queryfile = presets.queryfile or format("%s-temp.sql",name)
+ local resultfile = presets.resultfile or format("%s-temp.dat",name)
+ execute = function(specification) -- variables template
+ if not specification.presets then specification.presets = presets end
+ if not specification.queryfile then specification.queryfile = queryfile end
+ if not specification.resultfile then specification.resultfile = queryfile end
+ return m_execute(specification)
+ end
+ else
+ execute = function(specification) -- variables template
+ if not specification.presets then specification.presets = presets end
+ return m_execute(specification)
+ end
+ end
+ local function unpackdata(records,name)
+ if records then
+ name = name or "data"
+ for i=1,#records do
+ local record = records[i]
+ local data = record[name]
+ if data then
+ record[name] = deserialize(data)
+ end
+ end
+ end
+ end
+ return {
+ presets = preset,
+ base = base,
+ name = name,
+ basename = basename,
+ execute = execute,
+ serialize = serialize,
+ deserialize = deserialize,
+ unpackdata = unpackdata,
+ }
+ else
+ report_state("missing name in usedatabase specification")
+ end
+end
+
+-- local data = utilities.sql.prepare {
+-- templatefile = "test.sql",
+-- variables = { },
+-- host = "...",
+-- username = "...",
+-- password = "...",
+-- database = "...",
+-- }
+
+-- local presets = {
+-- host = "...",
+-- username = "...",
+-- password = "...",
+-- database = "...",
+-- }
+--
+-- local data = utilities.sql.prepare {
+-- templatefile = "test.sql",
+-- variables = { },
+-- presets = presets,
+-- }
+
+-- local data = utilities.sql.prepare {
+-- templatefile = "test.sql",
+-- variables = { },
+-- presets = dofile(...),
+-- }
+
+-- local data = utilities.sql.prepare {
+-- templatefile = "test.sql",
+-- variables = { },
+-- presets = "...",
+-- }
+
+-- for i=1,10 do
+-- local dummy = uuid() -- else same every time, don't ask
+-- end
+
+sql.tokens = {
+ length = 42, -- but in practice we will reserve some 50 characters
+ new = function()
+ return format("%s-%x06",osuuid(),random(0xFFFFF)) -- 36 + 1 + 6 = 42
+ end,
+}
+
+-- -- --
+
+-- local func, code = sql.makeconverter {
+-- { name = "a", type = "number" },
+-- { name = "b", type = "string" },
+-- { name = "c", type = "boolean" },
+-- { name = "d", type = { x = "1" } },
+-- { name = "e", type = os.fulltime },
+-- }
+--
+-- print(code)
+
+-- -- --
+
+if tex and tex.systemmodes then
+
+ local droptable = table.drop
+ local threshold = 16 * 1024 -- use slower but less memory hungry variant
+
+ function sql.prepare(specification,tag)
+ -- could go into tuc if needed
+ -- todo: serialize per column
+ local tag = tag or specification.tag or "last"
+ local filename = format("%s-sql-result-%s.tuc",tex.jobname,tag)
+ if tex.systemmodes["first"] then
+ local data, keys = sql.execute(specification)
+ if not data then
+ data = { }
+ end
+ if not keys then
+ keys = { }
+ end
+ io.savedata(filename,droptable({ data = data, keys = keys },#keys*#data>threshold))
+ return data, keys
+ else
+ local result = table.load(filename)
+ return result.data, result.keys
+ end
+ end
+
+else
+
+ sql.prepare = sql.execute
+
+end
+
+return sql
diff --git a/tex/context/base/util-sta.lua b/tex/context/base/util-sta.lua
index 1ea713a76..1a61ec4e6 100644
--- a/tex/context/base/util-sta.lua
+++ b/tex/context/base/util-sta.lua
@@ -1,342 +1,342 @@
-if not modules then modules = { } end modules ['util-sta'] = {
- version = 1.001,
- comment = "companion to util-ini.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local insert, remove, fastcopy, concat = table.insert, table.remove, table.fastcopy, table.concat
-local format = string.format
-local select, tostring = select, tostring
-
-local trace_stacker = false trackers.register("stacker.resolve", function(v) trace_stacker = v end)
-
-local stacker = stacker or { }
-
-utilities.stacker = stacker
-
-local function start(s,t,first,last)
- if s.mode == "switch" then
- local n = tostring(t[last])
- if trace_stacker then
- s.report("start: %s",n)
- end
- return n
- else
- local r = { }
- for i=first,last do
- r[#r+1] = tostring(t[i])
- end
- local n = concat(r," ")
- if trace_stacker then
- s.report("start: %s",n)
- end
- return n
- end
-end
-
-local function stop(s,t,first,last)
- if s.mode == "switch" then
- local n = tostring(false)
- if trace_stacker then
- s.report("stop: %s",n)
- end
- return n
- else
- local r = { }
- for i=last,first,-1 do
- r[#r+1] = tostring(false)
- end
- local n = concat(r," ")
- if trace_stacker then
- s.report("stop: %s",n)
- end
- return n
- end
-end
-
-local function change(s,t1,first1,last1,t2,first2,last2)
- if s.mode == "switch" then
- local n = tostring(t2[last2])
- if trace_stacker then
- s.report("change: %s",n)
- end
- return n
- else
- local r = { }
- for i=last1,first1,-1 do
- r[#r+1] = tostring(false)
- end
- local n = concat(r," ")
- for i=first2,last2 do
- r[#r+1] = tostring(t2[i])
- end
- if trace_stacker then
- s.report("change: %s",n)
- end
- return n
- end
-end
-
-function stacker.new(name)
-
- local s
-
- local stack = { }
- local list = { }
- local ids = { }
- local hash = { }
-
- local hashing = true
-
- local function push(...)
- for i=1,select("#",...) do
- insert(stack,(select(i,...))) -- watch the ()
- end
- if hashing then
- local c = concat(stack,"|")
- local n = hash[c]
- if not n then
- n = #list+1
- hash[c] = n
- list[n] = fastcopy(stack)
- end
- insert(ids,n)
- return n
- else
- local n = #list+1
- list[n] = fastcopy(stack)
- insert(ids,n)
- return n
- end
- end
-
- local function pop()
- remove(stack)
- remove(ids)
- return ids[#ids] or s.unset or -1
- end
-
- local function clean()
- if #stack == 0 then
- if trace_stacker then
- s.report("%s list entries, %s stack entries",#list,#stack)
- end
- end
- end
-
- local tops = { }
- local top, switch
-
- local function resolve_begin(mode)
- if mode then
- switch = mode == "switch"
- else
- switch = s.mode == "switch"
- end
- top = { switch = switch }
- insert(tops,top)
- end
-
- local function resolve_step(ti) -- keep track of changes outside function !
- -- todo: optimize for n=1 etc
- local result = nil
- local noftop = #top
- if ti > 0 then
- local current = list[ti]
- if current then
- local noflist = #current
- local nofsame = 0
- if noflist > noftop then
- for i=1,noflist do
- if current[i] == top[i] then
- nofsame = i
- else
- break
- end
- end
- else
- for i=1,noflist do
- if current[i] == top[i] then
- nofsame = i
- else
- break
- end
- end
- end
- local plus = nofsame + 1
- if plus <= noftop then
- if plus <= noflist then
- if switch then
- result = s.change(s,top,plus,noftop,current,nofsame,noflist)
- else
- result = s.change(s,top,plus,noftop,current,plus,noflist)
- end
- else
- if switch then
- result = s.change(s,top,plus,noftop,current,nofsame,noflist)
- else
- result = s.stop(s,top,plus,noftop)
- end
- end
- elseif plus <= noflist then
- if switch then
- result = s.start(s,current,nofsame,noflist)
- else
- result = s.start(s,current,plus,noflist)
- end
- end
- top = current
- else
- if 1 <= noftop then
- result = s.stop(s,top,1,noftop)
- end
- top = { }
- end
- return result
- else
- if 1 <= noftop then
- result = s.stop(s,top,1,noftop)
- end
- top = { }
- return result
- end
- end
-
- local function resolve_end()
- -- resolve_step(s.unset)
- local noftop = #top
- if noftop > 0 then
- local result = s.stop(s,top,1,#top)
- remove(tops)
- top = tops[#tops]
- switch = top and top.switch
- return result
- end
- end
-
- local function resolve(t)
- resolve_begin()
- for i=1,#t do
- resolve_step(t[i])
- end
- resolve_end()
- end
-
- local report = logs.reporter("stacker",name or nil)
-
- s = {
- name = name or "unknown",
- unset = -1,
- report = report,
- start = start,
- stop = stop,
- change = change,
- push = push,
- pop = pop,
- clean = clean,
- resolve = resolve,
- resolve_begin = resolve_begin,
- resolve_step = resolve_step,
- resolve_end = resolve_end,
- }
-
- return s -- we can overload functions
-
-end
-
--- local s = utilities.stacker.new("demo")
---
--- local unset = s.unset
--- local push = s.push
--- local pop = s.pop
---
--- local t = {
--- unset,
--- unset,
--- push("a"), -- a
--- push("b","c"), -- a b c
--- pop(), -- a b
--- push("d"), -- a b d
--- pop(), -- a b
--- unset,
--- pop(), -- a
--- pop(), -- b
--- unset,
--- unset,
--- }
---
--- s.resolve(t)
-
--- demostacker = utilities.stacker.new("demos")
---
--- local whatever = {
--- one = "1 0 0 RG 1 0 0 rg",
--- two = "1 1 0 RG 1 1 0 rg",
--- [false] = "0 G 0 g",
--- }
---
--- local concat = table.concat
---
--- local pdfliteral = nodes.pool.pdfliteral
---
--- function demostacker.start(s,t,first,last)
--- local n = whatever[t[last]]
--- -- s.report("start: %s",n)
--- return pdfliteral(n)
--- end
---
--- function demostacker.stop(s,t,first,last)
--- local n = whatever[false]
--- -- s.report("stop: %s",n)
--- return pdfliteral(n)
--- end
---
--- function demostacker.change(s,t1,first1,last1,t2,first2,last2)
--- local n = whatever[t2[last2]]
--- -- s.report("change: %s",n)
--- return pdfliteral(n)
--- end
---
--- demostacker.mode = "switch"
---
--- local whatever = {
--- one = "/OC /test1 BDC",
--- two = "/OC /test2 BDC",
--- [false] = "EMC",
--- }
---
--- demostacker = utilities.stacker.new("demos")
---
--- function demostacker.start(s,t,first,last)
--- local r = { }
--- for i=first,last do
--- r[#r+1] = whatever[t[i]]
--- end
--- -- s.report("start: %s",concat(r," "))
--- return pdfliteral(concat(r," "))
--- end
---
--- function demostacker.stop(s,t,first,last)
--- local r = { }
--- for i=last,first,-1 do
--- r[#r+1] = whatever[false]
--- end
--- -- s.report("stop: %s",concat(r," "))
--- return pdfliteral(concat(r," "))
--- end
---
--- function demostacker.change(s,t1,first1,last1,t2,first2,last2)
--- local r = { }
--- for i=last1,first1,-1 do
--- r[#r+1] = whatever[false]
--- end
--- for i=first2,last2 do
--- r[#r+1] = whatever[t2[i]]
--- end
--- -- s.report("change: %s",concat(r," "))
--- return pdfliteral(concat(r," "))
--- end
---
--- demostacker.mode = "stack"
+if not modules then modules = { } end modules ['util-sta'] = {
+ version = 1.001,
+ comment = "companion to util-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local insert, remove, fastcopy, concat = table.insert, table.remove, table.fastcopy, table.concat
+local format = string.format
+local select, tostring = select, tostring
+
+local trace_stacker = false trackers.register("stacker.resolve", function(v) trace_stacker = v end)
+
+local stacker = stacker or { }
+
+utilities.stacker = stacker
+
+local function start(s,t,first,last)
+ if s.mode == "switch" then
+ local n = tostring(t[last])
+ if trace_stacker then
+ s.report("start: %s",n)
+ end
+ return n
+ else
+ local r = { }
+ for i=first,last do
+ r[#r+1] = tostring(t[i])
+ end
+ local n = concat(r," ")
+ if trace_stacker then
+ s.report("start: %s",n)
+ end
+ return n
+ end
+end
+
+local function stop(s,t,first,last)
+ if s.mode == "switch" then
+ local n = tostring(false)
+ if trace_stacker then
+ s.report("stop: %s",n)
+ end
+ return n
+ else
+ local r = { }
+ for i=last,first,-1 do
+ r[#r+1] = tostring(false)
+ end
+ local n = concat(r," ")
+ if trace_stacker then
+ s.report("stop: %s",n)
+ end
+ return n
+ end
+end
+
+local function change(s,t1,first1,last1,t2,first2,last2)
+ if s.mode == "switch" then
+ local n = tostring(t2[last2])
+ if trace_stacker then
+ s.report("change: %s",n)
+ end
+ return n
+ else
+ local r = { }
+ for i=last1,first1,-1 do
+ r[#r+1] = tostring(false)
+ end
+ local n = concat(r," ")
+ for i=first2,last2 do
+ r[#r+1] = tostring(t2[i])
+ end
+ if trace_stacker then
+ s.report("change: %s",n)
+ end
+ return n
+ end
+end
+
+function stacker.new(name)
+
+ local s
+
+ local stack = { }
+ local list = { }
+ local ids = { }
+ local hash = { }
+
+ local hashing = true
+
+ local function push(...)
+ for i=1,select("#",...) do
+ insert(stack,(select(i,...))) -- watch the ()
+ end
+ if hashing then
+ local c = concat(stack,"|")
+ local n = hash[c]
+ if not n then
+ n = #list+1
+ hash[c] = n
+ list[n] = fastcopy(stack)
+ end
+ insert(ids,n)
+ return n
+ else
+ local n = #list+1
+ list[n] = fastcopy(stack)
+ insert(ids,n)
+ return n
+ end
+ end
+
+ local function pop()
+ remove(stack)
+ remove(ids)
+ return ids[#ids] or s.unset or -1
+ end
+
+ local function clean()
+ if #stack == 0 then
+ if trace_stacker then
+ s.report("%s list entries, %s stack entries",#list,#stack)
+ end
+ end
+ end
+
+ local tops = { }
+ local top, switch
+
+ local function resolve_begin(mode)
+ if mode then
+ switch = mode == "switch"
+ else
+ switch = s.mode == "switch"
+ end
+ top = { switch = switch }
+ insert(tops,top)
+ end
+
+ local function resolve_step(ti) -- keep track of changes outside function !
+ -- todo: optimize for n=1 etc
+ local result = nil
+ local noftop = #top
+ if ti > 0 then
+ local current = list[ti]
+ if current then
+ local noflist = #current
+ local nofsame = 0
+ if noflist > noftop then
+ for i=1,noflist do
+ if current[i] == top[i] then
+ nofsame = i
+ else
+ break
+ end
+ end
+ else
+ for i=1,noflist do
+ if current[i] == top[i] then
+ nofsame = i
+ else
+ break
+ end
+ end
+ end
+ local plus = nofsame + 1
+ if plus <= noftop then
+ if plus <= noflist then
+ if switch then
+ result = s.change(s,top,plus,noftop,current,nofsame,noflist)
+ else
+ result = s.change(s,top,plus,noftop,current,plus,noflist)
+ end
+ else
+ if switch then
+ result = s.change(s,top,plus,noftop,current,nofsame,noflist)
+ else
+ result = s.stop(s,top,plus,noftop)
+ end
+ end
+ elseif plus <= noflist then
+ if switch then
+ result = s.start(s,current,nofsame,noflist)
+ else
+ result = s.start(s,current,plus,noflist)
+ end
+ end
+ top = current
+ else
+ if 1 <= noftop then
+ result = s.stop(s,top,1,noftop)
+ end
+ top = { }
+ end
+ return result
+ else
+ if 1 <= noftop then
+ result = s.stop(s,top,1,noftop)
+ end
+ top = { }
+ return result
+ end
+ end
+
+ local function resolve_end()
+ -- resolve_step(s.unset)
+ local noftop = #top
+ if noftop > 0 then
+ local result = s.stop(s,top,1,#top)
+ remove(tops)
+ top = tops[#tops]
+ switch = top and top.switch
+ return result
+ end
+ end
+
+ local function resolve(t)
+ resolve_begin()
+ for i=1,#t do
+ resolve_step(t[i])
+ end
+ resolve_end()
+ end
+
+ local report = logs.reporter("stacker",name or nil)
+
+ s = {
+ name = name or "unknown",
+ unset = -1,
+ report = report,
+ start = start,
+ stop = stop,
+ change = change,
+ push = push,
+ pop = pop,
+ clean = clean,
+ resolve = resolve,
+ resolve_begin = resolve_begin,
+ resolve_step = resolve_step,
+ resolve_end = resolve_end,
+ }
+
+ return s -- we can overload functions
+
+end
+
+-- local s = utilities.stacker.new("demo")
+--
+-- local unset = s.unset
+-- local push = s.push
+-- local pop = s.pop
+--
+-- local t = {
+-- unset,
+-- unset,
+-- push("a"), -- a
+-- push("b","c"), -- a b c
+-- pop(), -- a b
+-- push("d"), -- a b d
+-- pop(), -- a b
+-- unset,
+-- pop(), -- a
+-- pop(), -- b
+-- unset,
+-- unset,
+-- }
+--
+-- s.resolve(t)
+
+-- demostacker = utilities.stacker.new("demos")
+--
+-- local whatever = {
+-- one = "1 0 0 RG 1 0 0 rg",
+-- two = "1 1 0 RG 1 1 0 rg",
+-- [false] = "0 G 0 g",
+-- }
+--
+-- local concat = table.concat
+--
+-- local pdfliteral = nodes.pool.pdfliteral
+--
+-- function demostacker.start(s,t,first,last)
+-- local n = whatever[t[last]]
+-- -- s.report("start: %s",n)
+-- return pdfliteral(n)
+-- end
+--
+-- function demostacker.stop(s,t,first,last)
+-- local n = whatever[false]
+-- -- s.report("stop: %s",n)
+-- return pdfliteral(n)
+-- end
+--
+-- function demostacker.change(s,t1,first1,last1,t2,first2,last2)
+-- local n = whatever[t2[last2]]
+-- -- s.report("change: %s",n)
+-- return pdfliteral(n)
+-- end
+--
+-- demostacker.mode = "switch"
+--
+-- local whatever = {
+-- one = "/OC /test1 BDC",
+-- two = "/OC /test2 BDC",
+-- [false] = "EMC",
+-- }
+--
+-- demostacker = utilities.stacker.new("demos")
+--
+-- function demostacker.start(s,t,first,last)
+-- local r = { }
+-- for i=first,last do
+-- r[#r+1] = whatever[t[i]]
+-- end
+-- -- s.report("start: %s",concat(r," "))
+-- return pdfliteral(concat(r," "))
+-- end
+--
+-- function demostacker.stop(s,t,first,last)
+-- local r = { }
+-- for i=last,first,-1 do
+-- r[#r+1] = whatever[false]
+-- end
+-- -- s.report("stop: %s",concat(r," "))
+-- return pdfliteral(concat(r," "))
+-- end
+--
+-- function demostacker.change(s,t1,first1,last1,t2,first2,last2)
+-- local r = { }
+-- for i=last1,first1,-1 do
+-- r[#r+1] = whatever[false]
+-- end
+-- for i=first2,last2 do
+-- r[#r+1] = whatever[t2[i]]
+-- end
+-- -- s.report("change: %s",concat(r," "))
+-- return pdfliteral(concat(r," "))
+-- end
+--
+-- demostacker.mode = "stack"
diff --git a/tex/context/base/util-sto.lua b/tex/context/base/util-sto.lua
index 355f0ecd3..191d6cd73 100644
--- a/tex/context/base/util-sto.lua
+++ b/tex/context/base/util-sto.lua
@@ -1,189 +1,189 @@
-if not modules then modules = { } end modules ['util-sto'] = {
- 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 setmetatable, getmetatable, type = setmetatable, getmetatable, type
-
-utilities = utilities or { }
-utilities.storage = utilities.storage or { }
-local storage = utilities.storage
-
-function storage.mark(t)
- if not t then
- print("\nfatal error: storage cannot be marked\n")
- os.exit()
- return
- end
- local m = getmetatable(t)
- if not m then
- m = { }
- setmetatable(t,m)
- end
- m.__storage__ = true
- return t
-end
-
-function storage.allocate(t)
- t = t or { }
- local m = getmetatable(t)
- if not m then
- m = { }
- setmetatable(t,m)
- end
- m.__storage__ = true
- return t
-end
-
-function storage.marked(t)
- local m = getmetatable(t)
- return m and m.__storage__
-end
-
-function storage.checked(t)
- if not t then
- report("\nfatal error: storage has not been allocated\n")
- os.exit()
- return
- end
- return t
-end
-
--- function utilities.storage.delay(parent,name,filename)
--- local m = getmetatable(parent)
--- m.__list[name] = filename
--- end
---
--- function utilities.storage.predefine(parent)
--- local list = { }
--- local m = getmetatable(parent) or {
--- __list = list,
--- __index = function(t,k)
--- local l = require(list[k])
--- t[k] = l
--- return l
--- end
--- }
--- setmetatable(parent,m)
--- end
---
--- bla = { }
--- utilities.storage.predefine(bla)
--- utilities.storage.delay(bla,"test","oepsoeps")
--- local t = bla.test
--- table.print(t)
--- print(t.a)
-
-function storage.setinitializer(data,initialize)
- local m = getmetatable(data) or { }
- m.__index = function(data,k)
- m.__index = nil -- so that we can access the entries during initializing
- initialize()
- return data[k]
- end
- setmetatable(data, m)
-end
-
-local keyisvalue = { __index = function(t,k)
- t[k] = k
- return k
-end }
-
-function storage.sparse(t)
- t = t or { }
- setmetatable(t,keyisvalue)
- return t
-end
-
--- table namespace ?
-
-local function f_empty () return "" end -- t,k
-local function f_self (t,k) t[k] = k return k end
-local function f_table (t,k) local v = { } t[k] = v return v end
-local function f_ignore() end -- t,k,v
-
-local t_empty = { __index = f_empty }
-local t_self = { __index = f_self }
-local t_table = { __index = f_table }
-local t_ignore = { __newindex = f_ignore }
-
-function table.setmetatableindex(t,f)
- if type(t) ~= "table" then
- f, t = t, { }
- end
- local m = getmetatable(t)
- if m then
- if f == "empty" then
- m.__index = f_empty
- elseif f == "key" then
- m.__index = f_self
- elseif f == "table" then
- m.__index = f_table
- else
- m.__index = f
- end
- else
- if f == "empty" then
- setmetatable(t, t_empty)
- elseif f == "key" then
- setmetatable(t, t_self)
- elseif f == "table" then
- setmetatable(t, t_table)
- else
- setmetatable(t,{ __index = f })
- end
- end
- return t
-end
-
-function table.setmetatablenewindex(t,f)
- if type(t) ~= "table" then
- f, t = t, { }
- end
- local m = getmetatable(t)
- if m then
- if f == "ignore" then
- m.__newindex = f_ignore
- else
- m.__newindex = f
- end
- else
- if f == "ignore" then
- setmetatable(t, t_ignore)
- else
- setmetatable(t,{ __newindex = f })
- end
- end
- return t
-end
-
-function table.setmetatablecall(t,f)
- if type(t) ~= "table" then
- f, t = t, { }
- end
- local m = getmetatable(t)
- if m then
- m.__call = f
- else
- setmetatable(t,{ __call = f })
- end
- return t
-end
-
-function table.setmetatablekey(t,key,value)
- local m = getmetatable(t)
- if not m then
- m = { }
- setmetatable(t,m)
- end
- m[key] = value
- return t
-end
-
-function table.getmetatablekey(t,key,value)
- local m = getmetatable(t)
- return m and m[key]
-end
+if not modules then modules = { } end modules ['util-sto'] = {
+ 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 setmetatable, getmetatable, type = setmetatable, getmetatable, type
+
+utilities = utilities or { }
+utilities.storage = utilities.storage or { }
+local storage = utilities.storage
+
+function storage.mark(t)
+ if not t then
+ print("\nfatal error: storage cannot be marked\n")
+ os.exit()
+ return
+ end
+ local m = getmetatable(t)
+ if not m then
+ m = { }
+ setmetatable(t,m)
+ end
+ m.__storage__ = true
+ return t
+end
+
+function storage.allocate(t)
+ t = t or { }
+ local m = getmetatable(t)
+ if not m then
+ m = { }
+ setmetatable(t,m)
+ end
+ m.__storage__ = true
+ return t
+end
+
+function storage.marked(t)
+ local m = getmetatable(t)
+ return m and m.__storage__
+end
+
+function storage.checked(t)
+ if not t then
+ report("\nfatal error: storage has not been allocated\n")
+ os.exit()
+ return
+ end
+ return t
+end
+
+-- function utilities.storage.delay(parent,name,filename)
+-- local m = getmetatable(parent)
+-- m.__list[name] = filename
+-- end
+--
+-- function utilities.storage.predefine(parent)
+-- local list = { }
+-- local m = getmetatable(parent) or {
+-- __list = list,
+-- __index = function(t,k)
+-- local l = require(list[k])
+-- t[k] = l
+-- return l
+-- end
+-- }
+-- setmetatable(parent,m)
+-- end
+--
+-- bla = { }
+-- utilities.storage.predefine(bla)
+-- utilities.storage.delay(bla,"test","oepsoeps")
+-- local t = bla.test
+-- table.print(t)
+-- print(t.a)
+
+function storage.setinitializer(data,initialize)
+ local m = getmetatable(data) or { }
+ m.__index = function(data,k)
+ m.__index = nil -- so that we can access the entries during initializing
+ initialize()
+ return data[k]
+ end
+ setmetatable(data, m)
+end
+
+local keyisvalue = { __index = function(t,k)
+ t[k] = k
+ return k
+end }
+
+function storage.sparse(t)
+ t = t or { }
+ setmetatable(t,keyisvalue)
+ return t
+end
+
+-- table namespace ?
+
+local function f_empty () return "" end -- t,k
+local function f_self (t,k) t[k] = k return k end
+local function f_table (t,k) local v = { } t[k] = v return v end
+local function f_ignore() end -- t,k,v
+
+local t_empty = { __index = f_empty }
+local t_self = { __index = f_self }
+local t_table = { __index = f_table }
+local t_ignore = { __newindex = f_ignore }
+
+function table.setmetatableindex(t,f)
+ if type(t) ~= "table" then
+ f, t = t, { }
+ end
+ local m = getmetatable(t)
+ if m then
+ if f == "empty" then
+ m.__index = f_empty
+ elseif f == "key" then
+ m.__index = f_self
+ elseif f == "table" then
+ m.__index = f_table
+ else
+ m.__index = f
+ end
+ else
+ if f == "empty" then
+ setmetatable(t, t_empty)
+ elseif f == "key" then
+ setmetatable(t, t_self)
+ elseif f == "table" then
+ setmetatable(t, t_table)
+ else
+ setmetatable(t,{ __index = f })
+ end
+ end
+ return t
+end
+
+function table.setmetatablenewindex(t,f)
+ if type(t) ~= "table" then
+ f, t = t, { }
+ end
+ local m = getmetatable(t)
+ if m then
+ if f == "ignore" then
+ m.__newindex = f_ignore
+ else
+ m.__newindex = f
+ end
+ else
+ if f == "ignore" then
+ setmetatable(t, t_ignore)
+ else
+ setmetatable(t,{ __newindex = f })
+ end
+ end
+ return t
+end
+
+function table.setmetatablecall(t,f)
+ if type(t) ~= "table" then
+ f, t = t, { }
+ end
+ local m = getmetatable(t)
+ if m then
+ m.__call = f
+ else
+ setmetatable(t,{ __call = f })
+ end
+ return t
+end
+
+function table.setmetatablekey(t,key,value)
+ local m = getmetatable(t)
+ if not m then
+ m = { }
+ setmetatable(t,m)
+ end
+ m[key] = value
+ return t
+end
+
+function table.getmetatablekey(t,key,value)
+ local m = getmetatable(t)
+ return m and m[key]
+end
diff --git a/tex/context/base/util-str.lua b/tex/context/base/util-str.lua
index f671b0012..4890a11d6 100644
--- a/tex/context/base/util-str.lua
+++ b/tex/context/base/util-str.lua
@@ -1,766 +1,766 @@
-if not modules then modules = { } end modules ['util-str'] = {
- 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"
-}
-
-utilities = utilities or {}
-utilities.strings = utilities.strings or { }
-local strings = utilities.strings
-
-local format, gsub, rep, sub = string.format, string.gsub, string.rep, string.sub
-local load, dump = load, string.dump
-local tonumber, type, tostring = tonumber, type, tostring
-local unpack, concat = table.unpack, table.concat
-local P, V, C, S, R, Ct, Cs, Cp, Carg, Cc = lpeg.P, lpeg.V, lpeg.C, lpeg.S, lpeg.R, lpeg.Ct, lpeg.Cs, lpeg.Cp, lpeg.Carg, lpeg.Cc
-local patterns, lpegmatch = lpeg.patterns, lpeg.match
-local utfchar, utfbyte = utf.char, utf.byte
------ loadstripped = utilities.lua.loadstripped
------ setmetatableindex = table.setmetatableindex
-
-local loadstripped = _LUAVERSION < 5.2 and load or function(str)
- return load(dump(load(str),true)) -- it only makes sense in luajit and luatex where we have a stipped load
-end
-
--- todo: make a special namespace for the formatter
-
-if not number then number = { } end -- temp hack for luatex-fonts
-
-local stripper = patterns.stripzeros
-
-local function points(n)
- return (not n or n == 0) and "0pt" or lpegmatch(stripper,format("%.5fpt",n/65536))
-end
-
-local function basepoints(n)
- return (not n or n == 0) and "0bp" or lpegmatch(stripper,format("%.5fbp", n*(7200/7227)/65536))
-end
-
-number.points = points
-number.basepoints = basepoints
-
--- str = " \n \ntest \n test\ntest "
--- print("["..string.gsub(string.collapsecrlf(str),"\n","+").."]")
-
-local rubish = patterns.spaceortab^0 * patterns.newline
-local anyrubish = patterns.spaceortab + patterns.newline
-local anything = patterns.anything
-local stripped = (patterns.spaceortab^1 / "") * patterns.newline
-local leading = rubish^0 / ""
-local trailing = (anyrubish^1 * patterns.endofstring) / ""
-local redundant = rubish^3 / "\n"
-
-local pattern = Cs(leading * (trailing + redundant + stripped + anything)^0)
-
-function strings.collapsecrlf(str)
- return lpegmatch(pattern,str)
-end
-
--- The following functions might end up in another namespace.
-
-local repeaters = { } -- watch how we also moved the -1 in depth-1 to the creator
-
-function strings.newrepeater(str,offset)
- offset = offset or 0
- local s = repeaters[str]
- if not s then
- s = { }
- repeaters[str] = s
- end
- local t = s[offset]
- if t then
- return t
- end
- t = { }
- setmetatable(t, { __index = function(t,k)
- if not k then
- return ""
- end
- local n = k + offset
- local s = n > 0 and rep(str,n) or ""
- t[k] = s
- return s
- end })
- s[offset] = t
- return t
-end
-
--- local dashes = strings.newrepeater("--",-1)
--- print(dashes[2],dashes[3],dashes[1])
-
-local extra, tab, start = 0, 0, 4, 0
-
-local nspaces = strings.newrepeater(" ")
-
-string.nspaces = nspaces
-
-local pattern =
- Carg(1) / function(t)
- extra, tab, start = 0, t or 7, 1
- end
- * Cs((
- Cp() * patterns.tab / function(position)
- local current = (position - start + 1) + extra
- local spaces = tab-(current-1) % tab
- if spaces > 0 then
- extra = extra + spaces - 1
- return nspaces[spaces] -- rep(" ",spaces)
- else
- return ""
- end
- end
- + patterns.newline * Cp() / function(position)
- extra, start = 0, position
- end
- + patterns.anything
- )^1)
-
-function strings.tabtospace(str,tab)
- return lpegmatch(pattern,str,1,tab or 7)
-end
-
--- local t = {
--- "1234567123456712345671234567",
--- "\tb\tc",
--- "a\tb\tc",
--- "aa\tbb\tcc",
--- "aaa\tbbb\tccc",
--- "aaaa\tbbbb\tcccc",
--- "aaaaa\tbbbbb\tccccc",
--- "aaaaaa\tbbbbbb\tcccccc\n aaaaaa\tbbbbbb\tcccccc",
--- "one\n two\nxxx three\nxx four\nx five\nsix",
--- }
--- for k=1,#t do
--- print(strings.tabtospace(t[k]))
--- end
-
-function strings.striplong(str) -- strips all leading spaces
- str = gsub(str,"^%s*","")
- str = gsub(str,"[\n\r]+ *","\n")
- return str
-end
-
--- local template = string.striplong([[
--- aaaa
--- bb
--- cccccc
--- ]])
-
-function strings.nice(str)
- str = gsub(str,"[:%-+_]+"," ") -- maybe more
- return str
-end
-
--- Work in progress. Interesting is that compared to the built-in this is faster in
--- luatex than in luajittex where we have a comparable speed. It only makes sense
--- to use the formatter when a (somewhat) complex format is used a lot. Each formatter
--- is a function so there is some overhead and not all formatted output is worth that
--- overhead. Keep in mind that there is an extra function call involved. In principle
--- we end up with a string concatination so one could inline such a sequence but often
--- at the cost of less readabinity. So, it's a sort of (visual) compromise. Of course
--- there is the benefit of more variants. (Concerning the speed: a simple format like
--- %05fpt is better off with format than with a formatter, but as soon as you put
--- something in front formatters become faster. Passing the pt as extra argument makes
--- formatters behave better. Of course this is rather implementation dependent. Also,
--- when a specific format is only used a few times the overhead in creating it is not
--- compensated by speed.)
---
--- More info can be found in cld-mkiv.pdf so here I stick to a simple list.
---
--- integer %...i number
--- integer %...d number
--- unsigned %...u number
--- character %...c number
--- hexadecimal %...x number
--- HEXADECIMAL %...X number
--- octal %...o number
--- string %...s string number
--- float %...f number
--- exponential %...e number
--- exponential %...E number
--- autofloat %...g number
--- autofloat %...G number
--- utf character %...c number
--- force tostring %...S any
--- force tostring %Q any
--- force tonumber %N number (strip leading zeros)
--- signed number %I number
--- rounded number %r number
--- 0xhexadecimal %...h character number
--- 0xHEXADECIMAL %...H character number
--- U+hexadecimal %...u character number
--- U+HEXADECIMAL %...U character number
--- points %p number (scaled points)
--- basepoints %b number (scaled points)
--- table concat %...t table
--- serialize %...T sequenced (no nested tables)
--- boolean (logic) %l boolean
--- BOOLEAN %L boolean
--- whitespace %...w
--- automatic %...a 'whatever' (string, table, ...)
--- automatic %...a "whatever" (string, table, ...)
-
-local n = 0
-
--- we are somewhat sloppy in parsing prefixes as it's not that critical
-
--- hard to avoid but we can collect them in a private namespace if needed
-
--- inline the next two makes no sense as we only use this in logging
-
-local sequenced = table.sequenced
-
-function string.autodouble(s,sep)
- if s == nil then
- return '""'
- end
- local t = type(s)
- if t == "number" then
- return tostring(s) -- tostring not really needed
- end
- if t == "table" then
- return ('"' .. sequenced(s,sep or ",") .. '"')
- end
- return ('"' .. tostring(s) .. '"')
-end
-
-function string.autosingle(s,sep)
- if s == nil then
- return "''"
- end
- local t = type(s)
- if t == "number" then
- return tostring(s) -- tostring not really needed
- end
- if t == "table" then
- return ("'" .. sequenced(s,sep or ",") .. "'")
- end
- return ("'" .. tostring(s) .. "'")
-end
-
-local tracedchars = { }
-string.tracedchars = tracedchars
-strings.tracers = tracedchars
-
-function string.tracedchar(b)
- -- todo: table
- if type(b) == "number" then
- return tracedchars[b] or (utfchar(b) .. " (U+" .. format('%05X',b) .. ")")
- else
- local c = utfbyte(b)
- return tracedchars[c] or (b .. " (U+" .. format('%05X',c) .. ")")
- end
-end
-
-function number.signed(i)
- if i > 0 then
- return "+", i
- else
- return "-", -i
- end
-end
-
-local preamble = [[
-local type = type
-local tostring = tostring
-local tonumber = tonumber
-local format = string.format
-local concat = table.concat
-local signed = number.signed
-local points = number.points
-local basepoints = number.basepoints
-local utfchar = utf.char
-local utfbyte = utf.byte
-local lpegmatch = lpeg.match
-local nspaces = string.nspaces
-local tracedchar = string.tracedchar
-local autosingle = string.autosingle
-local autodouble = string.autodouble
-local sequenced = table.sequenced
-]]
-
-local template = [[
-%s
-%s
-return function(%s) return %s end
-]]
-
-local arguments = { "a1" } -- faster than previously used (select(n,...))
-
-setmetatable(arguments, { __index =
- function(t,k)
- local v = t[k-1] .. ",a" .. k
- t[k] = v
- return v
- end
-})
-
-local prefix_any = C((S("+- .") + R("09"))^0)
-local prefix_tab = C((1-R("az","AZ","09","%%"))^0)
-
--- we've split all cases as then we can optimize them (let's omit the fuzzy u)
-
--- todo: replace outer formats in next by ..
-
-local format_s = function(f)
- n = n + 1
- if f and f ~= "" then
- return format("format('%%%ss',a%s)",f,n)
- else -- best no tostring in order to stay compatible (.. does a selective tostring too)
- return format("(a%s or '')",n) -- goodie: nil check
- end
-end
-
-local format_S = function(f) -- can be optimized
- n = n + 1
- if f and f ~= "" then
- return format("format('%%%ss',tostring(a%s))",f,n)
- else
- return format("tostring(a%s)",n)
- end
-end
-
-local format_q = function()
- n = n + 1
- return format("(a%s and format('%%q',a%s) or '')",n,n) -- goodie: nil check (maybe separate lpeg, not faster)
-end
-
-local format_Q = function() -- can be optimized
- n = n + 1
- return format("format('%%q',tostring(a%s))",n)
-end
-
-local format_i = function(f)
- n = n + 1
- if f and f ~= "" then
- return format("format('%%%si',a%s)",f,n)
- else
- return format("a%s",n)
- end
-end
-
-local format_d = format_i
-
-local format_I = function(f)
- n = n + 1
- return format("format('%%s%%%si',signed(a%s))",f,n)
-end
-
-local format_f = function(f)
- n = n + 1
- return format("format('%%%sf',a%s)",f,n)
-end
-
-local format_g = function(f)
- n = n + 1
- return format("format('%%%sg',a%s)",f,n)
-end
-
-local format_G = function(f)
- n = n + 1
- return format("format('%%%sG',a%s)",f,n)
-end
-
-local format_e = function(f)
- n = n + 1
- return format("format('%%%se',a%s)",f,n)
-end
-
-local format_E = function(f)
- n = n + 1
- return format("format('%%%sE',a%s)",f,n)
-end
-
-local format_x = function(f)
- n = n + 1
- return format("format('%%%sx',a%s)",f,n)
-end
-
-local format_X = function(f)
- n = n + 1
- return format("format('%%%sX',a%s)",f,n)
-end
-
-local format_o = function(f)
- n = n + 1
- return format("format('%%%so',a%s)",f,n)
-end
-
-local format_c = function()
- n = n + 1
- return format("utfchar(a%s)",n)
-end
-
-local format_C = function()
- n = n + 1
- return format("tracedchar(a%s)",n)
-end
-
-local format_r = function(f)
- n = n + 1
- return format("format('%%%s.0f',a%s)",f,n)
-end
-
-local format_h = function(f)
- n = n + 1
- if f == "-" then
- f = sub(f,2)
- return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f == "" and "05" or f,n,n,n)
- else
- return format("format('0x%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f == "" and "05" or f,n,n,n)
- end
-end
-
-local format_H = function(f)
- n = n + 1
- if f == "-" then
- f = sub(f,2)
- return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f == "" and "05" or f,n,n,n)
- else
- return format("format('0x%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f == "" and "05" or f,n,n,n)
- end
-end
-
-local format_u = function(f)
- n = n + 1
- if f == "-" then
- f = sub(f,2)
- return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f == "" and "05" or f,n,n,n)
- else
- return format("format('u+%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f == "" and "05" or f,n,n,n)
- end
-end
-
-local format_U = function(f)
- n = n + 1
- if f == "-" then
- f = sub(f,2)
- return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f == "" and "05" or f,n,n,n)
- else
- return format("format('U+%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f == "" and "05" or f,n,n,n)
- end
-end
-
-local format_p = function()
- n = n + 1
- return format("points(a%s)",n)
-end
-
-local format_b = function()
- n = n + 1
- return format("basepoints(a%s)",n)
-end
-
-local format_t = function(f)
- n = n + 1
- if f and f ~= "" then
- return format("concat(a%s,%q)",n,f)
- else
- return format("concat(a%s)",n)
- end
-end
-
-local format_T = function(f)
- n = n + 1
- if f and f ~= "" then
- return format("sequenced(a%s,%q)",n,f)
- else
- return format("sequenced(a%s)",n)
- end
-end
-
-local format_l = function()
- n = n + 1
- return format("(a%s and 'true' or 'false')",n)
-end
-
-local format_L = function()
- n = n + 1
- return format("(a%s and 'TRUE' or 'FALSE')",n)
-end
-
-local format_N = function() -- strips leading zeros
- n = n + 1
- return format("tostring(tonumber(a%s) or a%s)",n,n)
-end
-
-local format_a = function(f)
- n = n + 1
- if f and f ~= "" then
- return format("autosingle(a%s,%q)",n,f)
- else
- return format("autosingle(a%s)",n)
- end
-end
-
-local format_A = function(f)
- n = n + 1
- if f and f ~= "" then
- return format("autodouble(a%s,%q)",n,f)
- else
- return format("autodouble(a%s)",n)
- end
-end
-
-local format_w = function(f) -- handy when doing depth related indent
- n = n + 1
- f = tonumber(f)
- if f then -- not that useful
- return format("nspaces[%s+a%s]",f,n) -- no real need for tonumber
- else
- return format("nspaces[a%s]",n) -- no real need for tonumber
- end
-end
-
-local format_W = function(f) -- handy when doing depth related indent
- return format("nspaces[%s]",tonumber(f) or 0)
-end
-
-local format_rest = function(s)
- return format("%q",s) -- catches " and \n and such
-end
-
-local format_extension = function(extensions,f,name)
- local extension = extensions[name] or "tostring(%s)"
- local f = tonumber(f) or 1
- if f == 0 then
- return extension
- elseif f == 1 then
- n = n + 1
- local a = "a" .. n
- return format(extension,a,a) -- maybe more times?
- elseif f < 0 then
- local a = "a" .. (n + f + 1)
- return format(extension,a,a)
- else
- local t = { }
- for i=1,f do
- n = n + 1
- t[#t+1] = "a" .. n
- end
- return format(extension,unpack(t))
- end
-end
-
-local builder = Cs { "start",
- start = (
- (
- P("%") / ""
- * (
- V("!") -- new
- + V("s") + V("q")
- + V("i") + V("d")
- + V("f") + V("g") + V("G") + V("e") + V("E")
- + V("x") + V("X") + V("o")
- --
- + V("c")
- + V("C")
- + V("S") -- new
- + V("Q") -- new
- + V("N") -- new
- --
- + V("r")
- + V("h") + V("H") + V("u") + V("U")
- + V("p") + V("b")
- + V("t") + V("T")
- + V("l") + V("L")
- + V("I")
- + V("h") -- new
- + V("w") -- new
- + V("W") -- new
- + V("a") -- new
- + V("A") -- new
- --
- + V("*") -- ignores probably messed up %
- )
- + V("*")
- )
- * (P(-1) + Carg(1))
- )^0,
- --
- ["s"] = (prefix_any * P("s")) / format_s, -- %s => regular %s (string)
- ["q"] = (prefix_any * P("q")) / format_q, -- %q => regular %q (quoted string)
- ["i"] = (prefix_any * P("i")) / format_i, -- %i => regular %i (integer)
- ["d"] = (prefix_any * P("d")) / format_d, -- %d => regular %d (integer)
- ["f"] = (prefix_any * P("f")) / format_f, -- %f => regular %f (float)
- ["g"] = (prefix_any * P("g")) / format_g, -- %g => regular %g (float)
- ["G"] = (prefix_any * P("G")) / format_G, -- %G => regular %G (float)
- ["e"] = (prefix_any * P("e")) / format_e, -- %e => regular %e (float)
- ["E"] = (prefix_any * P("E")) / format_E, -- %E => regular %E (float)
- ["x"] = (prefix_any * P("x")) / format_x, -- %x => regular %x (hexadecimal)
- ["X"] = (prefix_any * P("X")) / format_X, -- %X => regular %X (HEXADECIMAL)
- ["o"] = (prefix_any * P("o")) / format_o, -- %o => regular %o (octal)
- --
- ["S"] = (prefix_any * P("S")) / format_S, -- %S => %s (tostring)
- ["Q"] = (prefix_any * P("Q")) / format_S, -- %Q => %q (tostring)
- ["N"] = (prefix_any * P("N")) / format_N, -- %N => tonumber (strips leading zeros)
- ["c"] = (prefix_any * P("c")) / format_c, -- %c => utf character (extension to regular)
- ["C"] = (prefix_any * P("C")) / format_C, -- %c => U+.... utf character
- --
- ["r"] = (prefix_any * P("r")) / format_r, -- %r => round
- ["h"] = (prefix_any * P("h")) / format_h, -- %h => 0x0a1b2 (when - no 0x) was v
- ["H"] = (prefix_any * P("H")) / format_H, -- %H => 0x0A1B2 (when - no 0x) was V
- ["u"] = (prefix_any * P("u")) / format_u, -- %u => u+0a1b2 (when - no u+)
- ["U"] = (prefix_any * P("U")) / format_U, -- %U => U+0A1B2 (when - no U+)
- ["p"] = (prefix_any * P("p")) / format_p, -- %p => 12.345pt / maybe: P (and more units)
- ["b"] = (prefix_any * P("b")) / format_b, -- %b => 12.342bp / maybe: B (and more units)
- ["t"] = (prefix_tab * P("t")) / format_t, -- %t => concat
- ["T"] = (prefix_tab * P("T")) / format_T, -- %t => sequenced
- ["l"] = (prefix_tab * P("l")) / format_l, -- %l => boolean
- ["L"] = (prefix_tab * P("L")) / format_L, -- %L => BOOLEAN
- ["I"] = (prefix_any * P("I")) / format_I, -- %I => signed integer
- --
- ["w"] = (prefix_any * P("w")) / format_w, -- %w => n spaces (optional prefix is added)
- ["W"] = (prefix_any * P("W")) / format_W, -- %W => mandate prefix, no specifier
- --
- ["a"] = (prefix_any * P("a")) / format_a, -- %a => '...' (forces tostring)
- ["A"] = (prefix_any * P("A")) / format_A, -- %A => "..." (forces tostring)
- --
- ["*"] = Cs(((1-P("%"))^1 + P("%%")/"%%%%")^1) / format_rest, -- rest (including %%)
- --
- ["!"] = Carg(2) * prefix_any * P("!") * C((1-P("!"))^1) * P("!") / format_extension,
-}
-
--- we can be clever and only alias what is needed
-
-local direct = Cs (
- P("%")/""
- * Cc([[local format = string.format return function(str) return format("%]])
- * (S("+- .") + R("09"))^0
- * S("sqidfgGeExXo")
- * Cc([[",str) end]])
- * P(-1)
- )
-
-local function make(t,str)
- local f
- local p
- local p = lpegmatch(direct,str)
- if p then
- f = loadstripped(p)()
- else
- n = 0
- p = lpegmatch(builder,str,1,"..",t._extensions_) -- after this we know n
- if n > 0 then
- p = format(template,preamble,t._preamble_,arguments[n],p)
--- print("builder>",p)
- f = loadstripped(p)()
- else
- f = function() return str end
- end
- end
- t[str] = f
- return f
-end
-
--- -- collect periodically
---
--- local threshold = 1000 -- max nof cached formats
---
--- local function make(t,str)
--- local f = rawget(t,str)
--- if f then
--- return f
--- end
--- local parent = t._t_
--- if parent._n_ > threshold then
--- local m = { _t_ = parent }
--- getmetatable(parent).__index = m
--- setmetatable(m, { __index = make })
--- else
--- parent._n_ = parent._n_ + 1
--- end
--- local f
--- local p = lpegmatch(direct,str)
--- if p then
--- f = loadstripped(p)()
--- else
--- n = 0
--- p = lpegmatch(builder,str,1,"..",parent._extensions_) -- after this we know n
--- if n > 0 then
--- p = format(template,preamble,parent._preamble_,arguments[n],p)
--- -- print("builder>",p)
--- f = loadstripped(p)()
--- else
--- f = function() return str end
--- end
--- end
--- t[str] = f
--- return f
--- end
-
-local function use(t,fmt,...)
- return t[fmt](...)
-end
-
-strings.formatters = { }
-
--- we cannot make these tables weak, unless we start using an indirect
--- table (metatable) in which case we could better keep a count and
--- clear that table when a threshold is reached
-
-function strings.formatters.new()
- local t = { _extensions_ = { }, _preamble_ = "", _type_ = "formatter" }
- setmetatable(t, { __index = make, __call = use })
- return t
-end
-
--- function strings.formatters.new()
--- local t = { _extensions_ = { }, _preamble_ = "", _type_ = "formatter", _n_ = 0 }
--- local m = { _t_ = t }
--- setmetatable(t, { __index = m, __call = use })
--- setmetatable(m, { __index = make })
--- return t
--- end
-
-local formatters = strings.formatters.new() -- the default instance
-
-string.formatters = formatters -- in the main string namespace
-string.formatter = function(str,...) return formatters[str](...) end -- sometimes nicer name
-
-local function add(t,name,template,preamble)
- if type(t) == "table" and t._type_ == "formatter" then
- t._extensions_[name] = template or "%s"
- if preamble then
- t._preamble_ = preamble .. "\n" .. t._preamble_ -- so no overload !
- end
- end
-end
-
-strings.formatters.add = add
-
--- registered in the default instance (should we fall back on this one?)
-
-lpeg.patterns.xmlescape = Cs((P("<")/"&lt;" + P(">")/"&gt;" + P("&")/"&amp;" + P('"')/"&quot;" + P(1))^0)
-lpeg.patterns.texescape = Cs((C(S("#$%\\{}"))/"\\%1" + P(1))^0)
-
-add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],[[local xmlescape = lpeg.patterns.xmlescape]])
-add(formatters,"tex",[[lpegmatch(texescape,%s)]],[[local texescape = lpeg.patterns.texescape]])
-
--- -- yes or no:
---
--- local function make(t,str)
--- local f
--- local p = lpegmatch(direct,str)
--- if p then
--- f = loadstripped(p)()
--- else
--- n = 0
--- p = lpegmatch(builder,str,1,",") -- after this we know n
--- if n > 0 then
--- p = format(template,template_shortcuts,arguments[n],p)
--- f = loadstripped(p)()
--- else
--- f = function() return str end
--- end
--- end
--- t[str] = f
--- return f
--- end
---
--- local formatteds = string.formatteds or { }
--- string.formatteds = formatteds
---
--- setmetatable(formatteds, { __index = make, __call = use })
+if not modules then modules = { } end modules ['util-str'] = {
+ 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"
+}
+
+utilities = utilities or {}
+utilities.strings = utilities.strings or { }
+local strings = utilities.strings
+
+local format, gsub, rep, sub = string.format, string.gsub, string.rep, string.sub
+local load, dump = load, string.dump
+local tonumber, type, tostring = tonumber, type, tostring
+local unpack, concat = table.unpack, table.concat
+local P, V, C, S, R, Ct, Cs, Cp, Carg, Cc = lpeg.P, lpeg.V, lpeg.C, lpeg.S, lpeg.R, lpeg.Ct, lpeg.Cs, lpeg.Cp, lpeg.Carg, lpeg.Cc
+local patterns, lpegmatch = lpeg.patterns, lpeg.match
+local utfchar, utfbyte = utf.char, utf.byte
+----- loadstripped = utilities.lua.loadstripped
+----- setmetatableindex = table.setmetatableindex
+
+local loadstripped = _LUAVERSION < 5.2 and load or function(str)
+ return load(dump(load(str),true)) -- it only makes sense in luajit and luatex where we have a stipped load
+end
+
+-- todo: make a special namespace for the formatter
+
+if not number then number = { } end -- temp hack for luatex-fonts
+
+local stripper = patterns.stripzeros
+
+local function points(n)
+ return (not n or n == 0) and "0pt" or lpegmatch(stripper,format("%.5fpt",n/65536))
+end
+
+local function basepoints(n)
+ return (not n or n == 0) and "0bp" or lpegmatch(stripper,format("%.5fbp", n*(7200/7227)/65536))
+end
+
+number.points = points
+number.basepoints = basepoints
+
+-- str = " \n \ntest \n test\ntest "
+-- print("["..string.gsub(string.collapsecrlf(str),"\n","+").."]")
+
+local rubish = patterns.spaceortab^0 * patterns.newline
+local anyrubish = patterns.spaceortab + patterns.newline
+local anything = patterns.anything
+local stripped = (patterns.spaceortab^1 / "") * patterns.newline
+local leading = rubish^0 / ""
+local trailing = (anyrubish^1 * patterns.endofstring) / ""
+local redundant = rubish^3 / "\n"
+
+local pattern = Cs(leading * (trailing + redundant + stripped + anything)^0)
+
+function strings.collapsecrlf(str)
+ return lpegmatch(pattern,str)
+end
+
+-- The following functions might end up in another namespace.
+
+local repeaters = { } -- watch how we also moved the -1 in depth-1 to the creator
+
+function strings.newrepeater(str,offset)
+ offset = offset or 0
+ local s = repeaters[str]
+ if not s then
+ s = { }
+ repeaters[str] = s
+ end
+ local t = s[offset]
+ if t then
+ return t
+ end
+ t = { }
+ setmetatable(t, { __index = function(t,k)
+ if not k then
+ return ""
+ end
+ local n = k + offset
+ local s = n > 0 and rep(str,n) or ""
+ t[k] = s
+ return s
+ end })
+ s[offset] = t
+ return t
+end
+
+-- local dashes = strings.newrepeater("--",-1)
+-- print(dashes[2],dashes[3],dashes[1])
+
+local extra, tab, start = 0, 0, 4, 0
+
+local nspaces = strings.newrepeater(" ")
+
+string.nspaces = nspaces
+
+local pattern =
+ Carg(1) / function(t)
+ extra, tab, start = 0, t or 7, 1
+ end
+ * Cs((
+ Cp() * patterns.tab / function(position)
+ local current = (position - start + 1) + extra
+ local spaces = tab-(current-1) % tab
+ if spaces > 0 then
+ extra = extra + spaces - 1
+ return nspaces[spaces] -- rep(" ",spaces)
+ else
+ return ""
+ end
+ end
+ + patterns.newline * Cp() / function(position)
+ extra, start = 0, position
+ end
+ + patterns.anything
+ )^1)
+
+function strings.tabtospace(str,tab)
+ return lpegmatch(pattern,str,1,tab or 7)
+end
+
+-- local t = {
+-- "1234567123456712345671234567",
+-- "\tb\tc",
+-- "a\tb\tc",
+-- "aa\tbb\tcc",
+-- "aaa\tbbb\tccc",
+-- "aaaa\tbbbb\tcccc",
+-- "aaaaa\tbbbbb\tccccc",
+-- "aaaaaa\tbbbbbb\tcccccc\n aaaaaa\tbbbbbb\tcccccc",
+-- "one\n two\nxxx three\nxx four\nx five\nsix",
+-- }
+-- for k=1,#t do
+-- print(strings.tabtospace(t[k]))
+-- end
+
+function strings.striplong(str) -- strips all leading spaces
+ str = gsub(str,"^%s*","")
+ str = gsub(str,"[\n\r]+ *","\n")
+ return str
+end
+
+-- local template = string.striplong([[
+-- aaaa
+-- bb
+-- cccccc
+-- ]])
+
+function strings.nice(str)
+ str = gsub(str,"[:%-+_]+"," ") -- maybe more
+ return str
+end
+
+-- Work in progress. Interesting is that compared to the built-in this is faster in
+-- luatex than in luajittex where we have a comparable speed. It only makes sense
+-- to use the formatter when a (somewhat) complex format is used a lot. Each formatter
+-- is a function so there is some overhead and not all formatted output is worth that
+-- overhead. Keep in mind that there is an extra function call involved. In principle
+-- we end up with a string concatination so one could inline such a sequence but often
+-- at the cost of less readabinity. So, it's a sort of (visual) compromise. Of course
+-- there is the benefit of more variants. (Concerning the speed: a simple format like
+-- %05fpt is better off with format than with a formatter, but as soon as you put
+-- something in front formatters become faster. Passing the pt as extra argument makes
+-- formatters behave better. Of course this is rather implementation dependent. Also,
+-- when a specific format is only used a few times the overhead in creating it is not
+-- compensated by speed.)
+--
+-- More info can be found in cld-mkiv.pdf so here I stick to a simple list.
+--
+-- integer %...i number
+-- integer %...d number
+-- unsigned %...u number
+-- character %...c number
+-- hexadecimal %...x number
+-- HEXADECIMAL %...X number
+-- octal %...o number
+-- string %...s string number
+-- float %...f number
+-- exponential %...e number
+-- exponential %...E number
+-- autofloat %...g number
+-- autofloat %...G number
+-- utf character %...c number
+-- force tostring %...S any
+-- force tostring %Q any
+-- force tonumber %N number (strip leading zeros)
+-- signed number %I number
+-- rounded number %r number
+-- 0xhexadecimal %...h character number
+-- 0xHEXADECIMAL %...H character number
+-- U+hexadecimal %...u character number
+-- U+HEXADECIMAL %...U character number
+-- points %p number (scaled points)
+-- basepoints %b number (scaled points)
+-- table concat %...t table
+-- serialize %...T sequenced (no nested tables)
+-- boolean (logic) %l boolean
+-- BOOLEAN %L boolean
+-- whitespace %...w
+-- automatic %...a 'whatever' (string, table, ...)
+-- automatic %...a "whatever" (string, table, ...)
+
+local n = 0
+
+-- we are somewhat sloppy in parsing prefixes as it's not that critical
+
+-- hard to avoid but we can collect them in a private namespace if needed
+
+-- inline the next two makes no sense as we only use this in logging
+
+local sequenced = table.sequenced
+
+function string.autodouble(s,sep)
+ if s == nil then
+ return '""'
+ end
+ local t = type(s)
+ if t == "number" then
+ return tostring(s) -- tostring not really needed
+ end
+ if t == "table" then
+ return ('"' .. sequenced(s,sep or ",") .. '"')
+ end
+ return ('"' .. tostring(s) .. '"')
+end
+
+function string.autosingle(s,sep)
+ if s == nil then
+ return "''"
+ end
+ local t = type(s)
+ if t == "number" then
+ return tostring(s) -- tostring not really needed
+ end
+ if t == "table" then
+ return ("'" .. sequenced(s,sep or ",") .. "'")
+ end
+ return ("'" .. tostring(s) .. "'")
+end
+
+local tracedchars = { }
+string.tracedchars = tracedchars
+strings.tracers = tracedchars
+
+function string.tracedchar(b)
+ -- todo: table
+ if type(b) == "number" then
+ return tracedchars[b] or (utfchar(b) .. " (U+" .. format('%05X',b) .. ")")
+ else
+ local c = utfbyte(b)
+ return tracedchars[c] or (b .. " (U+" .. format('%05X',c) .. ")")
+ end
+end
+
+function number.signed(i)
+ if i > 0 then
+ return "+", i
+ else
+ return "-", -i
+ end
+end
+
+local preamble = [[
+local type = type
+local tostring = tostring
+local tonumber = tonumber
+local format = string.format
+local concat = table.concat
+local signed = number.signed
+local points = number.points
+local basepoints = number.basepoints
+local utfchar = utf.char
+local utfbyte = utf.byte
+local lpegmatch = lpeg.match
+local nspaces = string.nspaces
+local tracedchar = string.tracedchar
+local autosingle = string.autosingle
+local autodouble = string.autodouble
+local sequenced = table.sequenced
+]]
+
+local template = [[
+%s
+%s
+return function(%s) return %s end
+]]
+
+local arguments = { "a1" } -- faster than previously used (select(n,...))
+
+setmetatable(arguments, { __index =
+ function(t,k)
+ local v = t[k-1] .. ",a" .. k
+ t[k] = v
+ return v
+ end
+})
+
+local prefix_any = C((S("+- .") + R("09"))^0)
+local prefix_tab = C((1-R("az","AZ","09","%%"))^0)
+
+-- we've split all cases as then we can optimize them (let's omit the fuzzy u)
+
+-- todo: replace outer formats in next by ..
+
+local format_s = function(f)
+ n = n + 1
+ if f and f ~= "" then
+ return format("format('%%%ss',a%s)",f,n)
+ else -- best no tostring in order to stay compatible (.. does a selective tostring too)
+ return format("(a%s or '')",n) -- goodie: nil check
+ end
+end
+
+local format_S = function(f) -- can be optimized
+ n = n + 1
+ if f and f ~= "" then
+ return format("format('%%%ss',tostring(a%s))",f,n)
+ else
+ return format("tostring(a%s)",n)
+ end
+end
+
+local format_q = function()
+ n = n + 1
+ return format("(a%s and format('%%q',a%s) or '')",n,n) -- goodie: nil check (maybe separate lpeg, not faster)
+end
+
+local format_Q = function() -- can be optimized
+ n = n + 1
+ return format("format('%%q',tostring(a%s))",n)
+end
+
+local format_i = function(f)
+ n = n + 1
+ if f and f ~= "" then
+ return format("format('%%%si',a%s)",f,n)
+ else
+ return format("a%s",n)
+ end
+end
+
+local format_d = format_i
+
+local format_I = function(f)
+ n = n + 1
+ return format("format('%%s%%%si',signed(a%s))",f,n)
+end
+
+local format_f = function(f)
+ n = n + 1
+ return format("format('%%%sf',a%s)",f,n)
+end
+
+local format_g = function(f)
+ n = n + 1
+ return format("format('%%%sg',a%s)",f,n)
+end
+
+local format_G = function(f)
+ n = n + 1
+ return format("format('%%%sG',a%s)",f,n)
+end
+
+local format_e = function(f)
+ n = n + 1
+ return format("format('%%%se',a%s)",f,n)
+end
+
+local format_E = function(f)
+ n = n + 1
+ return format("format('%%%sE',a%s)",f,n)
+end
+
+local format_x = function(f)
+ n = n + 1
+ return format("format('%%%sx',a%s)",f,n)
+end
+
+local format_X = function(f)
+ n = n + 1
+ return format("format('%%%sX',a%s)",f,n)
+end
+
+local format_o = function(f)
+ n = n + 1
+ return format("format('%%%so',a%s)",f,n)
+end
+
+local format_c = function()
+ n = n + 1
+ return format("utfchar(a%s)",n)
+end
+
+local format_C = function()
+ n = n + 1
+ return format("tracedchar(a%s)",n)
+end
+
+local format_r = function(f)
+ n = n + 1
+ return format("format('%%%s.0f',a%s)",f,n)
+end
+
+local format_h = function(f)
+ n = n + 1
+ if f == "-" then
+ f = sub(f,2)
+ return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f == "" and "05" or f,n,n,n)
+ else
+ return format("format('0x%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f == "" and "05" or f,n,n,n)
+ end
+end
+
+local format_H = function(f)
+ n = n + 1
+ if f == "-" then
+ f = sub(f,2)
+ return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f == "" and "05" or f,n,n,n)
+ else
+ return format("format('0x%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f == "" and "05" or f,n,n,n)
+ end
+end
+
+local format_u = function(f)
+ n = n + 1
+ if f == "-" then
+ f = sub(f,2)
+ return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f == "" and "05" or f,n,n,n)
+ else
+ return format("format('u+%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f == "" and "05" or f,n,n,n)
+ end
+end
+
+local format_U = function(f)
+ n = n + 1
+ if f == "-" then
+ f = sub(f,2)
+ return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f == "" and "05" or f,n,n,n)
+ else
+ return format("format('U+%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f == "" and "05" or f,n,n,n)
+ end
+end
+
+local format_p = function()
+ n = n + 1
+ return format("points(a%s)",n)
+end
+
+local format_b = function()
+ n = n + 1
+ return format("basepoints(a%s)",n)
+end
+
+local format_t = function(f)
+ n = n + 1
+ if f and f ~= "" then
+ return format("concat(a%s,%q)",n,f)
+ else
+ return format("concat(a%s)",n)
+ end
+end
+
+local format_T = function(f)
+ n = n + 1
+ if f and f ~= "" then
+ return format("sequenced(a%s,%q)",n,f)
+ else
+ return format("sequenced(a%s)",n)
+ end
+end
+
+local format_l = function()
+ n = n + 1
+ return format("(a%s and 'true' or 'false')",n)
+end
+
+local format_L = function()
+ n = n + 1
+ return format("(a%s and 'TRUE' or 'FALSE')",n)
+end
+
+local format_N = function() -- strips leading zeros
+ n = n + 1
+ return format("tostring(tonumber(a%s) or a%s)",n,n)
+end
+
+local format_a = function(f)
+ n = n + 1
+ if f and f ~= "" then
+ return format("autosingle(a%s,%q)",n,f)
+ else
+ return format("autosingle(a%s)",n)
+ end
+end
+
+local format_A = function(f)
+ n = n + 1
+ if f and f ~= "" then
+ return format("autodouble(a%s,%q)",n,f)
+ else
+ return format("autodouble(a%s)",n)
+ end
+end
+
+local format_w = function(f) -- handy when doing depth related indent
+ n = n + 1
+ f = tonumber(f)
+ if f then -- not that useful
+ return format("nspaces[%s+a%s]",f,n) -- no real need for tonumber
+ else
+ return format("nspaces[a%s]",n) -- no real need for tonumber
+ end
+end
+
+local format_W = function(f) -- handy when doing depth related indent
+ return format("nspaces[%s]",tonumber(f) or 0)
+end
+
+local format_rest = function(s)
+ return format("%q",s) -- catches " and \n and such
+end
+
+local format_extension = function(extensions,f,name)
+ local extension = extensions[name] or "tostring(%s)"
+ local f = tonumber(f) or 1
+ if f == 0 then
+ return extension
+ elseif f == 1 then
+ n = n + 1
+ local a = "a" .. n
+ return format(extension,a,a) -- maybe more times?
+ elseif f < 0 then
+ local a = "a" .. (n + f + 1)
+ return format(extension,a,a)
+ else
+ local t = { }
+ for i=1,f do
+ n = n + 1
+ t[#t+1] = "a" .. n
+ end
+ return format(extension,unpack(t))
+ end
+end
+
+local builder = Cs { "start",
+ start = (
+ (
+ P("%") / ""
+ * (
+ V("!") -- new
+ + V("s") + V("q")
+ + V("i") + V("d")
+ + V("f") + V("g") + V("G") + V("e") + V("E")
+ + V("x") + V("X") + V("o")
+ --
+ + V("c")
+ + V("C")
+ + V("S") -- new
+ + V("Q") -- new
+ + V("N") -- new
+ --
+ + V("r")
+ + V("h") + V("H") + V("u") + V("U")
+ + V("p") + V("b")
+ + V("t") + V("T")
+ + V("l") + V("L")
+ + V("I")
+ + V("h") -- new
+ + V("w") -- new
+ + V("W") -- new
+ + V("a") -- new
+ + V("A") -- new
+ --
+ + V("*") -- ignores probably messed up %
+ )
+ + V("*")
+ )
+ * (P(-1) + Carg(1))
+ )^0,
+ --
+ ["s"] = (prefix_any * P("s")) / format_s, -- %s => regular %s (string)
+ ["q"] = (prefix_any * P("q")) / format_q, -- %q => regular %q (quoted string)
+ ["i"] = (prefix_any * P("i")) / format_i, -- %i => regular %i (integer)
+ ["d"] = (prefix_any * P("d")) / format_d, -- %d => regular %d (integer)
+ ["f"] = (prefix_any * P("f")) / format_f, -- %f => regular %f (float)
+ ["g"] = (prefix_any * P("g")) / format_g, -- %g => regular %g (float)
+ ["G"] = (prefix_any * P("G")) / format_G, -- %G => regular %G (float)
+ ["e"] = (prefix_any * P("e")) / format_e, -- %e => regular %e (float)
+ ["E"] = (prefix_any * P("E")) / format_E, -- %E => regular %E (float)
+ ["x"] = (prefix_any * P("x")) / format_x, -- %x => regular %x (hexadecimal)
+ ["X"] = (prefix_any * P("X")) / format_X, -- %X => regular %X (HEXADECIMAL)
+ ["o"] = (prefix_any * P("o")) / format_o, -- %o => regular %o (octal)
+ --
+ ["S"] = (prefix_any * P("S")) / format_S, -- %S => %s (tostring)
+ ["Q"] = (prefix_any * P("Q")) / format_S, -- %Q => %q (tostring)
+ ["N"] = (prefix_any * P("N")) / format_N, -- %N => tonumber (strips leading zeros)
+ ["c"] = (prefix_any * P("c")) / format_c, -- %c => utf character (extension to regular)
+ ["C"] = (prefix_any * P("C")) / format_C, -- %c => U+.... utf character
+ --
+ ["r"] = (prefix_any * P("r")) / format_r, -- %r => round
+ ["h"] = (prefix_any * P("h")) / format_h, -- %h => 0x0a1b2 (when - no 0x) was v
+ ["H"] = (prefix_any * P("H")) / format_H, -- %H => 0x0A1B2 (when - no 0x) was V
+ ["u"] = (prefix_any * P("u")) / format_u, -- %u => u+0a1b2 (when - no u+)
+ ["U"] = (prefix_any * P("U")) / format_U, -- %U => U+0A1B2 (when - no U+)
+ ["p"] = (prefix_any * P("p")) / format_p, -- %p => 12.345pt / maybe: P (and more units)
+ ["b"] = (prefix_any * P("b")) / format_b, -- %b => 12.342bp / maybe: B (and more units)
+ ["t"] = (prefix_tab * P("t")) / format_t, -- %t => concat
+ ["T"] = (prefix_tab * P("T")) / format_T, -- %t => sequenced
+ ["l"] = (prefix_tab * P("l")) / format_l, -- %l => boolean
+ ["L"] = (prefix_tab * P("L")) / format_L, -- %L => BOOLEAN
+ ["I"] = (prefix_any * P("I")) / format_I, -- %I => signed integer
+ --
+ ["w"] = (prefix_any * P("w")) / format_w, -- %w => n spaces (optional prefix is added)
+ ["W"] = (prefix_any * P("W")) / format_W, -- %W => mandate prefix, no specifier
+ --
+ ["a"] = (prefix_any * P("a")) / format_a, -- %a => '...' (forces tostring)
+ ["A"] = (prefix_any * P("A")) / format_A, -- %A => "..." (forces tostring)
+ --
+ ["*"] = Cs(((1-P("%"))^1 + P("%%")/"%%%%")^1) / format_rest, -- rest (including %%)
+ --
+ ["!"] = Carg(2) * prefix_any * P("!") * C((1-P("!"))^1) * P("!") / format_extension,
+}
+
+-- we can be clever and only alias what is needed
+
+local direct = Cs (
+ P("%")/""
+ * Cc([[local format = string.format return function(str) return format("%]])
+ * (S("+- .") + R("09"))^0
+ * S("sqidfgGeExXo")
+ * Cc([[",str) end]])
+ * P(-1)
+ )
+
+local function make(t,str)
+ local f
+ local p
+ local p = lpegmatch(direct,str)
+ if p then
+ f = loadstripped(p)()
+ else
+ n = 0
+ p = lpegmatch(builder,str,1,"..",t._extensions_) -- after this we know n
+ if n > 0 then
+ p = format(template,preamble,t._preamble_,arguments[n],p)
+-- print("builder>",p)
+ f = loadstripped(p)()
+ else
+ f = function() return str end
+ end
+ end
+ t[str] = f
+ return f
+end
+
+-- -- collect periodically
+--
+-- local threshold = 1000 -- max nof cached formats
+--
+-- local function make(t,str)
+-- local f = rawget(t,str)
+-- if f then
+-- return f
+-- end
+-- local parent = t._t_
+-- if parent._n_ > threshold then
+-- local m = { _t_ = parent }
+-- getmetatable(parent).__index = m
+-- setmetatable(m, { __index = make })
+-- else
+-- parent._n_ = parent._n_ + 1
+-- end
+-- local f
+-- local p = lpegmatch(direct,str)
+-- if p then
+-- f = loadstripped(p)()
+-- else
+-- n = 0
+-- p = lpegmatch(builder,str,1,"..",parent._extensions_) -- after this we know n
+-- if n > 0 then
+-- p = format(template,preamble,parent._preamble_,arguments[n],p)
+-- -- print("builder>",p)
+-- f = loadstripped(p)()
+-- else
+-- f = function() return str end
+-- end
+-- end
+-- t[str] = f
+-- return f
+-- end
+
+local function use(t,fmt,...)
+ return t[fmt](...)
+end
+
+strings.formatters = { }
+
+-- we cannot make these tables weak, unless we start using an indirect
+-- table (metatable) in which case we could better keep a count and
+-- clear that table when a threshold is reached
+
+function strings.formatters.new()
+ local t = { _extensions_ = { }, _preamble_ = "", _type_ = "formatter" }
+ setmetatable(t, { __index = make, __call = use })
+ return t
+end
+
+-- function strings.formatters.new()
+-- local t = { _extensions_ = { }, _preamble_ = "", _type_ = "formatter", _n_ = 0 }
+-- local m = { _t_ = t }
+-- setmetatable(t, { __index = m, __call = use })
+-- setmetatable(m, { __index = make })
+-- return t
+-- end
+
+local formatters = strings.formatters.new() -- the default instance
+
+string.formatters = formatters -- in the main string namespace
+string.formatter = function(str,...) return formatters[str](...) end -- sometimes nicer name
+
+local function add(t,name,template,preamble)
+ if type(t) == "table" and t._type_ == "formatter" then
+ t._extensions_[name] = template or "%s"
+ if preamble then
+ t._preamble_ = preamble .. "\n" .. t._preamble_ -- so no overload !
+ end
+ end
+end
+
+strings.formatters.add = add
+
+-- registered in the default instance (should we fall back on this one?)
+
+lpeg.patterns.xmlescape = Cs((P("<")/"&lt;" + P(">")/"&gt;" + P("&")/"&amp;" + P('"')/"&quot;" + P(1))^0)
+lpeg.patterns.texescape = Cs((C(S("#$%\\{}"))/"\\%1" + P(1))^0)
+
+add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],[[local xmlescape = lpeg.patterns.xmlescape]])
+add(formatters,"tex",[[lpegmatch(texescape,%s)]],[[local texescape = lpeg.patterns.texescape]])
+
+-- -- yes or no:
+--
+-- local function make(t,str)
+-- local f
+-- local p = lpegmatch(direct,str)
+-- if p then
+-- f = loadstripped(p)()
+-- else
+-- n = 0
+-- p = lpegmatch(builder,str,1,",") -- after this we know n
+-- if n > 0 then
+-- p = format(template,template_shortcuts,arguments[n],p)
+-- f = loadstripped(p)()
+-- else
+-- f = function() return str end
+-- end
+-- end
+-- t[str] = f
+-- return f
+-- end
+--
+-- local formatteds = string.formatteds or { }
+-- string.formatteds = formatteds
+--
+-- setmetatable(formatteds, { __index = make, __call = use })
diff --git a/tex/context/base/util-tab.lua b/tex/context/base/util-tab.lua
index 30554015b..ecf36b137 100644
--- a/tex/context/base/util-tab.lua
+++ b/tex/context/base/util-tab.lua
@@ -1,493 +1,493 @@
-if not modules then modules = { } end modules ['util-tab'] = {
- 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"
-}
-
-utilities = utilities or {}
-utilities.tables = utilities.tables or { }
-local tables = utilities.tables
-
-local format, gmatch, gsub = string.format, string.gmatch, string.gsub
-local concat, insert, remove = table.concat, table.insert, table.remove
-local setmetatable, getmetatable, tonumber, tostring = setmetatable, getmetatable, tonumber, tostring
-local type, next, rawset, tonumber, tostring, load, select = type, next, rawset, tonumber, tostring, load, select
-local lpegmatch, P, Cs, Cc = lpeg.match, lpeg.P, lpeg.Cs, lpeg.Cc
-local serialize, sortedkeys, sortedpairs = table.serialize, table.sortedkeys, table.sortedpairs
-local formatters = string.formatters
-
-local splitter = lpeg.tsplitat(".")
-
-function tables.definetable(target,nofirst,nolast) -- defines undefined tables
- local composed, shortcut, t = nil, nil, { }
- local snippets = lpegmatch(splitter,target)
- for i=1,#snippets - (nolast and 1 or 0) do
- local name = snippets[i]
- if composed then
- composed = shortcut .. "." .. name
- shortcut = shortcut .. "_" .. name
- t[#t+1] = formatters["local %s = %s if not %s then %s = { } %s = %s end"](shortcut,composed,shortcut,shortcut,composed,shortcut)
- else
- composed = name
- shortcut = name
- if not nofirst then
- t[#t+1] = formatters["%s = %s or { }"](composed,composed)
- end
- end
- end
- if nolast then
- composed = shortcut .. "." .. snippets[#snippets]
- end
- return concat(t,"\n"), composed
-end
-
--- local t = tables.definedtable("a","b","c","d")
-
-function tables.definedtable(...)
- local t = _G
- for i=1,select("#",...) do
- local li = select(i,...)
- local tl = t[li]
- if not tl then
- tl = { }
- t[li] = tl
- end
- t = tl
- end
- return t
-end
-
-function tables.accesstable(target,root)
- local t = root or _G
- for name in gmatch(target,"([^%.]+)") do
- t = t[name]
- if not t then
- return
- end
- end
- return t
-end
-
-function tables.migratetable(target,v,root)
- local t = root or _G
- local names = string.split(target,".")
- for i=1,#names-1 do
- local name = names[i]
- t[name] = t[name] or { }
- t = t[name]
- if not t then
- return
- end
- end
- t[names[#names]] = v
-end
-
-function tables.removevalue(t,value) -- todo: n
- if value then
- for i=1,#t do
- if t[i] == value then
- remove(t,i)
- -- remove all, so no: return
- end
- end
- end
-end
-
-function tables.insertbeforevalue(t,value,extra)
- for i=1,#t do
- if t[i] == extra then
- remove(t,i)
- end
- end
- for i=1,#t do
- if t[i] == value then
- insert(t,i,extra)
- return
- end
- end
- insert(t,1,extra)
-end
-
-function tables.insertaftervalue(t,value,extra)
- for i=1,#t do
- if t[i] == extra then
- remove(t,i)
- end
- end
- for i=1,#t do
- if t[i] == value then
- insert(t,i+1,extra)
- return
- end
- end
- insert(t,#t+1,extra)
-end
-
--- experimental
-
-local escape = Cs(Cc('"') * ((P('"')/'""' + P(1))^0) * Cc('"'))
-
-function table.tocsv(t,specification)
- if t and #t > 0 then
- local result = { }
- local r = { }
- specification = specification or { }
- local fields = specification.fields
- if type(fields) ~= "string" then
- fields = sortedkeys(t[1])
- end
- local separator = specification.separator or ","
- if specification.preamble == true then
- for f=1,#fields do
- r[f] = lpegmatch(escape,tostring(fields[f]))
- end
- result[1] = concat(r,separator)
- end
- for i=1,#t do
- local ti = t[i]
- for f=1,#fields do
- local field = ti[fields[f]]
- if type(field) == "string" then
- r[f] = lpegmatch(escape,field)
- else
- r[f] = tostring(field)
- end
- end
- result[#result+1] = concat(r,separator)
- end
- return concat(result,"\n")
- else
- return ""
- end
-end
-
--- local nspaces = utilities.strings.newrepeater(" ")
--- local escape = Cs((P("<")/"&lt;" + P(">")/"&gt;" + P("&")/"&amp;" + P(1))^0)
---
--- local function toxml(t,d,result,step)
--- for k, v in sortedpairs(t) do
--- local s = nspaces[d]
--- local tk = type(k)
--- local tv = type(v)
--- if tv == "table" then
--- if tk == "number" then
--- result[#result+1] = format("%s<entry n='%s'>",s,k)
--- toxml(v,d+step,result,step)
--- result[#result+1] = format("%s</entry>",s,k)
--- else
--- result[#result+1] = format("%s<%s>",s,k)
--- toxml(v,d+step,result,step)
--- result[#result+1] = format("%s</%s>",s,k)
--- end
--- elseif tv == "string" then
--- if tk == "number" then
--- result[#result+1] = format("%s<entry n='%s'>%s</entry>",s,k,lpegmatch(escape,v),k)
--- else
--- result[#result+1] = format("%s<%s>%s</%s>",s,k,lpegmatch(escape,v),k)
--- end
--- elseif tk == "number" then
--- result[#result+1] = format("%s<entry n='%s'>%s</entry>",s,k,tostring(v),k)
--- else
--- result[#result+1] = format("%s<%s>%s</%s>",s,k,tostring(v),k)
--- end
--- end
--- end
---
--- much faster
-
-local nspaces = utilities.strings.newrepeater(" ")
-
-local function toxml(t,d,result,step)
- for k, v in sortedpairs(t) do
- local s = nspaces[d] -- inlining this is somewhat faster but gives more formatters
- local tk = type(k)
- local tv = type(v)
- if tv == "table" then
- if tk == "number" then
- result[#result+1] = formatters["%s<entry n='%s'>"](s,k)
- toxml(v,d+step,result,step)
- result[#result+1] = formatters["%s</entry>"](s,k)
- else
- result[#result+1] = formatters["%s<%s>"](s,k)
- toxml(v,d+step,result,step)
- result[#result+1] = formatters["%s</%s>"](s,k)
- end
- elseif tv == "string" then
- if tk == "number" then
- result[#result+1] = formatters["%s<entry n='%s'>%!xml!</entry>"](s,k,v,k)
- else
- result[#result+1] = formatters["%s<%s>%!xml!</%s>"](s,k,v,k)
- end
- elseif tk == "number" then
- result[#result+1] = formatters["%s<entry n='%s'>%S</entry>"](s,k,v,k)
- else
- result[#result+1] = formatters["%s<%s>%S</%s>"](s,k,v,k)
- end
- end
-end
-
--- function table.toxml(t,name,nobanner,indent,spaces)
--- local noroot = name == false
--- local result = (nobanner or noroot) and { } or { "<?xml version='1.0' standalone='yes' ?>" }
--- local indent = rep(" ",indent or 0)
--- local spaces = rep(" ",spaces or 1)
--- if noroot then
--- toxml( t, inndent, result, spaces)
--- else
--- toxml( { [name or "root"] = t }, indent, result, spaces)
--- end
--- return concat(result,"\n")
--- end
-
-function table.toxml(t,specification)
- specification = specification or { }
- local name = specification.name
- local noroot = name == false
- local result = (specification.nobanner or noroot) and { } or { "<?xml version='1.0' standalone='yes' ?>" }
- local indent = specification.indent or 0
- local spaces = specification.spaces or 1
- if noroot then
- toxml( t, indent, result, spaces)
- else
- toxml( { [name or "data"] = t }, indent, result, spaces)
- end
- return concat(result,"\n")
-end
-
--- also experimental
-
--- encapsulate(table,utilities.tables)
--- encapsulate(table,utilities.tables,true)
--- encapsulate(table,true)
-
-function tables.encapsulate(core,capsule,protect)
- if type(capsule) ~= "table" then
- protect = true
- capsule = { }
- end
- for key, value in next, core do
- if capsule[key] then
- print(formatters["\ninvalid %s %a in %a"]("inheritance",key,core))
- os.exit()
- else
- capsule[key] = value
- end
- end
- if protect then
- for key, value in next, core do
- core[key] = nil
- end
- setmetatable(core, {
- __index = capsule,
- __newindex = function(t,key,value)
- if capsule[key] then
- print(formatters["\ninvalid %s %a' in %a"]("overload",key,core))
- os.exit()
- else
- rawset(t,key,value)
- end
- end
- } )
- end
-end
-
-local function fastserialize(t,r,outer) -- no mixes
- r[#r+1] = "{"
- local n = #t
- if n > 0 then
- for i=1,n do
- local v = t[i]
- local tv = type(v)
- if tv == "string" then
- r[#r+1] = formatters["%q,"](v)
- elseif tv == "number" then
- r[#r+1] = formatters["%s,"](v)
- elseif tv == "table" then
- fastserialize(v,r)
- elseif tv == "boolean" then
- r[#r+1] = formatters["%S,"](v)
- end
- end
- else
- for k, v in next, t do
- local tv = type(v)
- if tv == "string" then
- r[#r+1] = formatters["[%q]=%q,"](k,v)
- elseif tv == "number" then
- r[#r+1] = formatters["[%q]=%s,"](k,v)
- elseif tv == "table" then
- r[#r+1] = formatters["[%q]="](k)
- fastserialize(v,r)
- elseif tv == "boolean" then
- r[#r+1] = formatters["[%q]=%S,"](k,v)
- end
- end
- end
- if outer then
- r[#r+1] = "}"
- else
- r[#r+1] = "},"
- end
- return r
-end
-
--- local f_hashed_string = formatters["[%q]=%q,"]
--- local f_hashed_number = formatters["[%q]=%s,"]
--- local f_hashed_table = formatters["[%q]="]
--- local f_hashed_true = formatters["[%q]=true,"]
--- local f_hashed_false = formatters["[%q]=false,"]
---
--- local f_indexed_string = formatters["%q,"]
--- local f_indexed_number = formatters["%s,"]
--- ----- f_indexed_true = formatters["true,"]
--- ----- f_indexed_false = formatters["false,"]
---
--- local function fastserialize(t,r,outer) -- no mixes
--- r[#r+1] = "{"
--- local n = #t
--- if n > 0 then
--- for i=1,n do
--- local v = t[i]
--- local tv = type(v)
--- if tv == "string" then
--- r[#r+1] = f_indexed_string(v)
--- elseif tv == "number" then
--- r[#r+1] = f_indexed_number(v)
--- elseif tv == "table" then
--- fastserialize(v,r)
--- elseif tv == "boolean" then
--- -- r[#r+1] = v and f_indexed_true(k) or f_indexed_false(k)
--- r[#r+1] = v and "true," or "false,"
--- end
--- end
--- else
--- for k, v in next, t do
--- local tv = type(v)
--- if tv == "string" then
--- r[#r+1] = f_hashed_string(k,v)
--- elseif tv == "number" then
--- r[#r+1] = f_hashed_number(k,v)
--- elseif tv == "table" then
--- r[#r+1] = f_hashed_table(k)
--- fastserialize(v,r)
--- elseif tv == "boolean" then
--- r[#r+1] = v and f_hashed_true(k) or f_hashed_false(k)
--- end
--- end
--- end
--- if outer then
--- r[#r+1] = "}"
--- else
--- r[#r+1] = "},"
--- end
--- return r
--- end
-
-function table.fastserialize(t,prefix) -- so prefix should contain the =
- return concat(fastserialize(t,{ prefix or "return" },true))
-end
-
-function table.deserialize(str)
- if not str or str == "" then
- return
- end
- local code = load(str)
- if not code then
- return
- end
- code = code()
- if not code then
- return
- end
- return code
-end
-
--- inspect(table.fastserialize { a = 1, b = { 4, { 5, 6 } }, c = { d = 7, e = 'f"g\nh' } })
-
-function table.load(filename)
- if filename then
- local t = io.loaddata(filename)
- if t and t ~= "" then
- t = load(t)
- if type(t) == "function" then
- t = t()
- if type(t) == "table" then
- return t
- end
- end
- end
- end
-end
-
-function table.save(filename,t,n,...)
- io.savedata(filename,serialize(t,n == nil and true or n,...))
-end
-
-local function slowdrop(t)
- local r = { }
- local l = { }
- for i=1,#t do
- local ti = t[i]
- local j = 0
- for k, v in next, ti do
- j = j + 1
- l[j] = formatters["%s=%q"](k,v)
- end
- r[i] = formatters[" {%t},\n"](l)
- end
- return formatters["return {\n%st}"](r)
-end
-
-local function fastdrop(t)
- local r = { "return {\n" }
- for i=1,#t do
- local ti = t[i]
- r[#r+1] = " {"
- for k, v in next, ti do
- r[#r+1] = formatters["%s=%q"](k,v)
- end
- r[#r+1] = "},\n"
- end
- r[#r+1] = "}"
- return concat(r)
-end
-
-function table.drop(t,slow) -- only { { a=2 }, {a=3} }
- if #t == 0 then
- return "return { }"
- elseif slow == true then
- return slowdrop(t) -- less memory
- else
- return fastdrop(t) -- some 15% faster
- end
-end
-
-function table.autokey(t,k)
- local v = { }
- t[k] = v
- return v
-end
-
-local selfmapper = { __index = function(t,k) t[k] = k return k end }
-
-function table.twowaymapper(t)
- if not t then
- t = { }
- else
- for i=0,#t do
- local ti = t[i] -- t[1] = "one"
- if ti then
- local i = tostring(i)
- t[i] = ti -- t["1"] = "one"
- t[ti] = i -- t["one"] = "1"
- end
- end
- t[""] = t[0] or ""
- end
- -- setmetatableindex(t,"key")
- setmetatable(t,selfmapper)
- return t
-end
-
+if not modules then modules = { } end modules ['util-tab'] = {
+ 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"
+}
+
+utilities = utilities or {}
+utilities.tables = utilities.tables or { }
+local tables = utilities.tables
+
+local format, gmatch, gsub = string.format, string.gmatch, string.gsub
+local concat, insert, remove = table.concat, table.insert, table.remove
+local setmetatable, getmetatable, tonumber, tostring = setmetatable, getmetatable, tonumber, tostring
+local type, next, rawset, tonumber, tostring, load, select = type, next, rawset, tonumber, tostring, load, select
+local lpegmatch, P, Cs, Cc = lpeg.match, lpeg.P, lpeg.Cs, lpeg.Cc
+local serialize, sortedkeys, sortedpairs = table.serialize, table.sortedkeys, table.sortedpairs
+local formatters = string.formatters
+
+local splitter = lpeg.tsplitat(".")
+
+function tables.definetable(target,nofirst,nolast) -- defines undefined tables
+ local composed, shortcut, t = nil, nil, { }
+ local snippets = lpegmatch(splitter,target)
+ for i=1,#snippets - (nolast and 1 or 0) do
+ local name = snippets[i]
+ if composed then
+ composed = shortcut .. "." .. name
+ shortcut = shortcut .. "_" .. name
+ t[#t+1] = formatters["local %s = %s if not %s then %s = { } %s = %s end"](shortcut,composed,shortcut,shortcut,composed,shortcut)
+ else
+ composed = name
+ shortcut = name
+ if not nofirst then
+ t[#t+1] = formatters["%s = %s or { }"](composed,composed)
+ end
+ end
+ end
+ if nolast then
+ composed = shortcut .. "." .. snippets[#snippets]
+ end
+ return concat(t,"\n"), composed
+end
+
+-- local t = tables.definedtable("a","b","c","d")
+
+function tables.definedtable(...)
+ local t = _G
+ for i=1,select("#",...) do
+ local li = select(i,...)
+ local tl = t[li]
+ if not tl then
+ tl = { }
+ t[li] = tl
+ end
+ t = tl
+ end
+ return t
+end
+
+function tables.accesstable(target,root)
+ local t = root or _G
+ for name in gmatch(target,"([^%.]+)") do
+ t = t[name]
+ if not t then
+ return
+ end
+ end
+ return t
+end
+
+function tables.migratetable(target,v,root)
+ local t = root or _G
+ local names = string.split(target,".")
+ for i=1,#names-1 do
+ local name = names[i]
+ t[name] = t[name] or { }
+ t = t[name]
+ if not t then
+ return
+ end
+ end
+ t[names[#names]] = v
+end
+
+function tables.removevalue(t,value) -- todo: n
+ if value then
+ for i=1,#t do
+ if t[i] == value then
+ remove(t,i)
+ -- remove all, so no: return
+ end
+ end
+ end
+end
+
+function tables.insertbeforevalue(t,value,extra)
+ for i=1,#t do
+ if t[i] == extra then
+ remove(t,i)
+ end
+ end
+ for i=1,#t do
+ if t[i] == value then
+ insert(t,i,extra)
+ return
+ end
+ end
+ insert(t,1,extra)
+end
+
+function tables.insertaftervalue(t,value,extra)
+ for i=1,#t do
+ if t[i] == extra then
+ remove(t,i)
+ end
+ end
+ for i=1,#t do
+ if t[i] == value then
+ insert(t,i+1,extra)
+ return
+ end
+ end
+ insert(t,#t+1,extra)
+end
+
+-- experimental
+
+local escape = Cs(Cc('"') * ((P('"')/'""' + P(1))^0) * Cc('"'))
+
+function table.tocsv(t,specification)
+ if t and #t > 0 then
+ local result = { }
+ local r = { }
+ specification = specification or { }
+ local fields = specification.fields
+ if type(fields) ~= "string" then
+ fields = sortedkeys(t[1])
+ end
+ local separator = specification.separator or ","
+ if specification.preamble == true then
+ for f=1,#fields do
+ r[f] = lpegmatch(escape,tostring(fields[f]))
+ end
+ result[1] = concat(r,separator)
+ end
+ for i=1,#t do
+ local ti = t[i]
+ for f=1,#fields do
+ local field = ti[fields[f]]
+ if type(field) == "string" then
+ r[f] = lpegmatch(escape,field)
+ else
+ r[f] = tostring(field)
+ end
+ end
+ result[#result+1] = concat(r,separator)
+ end
+ return concat(result,"\n")
+ else
+ return ""
+ end
+end
+
+-- local nspaces = utilities.strings.newrepeater(" ")
+-- local escape = Cs((P("<")/"&lt;" + P(">")/"&gt;" + P("&")/"&amp;" + P(1))^0)
+--
+-- local function toxml(t,d,result,step)
+-- for k, v in sortedpairs(t) do
+-- local s = nspaces[d]
+-- local tk = type(k)
+-- local tv = type(v)
+-- if tv == "table" then
+-- if tk == "number" then
+-- result[#result+1] = format("%s<entry n='%s'>",s,k)
+-- toxml(v,d+step,result,step)
+-- result[#result+1] = format("%s</entry>",s,k)
+-- else
+-- result[#result+1] = format("%s<%s>",s,k)
+-- toxml(v,d+step,result,step)
+-- result[#result+1] = format("%s</%s>",s,k)
+-- end
+-- elseif tv == "string" then
+-- if tk == "number" then
+-- result[#result+1] = format("%s<entry n='%s'>%s</entry>",s,k,lpegmatch(escape,v),k)
+-- else
+-- result[#result+1] = format("%s<%s>%s</%s>",s,k,lpegmatch(escape,v),k)
+-- end
+-- elseif tk == "number" then
+-- result[#result+1] = format("%s<entry n='%s'>%s</entry>",s,k,tostring(v),k)
+-- else
+-- result[#result+1] = format("%s<%s>%s</%s>",s,k,tostring(v),k)
+-- end
+-- end
+-- end
+--
+-- much faster
+
+local nspaces = utilities.strings.newrepeater(" ")
+
+local function toxml(t,d,result,step)
+ for k, v in sortedpairs(t) do
+ local s = nspaces[d] -- inlining this is somewhat faster but gives more formatters
+ local tk = type(k)
+ local tv = type(v)
+ if tv == "table" then
+ if tk == "number" then
+ result[#result+1] = formatters["%s<entry n='%s'>"](s,k)
+ toxml(v,d+step,result,step)
+ result[#result+1] = formatters["%s</entry>"](s,k)
+ else
+ result[#result+1] = formatters["%s<%s>"](s,k)
+ toxml(v,d+step,result,step)
+ result[#result+1] = formatters["%s</%s>"](s,k)
+ end
+ elseif tv == "string" then
+ if tk == "number" then
+ result[#result+1] = formatters["%s<entry n='%s'>%!xml!</entry>"](s,k,v,k)
+ else
+ result[#result+1] = formatters["%s<%s>%!xml!</%s>"](s,k,v,k)
+ end
+ elseif tk == "number" then
+ result[#result+1] = formatters["%s<entry n='%s'>%S</entry>"](s,k,v,k)
+ else
+ result[#result+1] = formatters["%s<%s>%S</%s>"](s,k,v,k)
+ end
+ end
+end
+
+-- function table.toxml(t,name,nobanner,indent,spaces)
+-- local noroot = name == false
+-- local result = (nobanner or noroot) and { } or { "<?xml version='1.0' standalone='yes' ?>" }
+-- local indent = rep(" ",indent or 0)
+-- local spaces = rep(" ",spaces or 1)
+-- if noroot then
+-- toxml( t, inndent, result, spaces)
+-- else
+-- toxml( { [name or "root"] = t }, indent, result, spaces)
+-- end
+-- return concat(result,"\n")
+-- end
+
+function table.toxml(t,specification)
+ specification = specification or { }
+ local name = specification.name
+ local noroot = name == false
+ local result = (specification.nobanner or noroot) and { } or { "<?xml version='1.0' standalone='yes' ?>" }
+ local indent = specification.indent or 0
+ local spaces = specification.spaces or 1
+ if noroot then
+ toxml( t, indent, result, spaces)
+ else
+ toxml( { [name or "data"] = t }, indent, result, spaces)
+ end
+ return concat(result,"\n")
+end
+
+-- also experimental
+
+-- encapsulate(table,utilities.tables)
+-- encapsulate(table,utilities.tables,true)
+-- encapsulate(table,true)
+
+function tables.encapsulate(core,capsule,protect)
+ if type(capsule) ~= "table" then
+ protect = true
+ capsule = { }
+ end
+ for key, value in next, core do
+ if capsule[key] then
+ print(formatters["\ninvalid %s %a in %a"]("inheritance",key,core))
+ os.exit()
+ else
+ capsule[key] = value
+ end
+ end
+ if protect then
+ for key, value in next, core do
+ core[key] = nil
+ end
+ setmetatable(core, {
+ __index = capsule,
+ __newindex = function(t,key,value)
+ if capsule[key] then
+ print(formatters["\ninvalid %s %a' in %a"]("overload",key,core))
+ os.exit()
+ else
+ rawset(t,key,value)
+ end
+ end
+ } )
+ end
+end
+
+local function fastserialize(t,r,outer) -- no mixes
+ r[#r+1] = "{"
+ local n = #t
+ if n > 0 then
+ for i=1,n do
+ local v = t[i]
+ local tv = type(v)
+ if tv == "string" then
+ r[#r+1] = formatters["%q,"](v)
+ elseif tv == "number" then
+ r[#r+1] = formatters["%s,"](v)
+ elseif tv == "table" then
+ fastserialize(v,r)
+ elseif tv == "boolean" then
+ r[#r+1] = formatters["%S,"](v)
+ end
+ end
+ else
+ for k, v in next, t do
+ local tv = type(v)
+ if tv == "string" then
+ r[#r+1] = formatters["[%q]=%q,"](k,v)
+ elseif tv == "number" then
+ r[#r+1] = formatters["[%q]=%s,"](k,v)
+ elseif tv == "table" then
+ r[#r+1] = formatters["[%q]="](k)
+ fastserialize(v,r)
+ elseif tv == "boolean" then
+ r[#r+1] = formatters["[%q]=%S,"](k,v)
+ end
+ end
+ end
+ if outer then
+ r[#r+1] = "}"
+ else
+ r[#r+1] = "},"
+ end
+ return r
+end
+
+-- local f_hashed_string = formatters["[%q]=%q,"]
+-- local f_hashed_number = formatters["[%q]=%s,"]
+-- local f_hashed_table = formatters["[%q]="]
+-- local f_hashed_true = formatters["[%q]=true,"]
+-- local f_hashed_false = formatters["[%q]=false,"]
+--
+-- local f_indexed_string = formatters["%q,"]
+-- local f_indexed_number = formatters["%s,"]
+-- ----- f_indexed_true = formatters["true,"]
+-- ----- f_indexed_false = formatters["false,"]
+--
+-- local function fastserialize(t,r,outer) -- no mixes
+-- r[#r+1] = "{"
+-- local n = #t
+-- if n > 0 then
+-- for i=1,n do
+-- local v = t[i]
+-- local tv = type(v)
+-- if tv == "string" then
+-- r[#r+1] = f_indexed_string(v)
+-- elseif tv == "number" then
+-- r[#r+1] = f_indexed_number(v)
+-- elseif tv == "table" then
+-- fastserialize(v,r)
+-- elseif tv == "boolean" then
+-- -- r[#r+1] = v and f_indexed_true(k) or f_indexed_false(k)
+-- r[#r+1] = v and "true," or "false,"
+-- end
+-- end
+-- else
+-- for k, v in next, t do
+-- local tv = type(v)
+-- if tv == "string" then
+-- r[#r+1] = f_hashed_string(k,v)
+-- elseif tv == "number" then
+-- r[#r+1] = f_hashed_number(k,v)
+-- elseif tv == "table" then
+-- r[#r+1] = f_hashed_table(k)
+-- fastserialize(v,r)
+-- elseif tv == "boolean" then
+-- r[#r+1] = v and f_hashed_true(k) or f_hashed_false(k)
+-- end
+-- end
+-- end
+-- if outer then
+-- r[#r+1] = "}"
+-- else
+-- r[#r+1] = "},"
+-- end
+-- return r
+-- end
+
+function table.fastserialize(t,prefix) -- so prefix should contain the =
+ return concat(fastserialize(t,{ prefix or "return" },true))
+end
+
+function table.deserialize(str)
+ if not str or str == "" then
+ return
+ end
+ local code = load(str)
+ if not code then
+ return
+ end
+ code = code()
+ if not code then
+ return
+ end
+ return code
+end
+
+-- inspect(table.fastserialize { a = 1, b = { 4, { 5, 6 } }, c = { d = 7, e = 'f"g\nh' } })
+
+function table.load(filename)
+ if filename then
+ local t = io.loaddata(filename)
+ if t and t ~= "" then
+ t = load(t)
+ if type(t) == "function" then
+ t = t()
+ if type(t) == "table" then
+ return t
+ end
+ end
+ end
+ end
+end
+
+function table.save(filename,t,n,...)
+ io.savedata(filename,serialize(t,n == nil and true or n,...))
+end
+
+local function slowdrop(t)
+ local r = { }
+ local l = { }
+ for i=1,#t do
+ local ti = t[i]
+ local j = 0
+ for k, v in next, ti do
+ j = j + 1
+ l[j] = formatters["%s=%q"](k,v)
+ end
+ r[i] = formatters[" {%t},\n"](l)
+ end
+ return formatters["return {\n%st}"](r)
+end
+
+local function fastdrop(t)
+ local r = { "return {\n" }
+ for i=1,#t do
+ local ti = t[i]
+ r[#r+1] = " {"
+ for k, v in next, ti do
+ r[#r+1] = formatters["%s=%q"](k,v)
+ end
+ r[#r+1] = "},\n"
+ end
+ r[#r+1] = "}"
+ return concat(r)
+end
+
+function table.drop(t,slow) -- only { { a=2 }, {a=3} }
+ if #t == 0 then
+ return "return { }"
+ elseif slow == true then
+ return slowdrop(t) -- less memory
+ else
+ return fastdrop(t) -- some 15% faster
+ end
+end
+
+function table.autokey(t,k)
+ local v = { }
+ t[k] = v
+ return v
+end
+
+local selfmapper = { __index = function(t,k) t[k] = k return k end }
+
+function table.twowaymapper(t)
+ if not t then
+ t = { }
+ else
+ for i=0,#t do
+ local ti = t[i] -- t[1] = "one"
+ if ti then
+ local i = tostring(i)
+ t[i] = ti -- t["1"] = "one"
+ t[ti] = i -- t["one"] = "1"
+ end
+ end
+ t[""] = t[0] or ""
+ end
+ -- setmetatableindex(t,"key")
+ setmetatable(t,selfmapper)
+ return t
+end
+
diff --git a/tex/context/base/util-tpl.lua b/tex/context/base/util-tpl.lua
index 045faf1d0..7a6abefd6 100644
--- a/tex/context/base/util-tpl.lua
+++ b/tex/context/base/util-tpl.lua
@@ -1,174 +1,174 @@
-if not modules then modules = { } end modules ['util-tpl'] = {
- 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"
-}
-
--- This is experimental code. Coming from dos and windows, I've always used %whatever%
--- as template variables so let's stick to it. After all, it's easy to parse and stands
--- out well. A double %% is turned into a regular %.
-
-utilities.templates = utilities.templates or { }
-local templates = utilities.templates
-
-local trace_template = false trackers.register("templates.trace",function(v) trace_template = v end)
-local report_template = logs.reporter("template")
-
-local tostring = tostring
-local format, sub = string.format, string.sub
-local P, C, Cs, Carg, lpegmatch = lpeg.P, lpeg.C, lpeg.Cs, lpeg.Carg, lpeg.match
-
--- todo: make installable template.new
-
-local replacer
-
-local function replacekey(k,t,how,recursive)
- local v = t[k]
- if not v then
- if trace_template then
- report_template("unknown key %a",k)
- end
- return ""
- else
- v = tostring(v)
- if trace_template then
- report_template("setting key %a to value %a",k,v)
- end
- if recursive then
- return lpegmatch(replacer,v,1,t,how,recursive)
- else
- return v
- end
- end
-end
-
-local sqlescape = lpeg.replacer {
- { "'", "''" },
- { "\\", "\\\\" },
- { "\r\n", "\\n" },
- { "\r", "\\n" },
- -- { "\t", "\\t" },
-}
-
-local sqlquotedescape = lpeg.Cs(lpeg.Cc("'") * sqlescape * lpeg.Cc("'"))
-
--- escapeset : \0\1\2\3\4\5\6\7\8\9\10\11\12\13\14\15\16\17\18\19\20\21\22\23\24\25\26\27\28\29\30\31\"\\\127
--- test string: [[1\0\31test23"\\]] .. string.char(19) .. "23"
---
--- slow:
---
--- local luaescape = lpeg.replacer {
--- { '"', [[\"]] },
--- { '\\', [[\\]] },
--- { R("\0\9") * #R("09"), function(s) return "\\00" .. byte(s) end },
--- { R("\10\31") * #R("09"), function(s) return "\\0" .. byte(s) end },
--- { R("\0\31") , function(s) return "\\" .. byte(s) end },
--- }
---
--- slightly faster:
---
--- local luaescape = Cs ((
--- P('"' ) / [[\"]] +
--- P('\\') / [[\\]] +
--- Cc("\\00") * (R("\0\9") / byte) * #R("09") +
--- Cc("\\0") * (R("\10\31") / byte) * #R("09") +
--- Cc("\\") * (R("\0\31") / byte) +
--- P(1)
--- )^0)
-
-local escapers = {
- lua = function(s)
- return sub(format("%q",s),2,-2)
- end,
- sql = function(s)
- return lpegmatch(sqlescape,s)
- end,
-}
-
-local quotedescapers = {
- lua = function(s)
- return format("%q",s)
- end,
- sql = function(s)
- return lpegmatch(sqlquotedescape,s)
- end,
-}
-
-lpeg.patterns.sqlescape = sqlescape
-lpeg.patterns.sqlescape = sqlquotedescape
-
-local luaescaper = escapers.lua
-local quotedluaescaper = quotedescapers.lua
-
-local function replacekeyunquoted(s,t,how,recurse) -- ".. \" "
- local escaper = how and escapers[how] or luaescaper
- return escaper(replacekey(s,t,how,recurse))
-end
-
-local function replacekeyquoted(s,t,how,recurse) -- ".. \" "
- local escaper = how and quotedescapers[how] or quotedluaescaper
- return escaper(replacekey(s,t,how,recurse))
-end
-
-local single = P("%") -- test %test% test : resolves test
-local double = P("%%") -- test 10%% test : %% becomes %
-local lquoted = P("%[") -- test '%[test]%' test : resolves to test with escaped "'s
-local rquoted = P("]%") --
-local lquotedq = P("%(") -- test %(test)% test : resolves to 'test' with escaped "'s
-local rquotedq = P(")%") --
-
-local escape = double / '%%'
-local nosingle = single / ''
-local nodouble = double / ''
-local nolquoted = lquoted / ''
-local norquoted = rquoted / ''
-local nolquotedq = lquotedq / ''
-local norquotedq = rquotedq / ''
-
-local key = nosingle * ((C((1-nosingle )^1) * Carg(1) * Carg(2) * Carg(3)) / replacekey ) * nosingle
-local quoted = nolquotedq * ((C((1-norquotedq)^1) * Carg(1) * Carg(2) * Carg(3)) / replacekeyquoted ) * norquotedq
-local unquoted = nolquoted * ((C((1-norquoted )^1) * Carg(1) * Carg(2) * Carg(3)) / replacekeyunquoted) * norquoted
-local any = P(1)
-
- replacer = Cs((unquoted + quoted + escape + key + any)^0)
-
-local function replace(str,mapping,how,recurse)
- if mapping and str then
- return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str
- else
- return str
- end
-end
-
--- print(replace("test '%[x]%' test",{ x = [[a 'x'  a]] }))
--- print(replace("test '%[x]%' test",{ x = true }))
--- print(replace("test '%[x]%' test",{ x = [[a 'x'  a]], y = "oeps" },'sql'))
--- print(replace("test '%[x]%' test",{ x = [[a '%y%'  a]], y = "oeps" },'sql',true))
--- print(replace([[test %[x]% test]],{ x = [[a "x"  a]]}))
--- print(replace([[test %(x)% test]],{ x = [[a "x"  a]]}))
-
-templates.replace = replace
-
-function templates.load(filename,mapping,how,recurse)
- local data = io.loaddata(filename) or ""
- if mapping and next(mapping) then
- return replace(data,mapping,how,recurse)
- else
- return data
- end
-end
-
-function templates.resolve(t,mapping,how,recurse)
- if not mapping then
- mapping = t
- end
- for k, v in next, t do
- t[k] = replace(v,mapping,how,recurse)
- end
- return t
-end
-
--- inspect(utilities.templates.replace("test %one% test", { one = "%two%", two = "two" }))
--- inspect(utilities.templates.resolve({ one = "%two%", two = "two", three = "%three%" }))
+if not modules then modules = { } end modules ['util-tpl'] = {
+ 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"
+}
+
+-- This is experimental code. Coming from dos and windows, I've always used %whatever%
+-- as template variables so let's stick to it. After all, it's easy to parse and stands
+-- out well. A double %% is turned into a regular %.
+
+utilities.templates = utilities.templates or { }
+local templates = utilities.templates
+
+local trace_template = false trackers.register("templates.trace",function(v) trace_template = v end)
+local report_template = logs.reporter("template")
+
+local tostring = tostring
+local format, sub = string.format, string.sub
+local P, C, Cs, Carg, lpegmatch = lpeg.P, lpeg.C, lpeg.Cs, lpeg.Carg, lpeg.match
+
+-- todo: make installable template.new
+
+local replacer
+
+local function replacekey(k,t,how,recursive)
+ local v = t[k]
+ if not v then
+ if trace_template then
+ report_template("unknown key %a",k)
+ end
+ return ""
+ else
+ v = tostring(v)
+ if trace_template then
+ report_template("setting key %a to value %a",k,v)
+ end
+ if recursive then
+ return lpegmatch(replacer,v,1,t,how,recursive)
+ else
+ return v
+ end
+ end
+end
+
+local sqlescape = lpeg.replacer {
+ { "'", "''" },
+ { "\\", "\\\\" },
+ { "\r\n", "\\n" },
+ { "\r", "\\n" },
+ -- { "\t", "\\t" },
+}
+
+local sqlquotedescape = lpeg.Cs(lpeg.Cc("'") * sqlescape * lpeg.Cc("'"))
+
+-- escapeset : \0\1\2\3\4\5\6\7\8\9\10\11\12\13\14\15\16\17\18\19\20\21\22\23\24\25\26\27\28\29\30\31\"\\\127
+-- test string: [[1\0\31test23"\\]] .. string.char(19) .. "23"
+--
+-- slow:
+--
+-- local luaescape = lpeg.replacer {
+-- { '"', [[\"]] },
+-- { '\\', [[\\]] },
+-- { R("\0\9") * #R("09"), function(s) return "\\00" .. byte(s) end },
+-- { R("\10\31") * #R("09"), function(s) return "\\0" .. byte(s) end },
+-- { R("\0\31") , function(s) return "\\" .. byte(s) end },
+-- }
+--
+-- slightly faster:
+--
+-- local luaescape = Cs ((
+-- P('"' ) / [[\"]] +
+-- P('\\') / [[\\]] +
+-- Cc("\\00") * (R("\0\9") / byte) * #R("09") +
+-- Cc("\\0") * (R("\10\31") / byte) * #R("09") +
+-- Cc("\\") * (R("\0\31") / byte) +
+-- P(1)
+-- )^0)
+
+local escapers = {
+ lua = function(s)
+ return sub(format("%q",s),2,-2)
+ end,
+ sql = function(s)
+ return lpegmatch(sqlescape,s)
+ end,
+}
+
+local quotedescapers = {
+ lua = function(s)
+ return format("%q",s)
+ end,
+ sql = function(s)
+ return lpegmatch(sqlquotedescape,s)
+ end,
+}
+
+lpeg.patterns.sqlescape = sqlescape
+lpeg.patterns.sqlescape = sqlquotedescape
+
+local luaescaper = escapers.lua
+local quotedluaescaper = quotedescapers.lua
+
+local function replacekeyunquoted(s,t,how,recurse) -- ".. \" "
+ local escaper = how and escapers[how] or luaescaper
+ return escaper(replacekey(s,t,how,recurse))
+end
+
+local function replacekeyquoted(s,t,how,recurse) -- ".. \" "
+ local escaper = how and quotedescapers[how] or quotedluaescaper
+ return escaper(replacekey(s,t,how,recurse))
+end
+
+local single = P("%") -- test %test% test : resolves test
+local double = P("%%") -- test 10%% test : %% becomes %
+local lquoted = P("%[") -- test '%[test]%' test : resolves to test with escaped "'s
+local rquoted = P("]%") --
+local lquotedq = P("%(") -- test %(test)% test : resolves to 'test' with escaped "'s
+local rquotedq = P(")%") --
+
+local escape = double / '%%'
+local nosingle = single / ''
+local nodouble = double / ''
+local nolquoted = lquoted / ''
+local norquoted = rquoted / ''
+local nolquotedq = lquotedq / ''
+local norquotedq = rquotedq / ''
+
+local key = nosingle * ((C((1-nosingle )^1) * Carg(1) * Carg(2) * Carg(3)) / replacekey ) * nosingle
+local quoted = nolquotedq * ((C((1-norquotedq)^1) * Carg(1) * Carg(2) * Carg(3)) / replacekeyquoted ) * norquotedq
+local unquoted = nolquoted * ((C((1-norquoted )^1) * Carg(1) * Carg(2) * Carg(3)) / replacekeyunquoted) * norquoted
+local any = P(1)
+
+ replacer = Cs((unquoted + quoted + escape + key + any)^0)
+
+local function replace(str,mapping,how,recurse)
+ if mapping and str then
+ return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str
+ else
+ return str
+ end
+end
+
+-- print(replace("test '%[x]%' test",{ x = [[a 'x'  a]] }))
+-- print(replace("test '%[x]%' test",{ x = true }))
+-- print(replace("test '%[x]%' test",{ x = [[a 'x'  a]], y = "oeps" },'sql'))
+-- print(replace("test '%[x]%' test",{ x = [[a '%y%'  a]], y = "oeps" },'sql',true))
+-- print(replace([[test %[x]% test]],{ x = [[a "x"  a]]}))
+-- print(replace([[test %(x)% test]],{ x = [[a "x"  a]]}))
+
+templates.replace = replace
+
+function templates.load(filename,mapping,how,recurse)
+ local data = io.loaddata(filename) or ""
+ if mapping and next(mapping) then
+ return replace(data,mapping,how,recurse)
+ else
+ return data
+ end
+end
+
+function templates.resolve(t,mapping,how,recurse)
+ if not mapping then
+ mapping = t
+ end
+ for k, v in next, t do
+ t[k] = replace(v,mapping,how,recurse)
+ end
+ return t
+end
+
+-- inspect(utilities.templates.replace("test %one% test", { one = "%two%", two = "two" }))
+-- inspect(utilities.templates.resolve({ one = "%two%", two = "two", three = "%three%" }))
diff --git a/tex/context/base/x-asciimath.lua b/tex/context/base/x-asciimath.lua
index 9f4021212..5ef741ce3 100644
--- a/tex/context/base/x-asciimath.lua
+++ b/tex/context/base/x-asciimath.lua
@@ -1,270 +1,270 @@
-if not modules then modules = { } end modules ['x-asciimath'] = {
- version = 1.001,
- comment = "companion to x-asciimath.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
---[[ldx--
-<p>Some backgrounds are discussed in <t>x-asciimath.mkiv</t>.</p>
---ldx]]--
-
-local trace_mapping = false if trackers then trackers.register("modules.asciimath.mapping", function(v) trace_mapping = v end) end
-
-local asciimath = { }
-local moduledata = moduledata or { }
-moduledata.asciimath = asciimath
-
-local report_asciimath = logs.reporter("mathematics","asciimath")
-
-local format = string.format
-local lpegmatch = lpeg.match
-local S, P, R, C, V, Cc, Ct, Cs = lpeg.S, lpeg.P, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc, lpeg.Ct, lpeg.Cs
-
-local letter = lpeg.patterns.utf8
-local space = S(" \n\r\t")
-local spaces = space^0/""
-local integer = P("-")^-1 * R("09")^1
-local realpart = P("-")^-1 * R("09")^1 * S(".")^1 * R("09")^1
-local number = integer -- so we can support nice formatting if needed
-local real = realpart -- so we can support nice formatting if needed
-local float = realpart * P("E") * integer -- so we can support nice formatting if needed
-local texnic = P("\\") * (R("az","AZ")^1)
-
-local premapper = Cs ( (
-
- P("@") / "\\degrees " +
- P("O/") / "\\varnothing " +
- P("o+") / "\\oplus " +
- P("o.") / "\\ocirc " +
- P("!in") / "\\not\\in " +
- P("!=") / "\\neq " +
- P("**") / "\\star " +
- P("*") / "\\cdot " +
- P("//") / "\\slash " +
- P("/_") / "\\angle " +
- P("\\\\") / "\\backslash " +
- P("^^^") / "\\wedge " +
- P("^^") / "\\wedge " +
- P("<<") / "\\left\\langle " +
- P(">>") / "\\right\\rangle " +
- P("<=") / "\\leq " +
- P(">=") / "\\geq " +
- P("-<") / "\\precc " +
- P(">-") / "\\succ " +
- P("~=") / "\\cong " +
- P("~~") / "\\approx " +
- P("=>") / "\\Rightarrow " +
- P("(:") / "\\left\\langle " +
- P(":)") / "\\right\\rangle " +
- P(":.") / "\\therefore " +
- P("~|") / "\\right\\rceil " +
- P("_|_") / "\\bot " +
- P("_|") / "\\right\\rfloor " +
- P("+-") / "\\pm " +
- P("|--") / "\\vdash " +
- P("|==") / "\\models " +
- P("|_") / "\\left\\lfloor " +
- P("|~") / "\\left\\lceil " +
- P("-:") / "\\div " +
- P("_=") / "\\equiv " +
-
- P("|") / "\\middle\\| " +
-
- P("dx") / "(dx)" +
- P("dy") / "(dy)" +
- P("dz") / "(dz)" +
-
- letter + P(1)
-
-)^0 )
-
-local reserved = {
- ["aleph"] = "\\aleph ",
- ["vdots"] = "\\vdots ",
- ["ddots"] = "\\ddots ",
- ["oint"] = "\\oint ",
- ["grad"] = "\\nabla ",
- ["prod"] = "\\prod ",
- ["prop"] = "\\propto ",
- ["sube"] = "\\subseteq ",
- ["supe"] = "\\supseteq ",
- ["sinh"] = "\\sinh ",
- ["cosh"] = "\\cosh ",
- ["tanh"] = "\\tanh ",
- ["sum"] = "\\sum ",
- ["vvv"] = "\\vee ",
- ["nnn"] = "\\cap ",
- ["uuu"] = "\\cup ",
- ["sub"] = "\\subset ",
- ["sup"] = "\\supset ",
- ["not"] = "\\lnot ",
- ["iff"] = "\\Leftrightarrow ",
- ["int"] = "\\int ",
- ["del"] = "\\partial ",
- ["and"] = "\\and ",
- ["not"] = "\\not ",
- ["sin"] = "\\sin ",
- ["cos"] = "\\cos ",
- ["tan"] = "\\tan ",
- ["csc"] = "\\csc ",
- ["sec"] = "\\sec ",
- ["cot"] = "\\cot ",
- ["log"] = "\\log ",
- ["det"] = "\\det ",
- ["lim"] = "\\lim ",
- ["mod"] = "\\mod ",
- ["gcd"] = "\\gcd ",
- ["lcm"] = "\\lcm ",
- ["min"] = "\\min ",
- ["max"] = "\\max ",
- ["xx"] = "\\times ",
- ["in"] = "\\in ",
- ["ox"] = "\\otimes ",
- ["vv"] = "\\vee ",
- ["nn"] = "\\cap ",
- ["uu"] = "\\cup ",
- ["oo"] = "\\infty ",
- ["ln"] = "\\ln ",
- ["or"] = "\\or ",
-
- ["AA"] = "\\forall ",
- ["EE"] = "\\exists ",
- ["TT"] = "\\top ",
- ["CC"] = "\\Bbb{C}",
- ["NN"] = "\\Bbb{N}",
- ["QQ"] = "\\Bbb{Q}",
- ["RR"] = "\\Bbb{R}",
- ["ZZ"] = "\\Bbb{Z}",
-
-}
-
-local postmapper = Cs ( (
-
- P("\\mathoptext ") * spaces * (P("\\bgroup ")/"{") * (1-P("\\egroup "))^1 * (P("\\egroup ")/"}") +
-
- (P("\\bgroup ")) / "{" +
- (P("\\egroup ")) / "}" +
-
- P("\\") * (R("az","AZ")^2) +
-
- (R("AZ","az")^2) / reserved +
-
- P("{:") / "\\left." +
- P(":}") / "\\right." +
- P("(") / "\\left(" +
- P(")") / "\\right)" +
- P("[") / "\\left[" +
- P("]") / "\\right]" +
- P("{") / "\\left\\{" +
- P("}") / "\\right\\}" +
-
- letter + P(1)
-)^0 )
-
-local parser
-
-local function converted(original,totex)
- local ok, result
- if trace_mapping then
- report_asciimath("original : %s",original)
- end
- local premapped = lpegmatch(premapper,original)
- if premapped then
- if trace_mapping then
- report_asciimath("prepared : %s",premapped)
- end
- local parsed = lpegmatch(parser,premapped)
- if parsed then
- if trace_mapping then
- report_asciimath("parsed : %s",parsed)
- end
- local postmapped = lpegmatch(postmapper,parsed)
- if postmapped then
- if trace_mapping then
- report_asciimath("finalized: %s",postmapped)
- end
- result, ok = postmapped, true
- else
- result = "error in postmapping"
- end
- else
- result = "error in mapping"
- end
- else
- result = "error in premapping"
- end
- if totex then
- if ok then
- context.mathematics(result)
- else
- context.type(result) -- some day monospaced
- end
- else
- return result
- end
-end
-
-local function onlyconverted(str)
- local parsed = lpegmatch(parser,str)
- return parsed or str
-end
-
-local sqrt = P("sqrt") / "\\rootradical \\bgroup \\egroup "
-local root = P("root") / "\\rootradical "
-local frac = P("frac") / "\\frac "
-local stackrel = P("stackrel") / "\\stackrel "
-local text = P("text") / "\\mathoptext "
-local hat = P("hat") / "\\widehat "
-local overbar = P("bar") / "\\overbar "
-local underline = P("ul") / "\\underline "
-local vec = P("vec") / "\\overrightarrow "
-local dot = P("dot") / "\\dot "
-local ddot = P("ddot") / "\\ddot "
-
-local left = P("(:") + P("{:") + P("(") + P("[") + P("{")
-local right = P(":)") + P(":}") + P(")") + P("]") + P("}")
-local leftnorright = 1 - left - right
-local singles = sqrt + text + hat + underline + overbar + vec + ddot + dot
-local doubles = root + frac + stackrel
-local ignoreleft = (left/"") * spaces * spaces
-local ignoreright = spaces * (right/"") * spaces
-local ignoreslash = spaces * (P("/")/"") * spaces
-local comma = P(",")
-local nocomma = 1-comma
-local anychar = P(1)
-local openmatrix = left * spaces * Cc("\\matrix\\bgroup ")
-local closematrix = Cc("\\egroup ") * spaces * right
-local nextcolumn = spaces * (comma/"&") * spaces
-local nextrow = spaces * (comma/"\\cr ") * spaces
-local finishrow = Cc("\\cr ")
-local opengroup = left/"\\bgroup "
-local closegroup = right/"\\egroup "
-local somescript = S("^_") * spaces
-local beginargument = Cc("\\bgroup ")
-local endargument = Cc("\\egroup ")
-
-parser = Cs { "main",
-
- scripts = somescript * V("argument"),
- division = Cc("\\frac") * V("argument") * spaces * ignoreslash * spaces * V("argument"),
- double = doubles * spaces * V("argument") * spaces * V("argument"),
- single = singles * spaces * V("argument"),
-
- balanced = opengroup * (C((leftnorright + V("balanced"))^0)/onlyconverted) * closegroup,
- argument = V("balanced") + V("token"),
-
- element = (V("step") + (V("argument") + V("step")) - ignoreright - nextcolumn - comma)^1,
- commalist = ignoreleft * V("element") * (nextcolumn * spaces * V("element"))^0 * ignoreright,
- matrix = openmatrix * spaces * (V("commalist") * (nextrow * V("commalist"))^0) * finishrow * closematrix,
-
- token = beginargument * (texnic + float + real + number + letter) * endargument,
-
- step = V("scripts") + V("division") + V("single") + V("double"),
- main = (V("matrix") + V("step") + anychar)^0,
-
-}
-
-asciimath.reserved = reserved
-asciimath.convert = converted
+if not modules then modules = { } end modules ['x-asciimath'] = {
+ version = 1.001,
+ comment = "companion to x-asciimath.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+--[[ldx--
+<p>Some backgrounds are discussed in <t>x-asciimath.mkiv</t>.</p>
+--ldx]]--
+
+local trace_mapping = false if trackers then trackers.register("modules.asciimath.mapping", function(v) trace_mapping = v end) end
+
+local asciimath = { }
+local moduledata = moduledata or { }
+moduledata.asciimath = asciimath
+
+local report_asciimath = logs.reporter("mathematics","asciimath")
+
+local format = string.format
+local lpegmatch = lpeg.match
+local S, P, R, C, V, Cc, Ct, Cs = lpeg.S, lpeg.P, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc, lpeg.Ct, lpeg.Cs
+
+local letter = lpeg.patterns.utf8
+local space = S(" \n\r\t")
+local spaces = space^0/""
+local integer = P("-")^-1 * R("09")^1
+local realpart = P("-")^-1 * R("09")^1 * S(".")^1 * R("09")^1
+local number = integer -- so we can support nice formatting if needed
+local real = realpart -- so we can support nice formatting if needed
+local float = realpart * P("E") * integer -- so we can support nice formatting if needed
+local texnic = P("\\") * (R("az","AZ")^1)
+
+local premapper = Cs ( (
+
+ P("@") / "\\degrees " +
+ P("O/") / "\\varnothing " +
+ P("o+") / "\\oplus " +
+ P("o.") / "\\ocirc " +
+ P("!in") / "\\not\\in " +
+ P("!=") / "\\neq " +
+ P("**") / "\\star " +
+ P("*") / "\\cdot " +
+ P("//") / "\\slash " +
+ P("/_") / "\\angle " +
+ P("\\\\") / "\\backslash " +
+ P("^^^") / "\\wedge " +
+ P("^^") / "\\wedge " +
+ P("<<") / "\\left\\langle " +
+ P(">>") / "\\right\\rangle " +
+ P("<=") / "\\leq " +
+ P(">=") / "\\geq " +
+ P("-<") / "\\precc " +
+ P(">-") / "\\succ " +
+ P("~=") / "\\cong " +
+ P("~~") / "\\approx " +
+ P("=>") / "\\Rightarrow " +
+ P("(:") / "\\left\\langle " +
+ P(":)") / "\\right\\rangle " +
+ P(":.") / "\\therefore " +
+ P("~|") / "\\right\\rceil " +
+ P("_|_") / "\\bot " +
+ P("_|") / "\\right\\rfloor " +
+ P("+-") / "\\pm " +
+ P("|--") / "\\vdash " +
+ P("|==") / "\\models " +
+ P("|_") / "\\left\\lfloor " +
+ P("|~") / "\\left\\lceil " +
+ P("-:") / "\\div " +
+ P("_=") / "\\equiv " +
+
+ P("|") / "\\middle\\| " +
+
+ P("dx") / "(dx)" +
+ P("dy") / "(dy)" +
+ P("dz") / "(dz)" +
+
+ letter + P(1)
+
+)^0 )
+
+local reserved = {
+ ["aleph"] = "\\aleph ",
+ ["vdots"] = "\\vdots ",
+ ["ddots"] = "\\ddots ",
+ ["oint"] = "\\oint ",
+ ["grad"] = "\\nabla ",
+ ["prod"] = "\\prod ",
+ ["prop"] = "\\propto ",
+ ["sube"] = "\\subseteq ",
+ ["supe"] = "\\supseteq ",
+ ["sinh"] = "\\sinh ",
+ ["cosh"] = "\\cosh ",
+ ["tanh"] = "\\tanh ",
+ ["sum"] = "\\sum ",
+ ["vvv"] = "\\vee ",
+ ["nnn"] = "\\cap ",
+ ["uuu"] = "\\cup ",
+ ["sub"] = "\\subset ",
+ ["sup"] = "\\supset ",
+ ["not"] = "\\lnot ",
+ ["iff"] = "\\Leftrightarrow ",
+ ["int"] = "\\int ",
+ ["del"] = "\\partial ",
+ ["and"] = "\\and ",
+ ["not"] = "\\not ",
+ ["sin"] = "\\sin ",
+ ["cos"] = "\\cos ",
+ ["tan"] = "\\tan ",
+ ["csc"] = "\\csc ",
+ ["sec"] = "\\sec ",
+ ["cot"] = "\\cot ",
+ ["log"] = "\\log ",
+ ["det"] = "\\det ",
+ ["lim"] = "\\lim ",
+ ["mod"] = "\\mod ",
+ ["gcd"] = "\\gcd ",
+ ["lcm"] = "\\lcm ",
+ ["min"] = "\\min ",
+ ["max"] = "\\max ",
+ ["xx"] = "\\times ",
+ ["in"] = "\\in ",
+ ["ox"] = "\\otimes ",
+ ["vv"] = "\\vee ",
+ ["nn"] = "\\cap ",
+ ["uu"] = "\\cup ",
+ ["oo"] = "\\infty ",
+ ["ln"] = "\\ln ",
+ ["or"] = "\\or ",
+
+ ["AA"] = "\\forall ",
+ ["EE"] = "\\exists ",
+ ["TT"] = "\\top ",
+ ["CC"] = "\\Bbb{C}",
+ ["NN"] = "\\Bbb{N}",
+ ["QQ"] = "\\Bbb{Q}",
+ ["RR"] = "\\Bbb{R}",
+ ["ZZ"] = "\\Bbb{Z}",
+
+}
+
+local postmapper = Cs ( (
+
+ P("\\mathoptext ") * spaces * (P("\\bgroup ")/"{") * (1-P("\\egroup "))^1 * (P("\\egroup ")/"}") +
+
+ (P("\\bgroup ")) / "{" +
+ (P("\\egroup ")) / "}" +
+
+ P("\\") * (R("az","AZ")^2) +
+
+ (R("AZ","az")^2) / reserved +
+
+ P("{:") / "\\left." +
+ P(":}") / "\\right." +
+ P("(") / "\\left(" +
+ P(")") / "\\right)" +
+ P("[") / "\\left[" +
+ P("]") / "\\right]" +
+ P("{") / "\\left\\{" +
+ P("}") / "\\right\\}" +
+
+ letter + P(1)
+)^0 )
+
+local parser
+
+local function converted(original,totex)
+ local ok, result
+ if trace_mapping then
+ report_asciimath("original : %s",original)
+ end
+ local premapped = lpegmatch(premapper,original)
+ if premapped then
+ if trace_mapping then
+ report_asciimath("prepared : %s",premapped)
+ end
+ local parsed = lpegmatch(parser,premapped)
+ if parsed then
+ if trace_mapping then
+ report_asciimath("parsed : %s",parsed)
+ end
+ local postmapped = lpegmatch(postmapper,parsed)
+ if postmapped then
+ if trace_mapping then
+ report_asciimath("finalized: %s",postmapped)
+ end
+ result, ok = postmapped, true
+ else
+ result = "error in postmapping"
+ end
+ else
+ result = "error in mapping"
+ end
+ else
+ result = "error in premapping"
+ end
+ if totex then
+ if ok then
+ context.mathematics(result)
+ else
+ context.type(result) -- some day monospaced
+ end
+ else
+ return result
+ end
+end
+
+local function onlyconverted(str)
+ local parsed = lpegmatch(parser,str)
+ return parsed or str
+end
+
+local sqrt = P("sqrt") / "\\rootradical \\bgroup \\egroup "
+local root = P("root") / "\\rootradical "
+local frac = P("frac") / "\\frac "
+local stackrel = P("stackrel") / "\\stackrel "
+local text = P("text") / "\\mathoptext "
+local hat = P("hat") / "\\widehat "
+local overbar = P("bar") / "\\overbar "
+local underline = P("ul") / "\\underline "
+local vec = P("vec") / "\\overrightarrow "
+local dot = P("dot") / "\\dot "
+local ddot = P("ddot") / "\\ddot "
+
+local left = P("(:") + P("{:") + P("(") + P("[") + P("{")
+local right = P(":)") + P(":}") + P(")") + P("]") + P("}")
+local leftnorright = 1 - left - right
+local singles = sqrt + text + hat + underline + overbar + vec + ddot + dot
+local doubles = root + frac + stackrel
+local ignoreleft = (left/"") * spaces * spaces
+local ignoreright = spaces * (right/"") * spaces
+local ignoreslash = spaces * (P("/")/"") * spaces
+local comma = P(",")
+local nocomma = 1-comma
+local anychar = P(1)
+local openmatrix = left * spaces * Cc("\\matrix\\bgroup ")
+local closematrix = Cc("\\egroup ") * spaces * right
+local nextcolumn = spaces * (comma/"&") * spaces
+local nextrow = spaces * (comma/"\\cr ") * spaces
+local finishrow = Cc("\\cr ")
+local opengroup = left/"\\bgroup "
+local closegroup = right/"\\egroup "
+local somescript = S("^_") * spaces
+local beginargument = Cc("\\bgroup ")
+local endargument = Cc("\\egroup ")
+
+parser = Cs { "main",
+
+ scripts = somescript * V("argument"),
+ division = Cc("\\frac") * V("argument") * spaces * ignoreslash * spaces * V("argument"),
+ double = doubles * spaces * V("argument") * spaces * V("argument"),
+ single = singles * spaces * V("argument"),
+
+ balanced = opengroup * (C((leftnorright + V("balanced"))^0)/onlyconverted) * closegroup,
+ argument = V("balanced") + V("token"),
+
+ element = (V("step") + (V("argument") + V("step")) - ignoreright - nextcolumn - comma)^1,
+ commalist = ignoreleft * V("element") * (nextcolumn * spaces * V("element"))^0 * ignoreright,
+ matrix = openmatrix * spaces * (V("commalist") * (nextrow * V("commalist"))^0) * finishrow * closematrix,
+
+ token = beginargument * (texnic + float + real + number + letter) * endargument,
+
+ step = V("scripts") + V("division") + V("single") + V("double"),
+ main = (V("matrix") + V("step") + anychar)^0,
+
+}
+
+asciimath.reserved = reserved
+asciimath.convert = converted
diff --git a/tex/context/base/x-calcmath.lua b/tex/context/base/x-calcmath.lua
index 631cd613b..1394f3450 100644
--- a/tex/context/base/x-calcmath.lua
+++ b/tex/context/base/x-calcmath.lua
@@ -1,362 +1,362 @@
-if not modules then modules = { } end modules ['x-calcmath'] = {
- version = 1.001,
- comment = "companion to x-calcmath.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- this really needs to be redone
-
-local format, lower, upper, gsub, sub = string.format, string.lower, string.upper, string.gsub, string.sub
-local concat = table.concat
-local lpegmatch = lpeg.match
-
-local calcmath = { }
-local moduledata = moduledata or { }
-moduledata.calcmath = calcmath
-
-local list_1 = {
- "median", "min", "max", "round", "ln", "log",
- "sin", "cos", "tan", "sinh", "cosh", "tanh"
-}
-local list_2 = {
- "int", "sum", "prod"
-}
-local list_3 = {
- "f", "g"
-}
-local list_4 = {
- "pi", "inf"
-}
-
-local list_1_1 = { }
-local list_2_1 = { }
-local list_2_2 = { }
-local list_2_3 = { }
-local list_4_1 = { }
-
-local frozen = false
-
-local function freeze()
- for k=1,#list_1 do
- local v = list_1[k]
- list_1_1[v] = "\\".. upper(v) .." "
- end
- for k=1,#list_2 do
- local v = list_2[k]
- list_2_1[v .. "%((.-),(.-),(.-)%)"] = "\\" .. upper(v) .. "^{%1}_{%2}{%3}"
- list_2_2[v .. "%((.-),(.-)%)"] = "\\" .. upper(v) .. "^{%1}{%2}"
- list_2_3[v .. "%((.-)%)"] = "\\" .. upper(v) .. "{%1}"
- end
- for k=1,#list_4 do
- local v = list_4[k]
- list_4_1[v] = "\\" .. upper(v)
- end
- frozen = true
-end
-
-local entities = {
- ['gt'] = '>',
- ['lt'] = '<',
-}
-
-local symbols = {
- ["<="] = "\\LE ",
- [">="] = "\\GE ",
- ["=<"] = "\\LE ",
- ["=>"] = "\\GE ",
- ["=="] = "\\EQ ",
- ["<" ] = "\\LT ",
- [">" ] = "\\GT ",
- ["="] = "\\EQ ",
-}
-
-local function nsub(str,tag,pre,post)
- return (gsub(str,tag .. "(%b())", function(body)
- return pre .. nsub(sub(body,2,-2),tag,pre,post) .. post
- end))
-end
-
-local function totex(str,mode)
- if not frozen then freeze() end
- local n = 0
- -- crap
- str = gsub(str,"%s+",' ')
- -- xml
- str = gsub(str,"&(.-);",entities)
- -- ...E...
- str = gsub(str,"([%-%+]?[%d%.%+%-]+)E([%-%+]?[%d%.]+)", "{\\SCINOT{%1}{%2}}")
- -- ^-..
- str = gsub(str,"%^([%-%+]*%d+)", "^{%1}")
- -- ^(...)
- str = nsub(str,"%^", "^{", "}")
- -- 1/x^2
- repeat
- str, n = gsub(str,"([%d%w%.]+)/([%d%w%.]+%^{[%d%w%.]+})", "\\frac{%1}{%2}")
- until n == 0
- -- todo: autoparenthesis
- -- int(a,b,c)
- for k, v in next, list_2_1 do
- repeat str, n = gsub(str,k,v) until n == 0
- end
- -- int(a,b)
- for k, v in next, list_2_2 do
- repeat str, n = gsub(str,k,v) until n == 0
- end
- -- int(a)
- for k, v in next, list_2_3 do
- repeat str, n = gsub(str,k,v) until n == 0
- end
- -- sin(x) => {\\sin(x)}
- for k, v in next, list_1_1 do
- repeat str, n = gsub(str,k,v) until n == 0
- end
- -- mean
- str = nsub(str, "mean", "\\OVERLINE{", "}")
- -- (1+x)/(1+x) => \\FRAC{1+x}{1+x}
- repeat
- str, n = gsub(str,"(%b())/(%b())", function(a,b)
- return "\\FRAC{" .. sub(a,2,-2) .. "}{" .. sub(b,2,-2) .. "}"
- end )
- until n == 0
- -- (1+x)/x => \\FRAC{1+x}{x}
- repeat
- str, n = gsub(str,"(%b())/([%+%-]?[%.%d%w]+)", function(a,b)
- return "\\FRAC{" .. sub(a,2,-2) .. "}{" .. b .. "}"
- end )
- until n == 0
- -- 1/(1+x) => \\FRAC{1}{1+x}
- repeat
- str, n = gsub(str,"([%.%d%w]+)/(%b())", function(a,b)
- return "\\FRAC{" .. a .. "}{" .. sub(b,2,-2) .. "}"
- end )
- until n == 0
- -- 1/x => \\FRAC{1}{x}
- repeat
- str, n = gsub(str,"([%.%d%w]+)/([%+%-]?[%.%d%w]+)", "\\FRAC{%1}{%2}")
- until n == 0
- -- times
- str = gsub(str,"%*", " ")
- -- symbols -- we can use a table substitution here
- str = gsub(str,"([<>=][<>=]*)", symbols)
- -- functions
- str = nsub(str,"sqrt", "\\SQRT{", "}")
- str = nsub(str,"exp", "e^{", "}")
- str = nsub(str,"abs", "\\left|", "\\right|")
- -- d/D
- str = nsub(str,"D", "{\\FRAC{\\MBOX{d}}{\\MBOX{d}x}{(", ")}}")
- str = gsub(str,"D([xy])", "\\FRAC{{\\RM d}%1}{{\\RM d}x}")
- -- f/g
- for k,v in next, list_3 do -- todo : prepare k,v
- str = nsub(str,"D"..v,"{\\RM "..v.."}^{\\PRIME}(",")")
- str = nsub(str,v,"{\\RM "..v.."}(",")")
- end
- -- more symbols
- for k,v in next, list_4_1 do
- str = gsub(str,k,v)
- end
- -- parenthesis (optional)
- if mode == 2 then
- str = gsub(str,"%(", "\\left(")
- str = gsub(str,"%)", "\\right)")
- end
- -- csnames
- str = gsub(str,"(\\[A-Z]+)", lower)
- -- report
- return str
-end
-
-calcmath.totex = totex
-
-function calcmath.tex(str,mode)
- context(totex(str))
-end
-
-function calcmath.xml(id,mode)
- context(totex(lxml.id(id).dt[1],mode))
-end
-
--- work in progress ... lpeg variant
-
-if false then
-
- -- todo:
-
- -- maybe rewrite to current lpeg, i.e. string replacement and no Cc's
-
- -- table approach we have now is less efficient but more flexible
-
- -- D \frac {\rm d} {{\rm d}x}
- -- Dx Dy \frac {{\rm d}y} {{\rm d}x}
- -- Df Dg {\rm f}^{\prime}
- -- f() g() {\rm f}()
-
-
- -- valid utf8
-
- local S, P, R, C, V, Cc, Ct = lpeg.S, lpeg.P, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc, lpeg.Ct
-
- local space = S(" \n\r\t")^0
- local integer = P("-")^-1 * R("09")^1
- local realpart = P("-")^-1 * R("09")^1 * S(".")^1 * R("09")^1
- local number = Cc("number") * C(integer) * space
- local real = Cc("real") * C(realpart) * space
- local float = Cc("float") * C(realpart) * lpeg.P("E") * lpeg.C(integer) * space
- local identifier = Cc("identifier") * C(R("az","AZ")) * space
- local compareop = Cc("compare") * C(P("<") + P("=") + P(">") + P(">=") + P("<=") + P("&gt;") + P("&lt;")) * space
- local factorop = Cc("factor") * C(S("+-^_,")) * space
- local termop = Cc("term") * C(S("*/")) * space
- local constant = Cc("constant") * C(P("pi") + lpeg.P("inf")) * space
- local functionop = Cc("function") * C(R("az")^1) * space
- local open = P("(") * space
- local close = P(")") * space
-
- local grammar = P {
- "expression",
- expression = Ct(V("factor") * ((factorop+compareop) * V("factor"))^0),
- factor = Ct(V("term") * (termop * V("term"))^0),
- term = Ct(
- float + real + number +
- (open * V("expression") * close) +
- (functionop * open * (V("expression") * (P(",") * V("expression"))^0) * close) +
- (functionop * V("term")) +
- constant + identifier
- ),
- }
-
- local parser = space * grammar * -1
-
- local function has_factor(t)
- for i=1,#t do
- if t[i] == "factor" then
- return true
- end
- end
- end
-
- -- can be sped up if needed ...
-
- function totex(t)
- if t then
- local one = t[1]
- if type(one) == "string" then
- local two, three = t[2], t[3]
- if one == "number" then
- context(two)
- elseif one == "real" then
- context(two)
- elseif one == "float" then
- context("\\scinot{",two,"}{",three,"}")
- elseif one == "identifier" then
- context(two)
- elseif one == "constant" then
- context("\\"..two)
- elseif one == "function" then
- if two == "sqrt" then
- context("\\sqrt{")
- totex(three)
- context("}")
- elseif two == "exp" then
- context(" e^{")
- totex(three)
- context("}")
- elseif two == "abs" then
- context("\\left|")
- totex(three)
- context("\\right|")
- elseif two == "mean" then
- context("\\overline{")
- totex(three)
- context("}")
- elseif two == "int" or two == "prod" or two == "sum" then
- local four, five = t[4], t[5]
- if five then
- context("\\"..two.."^{") -- context[two]("{")
- totex(three)
- context("}_{")
- totex(four)
- context("}")
- totex(five)
- elseif four then
- context("\\"..two.."^{")
- totex(three)
- context("}")
- totex(four)
- elseif three then
- context("\\"..two.." ") -- " " not needed
- totex(three)
- else
- context("\\"..two)
- end
- else
- context("\\"..two.."(")
- totex(three)
- context(")")
- end
- end
- else
- local nt = #t
- local hasfactor = has_factor(t)
- if hasfactor then
- context("\\left(")
- end
- totex(one)
- for i=2,nt,3 do
- local what, how, rest = t[i], t[i+1], t[i+2]
- if what == "factor" then
- if how == '^' or how == "_" then
- context(how)
- context("{")
- totex(rest)
- context("}")
- else
- context(how)
- totex(rest)
- end
- elseif what == "term" then
- if how == '/' then
- context("\\frac{")
- totex(rest)
- context("}{")
- totex(t[i+3] or "")
- context("}")
- elseif how == '*' then
- context("\\times")
- totex(rest)
- else
- context(how)
- totex(three)
- end
- elseif what == "compare" then
- if two == ">=" then
- context("\\ge")
- elseif two == "<=" then
- context("\\le")
- elseif two == "&gt;" then
- context(">")
- elseif two == "&lt;" then
- context("<")
- end
- totex(three)
- end
- end
- if hasfactor then
- context("\\right)")
- end
- end
- end
- end
-
- calcmath = { }
-
- function calcmath.parse(str)
- return lpegmatch(parser,str)
- end
-
- function calcmath.tex(str)
- str = totex(lpegmatch(parser,str))
- return (str == "" and "[error]") or str
- end
-
-end
+if not modules then modules = { } end modules ['x-calcmath'] = {
+ version = 1.001,
+ comment = "companion to x-calcmath.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- this really needs to be redone
+
+local format, lower, upper, gsub, sub = string.format, string.lower, string.upper, string.gsub, string.sub
+local concat = table.concat
+local lpegmatch = lpeg.match
+
+local calcmath = { }
+local moduledata = moduledata or { }
+moduledata.calcmath = calcmath
+
+local list_1 = {
+ "median", "min", "max", "round", "ln", "log",
+ "sin", "cos", "tan", "sinh", "cosh", "tanh"
+}
+local list_2 = {
+ "int", "sum", "prod"
+}
+local list_3 = {
+ "f", "g"
+}
+local list_4 = {
+ "pi", "inf"
+}
+
+local list_1_1 = { }
+local list_2_1 = { }
+local list_2_2 = { }
+local list_2_3 = { }
+local list_4_1 = { }
+
+local frozen = false
+
+local function freeze()
+ for k=1,#list_1 do
+ local v = list_1[k]
+ list_1_1[v] = "\\".. upper(v) .." "
+ end
+ for k=1,#list_2 do
+ local v = list_2[k]
+ list_2_1[v .. "%((.-),(.-),(.-)%)"] = "\\" .. upper(v) .. "^{%1}_{%2}{%3}"
+ list_2_2[v .. "%((.-),(.-)%)"] = "\\" .. upper(v) .. "^{%1}{%2}"
+ list_2_3[v .. "%((.-)%)"] = "\\" .. upper(v) .. "{%1}"
+ end
+ for k=1,#list_4 do
+ local v = list_4[k]
+ list_4_1[v] = "\\" .. upper(v)
+ end
+ frozen = true
+end
+
+local entities = {
+ ['gt'] = '>',
+ ['lt'] = '<',
+}
+
+local symbols = {
+ ["<="] = "\\LE ",
+ [">="] = "\\GE ",
+ ["=<"] = "\\LE ",
+ ["=>"] = "\\GE ",
+ ["=="] = "\\EQ ",
+ ["<" ] = "\\LT ",
+ [">" ] = "\\GT ",
+ ["="] = "\\EQ ",
+}
+
+local function nsub(str,tag,pre,post)
+ return (gsub(str,tag .. "(%b())", function(body)
+ return pre .. nsub(sub(body,2,-2),tag,pre,post) .. post
+ end))
+end
+
+local function totex(str,mode)
+ if not frozen then freeze() end
+ local n = 0
+ -- crap
+ str = gsub(str,"%s+",' ')
+ -- xml
+ str = gsub(str,"&(.-);",entities)
+ -- ...E...
+ str = gsub(str,"([%-%+]?[%d%.%+%-]+)E([%-%+]?[%d%.]+)", "{\\SCINOT{%1}{%2}}")
+ -- ^-..
+ str = gsub(str,"%^([%-%+]*%d+)", "^{%1}")
+ -- ^(...)
+ str = nsub(str,"%^", "^{", "}")
+ -- 1/x^2
+ repeat
+ str, n = gsub(str,"([%d%w%.]+)/([%d%w%.]+%^{[%d%w%.]+})", "\\frac{%1}{%2}")
+ until n == 0
+ -- todo: autoparenthesis
+ -- int(a,b,c)
+ for k, v in next, list_2_1 do
+ repeat str, n = gsub(str,k,v) until n == 0
+ end
+ -- int(a,b)
+ for k, v in next, list_2_2 do
+ repeat str, n = gsub(str,k,v) until n == 0
+ end
+ -- int(a)
+ for k, v in next, list_2_3 do
+ repeat str, n = gsub(str,k,v) until n == 0
+ end
+ -- sin(x) => {\\sin(x)}
+ for k, v in next, list_1_1 do
+ repeat str, n = gsub(str,k,v) until n == 0
+ end
+ -- mean
+ str = nsub(str, "mean", "\\OVERLINE{", "}")
+ -- (1+x)/(1+x) => \\FRAC{1+x}{1+x}
+ repeat
+ str, n = gsub(str,"(%b())/(%b())", function(a,b)
+ return "\\FRAC{" .. sub(a,2,-2) .. "}{" .. sub(b,2,-2) .. "}"
+ end )
+ until n == 0
+ -- (1+x)/x => \\FRAC{1+x}{x}
+ repeat
+ str, n = gsub(str,"(%b())/([%+%-]?[%.%d%w]+)", function(a,b)
+ return "\\FRAC{" .. sub(a,2,-2) .. "}{" .. b .. "}"
+ end )
+ until n == 0
+ -- 1/(1+x) => \\FRAC{1}{1+x}
+ repeat
+ str, n = gsub(str,"([%.%d%w]+)/(%b())", function(a,b)
+ return "\\FRAC{" .. a .. "}{" .. sub(b,2,-2) .. "}"
+ end )
+ until n == 0
+ -- 1/x => \\FRAC{1}{x}
+ repeat
+ str, n = gsub(str,"([%.%d%w]+)/([%+%-]?[%.%d%w]+)", "\\FRAC{%1}{%2}")
+ until n == 0
+ -- times
+ str = gsub(str,"%*", " ")
+ -- symbols -- we can use a table substitution here
+ str = gsub(str,"([<>=][<>=]*)", symbols)
+ -- functions
+ str = nsub(str,"sqrt", "\\SQRT{", "}")
+ str = nsub(str,"exp", "e^{", "}")
+ str = nsub(str,"abs", "\\left|", "\\right|")
+ -- d/D
+ str = nsub(str,"D", "{\\FRAC{\\MBOX{d}}{\\MBOX{d}x}{(", ")}}")
+ str = gsub(str,"D([xy])", "\\FRAC{{\\RM d}%1}{{\\RM d}x}")
+ -- f/g
+ for k,v in next, list_3 do -- todo : prepare k,v
+ str = nsub(str,"D"..v,"{\\RM "..v.."}^{\\PRIME}(",")")
+ str = nsub(str,v,"{\\RM "..v.."}(",")")
+ end
+ -- more symbols
+ for k,v in next, list_4_1 do
+ str = gsub(str,k,v)
+ end
+ -- parenthesis (optional)
+ if mode == 2 then
+ str = gsub(str,"%(", "\\left(")
+ str = gsub(str,"%)", "\\right)")
+ end
+ -- csnames
+ str = gsub(str,"(\\[A-Z]+)", lower)
+ -- report
+ return str
+end
+
+calcmath.totex = totex
+
+function calcmath.tex(str,mode)
+ context(totex(str))
+end
+
+function calcmath.xml(id,mode)
+ context(totex(lxml.id(id).dt[1],mode))
+end
+
+-- work in progress ... lpeg variant
+
+if false then
+
+ -- todo:
+
+ -- maybe rewrite to current lpeg, i.e. string replacement and no Cc's
+
+ -- table approach we have now is less efficient but more flexible
+
+ -- D \frac {\rm d} {{\rm d}x}
+ -- Dx Dy \frac {{\rm d}y} {{\rm d}x}
+ -- Df Dg {\rm f}^{\prime}
+ -- f() g() {\rm f}()
+
+
+ -- valid utf8
+
+ local S, P, R, C, V, Cc, Ct = lpeg.S, lpeg.P, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc, lpeg.Ct
+
+ local space = S(" \n\r\t")^0
+ local integer = P("-")^-1 * R("09")^1
+ local realpart = P("-")^-1 * R("09")^1 * S(".")^1 * R("09")^1
+ local number = Cc("number") * C(integer) * space
+ local real = Cc("real") * C(realpart) * space
+ local float = Cc("float") * C(realpart) * lpeg.P("E") * lpeg.C(integer) * space
+ local identifier = Cc("identifier") * C(R("az","AZ")) * space
+ local compareop = Cc("compare") * C(P("<") + P("=") + P(">") + P(">=") + P("<=") + P("&gt;") + P("&lt;")) * space
+ local factorop = Cc("factor") * C(S("+-^_,")) * space
+ local termop = Cc("term") * C(S("*/")) * space
+ local constant = Cc("constant") * C(P("pi") + lpeg.P("inf")) * space
+ local functionop = Cc("function") * C(R("az")^1) * space
+ local open = P("(") * space
+ local close = P(")") * space
+
+ local grammar = P {
+ "expression",
+ expression = Ct(V("factor") * ((factorop+compareop) * V("factor"))^0),
+ factor = Ct(V("term") * (termop * V("term"))^0),
+ term = Ct(
+ float + real + number +
+ (open * V("expression") * close) +
+ (functionop * open * (V("expression") * (P(",") * V("expression"))^0) * close) +
+ (functionop * V("term")) +
+ constant + identifier
+ ),
+ }
+
+ local parser = space * grammar * -1
+
+ local function has_factor(t)
+ for i=1,#t do
+ if t[i] == "factor" then
+ return true
+ end
+ end
+ end
+
+ -- can be sped up if needed ...
+
+ function totex(t)
+ if t then
+ local one = t[1]
+ if type(one) == "string" then
+ local two, three = t[2], t[3]
+ if one == "number" then
+ context(two)
+ elseif one == "real" then
+ context(two)
+ elseif one == "float" then
+ context("\\scinot{",two,"}{",three,"}")
+ elseif one == "identifier" then
+ context(two)
+ elseif one == "constant" then
+ context("\\"..two)
+ elseif one == "function" then
+ if two == "sqrt" then
+ context("\\sqrt{")
+ totex(three)
+ context("}")
+ elseif two == "exp" then
+ context(" e^{")
+ totex(three)
+ context("}")
+ elseif two == "abs" then
+ context("\\left|")
+ totex(three)
+ context("\\right|")
+ elseif two == "mean" then
+ context("\\overline{")
+ totex(three)
+ context("}")
+ elseif two == "int" or two == "prod" or two == "sum" then
+ local four, five = t[4], t[5]
+ if five then
+ context("\\"..two.."^{") -- context[two]("{")
+ totex(three)
+ context("}_{")
+ totex(four)
+ context("}")
+ totex(five)
+ elseif four then
+ context("\\"..two.."^{")
+ totex(three)
+ context("}")
+ totex(four)
+ elseif three then
+ context("\\"..two.." ") -- " " not needed
+ totex(three)
+ else
+ context("\\"..two)
+ end
+ else
+ context("\\"..two.."(")
+ totex(three)
+ context(")")
+ end
+ end
+ else
+ local nt = #t
+ local hasfactor = has_factor(t)
+ if hasfactor then
+ context("\\left(")
+ end
+ totex(one)
+ for i=2,nt,3 do
+ local what, how, rest = t[i], t[i+1], t[i+2]
+ if what == "factor" then
+ if how == '^' or how == "_" then
+ context(how)
+ context("{")
+ totex(rest)
+ context("}")
+ else
+ context(how)
+ totex(rest)
+ end
+ elseif what == "term" then
+ if how == '/' then
+ context("\\frac{")
+ totex(rest)
+ context("}{")
+ totex(t[i+3] or "")
+ context("}")
+ elseif how == '*' then
+ context("\\times")
+ totex(rest)
+ else
+ context(how)
+ totex(three)
+ end
+ elseif what == "compare" then
+ if two == ">=" then
+ context("\\ge")
+ elseif two == "<=" then
+ context("\\le")
+ elseif two == "&gt;" then
+ context(">")
+ elseif two == "&lt;" then
+ context("<")
+ end
+ totex(three)
+ end
+ end
+ if hasfactor then
+ context("\\right)")
+ end
+ end
+ end
+ end
+
+ calcmath = { }
+
+ function calcmath.parse(str)
+ return lpegmatch(parser,str)
+ end
+
+ function calcmath.tex(str)
+ str = totex(lpegmatch(parser,str))
+ return (str == "" and "[error]") or str
+ end
+
+end
diff --git a/tex/context/base/x-cals.lua b/tex/context/base/x-cals.lua
index f88800df9..4051dd157 100644
--- a/tex/context/base/x-cals.lua
+++ b/tex/context/base/x-cals.lua
@@ -1,218 +1,218 @@
-if not modules then modules = { } end modules ['x-cals'] = {
- version = 1.001,
- comment = "companion to x-cals.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
-local format, lower = string.format, string.lower
-local xmlsprint, xmlcprint, xmlcollected, xmlelements = xml.sprint, xml.cprint, xml.collected, xml.elements
-local n_todimen, s_todimen = number.todimen, string.todimen
-
--- there is room for speedups as well as cleanup (using context functions)
-
-local cals = { }
-moduledata.cals = cals
-lxml.mathml = cals -- for the moment
-
-cals.ignore_widths = false
-cals.shrink_widths = false
-cals.stretch_widths = false
-
--- the following flags only apply to columns that have a specified width
---
--- proportional : shrink or stretch proportionally to the width
--- equal : shrink or stretch equaly distributed
--- n < 1 : shrink or stretch proportionally to the width but multiplied by n
---
--- more clever things, e.g. the same but applied to unspecified widths
--- has to happen at the core-ntb level (todo)
-
-local halignments = {
- left = "flushleft",
- right = "flushright",
- center = "middle",
- centre = "middle",
- justify = "normal",
-}
-
-local valignments = {
- top = "high",
- bottom = "low",
- middle = "lohi",
-}
-
-local function adapt(widths,b,w,delta,sum,n,what)
- if b == "equal" then
- delta = delta/n
- for k, v in next, w do
- widths[k] = n_todimen(v - delta)
- end
- elseif b == "proportional" then
- delta = delta/sum
- for k, v in next, w do
- widths[k] = n_todimen(v - v*delta)
- end
- elseif type(b) == "number" and b < 1 then
- delta = b*delta/sum
- for k, v in next, w do
- widths[k] = n_todimen(v - v*delta)
- end
- end
-end
-
-local function getspecs(root, pattern, names, widths)
- -- here, but actually we need this in core-ntb.tex
- -- but ideally we need an mkiv enhanced core-ntb.tex
- local ignore_widths = cals.ignore_widths
- local shrink_widths = cals.shrink_widths
- local stretch_widths = cals.stretch_widths
- for e in xmlcollected(root,pattern) do
- local at = e.at
- local column = at.colnum
- if column then
- if not ignore_widths then
- local width = at.colwidth
- if width then
- widths[tonumber(column)] = lower(width)
- end
- end
- local name = at.colname
- if name then
- names[name] = tonumber(column)
- end
- end
- end
- if ignore_width then
- -- forget about it
- elseif shrink_widths or stretch_widths then
- local sum, n, w = 0, 0, { }
- for _, v in next, widths do
- n = n + 1
- v = (type(v) == "string" and s_todimen(v)) or v
- if v then
- w[n] = v
- sum = sum + v
- end
- end
- local hsize = tex.hsize
- if type(hsize) == "string" then
- hsize = s_todimen(hsize)
- end
- local delta = sum - hsize
- if shrink_widths and delta > 0 then
- adapt(widths,shrink_widths,w,delta,sum,n,"shrink")
- elseif stretch_widths and delta < 0 then
- adapt(widths,stretch_widths,w,delta,sum,n,"stretch")
- end
- end
-end
-
-local function getspans(root, pattern, names, spans)
- for e in xmlcollected(root,pattern) do
- local at = e.at
- local name, namest, nameend = at.colname, names[at.namest or "?"], names[at.nameend or "?"]
- if name and namest and nameend then
- spans[name] = tonumber(nameend) - tonumber(namest) + 1
- end
- end
-end
-
-local bTR, eTR, bTD, eTD = context.bTR, context.eTR, context.bTD, context.eTD
-
-function cals.table(root,namespace)
-
- local prefix = (namespace or "cals") .. ":"
-
- local prefix = namespace and namespace ~= "" and (namespace .. ":") or ""
- local p = "/" .. prefix
-
- local tgroupspec = p .. "tgroup"
- local colspec = p .. "colspec"
- local spanspec = p .. "spanspec"
- local hcolspec = p .. "thead" .. p .. "colspec"
- local bcolspec = p .. "tbody" .. p .. "colspec"
- local fcolspec = p .. "tfoot" .. p .. "colspec"
- local entryspec = p .. "entry" .. "|" .. prefix .. "entrytbl" -- shouldn't that be p ?
- local hrowspec = p .. "thead" .. p .. "row"
- local browspec = p .. "tbody" .. p .. "row"
- local frowspec = p .. "tfoot" .. p .. "row"
-
- local function tablepart(root, xcolspec, xrowspec, before, after) -- move this one outside
- before()
- local at = root.at
- local pphalign, ppvalign = at.align, at.valign
- local names, widths, spans = { }, { }, { }
- getspecs(root, colspec , names, widths)
- getspecs(root, xcolspec, names, widths)
- getspans(root, spanspec, names, spans)
- for r, d, k in xmlelements(root,xrowspec) do
- bTR()
- local dk = d[k]
- local at = dk.at
- local phalign, pvalign = at.align or pphalign, at.valign or ppvalign -- todo: __p__ test
- local col = 1
- for rr, dd, kk in xmlelements(dk,entryspec) do
- local dk = dd[kk]
- if dk.tg == "entrytbl" then
- -- bTD(function() cals.table(dk) end)
- bTD()
- context("{")
- cals.table(dk)
- context("}")
- eTD()
- col = col + 1
- else
- local at = dk.at
- local b, e, s, m = names[at.namest or "?"], names[at.nameend or "?"], spans[at.spanname or "?"], at.morerows
- local halign, valign = at.align or phalign, at.valign or pvalign
- if b and e then
- s = e - b + 1
- end
- if halign then
- halign = halignments[halign]
- end
- if valign then
- valign = valignments[valign]
- end
- local width = widths[col]
- if s or m or halign or valign or width then -- currently only english interface !
- bTD {
- nx = s or 1,
- ny = (m or 0) + 1,
- align = format("{%s,%s}",halign or "flushleft",valign or "high"),
- width = width or "fit",
- }
- else
- bTD {
- align = "{flushleft,high}",
- width = "fit", -- else problems with vertical material
- }
- end
- xmlcprint(dk)
- eTD()
- col = col + (s or 1)
- end
- end
- eTR()
- end
- after()
- end
-
- for tgroup in lxml.collected(root,tgroupspec) do
- context.directsetup("cals:table:before")
- lxml.directives.before(root,"cdx") -- "cals:table"
- context.bgroup()
- lxml.directives.setup(root,"cdx") -- "cals:table"
- context.bTABLE()
- tablepart(tgroup, hcolspec, hrowspec, context.bTABLEhead, context.eTABLEhead)
- tablepart(tgroup, bcolspec, browspec, context.bTABLEbody, context.eTABLEbody)
- tablepart(tgroup, fcolspec, frowspec, context.bTABLEfoot, context.eTABLEfoot)
- context.eTABLE()
- context.egroup()
- lxml.directives.after(root,"cdx") -- "cals:table"
- context.directsetup("cals:table:after")
- end
-
-end
+if not modules then modules = { } end modules ['x-cals'] = {
+ version = 1.001,
+ comment = "companion to x-cals.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local format, lower = string.format, string.lower
+local xmlsprint, xmlcprint, xmlcollected, xmlelements = xml.sprint, xml.cprint, xml.collected, xml.elements
+local n_todimen, s_todimen = number.todimen, string.todimen
+
+-- there is room for speedups as well as cleanup (using context functions)
+
+local cals = { }
+moduledata.cals = cals
+lxml.mathml = cals -- for the moment
+
+cals.ignore_widths = false
+cals.shrink_widths = false
+cals.stretch_widths = false
+
+-- the following flags only apply to columns that have a specified width
+--
+-- proportional : shrink or stretch proportionally to the width
+-- equal : shrink or stretch equaly distributed
+-- n < 1 : shrink or stretch proportionally to the width but multiplied by n
+--
+-- more clever things, e.g. the same but applied to unspecified widths
+-- has to happen at the core-ntb level (todo)
+
+local halignments = {
+ left = "flushleft",
+ right = "flushright",
+ center = "middle",
+ centre = "middle",
+ justify = "normal",
+}
+
+local valignments = {
+ top = "high",
+ bottom = "low",
+ middle = "lohi",
+}
+
+local function adapt(widths,b,w,delta,sum,n,what)
+ if b == "equal" then
+ delta = delta/n
+ for k, v in next, w do
+ widths[k] = n_todimen(v - delta)
+ end
+ elseif b == "proportional" then
+ delta = delta/sum
+ for k, v in next, w do
+ widths[k] = n_todimen(v - v*delta)
+ end
+ elseif type(b) == "number" and b < 1 then
+ delta = b*delta/sum
+ for k, v in next, w do
+ widths[k] = n_todimen(v - v*delta)
+ end
+ end
+end
+
+local function getspecs(root, pattern, names, widths)
+ -- here, but actually we need this in core-ntb.tex
+ -- but ideally we need an mkiv enhanced core-ntb.tex
+ local ignore_widths = cals.ignore_widths
+ local shrink_widths = cals.shrink_widths
+ local stretch_widths = cals.stretch_widths
+ for e in xmlcollected(root,pattern) do
+ local at = e.at
+ local column = at.colnum
+ if column then
+ if not ignore_widths then
+ local width = at.colwidth
+ if width then
+ widths[tonumber(column)] = lower(width)
+ end
+ end
+ local name = at.colname
+ if name then
+ names[name] = tonumber(column)
+ end
+ end
+ end
+ if ignore_width then
+ -- forget about it
+ elseif shrink_widths or stretch_widths then
+ local sum, n, w = 0, 0, { }
+ for _, v in next, widths do
+ n = n + 1
+ v = (type(v) == "string" and s_todimen(v)) or v
+ if v then
+ w[n] = v
+ sum = sum + v
+ end
+ end
+ local hsize = tex.hsize
+ if type(hsize) == "string" then
+ hsize = s_todimen(hsize)
+ end
+ local delta = sum - hsize
+ if shrink_widths and delta > 0 then
+ adapt(widths,shrink_widths,w,delta,sum,n,"shrink")
+ elseif stretch_widths and delta < 0 then
+ adapt(widths,stretch_widths,w,delta,sum,n,"stretch")
+ end
+ end
+end
+
+local function getspans(root, pattern, names, spans)
+ for e in xmlcollected(root,pattern) do
+ local at = e.at
+ local name, namest, nameend = at.colname, names[at.namest or "?"], names[at.nameend or "?"]
+ if name and namest and nameend then
+ spans[name] = tonumber(nameend) - tonumber(namest) + 1
+ end
+ end
+end
+
+local bTR, eTR, bTD, eTD = context.bTR, context.eTR, context.bTD, context.eTD
+
+function cals.table(root,namespace)
+
+ local prefix = (namespace or "cals") .. ":"
+
+ local prefix = namespace and namespace ~= "" and (namespace .. ":") or ""
+ local p = "/" .. prefix
+
+ local tgroupspec = p .. "tgroup"
+ local colspec = p .. "colspec"
+ local spanspec = p .. "spanspec"
+ local hcolspec = p .. "thead" .. p .. "colspec"
+ local bcolspec = p .. "tbody" .. p .. "colspec"
+ local fcolspec = p .. "tfoot" .. p .. "colspec"
+ local entryspec = p .. "entry" .. "|" .. prefix .. "entrytbl" -- shouldn't that be p ?
+ local hrowspec = p .. "thead" .. p .. "row"
+ local browspec = p .. "tbody" .. p .. "row"
+ local frowspec = p .. "tfoot" .. p .. "row"
+
+ local function tablepart(root, xcolspec, xrowspec, before, after) -- move this one outside
+ before()
+ local at = root.at
+ local pphalign, ppvalign = at.align, at.valign
+ local names, widths, spans = { }, { }, { }
+ getspecs(root, colspec , names, widths)
+ getspecs(root, xcolspec, names, widths)
+ getspans(root, spanspec, names, spans)
+ for r, d, k in xmlelements(root,xrowspec) do
+ bTR()
+ local dk = d[k]
+ local at = dk.at
+ local phalign, pvalign = at.align or pphalign, at.valign or ppvalign -- todo: __p__ test
+ local col = 1
+ for rr, dd, kk in xmlelements(dk,entryspec) do
+ local dk = dd[kk]
+ if dk.tg == "entrytbl" then
+ -- bTD(function() cals.table(dk) end)
+ bTD()
+ context("{")
+ cals.table(dk)
+ context("}")
+ eTD()
+ col = col + 1
+ else
+ local at = dk.at
+ local b, e, s, m = names[at.namest or "?"], names[at.nameend or "?"], spans[at.spanname or "?"], at.morerows
+ local halign, valign = at.align or phalign, at.valign or pvalign
+ if b and e then
+ s = e - b + 1
+ end
+ if halign then
+ halign = halignments[halign]
+ end
+ if valign then
+ valign = valignments[valign]
+ end
+ local width = widths[col]
+ if s or m or halign or valign or width then -- currently only english interface !
+ bTD {
+ nx = s or 1,
+ ny = (m or 0) + 1,
+ align = format("{%s,%s}",halign or "flushleft",valign or "high"),
+ width = width or "fit",
+ }
+ else
+ bTD {
+ align = "{flushleft,high}",
+ width = "fit", -- else problems with vertical material
+ }
+ end
+ xmlcprint(dk)
+ eTD()
+ col = col + (s or 1)
+ end
+ end
+ eTR()
+ end
+ after()
+ end
+
+ for tgroup in lxml.collected(root,tgroupspec) do
+ context.directsetup("cals:table:before")
+ lxml.directives.before(root,"cdx") -- "cals:table"
+ context.bgroup()
+ lxml.directives.setup(root,"cdx") -- "cals:table"
+ context.bTABLE()
+ tablepart(tgroup, hcolspec, hrowspec, context.bTABLEhead, context.eTABLEhead)
+ tablepart(tgroup, bcolspec, browspec, context.bTABLEbody, context.eTABLEbody)
+ tablepart(tgroup, fcolspec, frowspec, context.bTABLEfoot, context.eTABLEfoot)
+ context.eTABLE()
+ context.egroup()
+ lxml.directives.after(root,"cdx") -- "cals:table"
+ context.directsetup("cals:table:after")
+ end
+
+end
diff --git a/tex/context/base/x-chemml.lua b/tex/context/base/x-chemml.lua
index 46a13a37e..79c1d9525 100644
--- a/tex/context/base/x-chemml.lua
+++ b/tex/context/base/x-chemml.lua
@@ -1,51 +1,51 @@
-if not modules then modules = { } end modules ['x-chemml'] = {
- version = 1.001,
- comment = "companion to x-chemml.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- not yet acceptable cld
-
-local format, lower, upper, gsub, sub, match = string.format, string.lower, string.upper, string.gsub, string.sub, string.match
-local concat = table.concat
-
-local chemml = { }
-local moduledata = moduledata or { }
-moduledata.chemml = chemml
-
-function chemml.pi(id)
- local str = xml.content(lxml.id(id))
- local _, class, key, value = match(str,"^(%S+)%s+(%S+)%s+(%S+)%s+(%S+)%s*$")
- if key and value then
- context("\\setupCMLappearance[%s][%s=%s]",class, key, value)
- end
-end
-
-function chemml.do_graphic(id)
- local t = { }
- for r, d, k in xml.elements(lxml.id(id),"cml:graphic") do
- t[#t+1] = xml.tostring(d[k].dt)
- end
- context(concat(t,","))
-end
-
-function chemml.no_graphic(id)
- local t = { }
- for r, d, k in xml.elements(lxml.id(id),"cml:text|cml:oxidation|cml:annotation") do
- local dk = d[k]
- if dk.tg == "oxidation" then
- t[#t+1] = format("\\chemicaloxidation{%s}{%s}{%s}",r.at.sign or "",r.at.n or 1,xml.tostring(dk.dt))
- elseif dk.tg == "annotation" then
- local location = r.at.location or "r"
- local caption = xml.content(xml.first(dk,"cml:caption"))
- local text = xml.content(xml.first(dk,"cml:text"))
- t[#t+1] = format("\\doCMLannotation{%s}{%s}{%s}",location,caption,text)
- else
- t[#t+1] = xml.tostring(dk.dt) or ""
- end
- end
- context(concat(t,","))
-end
-
+if not modules then modules = { } end modules ['x-chemml'] = {
+ version = 1.001,
+ comment = "companion to x-chemml.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- not yet acceptable cld
+
+local format, lower, upper, gsub, sub, match = string.format, string.lower, string.upper, string.gsub, string.sub, string.match
+local concat = table.concat
+
+local chemml = { }
+local moduledata = moduledata or { }
+moduledata.chemml = chemml
+
+function chemml.pi(id)
+ local str = xml.content(lxml.id(id))
+ local _, class, key, value = match(str,"^(%S+)%s+(%S+)%s+(%S+)%s+(%S+)%s*$")
+ if key and value then
+ context("\\setupCMLappearance[%s][%s=%s]",class, key, value)
+ end
+end
+
+function chemml.do_graphic(id)
+ local t = { }
+ for r, d, k in xml.elements(lxml.id(id),"cml:graphic") do
+ t[#t+1] = xml.tostring(d[k].dt)
+ end
+ context(concat(t,","))
+end
+
+function chemml.no_graphic(id)
+ local t = { }
+ for r, d, k in xml.elements(lxml.id(id),"cml:text|cml:oxidation|cml:annotation") do
+ local dk = d[k]
+ if dk.tg == "oxidation" then
+ t[#t+1] = format("\\chemicaloxidation{%s}{%s}{%s}",r.at.sign or "",r.at.n or 1,xml.tostring(dk.dt))
+ elseif dk.tg == "annotation" then
+ local location = r.at.location or "r"
+ local caption = xml.content(xml.first(dk,"cml:caption"))
+ local text = xml.content(xml.first(dk,"cml:text"))
+ t[#t+1] = format("\\doCMLannotation{%s}{%s}{%s}",location,caption,text)
+ else
+ t[#t+1] = xml.tostring(dk.dt) or ""
+ end
+ end
+ context(concat(t,","))
+end
+
diff --git a/tex/context/base/x-ct.lua b/tex/context/base/x-ct.lua
index 190da78fc..2dee985c3 100644
--- a/tex/context/base/x-ct.lua
+++ b/tex/context/base/x-ct.lua
@@ -1,165 +1,165 @@
-if not modules then modules = { } end modules ['x-ct'] = {
- version = 1.001,
- comment = "companion to x-ct.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- needs testing
-
-local xmlsprint, xmlcprint, xmlfilter, xmlcollected = xml.sprint, xml.cprint, xml.filter, xml.collected
-local format, concat, rep, find = string.format, table.concat, string.rep, string.find
-
-moduledata.ct = moduledata.ct or { }
-
-local halignments = {
- left = 'l',
- flushleft = 'l',
- right = 'r',
- flushright = 'r',
- center = 'c',
- middle = 'c',
- centre = 'c',
- justify = '',
-}
-
-local templates = { }
-
-function moduledata.ct.registertabulatetemplate(name,str)
- templates[name] = str
-end
-
-local function roottemplate(root)
- local rt = root.at.template
- if rt then
- local template = templates[rt]
- if template then
- return template
- else
- if not find(rt,"|") then
- rt = gsub(rt,",","|")
- end
- if not find(rt,"^|") then rt = "|" .. rt end
- if not find(rt,"|$") then rt = rt .. "|" end
- return rt
- end
- end
-end
-
-local function specifiedtemplate(root,templatespec)
- local template = { }
- for e in xmlcollected(root,templatespec) do
- local at = e.at
- local tm = halignments[at.align] or ""
- if toboolean(at.paragraph) then
- tm = tm .. "p"
- end
- template[#template+1] = tm
- end
- if #template > 0 then
- return "|" .. concat(template,"|") .. "|"
- else
- return nil
- end
-end
-
-local function autotemplate(root,rowspec,cellspec)
- local max = 0
- for e in xmlcollected(root,rowspec) do
- local n = xml.count(e,cellspec)
- if n > max then max = n end
- end
- if max == 2 then
- return "|l|p|"
- elseif max > 0 then
- return "|" .. rep("p|",max)
- else
- return nil
- end
-end
-
-local defaulttemplate = "|l|p|"
-
-function moduledata.ct.tabulate(root,namespace)
- if not root then
- return
- else
- root = lxml.id(root)
- end
-
- local prefix = (namespace or "context") .. ":"
-
- local templatespec = "/" .. prefix .. "template" .. "/" .. prefix .. "column"
- local bodyrowspec = "/" .. prefix .. "body" .. "/" .. prefix .. "row"
- local cellspec = "/" .. prefix .. "cell"
-
- local template =
- roottemplate (root) or
- specifiedtemplate (root,templatespec) or
- autotemplate (root,bodyrowspec,cellspec) or
- defaulttemplate
-
- -- todo: head and foot
-
- local NC, NR = context.NC, context.NR
-
- lxml.directives.before(root,'cdx')
- context.bgroup()
- lxml.directives.setup(root,'cdx')
- context.starttabulate { template }
- for e in xmlcollected(root,bodyrowspec) do
- NC()
- for e in xmlcollected(e,cellspec) do
- xmlcprint(e)
- NC()
- end
- NR()
- end
- context.stoptabulate()
- context.egroup()
- lxml.directives.after(root,'cdx')
-
-end
-
-function moduledata.ct.combination(root,namespace)
-
- if not root then
- return
- else
- root = lxml.id(root)
- end
-
- local prefix = (namespace or "context") .. ":"
-
- local pairspec = "/" .. prefix .. "pair"
- local contentspec = "/" .. prefix .. "content" .. "/text()"
- local captionspec = "/" .. prefix .. "caption" .. "/text()"
-
- local nx, ny = root.at.nx, root.at.ny
-
- if not (nx or ny) then
- nx = xml.count(root,pairspec) or 2
- end
- local template = format("%s*%s", nx or 1, ny or 1)
-
- lxml.directives.before(root,'cdx')
- context.bgroup()
- lxml.directives.setup(root,'cdx')
- context.startcombination { template }
- for e in xmlcollected(root,pairspec) do
- -- context.combination(
- -- function() xmlfilter(e,contentspec) end,
- -- function() xmlfilter(e,captionspec) end
- -- )
- context("{")
- xmlfilter(e,contentspec)
- context("}{")
- xmlfilter(e,captionspec)
- context("}")
- end
- context.stopcombination()
- context.egroup()
- lxml.directives.after(root,'cdx')
-
-end
+if not modules then modules = { } end modules ['x-ct'] = {
+ version = 1.001,
+ comment = "companion to x-ct.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- needs testing
+
+local xmlsprint, xmlcprint, xmlfilter, xmlcollected = xml.sprint, xml.cprint, xml.filter, xml.collected
+local format, concat, rep, find = string.format, table.concat, string.rep, string.find
+
+moduledata.ct = moduledata.ct or { }
+
+local halignments = {
+ left = 'l',
+ flushleft = 'l',
+ right = 'r',
+ flushright = 'r',
+ center = 'c',
+ middle = 'c',
+ centre = 'c',
+ justify = '',
+}
+
+local templates = { }
+
+function moduledata.ct.registertabulatetemplate(name,str)
+ templates[name] = str
+end
+
+local function roottemplate(root)
+ local rt = root.at.template
+ if rt then
+ local template = templates[rt]
+ if template then
+ return template
+ else
+ if not find(rt,"|") then
+ rt = gsub(rt,",","|")
+ end
+ if not find(rt,"^|") then rt = "|" .. rt end
+ if not find(rt,"|$") then rt = rt .. "|" end
+ return rt
+ end
+ end
+end
+
+local function specifiedtemplate(root,templatespec)
+ local template = { }
+ for e in xmlcollected(root,templatespec) do
+ local at = e.at
+ local tm = halignments[at.align] or ""
+ if toboolean(at.paragraph) then
+ tm = tm .. "p"
+ end
+ template[#template+1] = tm
+ end
+ if #template > 0 then
+ return "|" .. concat(template,"|") .. "|"
+ else
+ return nil
+ end
+end
+
+local function autotemplate(root,rowspec,cellspec)
+ local max = 0
+ for e in xmlcollected(root,rowspec) do
+ local n = xml.count(e,cellspec)
+ if n > max then max = n end
+ end
+ if max == 2 then
+ return "|l|p|"
+ elseif max > 0 then
+ return "|" .. rep("p|",max)
+ else
+ return nil
+ end
+end
+
+local defaulttemplate = "|l|p|"
+
+function moduledata.ct.tabulate(root,namespace)
+ if not root then
+ return
+ else
+ root = lxml.id(root)
+ end
+
+ local prefix = (namespace or "context") .. ":"
+
+ local templatespec = "/" .. prefix .. "template" .. "/" .. prefix .. "column"
+ local bodyrowspec = "/" .. prefix .. "body" .. "/" .. prefix .. "row"
+ local cellspec = "/" .. prefix .. "cell"
+
+ local template =
+ roottemplate (root) or
+ specifiedtemplate (root,templatespec) or
+ autotemplate (root,bodyrowspec,cellspec) or
+ defaulttemplate
+
+ -- todo: head and foot
+
+ local NC, NR = context.NC, context.NR
+
+ lxml.directives.before(root,'cdx')
+ context.bgroup()
+ lxml.directives.setup(root,'cdx')
+ context.starttabulate { template }
+ for e in xmlcollected(root,bodyrowspec) do
+ NC()
+ for e in xmlcollected(e,cellspec) do
+ xmlcprint(e)
+ NC()
+ end
+ NR()
+ end
+ context.stoptabulate()
+ context.egroup()
+ lxml.directives.after(root,'cdx')
+
+end
+
+function moduledata.ct.combination(root,namespace)
+
+ if not root then
+ return
+ else
+ root = lxml.id(root)
+ end
+
+ local prefix = (namespace or "context") .. ":"
+
+ local pairspec = "/" .. prefix .. "pair"
+ local contentspec = "/" .. prefix .. "content" .. "/text()"
+ local captionspec = "/" .. prefix .. "caption" .. "/text()"
+
+ local nx, ny = root.at.nx, root.at.ny
+
+ if not (nx or ny) then
+ nx = xml.count(root,pairspec) or 2
+ end
+ local template = format("%s*%s", nx or 1, ny or 1)
+
+ lxml.directives.before(root,'cdx')
+ context.bgroup()
+ lxml.directives.setup(root,'cdx')
+ context.startcombination { template }
+ for e in xmlcollected(root,pairspec) do
+ -- context.combination(
+ -- function() xmlfilter(e,contentspec) end,
+ -- function() xmlfilter(e,captionspec) end
+ -- )
+ context("{")
+ xmlfilter(e,contentspec)
+ context("}{")
+ xmlfilter(e,captionspec)
+ context("}")
+ end
+ context.stopcombination()
+ context.egroup()
+ lxml.directives.after(root,'cdx')
+
+end
diff --git a/tex/context/base/x-ldx.lua b/tex/context/base/x-ldx.lua
index 8a6864033..31cbebf13 100644
--- a/tex/context/base/x-ldx.lua
+++ b/tex/context/base/x-ldx.lua
@@ -1,341 +1,341 @@
-if not modules then modules = { } end modules ['x-ldx'] = {
- version = 1.001,
- comment = "companion to x-ldx.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- --[[ldx--
--- <topic>Introduction</topic>
--- --ldx]]--
-
---[[ldx--
-<source>Lua Documentation Module</source>
-
-This file is part of the <logo label='context'/> documentation suite and
-itself serves as an example of using <logo label='lua'/> in combination
-with <logo label='tex'/>.
-
-I will rewrite this using lpeg. On the other hand, we cannot expect proper
-<logo label='tex'/> and for educational purposed the syntax might be wrong.
---ldx]]--
-
--- there is a nice parser on from http://lua-users.org/wiki/LpegRecipes (by
--- Patrick Donnelly) but lua crashes when I apply functions to some of the
--- matches
-
-banner = "version 1.0.1 - 2007+ - PRAGMA ADE / CONTEXT"
-
---[[
-This script needs a few libraries. Instead of merging the code here
-we can use
-
-<typing>
-mtxrun --internal x-ldx.lua
-</typing>
-
-That way, the libraries included in the runner will be used.
-]]--
-
--- libraries l-string.lua l-table.lua l-io.lua l-file.lua
-
--- begin library merge
--- end library merge
-
-local gsub, find, sub = string.gsub, string.find, string.sub
-local splitstring, emptystring = string.split, string.is_empty
-local concat = table.concat
-
---[[
-Just a demo comment line. We will handle such multiline comments but
-only when they start and end at the beginning of a line. More rich
-comments are tagged differently.
-]]--
-
---[[ldx--
-First we define a proper namespace for this module. The <q>l</q> stands for
-<logo label='lua'/>, the <q>d</q> for documentation and the <q>x</q> for
-<logo label='xml'/>.
---ldx]]--
-
-if not ldx then ldx = { } end
-
---[[ldx--
-We load the lua file into a table. The entries in this table themselves are
-tables and have keys like <t>code</t> and <t>comment</t>.
---ldx]]--
-
-function ldx.load(filename)
- local data = file.readdata(filename)
- local expr = "%s*%-%-%[%[ldx%-*%s*(.-)%s*%-%-ldx%]%]%-*%s*"
- local i, j, t = 0, 0, { }
- while true do
- local comment, ni
- ni, j, comment = find(data, expr, j)
- if not ni then break end
- t[#t+1] = { code = sub(data, i, ni-1) }
- t[#t+1] = { comment = comment }
- i = j + 1
- end
- local str = sub(data, i, #data)
- str = gsub(str, "^%s*(.-)%s*$", "%1")
- if #str > 0 then
- t[#t+1] = { code = str }
- end
- return t
-end
-
---[[ldx--
-We will tag keywords so that we can higlight them using a special font
-or color. Users can extend this list when needed.
---ldx]]--
-
-ldx.keywords = { }
-
---[[ldx--
-Here come the reserved words:
---ldx]]--
-
-ldx.keywords.reserved = {
- ["and"] = 1,
- ["break"] = 1,
- ["do"] = 1,
- ["else"] = 1,
- ["elseif"] = 1,
- ["end"] = 1,
- ["false"] = 1,
- ["for"] = 1,
- ["function"] = 1,
- ["if"] = 1,
- ["in"] = 1,
- ["local"] = 1,
- ["nil"] = 1,
- ["not"] = 1,
- ["or"] = 1,
- ["repeat"] = 1,
- ["return"] = 1,
- ["then"] = 1,
- ["true"] = 1,
- ["until"] = 1,
- ["while"] = 1
-}
-
---[[ldx--
-We need to escape a few tokens. We keep the hash local to the
-definition but set it up only once, hence the <key>do</key>
-construction.
---ldx]]--
-
-do
- local e = { [">"] = "&gt;", ["<"] = "&lt;", ["&"] = "&amp;" }
- function ldx.escape(str)
- return (gsub(str, "([><&])",e))
- end
-end
-
---[[ldx--
-Enhancing the code is a bit tricky due to the fact that we have to
-deal with strings and escaped quotes within these strings. Before we
-mess around with the code, we hide the strings, and after that we
-insert them again. Single and double quoted strings are tagged so
-that we can use a different font to highlight them.
---ldx]]--
-
-ldx.make_index = true
-
-function ldx.enhance(data) -- i need to use lpeg and then we can properly autoindent -)
- local e = ldx.escape
- for k=1,#data do
- local v = data[k]
- if v.code then
- local dqs, sqs, com, cmt, cod = { }, { }, { }, { }, e(v.code)
- cod = gsub(cod, '\\"', "##d##")
- cod = gsub(cod, "\\'", "##s##")
- cod = gsub(cod, "%-%-%[%[.-%]%]%-%-", function(s)
- cmt[#cmt+1] = s
- return "<l<<<".. #cmt ..">>>l>"
- end)
- cod = gsub(cod, "%-%-([^\n]*)", function(s)
- com[#com+1] = s
- return "<c<<<".. #com ..">>>c>"
- end)
- cod = gsub(cod, "(%b\"\")", function(s)
- dqs[#dqs+1] = sub(s,2,-2) or ""
- return "<d<<<".. #dqs ..">>>d>"
- end)
- cod = gsub(cod, "(%b\'\')", function(s)
- sqs[#sqs+1] = sub(s,2,-2) or ""
- return "<s<<<".. #sqs ..">>>s>"
- end)
- cod = gsub(cod, "(%a+)",function(key)
- local class = ldx.keywords.reserved[key]
- if class then
- return "<key class='" .. class .. "'>" .. key .. "</key>"
- else
- return key
- end
- end)
- cod = gsub(cod, "<s<<<(%d+)>>>s>", function(s)
- return "<sqs>" .. sqs[tonumber(s)] .. "</sqs>"
- end)
- cod = gsub(cod, "<d<<<(%d+)>>>d>", function(s)
- return "<dqs>" .. dqs[tonumber(s)] .. "</dqs>"
- end)
- cod = gsub(cod, "<c<<<(%d+)>>>c>", function(s)
- return "<com>" .. com[tonumber(s)] .. "</com>"
- end)
- cod = gsub(cod, "<l<<<(%d+)>>>l>", function(s)
- return cmt[tonumber(s)]
- end)
- cod = gsub(cod, "##d##", "\\\"")
- cod = gsub(cod, "##s##", "\\\'")
- if ldx.make_index then
- local lines = splitstring(cod,"\n")
- local f = "(<key class='1'>function</key>)%s+([%w%.]+)%s*%("
- for k=1,#lines do
- local v = lines[k]
- -- functies
- v = gsub(v,f,function(key, str)
- return "<function>" .. str .. "</function>("
- end)
- -- variables
- v = gsub(v,"^([%w][%w%,%s]-)(=[^=])",function(str, rest)
- local t = splitstring(str,",%s*")
- for k=1,#t do
- t[k] = "<variable>" .. t[k] .. "</variable>"
- end
- return concat(t,", ") .. rest
- end)
- -- so far
- lines[k] = v
- end
- v.code = concat(lines,"\n")
- else
- v.code = cod
- end
- end
- end
-end
-
---[[ldx--
-We're now ready to save the file in <logo label='xml'/> format. This boils
-down to wrapping the code and comment as well as the whole document. We tag
-lines in the code as such so that we don't need messy <t>CDATA</t> constructs
-and by calculating the indentation we also avoid space troubles. It also makes
-it possible to change the indentation afterwards.
---ldx]]--
-
-function ldx.as_xml(data) -- ldx: not needed
- local t, cmode = { }, false
- t[#t+1] = "<?xml version='1.0' standalone='yes'?>\n"
- t[#t+1] = "\n<document xmlns:ldx='http://www.pragma-ade.com/schemas/ldx.rng' xmlns='http://www.pragma-ade.com/schemas/ldx.rng'>\n"
- for k=1,#data do
- local v = data[k]
- if v.code and not emptystring(v.code) then
- t[#t+1] = "\n<code>\n"
- local split = splitstring(v.code,"\n")
- for k=1,#split do -- make this faster
- local v = split[k]
- local a, b = find(v,"^(%s+)")
- if v then v = gsub(v,"[\n\r ]+$","") end
- if a and b then
- v = sub(v,b+1,#v)
- if cmode then
- t[#t+1] = "<line comment='yes' n='" .. b .. "'>" .. v .. "</line>\n"
- else
- t[#t+1] = "<line n='" .. b .. "'>" .. v .. "</line>\n"
- end
- elseif emptystring(v) then
- if cmode then
- t[#t+1] = "<line comment='yes'/>\n"
- else
- t[#t+1] = "<line/>\n"
- end
- elseif find(v,"^%-%-%[%[") then
- t[#t+1] = "<line comment='yes'>" .. v .. "</line>\n"
- cmode= true
- elseif find(v,"^%]%]%-%-") then
- t[#t+1] = "<line comment='yes'>" .. v .. "</line>\n"
- cmode= false
- elseif cmode then
- t[#t+1] = "<line comment='yes'>" .. v .. "</line>\n"
- else
- t[#t+1] = "<line>" .. v .. "</line>\n"
- end
- end
- t[#t+1] = "</code>\n"
- elseif v.comment then
- t[#t+1] = "\n<comment>\n" .. v.comment .. "\n</comment>\n"
- else
- -- cannot happen
- end
- end
- t[#t+1] = "\n</document>\n"
- return concat(t,"")
-end
-
---[[ldx--
-Saving the result is a trivial effort.
---ldx]]--
-
-function ldx.save(filename,data)
- file.savedata(filename,ldx.as_xml(data))
-end
-
---[[ldx--
-The next function wraps it all in one call:
---ldx]]--
-
-function ldx.convert(luaname,ldxname)
- if not file.is_readable(luaname) then
- luaname = luaname .. ".lua"
- end
- if file.is_readable(luaname) then
- if not ldxname then
- ldxname = file.replacesuffix(luaname,"ldx")
- end
- local data = ldx.load(luaname)
- if data then
- ldx.enhance(data)
- if ldxname ~= luaname then
- ldx.save(ldxname,data)
- end
- end
- end
-end
-
---[[ldx--
-This module can be used directly:
-
-<typing>
-mtxrun --internal x-ldx somefile.lua
-</typing>
-
-will produce an ldx file that can be processed with <logo label='context'/>
-by running:
-
-<typing>
-context --use=x-ldx --forcexml somefile.ldx
-</typing>
-
-You can do this in one step by saying:
-
-<typing>
-context --ctx=x-ldx somefile.lua
-</typing>
-
-This will trigger <logo label='context'/> into loading the mentioned
-<logo label='ctx'/> file. That file describes the conversion as well
-as the module to be used.
-
-The main conversion call is:
---ldx]]--
-
--- todo: assume usage of "mtxrun --script x-ldx", maybe make it mtx-ldx
-
-if environment.files and environment.files[1] then
- ldx.convert(environment.files[1],environment.files[2])
-end
-
---~ exit(1)
+if not modules then modules = { } end modules ['x-ldx'] = {
+ version = 1.001,
+ comment = "companion to x-ldx.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- --[[ldx--
+-- <topic>Introduction</topic>
+-- --ldx]]--
+
+--[[ldx--
+<source>Lua Documentation Module</source>
+
+This file is part of the <logo label='context'/> documentation suite and
+itself serves as an example of using <logo label='lua'/> in combination
+with <logo label='tex'/>.
+
+I will rewrite this using lpeg. On the other hand, we cannot expect proper
+<logo label='tex'/> and for educational purposed the syntax might be wrong.
+--ldx]]--
+
+-- there is a nice parser on from http://lua-users.org/wiki/LpegRecipes (by
+-- Patrick Donnelly) but lua crashes when I apply functions to some of the
+-- matches
+
+banner = "version 1.0.1 - 2007+ - PRAGMA ADE / CONTEXT"
+
+--[[
+This script needs a few libraries. Instead of merging the code here
+we can use
+
+<typing>
+mtxrun --internal x-ldx.lua
+</typing>
+
+That way, the libraries included in the runner will be used.
+]]--
+
+-- libraries l-string.lua l-table.lua l-io.lua l-file.lua
+
+-- begin library merge
+-- end library merge
+
+local gsub, find, sub = string.gsub, string.find, string.sub
+local splitstring, emptystring = string.split, string.is_empty
+local concat = table.concat
+
+--[[
+Just a demo comment line. We will handle such multiline comments but
+only when they start and end at the beginning of a line. More rich
+comments are tagged differently.
+]]--
+
+--[[ldx--
+First we define a proper namespace for this module. The <q>l</q> stands for
+<logo label='lua'/>, the <q>d</q> for documentation and the <q>x</q> for
+<logo label='xml'/>.
+--ldx]]--
+
+if not ldx then ldx = { } end
+
+--[[ldx--
+We load the lua file into a table. The entries in this table themselves are
+tables and have keys like <t>code</t> and <t>comment</t>.
+--ldx]]--
+
+function ldx.load(filename)
+ local data = file.readdata(filename)
+ local expr = "%s*%-%-%[%[ldx%-*%s*(.-)%s*%-%-ldx%]%]%-*%s*"
+ local i, j, t = 0, 0, { }
+ while true do
+ local comment, ni
+ ni, j, comment = find(data, expr, j)
+ if not ni then break end
+ t[#t+1] = { code = sub(data, i, ni-1) }
+ t[#t+1] = { comment = comment }
+ i = j + 1
+ end
+ local str = sub(data, i, #data)
+ str = gsub(str, "^%s*(.-)%s*$", "%1")
+ if #str > 0 then
+ t[#t+1] = { code = str }
+ end
+ return t
+end
+
+--[[ldx--
+We will tag keywords so that we can higlight them using a special font
+or color. Users can extend this list when needed.
+--ldx]]--
+
+ldx.keywords = { }
+
+--[[ldx--
+Here come the reserved words:
+--ldx]]--
+
+ldx.keywords.reserved = {
+ ["and"] = 1,
+ ["break"] = 1,
+ ["do"] = 1,
+ ["else"] = 1,
+ ["elseif"] = 1,
+ ["end"] = 1,
+ ["false"] = 1,
+ ["for"] = 1,
+ ["function"] = 1,
+ ["if"] = 1,
+ ["in"] = 1,
+ ["local"] = 1,
+ ["nil"] = 1,
+ ["not"] = 1,
+ ["or"] = 1,
+ ["repeat"] = 1,
+ ["return"] = 1,
+ ["then"] = 1,
+ ["true"] = 1,
+ ["until"] = 1,
+ ["while"] = 1
+}
+
+--[[ldx--
+We need to escape a few tokens. We keep the hash local to the
+definition but set it up only once, hence the <key>do</key>
+construction.
+--ldx]]--
+
+do
+ local e = { [">"] = "&gt;", ["<"] = "&lt;", ["&"] = "&amp;" }
+ function ldx.escape(str)
+ return (gsub(str, "([><&])",e))
+ end
+end
+
+--[[ldx--
+Enhancing the code is a bit tricky due to the fact that we have to
+deal with strings and escaped quotes within these strings. Before we
+mess around with the code, we hide the strings, and after that we
+insert them again. Single and double quoted strings are tagged so
+that we can use a different font to highlight them.
+--ldx]]--
+
+ldx.make_index = true
+
+function ldx.enhance(data) -- i need to use lpeg and then we can properly autoindent -)
+ local e = ldx.escape
+ for k=1,#data do
+ local v = data[k]
+ if v.code then
+ local dqs, sqs, com, cmt, cod = { }, { }, { }, { }, e(v.code)
+ cod = gsub(cod, '\\"', "##d##")
+ cod = gsub(cod, "\\'", "##s##")
+ cod = gsub(cod, "%-%-%[%[.-%]%]%-%-", function(s)
+ cmt[#cmt+1] = s
+ return "<l<<<".. #cmt ..">>>l>"
+ end)
+ cod = gsub(cod, "%-%-([^\n]*)", function(s)
+ com[#com+1] = s
+ return "<c<<<".. #com ..">>>c>"
+ end)
+ cod = gsub(cod, "(%b\"\")", function(s)
+ dqs[#dqs+1] = sub(s,2,-2) or ""
+ return "<d<<<".. #dqs ..">>>d>"
+ end)
+ cod = gsub(cod, "(%b\'\')", function(s)
+ sqs[#sqs+1] = sub(s,2,-2) or ""
+ return "<s<<<".. #sqs ..">>>s>"
+ end)
+ cod = gsub(cod, "(%a+)",function(key)
+ local class = ldx.keywords.reserved[key]
+ if class then
+ return "<key class='" .. class .. "'>" .. key .. "</key>"
+ else
+ return key
+ end
+ end)
+ cod = gsub(cod, "<s<<<(%d+)>>>s>", function(s)
+ return "<sqs>" .. sqs[tonumber(s)] .. "</sqs>"
+ end)
+ cod = gsub(cod, "<d<<<(%d+)>>>d>", function(s)
+ return "<dqs>" .. dqs[tonumber(s)] .. "</dqs>"
+ end)
+ cod = gsub(cod, "<c<<<(%d+)>>>c>", function(s)
+ return "<com>" .. com[tonumber(s)] .. "</com>"
+ end)
+ cod = gsub(cod, "<l<<<(%d+)>>>l>", function(s)
+ return cmt[tonumber(s)]
+ end)
+ cod = gsub(cod, "##d##", "\\\"")
+ cod = gsub(cod, "##s##", "\\\'")
+ if ldx.make_index then
+ local lines = splitstring(cod,"\n")
+ local f = "(<key class='1'>function</key>)%s+([%w%.]+)%s*%("
+ for k=1,#lines do
+ local v = lines[k]
+ -- functies
+ v = gsub(v,f,function(key, str)
+ return "<function>" .. str .. "</function>("
+ end)
+ -- variables
+ v = gsub(v,"^([%w][%w%,%s]-)(=[^=])",function(str, rest)
+ local t = splitstring(str,",%s*")
+ for k=1,#t do
+ t[k] = "<variable>" .. t[k] .. "</variable>"
+ end
+ return concat(t,", ") .. rest
+ end)
+ -- so far
+ lines[k] = v
+ end
+ v.code = concat(lines,"\n")
+ else
+ v.code = cod
+ end
+ end
+ end
+end
+
+--[[ldx--
+We're now ready to save the file in <logo label='xml'/> format. This boils
+down to wrapping the code and comment as well as the whole document. We tag
+lines in the code as such so that we don't need messy <t>CDATA</t> constructs
+and by calculating the indentation we also avoid space troubles. It also makes
+it possible to change the indentation afterwards.
+--ldx]]--
+
+function ldx.as_xml(data) -- ldx: not needed
+ local t, cmode = { }, false
+ t[#t+1] = "<?xml version='1.0' standalone='yes'?>\n"
+ t[#t+1] = "\n<document xmlns:ldx='http://www.pragma-ade.com/schemas/ldx.rng' xmlns='http://www.pragma-ade.com/schemas/ldx.rng'>\n"
+ for k=1,#data do
+ local v = data[k]
+ if v.code and not emptystring(v.code) then
+ t[#t+1] = "\n<code>\n"
+ local split = splitstring(v.code,"\n")
+ for k=1,#split do -- make this faster
+ local v = split[k]
+ local a, b = find(v,"^(%s+)")
+ if v then v = gsub(v,"[\n\r ]+$","") end
+ if a and b then
+ v = sub(v,b+1,#v)
+ if cmode then
+ t[#t+1] = "<line comment='yes' n='" .. b .. "'>" .. v .. "</line>\n"
+ else
+ t[#t+1] = "<line n='" .. b .. "'>" .. v .. "</line>\n"
+ end
+ elseif emptystring(v) then
+ if cmode then
+ t[#t+1] = "<line comment='yes'/>\n"
+ else
+ t[#t+1] = "<line/>\n"
+ end
+ elseif find(v,"^%-%-%[%[") then
+ t[#t+1] = "<line comment='yes'>" .. v .. "</line>\n"
+ cmode= true
+ elseif find(v,"^%]%]%-%-") then
+ t[#t+1] = "<line comment='yes'>" .. v .. "</line>\n"
+ cmode= false
+ elseif cmode then
+ t[#t+1] = "<line comment='yes'>" .. v .. "</line>\n"
+ else
+ t[#t+1] = "<line>" .. v .. "</line>\n"
+ end
+ end
+ t[#t+1] = "</code>\n"
+ elseif v.comment then
+ t[#t+1] = "\n<comment>\n" .. v.comment .. "\n</comment>\n"
+ else
+ -- cannot happen
+ end
+ end
+ t[#t+1] = "\n</document>\n"
+ return concat(t,"")
+end
+
+--[[ldx--
+Saving the result is a trivial effort.
+--ldx]]--
+
+function ldx.save(filename,data)
+ file.savedata(filename,ldx.as_xml(data))
+end
+
+--[[ldx--
+The next function wraps it all in one call:
+--ldx]]--
+
+function ldx.convert(luaname,ldxname)
+ if not file.is_readable(luaname) then
+ luaname = luaname .. ".lua"
+ end
+ if file.is_readable(luaname) then
+ if not ldxname then
+ ldxname = file.replacesuffix(luaname,"ldx")
+ end
+ local data = ldx.load(luaname)
+ if data then
+ ldx.enhance(data)
+ if ldxname ~= luaname then
+ ldx.save(ldxname,data)
+ end
+ end
+ end
+end
+
+--[[ldx--
+This module can be used directly:
+
+<typing>
+mtxrun --internal x-ldx somefile.lua
+</typing>
+
+will produce an ldx file that can be processed with <logo label='context'/>
+by running:
+
+<typing>
+context --use=x-ldx --forcexml somefile.ldx
+</typing>
+
+You can do this in one step by saying:
+
+<typing>
+context --ctx=x-ldx somefile.lua
+</typing>
+
+This will trigger <logo label='context'/> into loading the mentioned
+<logo label='ctx'/> file. That file describes the conversion as well
+as the module to be used.
+
+The main conversion call is:
+--ldx]]--
+
+-- todo: assume usage of "mtxrun --script x-ldx", maybe make it mtx-ldx
+
+if environment.files and environment.files[1] then
+ ldx.convert(environment.files[1],environment.files[2])
+end
+
+--~ exit(1)
diff --git a/tex/context/base/x-mathml.lua b/tex/context/base/x-mathml.lua
index 676b9a7c6..31483bbea 100644
--- a/tex/context/base/x-mathml.lua
+++ b/tex/context/base/x-mathml.lua
@@ -1,829 +1,829 @@
-if not modules then modules = { } end modules ['x-mathml'] = {
- version = 1.001,
- comment = "companion to x-mathml.mkiv",
- author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
- copyright = "PRAGMA ADE / ConTeXt Development Team",
- license = "see context related readme files"
-}
-
--- This needs an upgrade to the latest greatest mechanisms.
-
-local type, next = type, next
-local format, lower, find, gsub = string.format, string.lower, string.find, string.gsub
-local strip = string.strip
-local xmlsprint, xmlcprint, xmltext, xmlcontent = xml.sprint, xml.cprint, xml.text, xml.content
-local getid = lxml.getid
-local utfchar, utfcharacters, utfvalues = utf.char, utf.characters, utf.values
-local lpegmatch = lpeg.match
-
-local mathml = { }
-moduledata.mathml = mathml
-lxml.mathml = mathml -- for the moment
-
--- an alternative is to remap to private codes, where we can have
--- different properties .. to be done; this will move and become
--- generic; we can then make the private ones active in math mode
-
--- todo: handle opening/closing mo's here ... presentation mml is such a mess ...
-
-characters.registerentities()
-
-local doublebar = utfchar(0x2016)
-
-local n_replacements = {
--- [" "] = utfchar(0x2002), -- "&textspace;" -> tricky, no &; in mkiv
- ["."] = "{.}",
- [","] = "{,}",
- [" "] = "",
-}
-
-local l_replacements = { -- in main table
- ["|"] = "\\mmlleftdelimiter\\vert",
- ["{"] = "\\mmlleftdelimiter\\lbrace",
- ["("] = "\\mmlleftdelimiter(",
- ["["] = "\\mmlleftdelimiter[",
- ["<"] = "\\mmlleftdelimiter<",
- [doublebar] = "\\mmlleftdelimiter\\Vert",
-}
-local r_replacements = { -- in main table
- ["|"] = "\\mmlrightdelimiter\\vert",
- ["}"] = "\\mmlrightdelimiter\\rbrace",
- [")"] = "\\mmlrightdelimiter)",
- ["]"] = "\\mmlrightdelimiter]",
- [">"] = "\\mmlrightdelimiter>",
- [doublebar] = "\\mmlrightdelimiter\\Vert",
-}
-
--- todo: play with asciimode and avoid mmlchar
-
-local o_replacements = { -- in main table
- ["@l"] = "\\mmlleftdelimiter.",
- ["@r"] = "\\mmlrightdelimiter.",
- ["{"] = "\\mmlleftdelimiter \\lbrace",
- ["}"] = "\\mmlrightdelimiter\\rbrace",
- ["|"] = "\\mmlleftorrightdelimiter\\vert",
- [doublebar] = "\\mmlleftorrightdelimiter\\Vert",
- ["("] = "\\mmlleftdelimiter(",
- [")"] = "\\mmlrightdelimiter)",
- ["["] = "\\mmlleftdelimiter[",
- ["]"] = "\\mmlrightdelimiter]",
- -- ["<"] = "\\mmlleftdelimiter<",
- -- [">"] = "\\mmlrightdelimiter>",
- ["#"] = "\\mmlchar{35}",
- ["$"] = "\\mmlchar{36}", -- $
- ["%"] = "\\mmlchar{37}",
- ["&"] = "\\mmlchar{38}",
- ["^"] = "\\mmlchar{94}{}", -- strange, sometimes luatex math sees the char instead of \char
- ["_"] = "\\mmlchar{95}{}", -- so we need the {}
- ["~"] = "\\mmlchar{126}",
- [" "] = "",
- ["°"] = "^\\circ", -- hack
-
- -- [utfchar(0xF103C)] = "\\mmlleftdelimiter<",
- [utfchar(0xF1026)] = "\\mmlchar{38}",
- -- [utfchar(0xF103E)] = "\\mmlleftdelimiter>",
-
-}
-
-local simpleoperatorremapper = utf.remapper(o_replacements)
-
---~ languages.data.labels.functions
-
-local i_replacements = {
- ["sin"] = "\\mathopnolimits{sin}",
- ["cos"] = "\\mathopnolimits{cos}",
- ["abs"] = "\\mathopnolimits{abs}",
- ["arg"] = "\\mathopnolimits{arg}",
- ["codomain"] = "\\mathopnolimits{codomain}",
- ["curl"] = "\\mathopnolimits{curl}",
- ["determinant"] = "\\mathopnolimits{det}",
- ["divergence"] = "\\mathopnolimits{div}",
- ["domain"] = "\\mathopnolimits{domain}",
- ["gcd"] = "\\mathopnolimits{gcd}",
- ["grad"] = "\\mathopnolimits{grad}",
- ["identity"] = "\\mathopnolimits{id}",
- ["image"] = "\\mathopnolimits{image}",
- ["lcm"] = "\\mathopnolimits{lcm}",
- ["lim"] = "\\mathopnolimits{lim}",
- ["max"] = "\\mathopnolimits{max}",
- ["median"] = "\\mathopnolimits{median}",
- ["min"] = "\\mathopnolimits{min}",
- ["mode"] = "\\mathopnolimits{mode}",
- ["mod"] = "\\mathopnolimits{mod}",
- ["polar"] = "\\mathopnolimits{Polar}",
- ["exp"] = "\\mathopnolimits{exp}",
- ["ln"] = "\\mathopnolimits{ln}",
- ["log"] = "\\mathopnolimits{log}",
- ["sin"] = "\\mathopnolimits{sin}",
- ["arcsin"] = "\\mathopnolimits{arcsin}",
- ["sinh"] = "\\mathopnolimits{sinh}",
- ["arcsinh"] = "\\mathopnolimits{arcsinh}",
- ["cos"] = "\\mathopnolimits{cos}",
- ["arccos"] = "\\mathopnolimits{arccos}",
- ["cosh"] = "\\mathopnolimits{cosh}",
- ["arccosh"] = "\\mathopnolimits{arccosh}",
- ["tan"] = "\\mathopnolimits{tan}",
- ["arctan"] = "\\mathopnolimits{arctan}",
- ["tanh"] = "\\mathopnolimits{tanh}",
- ["arctanh"] = "\\mathopnolimits{arctanh}",
- ["cot"] = "\\mathopnolimits{cot}",
- ["arccot"] = "\\mathopnolimits{arccot}",
- ["coth"] = "\\mathopnolimits{coth}",
- ["arccoth"] = "\\mathopnolimits{arccoth}",
- ["csc"] = "\\mathopnolimits{csc}",
- ["arccsc"] = "\\mathopnolimits{arccsc}",
- ["csch"] = "\\mathopnolimits{csch}",
- ["arccsch"] = "\\mathopnolimits{arccsch}",
- ["sec"] = "\\mathopnolimits{sec}",
- ["arcsec"] = "\\mathopnolimits{arcsec}",
- ["sech"] = "\\mathopnolimits{sech}",
- ["arcsech"] = "\\mathopnolimits{arcsech}",
- [" "] = "",
-
- ["false"] = "{\\mr false}",
- ["notanumber"] = "{\\mr NaN}",
- ["otherwise"] = "{\\mr otherwise}",
- ["true"] = "{\\mr true}",
- ["declare"] = "{\\mr declare}",
- ["as"] = "{\\mr as}",
-}
-
--- we could use a metatable or when accessing fallback on the
--- key but at least we now have an overview
-
-local csymbols = {
- arith1 = {
- lcm = "lcm",
- big_lcm = "lcm",
- gcd = "gcd",
- big_gcd = "big_gcd",
- plus = "plus",
- unary_minus = "minus",
- minus = "minus",
- times = "times",
- divide = "divide",
- power = "power",
- abs = "abs",
- root = "root",
- sum = "sum",
- product = "product",
- },
- fns = {
- domain = "domain",
- range = "codomain",
- image = "image",
- identity = "ident",
- -- left_inverse = "",
- -- right_inverse = "",
- inverse = "inverse",
- left_compose = "compose",
- lambda = "labmda",
- },
- linalg1 = {
- vectorproduct = "vectorproduct",
- scalarproduct = "scalarproduct",
- outerproduct = "outerproduct",
- transpose = "transpose",
- determinant = "determinant",
- vector_selector = "selector",
- -- matrix_selector = "matrix_selector",
- },
- logic1 = {
- equivalent = "equivalent",
- ["not"] = "not",
- ["and"] = "and",
- -- big_and = "",
- ["xor"] = "xor",
- -- big_xor = "",
- ["or"] = "or",
- -- big-or = "",
- implies = "implies",
- ["true"] = "true",
- ["false"] = "false",
- },
- nums1 = {
- -- based_integer = "based_integer"
- rational = "rational",
- inifinity = "infinity",
- e = "expenonentiale",
- i = "imaginaryi",
- pi = "pi",
- gamma = "gamma",
- NaN = "NaN",
- },
- relation1 = {
- eq = "eq",
- lt = "lt",
- gt = "gt",
- neq = "neq",
- leq = "leq",
- geq = "geq",
- approx = "approx",
- },
- set1 = {
- cartesian_product = "cartesianproduct",
- empty_set = "emptyset",
- map = "map",
- size = "card",
- -- suchthat = "suchthat",
- set = "set",
- intersect = "intersect",
- -- big_intersect = "",
- union = "union",
- -- big_union = "",
- setdiff = "setdiff",
- subset = "subset",
- ["in"] = "in",
- notin = "notin",
- prsubset = "prsubset",
- notsubset = "notsubset",
- notprsubset = "notprsubset",
- },
- veccalc1 = {
- divergence = "divergence",
- grad = "grad",
- curl = "curl",
- laplacian = "laplacian",
- Laplacian = "laplacian",
- },
- calculus1 = {
- diff = "diff",
- -- nthdiff = "",
- partialdiff = "partialdiff",
- int = "int",
- -- defint = "defint",
- },
- integer1 = {
- factorof = "factorof",
- factorial = "factorial",
- quotient = "quotient",
- remainder = "rem",
- },
- linalg2 = {
- vector = "vector",
- matrix = "matrix",
- matrixrow = "matrixrow",
- },
- mathmkeys = {
- -- equiv = "",
- -- contentequiv = "",
- -- contentequiv_strict = "",
- },
- rounding1 = {
- ceiling = "ceiling",
- floor = "floor",
- -- trunc = "trunc",
- -- round = "round",
- },
- setname1 = {
- P = "primes",
- N = "naturalnumbers",
- Z = "integers",
- rationals = "rationals",
- R = "reals",
- complexes = "complexes",
- },
- complex1 = {
- -- complex_cartesian = "complex_cartesian", -- ci ?
- real = "real",
- imaginary = "imaginary",
- -- complex_polar = "complex_polar", -- ci ?
- argument = "arg",
- conjugate = "conjugate",
- },
- interval1 = { -- not an apply
- -- integer_interval = "integer_interval",
- interval = "interval",
- interval_oo = { tag = "interval", closure = "open" },
- interval_cc = { tag = "interval", closure = "closed" },
- interval_oc = { tag = "interval", closure = "open-closed" },
- interval_co = { tag = "interval", closure = "closed-open" },
- },
- linalg3 = {
- -- vector = "vector.column",
- -- matrixcolumn = "matrixcolumn",
- -- matrix = "matrix.column",
- },
- minmax1 = {
- min = "min",
- -- big_min = "",
- max = "max",
- -- big_max = "",
- },
- piece1 = {
- piecewise = "piecewise",
- piece = "piece",
- otherwise = "otherwise",
- },
- error1 = {
- -- unhandled_symbol = "",
- -- unexpected_symbol = "",
- -- unsupported_CD = "",
- },
- limit1 = {
- -- limit = "limit",
- -- both_sides = "both_sides",
- -- above = "above",
- -- below = "below",
- -- null = "null",
- tendsto = "tendsto",
- },
- list1 = {
- -- map = "",
- -- suchthat = "",
- -- list = "list",
- },
- multiset1 = {
- size = { tag = "card", type = "multiset" },
- cartesian_product = { tag = "cartesianproduct", type = "multiset" },
- empty_set = { tag = "emptyset", type = "multiset" },
- -- multi_set = { tag = "multiset", type = "multiset" },
- intersect = { tag = "intersect", type = "multiset" },
- -- big_intersect = "",
- union = { tag = "union", type = "multiset" },
- -- big_union = "",
- setdiff = { tag = "setdiff", type = "multiset" },
- subset = { tag = "subset", type = "multiset" },
- ["in"] = { tag = "in", type = "multiset" },
- notin = { tag = "notin", type = "multiset" },
- prsubset = { tag = "prsubset", type = "multiset" },
- notsubset = { tag = "notsubset", type = "multiset" },
- notprsubset = { tag = "notprsubset", type = "multiset" },
- },
- quant1 = {
- forall = "forall",
- exists = "exists",
- },
- s_dist = {
- -- mean = "mean.dist",
- -- sdev = "sdev.dist",
- -- variance = "variance.dist",
- -- moment = "moment.dist",
- },
- s_data = {
- mean = "mean",
- sdev = "sdev",
- variance = "vriance",
- mode = "mode",
- median = "median",
- moment = "moment",
- },
- transc1 = {
- log = "log",
- ln = "ln",
- exp = "exp",
- sin = "sin",
- cos = "cos",
- tan = "tan",
- sec = "sec",
- csc = "csc",
- cot = "cot",
- sinh = "sinh",
- cosh = "cosh",
- tanh = "tanh",
- sech = "sech",
- csch = "cscs",
- coth = "coth",
- arcsin = "arcsin",
- arccos = "arccos",
- arctan = "arctan",
- arcsec = "arcsec",
- arcscs = "arccsc",
- arccot = "arccot",
- arcsinh = "arcsinh",
- arccosh = "arccosh",
- arctanh = "arstanh",
- arcsech = "arcsech",
- arccsch = "arccsch",
- arccoth = "arccoth",
- },
-}
-
-function xml.functions.remapmmlcsymbol(e)
- local at = e.at
- local cd = at.cd
- if cd then
- cd = csymbols[cd]
- if cd then
- local tx = e.dt[1]
- if tx and tx ~= "" then
- local tg = cd[tx]
- if tg then
- at.cd = nil
- at.cdbase = nil
- e.dt = { }
- if type(tg) == "table" then
- for k, v in next, tg do
- if k == "tag" then
- e.tg = v
- else
- at[k] = v
- end
- end
- else
- e.tg = tg
- end
- end
- end
- end
- end
-end
-
-function xml.functions.remapmmlbind(e)
- e.tg = "apply"
-end
-
-function xml.functions.remapopenmath(e)
- local tg = e.tg
- if tg == "OMOBJ" then
- e.tg = "math"
- elseif tg == "OMA" then
- e.tg = "apply"
- elseif tg == "OMB" then
- e.tg = "apply"
- elseif tg == "OMS" then
- local at = e.at
- e.tg = "csymbol"
- e.dt = { at.name or "unknown" }
- at.name = nil
- elseif tg == "OMV" then
- local at = e.at
- e.tg = "ci"
- e.dt = { at.name or "unknown" }
- at.name = nil
- elseif tg == "OMI" then
- e.tg = "ci"
- end
- e.rn = "mml"
-end
-
-function mathml.checked_operator(str)
- context(simpleoperatorremapper(str))
-end
-
-function mathml.stripped(str)
- context(strip(str))
-end
-
-function mathml.mn(id,pattern)
- -- maybe at some point we need to interpret the number, but
- -- currently we assume an upright font
- local str = xmlcontent(getid(id)) or ""
- local rep = gsub(str,"&.-;","")
- local rep = gsub(rep,"(%s+)",utfchar(0x205F)) -- medspace e.g.: twenty one (nbsp is not seen)
- local rep = gsub(rep,".",n_replacements)
- context.mn(rep)
-end
-
-function mathml.mo(id)
- local str = xmlcontent(getid(id)) or ""
- local rep = gsub(str,"&.-;","") -- todo
- context(simpleoperatorremapper(rep))
-end
-
-function mathml.mi(id)
- -- we need to strip comments etc .. todo when reading in tree
- local e = getid(id)
- local str = e.dt
- if type(str) == "string" then
- local n = #str
- if n == 0 then
- -- nothing to do
- elseif n == 1 then
- local str = gsub(str[1],"&.-;","") -- bah
- local rep = i_replacements[str]
- if not rep then
- rep = gsub(str,".",i_replacements)
- end
- context(rep)
- -- context.mi(rep)
- else
- context.xmlflush(id) -- xmlsprint or so
- end
- else
- context.xmlflush(id) -- xmlsprint or so
- end
-end
-
-function mathml.mfenced(id) -- multiple separators
- id = getid(id)
- local left, right, separators = id.at.open or "(", id.at.close or ")", id.at.separators or ","
- local l, r = l_replacements[left], r_replacements[right]
- context.enabledelimiter()
- if l then
- context(l_replacements[left] or o_replacements[left] or "")
- else
- context(o_replacements["@l"])
- context(left)
- end
- context.disabledelimiter()
- local collected = lxml.filter(id,"/*") -- check the *
- if collected then
- local n = #collected
- if n == 0 then
- -- skip
- elseif n == 1 then
- xmlsprint(collected[1]) -- to be checked
- else
- local t = utf.split(separators,true)
- for i=1,n do
- xmlsprint(collected[i]) -- to be checked
- if i < n then
- local m = t[i] or t[#t] or ""
- if m == "|" then
- m = "\\enabledelimiter\\middle|\\relax\\disabledelimiter"
- elseif m == doublebar then
- m = "\\enabledelimiter\\middle|\\relax\\disabledelimiter"
- elseif m == "{" then
- m = "\\{"
- elseif m == "}" then
- m = "\\}"
- end
- context(m)
- end
- end
- end
- end
- context.enabledelimiter()
- if r then
- context(r_replacements[right] or o_replacements[right] or "")
- else
- context(right)
- context(o_replacements["@r"])
- end
- context.disabledelimiter()
-end
-
---~ local function flush(e,tag,toggle)
---~ if toggle then
---~ context("^{")
---~ else
---~ context("_{")
---~ end
---~ if tag == "none" then
---~ context("{}")
---~ else
---~ xmlsprint(e.dt)
---~ end
---~ if not toggle then
---~ context("}")
---~ else
---~ context("}{}")
---~ end
---~ return not toggle
---~ end
-
-local function flush(e,tag,toggle)
- if tag == "none" then
- -- if not toggle then
- context("{}") -- {} starts a new ^_ set
- -- end
- elseif toggle then
- context("^{")
- xmlsprint(e.dt)
- context("}{}") -- {} starts a new ^_ set
- else
- context("_{")
- xmlsprint(e.dt)
- context("}")
- end
- return not toggle
-end
-
-function mathml.mmultiscripts(id)
- local done, toggle = false, false
- for e in lxml.collected(id,"/*") do
- local tag = e.tg
- if tag == "mprescripts" then
- context("{}")
- done = true
- elseif done then
- toggle = flush(e,tag,toggle)
- end
- end
- local done, toggle = false, false
- for e in lxml.collected(id,"/*") do
- local tag = e.tg
- if tag == "mprescripts" then
- break
- elseif done then
- toggle = flush(e,tag,toggle)
- else
- xmlsprint(e.dt)
- done = true
- end
- end
-end
-
-local columnalignments = {
- left = "flushleft",
- right = "flushright",
- center = "middle",
-}
-
-local rowalignments = {
- top = "high",
- bottom = "low",
- center = "lohi",
- baseline = "top",
- axis = "lohi",
-}
-
-local frametypes = {
- none = "off",
- solid = "on",
- dashed = "on",
-}
-
--- crazy element ... should be a proper structure instead of such a mess
-
-function mathml.mcolumn(root)
- root = getid(root)
- local matrix, numbers = { }, 0
- local function collect(m,e)
- local tag = e.tg
- if tag == "mi" or tag == "mn" or tag == "mo" or tag == "mtext" then
- local str = xmltext(e)
- str = gsub(str,"&.-;","")
- for s in utfcharacters(str) do
- m[#m+1] = { tag, s }
- end
- if tag == "mn" then
- local n = utf.len(str)
- if n > numbers then
- numbers = n
- end
- end
- elseif tag == "mspace" or tag == "mline" then
- local str = e.at.spacing or ""
- for s in utfcharacters(str) do
- m[#m+1] = { tag, s }
- end
- -- elseif tag == "mline" then
- -- m[#m+1] = { tag, e }
- end
- end
- for e in lxml.collected(root,"/*") do
- local m = { }
- matrix[#matrix+1] = m
- if e.tg == "mrow" then
- -- only one level
- for e in lxml.collected(e,"/*") do
- collect(m,e)
- end
- else
- collect(m,e)
- end
- end
- context.halign()
- context.bgroup()
- context([[\hss\startimath\alignmark\stopimath\aligntab\startimath\alignmark\stopimath\cr]])
- for i=1,#matrix do
- local m = matrix[i]
- local mline = true
- for j=1,#m do
- if m[j][1] ~= "mline" then
- mline = false
- break
- end
- end
- if mline then
- context.noalign([[\obeydepth\nointerlineskip]])
- end
- for j=1,#m do
- local mm = m[j]
- local tag, chr = mm[1], mm[2]
- if tag == "mline" then
- -- This code is under construction ... I need some real motivation
- -- to deal with this kind of crap.
---~ local n, p = true, true
---~ for c=1,#matrix do
---~ local mc = matrix[c][j]
---~ if mc then
---~ mc = mc[2]
---~ if type(mc) ~= "string" then
---~ n, p = false, false
---~ break
---~ elseif find(mc,"^[%d ]$") then -- rangecheck is faster
---~ -- digit
---~ elseif not find(mc,"^[%.%,]$") then -- rangecheck is faster
---~ -- punctuation
---~ else
---~ n = false
---~ break
---~ end
---~ end
---~ end
---~ if n then
---~ chr = "\\mmlmcolumndigitrule"
---~ elseif p then
---~ chr = "\\mmlmcolumnpunctuationrule"
---~ else
---~ chr = "\\mmlmcolumnsymbolrule" -- should be widest char
---~ end
- chr = "\\hrulefill"
- elseif tag == "mspace" then
- chr = "\\mmlmcolumndigitspace" -- utfchar(0x2007)
- end
- if j == numbers + 1 then
- context("\\aligntab")
- end
- local nchr = n_replacements[chr]
- context(nchr or chr)
- end
- context.crcr()
- end
- context.egroup()
-end
-
-local spacesplitter = lpeg.tsplitat(" ")
-
-function mathml.mtable(root)
- -- todo: align, rowspacing, columnspacing, rowlines, columnlines
- root = getid(root)
- local at = root.at
- local rowalign = at.rowalign
- local columnalign = at.columnalign
- local frame = at.frame
- local rowaligns = rowalign and lpegmatch(spacesplitter,rowalign)
- local columnaligns = columnalign and lpegmatch(spacesplitter,columnalign)
- local frames = frame and lpegmatch(spacesplitter,frame)
- local framespacing = at.framespacing or "0pt"
- local framespacing = at.framespacing or "-\\ruledlinewidth" -- make this an option
-
- context.bTABLE { frame = frametypes[frame or "none"] or "off", offset = framespacing }
- for e in lxml.collected(root,"/(mml:mtr|mml:mlabeledtr)") do
- context.bTR()
- local at = e.at
- local col = 0
- local rfr = at.frame or (frames and frames [#frames])
- local rra = at.rowalign or (rowaligns and rowaligns [#rowaligns])
- local rca = at.columnalign or (columnaligns and columnaligns[#columnaligns])
- local ignorelabel = e.tg == "mlabeledtr"
- for e in lxml.collected(e,"/mml:mtd") do -- nested we can use xml.collected
- col = col + 1
- if ignorelabel and col == 1 then
- -- get rid of label, should happen at the document level
- else
- local at = e.at
- local rowspan, columnspan = at.rowspan or 1, at.columnspan or 1
- local cra = rowalignments [at.rowalign or (rowaligns and rowaligns [col]) or rra or "center"] or "lohi"
- local cca = columnalignments[at.columnalign or (columnaligns and columnaligns[col]) or rca or "center"] or "middle"
- local cfr = frametypes [at.frame or (frames and frames [col]) or rfr or "none" ] or "off"
- context.bTD { align = format("{%s,%s}",cra,cca), frame = cfr, nx = columnspan, ny = rowspan }
- context.startimath()
- context.ignorespaces()
- xmlcprint(e)
- context.stopimath()
- context.removeunwantedspaces()
- context.eTD()
- end
- end
- -- if e.tg == "mlabeledtr" then
- -- context.bTD()
- -- xmlcprint(xml.first(e,"/!mml:mtd"))
- -- context.eTD()
- -- end
- context.eTR()
- end
- context.eTABLE()
-end
-
-function mathml.csymbol(root)
- root = getid(root)
- local at = root.at
- local encoding = at.encoding or ""
- local hash = url.hashed(lower(at.definitionUrl or ""))
- local full = hash.original or ""
- local base = hash.path or ""
- local text = strip(xmltext(root) or "")
- context.mmlapplycsymbol(full,base,encoding,text)
-end
-
-function mathml.menclosepattern(root)
- root = getid(root)
- local a = root.at.notation
- if a and a ~= "" then
- context("mml:enclose:",(gsub(a," +",",mml:enclose:")))
- end
-end
-
-function xml.is_element(e,name)
- return type(e) == "table" and (not name or e.tg == name)
-end
-
-function mathml.cpolar_a(root)
- root = getid(root)
- local dt = root.dt
- context.mathopnolimits("Polar")
- context.left(false,"(")
- for k=1,#dt do
- local dk = dt[k]
- if xml.is_element(dk,"sep") then
- context(",")
- else
- xmlsprint(dk)
- end
- end
- context.right(false,")")
-end
+if not modules then modules = { } end modules ['x-mathml'] = {
+ version = 1.001,
+ comment = "companion to x-mathml.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- This needs an upgrade to the latest greatest mechanisms.
+
+local type, next = type, next
+local format, lower, find, gsub = string.format, string.lower, string.find, string.gsub
+local strip = string.strip
+local xmlsprint, xmlcprint, xmltext, xmlcontent = xml.sprint, xml.cprint, xml.text, xml.content
+local getid = lxml.getid
+local utfchar, utfcharacters, utfvalues = utf.char, utf.characters, utf.values
+local lpegmatch = lpeg.match
+
+local mathml = { }
+moduledata.mathml = mathml
+lxml.mathml = mathml -- for the moment
+
+-- an alternative is to remap to private codes, where we can have
+-- different properties .. to be done; this will move and become
+-- generic; we can then make the private ones active in math mode
+
+-- todo: handle opening/closing mo's here ... presentation mml is such a mess ...
+
+characters.registerentities()
+
+local doublebar = utfchar(0x2016)
+
+local n_replacements = {
+-- [" "] = utfchar(0x2002), -- "&textspace;" -> tricky, no &; in mkiv
+ ["."] = "{.}",
+ [","] = "{,}",
+ [" "] = "",
+}
+
+local l_replacements = { -- in main table
+ ["|"] = "\\mmlleftdelimiter\\vert",
+ ["{"] = "\\mmlleftdelimiter\\lbrace",
+ ["("] = "\\mmlleftdelimiter(",
+ ["["] = "\\mmlleftdelimiter[",
+ ["<"] = "\\mmlleftdelimiter<",
+ [doublebar] = "\\mmlleftdelimiter\\Vert",
+}
+local r_replacements = { -- in main table
+ ["|"] = "\\mmlrightdelimiter\\vert",
+ ["}"] = "\\mmlrightdelimiter\\rbrace",
+ [")"] = "\\mmlrightdelimiter)",
+ ["]"] = "\\mmlrightdelimiter]",
+ [">"] = "\\mmlrightdelimiter>",
+ [doublebar] = "\\mmlrightdelimiter\\Vert",
+}
+
+-- todo: play with asciimode and avoid mmlchar
+
+local o_replacements = { -- in main table
+ ["@l"] = "\\mmlleftdelimiter.",
+ ["@r"] = "\\mmlrightdelimiter.",
+ ["{"] = "\\mmlleftdelimiter \\lbrace",
+ ["}"] = "\\mmlrightdelimiter\\rbrace",
+ ["|"] = "\\mmlleftorrightdelimiter\\vert",
+ [doublebar] = "\\mmlleftorrightdelimiter\\Vert",
+ ["("] = "\\mmlleftdelimiter(",
+ [")"] = "\\mmlrightdelimiter)",
+ ["["] = "\\mmlleftdelimiter[",
+ ["]"] = "\\mmlrightdelimiter]",
+ -- ["<"] = "\\mmlleftdelimiter<",
+ -- [">"] = "\\mmlrightdelimiter>",
+ ["#"] = "\\mmlchar{35}",
+ ["$"] = "\\mmlchar{36}", -- $
+ ["%"] = "\\mmlchar{37}",
+ ["&"] = "\\mmlchar{38}",
+ ["^"] = "\\mmlchar{94}{}", -- strange, sometimes luatex math sees the char instead of \char
+ ["_"] = "\\mmlchar{95}{}", -- so we need the {}
+ ["~"] = "\\mmlchar{126}",
+ [" "] = "",
+ ["°"] = "^\\circ", -- hack
+
+ -- [utfchar(0xF103C)] = "\\mmlleftdelimiter<",
+ [utfchar(0xF1026)] = "\\mmlchar{38}",
+ -- [utfchar(0xF103E)] = "\\mmlleftdelimiter>",
+
+}
+
+local simpleoperatorremapper = utf.remapper(o_replacements)
+
+--~ languages.data.labels.functions
+
+local i_replacements = {
+ ["sin"] = "\\mathopnolimits{sin}",
+ ["cos"] = "\\mathopnolimits{cos}",
+ ["abs"] = "\\mathopnolimits{abs}",
+ ["arg"] = "\\mathopnolimits{arg}",
+ ["codomain"] = "\\mathopnolimits{codomain}",
+ ["curl"] = "\\mathopnolimits{curl}",
+ ["determinant"] = "\\mathopnolimits{det}",
+ ["divergence"] = "\\mathopnolimits{div}",
+ ["domain"] = "\\mathopnolimits{domain}",
+ ["gcd"] = "\\mathopnolimits{gcd}",
+ ["grad"] = "\\mathopnolimits{grad}",
+ ["identity"] = "\\mathopnolimits{id}",
+ ["image"] = "\\mathopnolimits{image}",
+ ["lcm"] = "\\mathopnolimits{lcm}",
+ ["lim"] = "\\mathopnolimits{lim}",
+ ["max"] = "\\mathopnolimits{max}",
+ ["median"] = "\\mathopnolimits{median}",
+ ["min"] = "\\mathopnolimits{min}",
+ ["mode"] = "\\mathopnolimits{mode}",
+ ["mod"] = "\\mathopnolimits{mod}",
+ ["polar"] = "\\mathopnolimits{Polar}",
+ ["exp"] = "\\mathopnolimits{exp}",
+ ["ln"] = "\\mathopnolimits{ln}",
+ ["log"] = "\\mathopnolimits{log}",
+ ["sin"] = "\\mathopnolimits{sin}",
+ ["arcsin"] = "\\mathopnolimits{arcsin}",
+ ["sinh"] = "\\mathopnolimits{sinh}",
+ ["arcsinh"] = "\\mathopnolimits{arcsinh}",
+ ["cos"] = "\\mathopnolimits{cos}",
+ ["arccos"] = "\\mathopnolimits{arccos}",
+ ["cosh"] = "\\mathopnolimits{cosh}",
+ ["arccosh"] = "\\mathopnolimits{arccosh}",
+ ["tan"] = "\\mathopnolimits{tan}",
+ ["arctan"] = "\\mathopnolimits{arctan}",
+ ["tanh"] = "\\mathopnolimits{tanh}",
+ ["arctanh"] = "\\mathopnolimits{arctanh}",
+ ["cot"] = "\\mathopnolimits{cot}",
+ ["arccot"] = "\\mathopnolimits{arccot}",
+ ["coth"] = "\\mathopnolimits{coth}",
+ ["arccoth"] = "\\mathopnolimits{arccoth}",
+ ["csc"] = "\\mathopnolimits{csc}",
+ ["arccsc"] = "\\mathopnolimits{arccsc}",
+ ["csch"] = "\\mathopnolimits{csch}",
+ ["arccsch"] = "\\mathopnolimits{arccsch}",
+ ["sec"] = "\\mathopnolimits{sec}",
+ ["arcsec"] = "\\mathopnolimits{arcsec}",
+ ["sech"] = "\\mathopnolimits{sech}",
+ ["arcsech"] = "\\mathopnolimits{arcsech}",
+ [" "] = "",
+
+ ["false"] = "{\\mr false}",
+ ["notanumber"] = "{\\mr NaN}",
+ ["otherwise"] = "{\\mr otherwise}",
+ ["true"] = "{\\mr true}",
+ ["declare"] = "{\\mr declare}",
+ ["as"] = "{\\mr as}",
+}
+
+-- we could use a metatable or when accessing fallback on the
+-- key but at least we now have an overview
+
+local csymbols = {
+ arith1 = {
+ lcm = "lcm",
+ big_lcm = "lcm",
+ gcd = "gcd",
+ big_gcd = "big_gcd",
+ plus = "plus",
+ unary_minus = "minus",
+ minus = "minus",
+ times = "times",
+ divide = "divide",
+ power = "power",
+ abs = "abs",
+ root = "root",
+ sum = "sum",
+ product = "product",
+ },
+ fns = {
+ domain = "domain",
+ range = "codomain",
+ image = "image",
+ identity = "ident",
+ -- left_inverse = "",
+ -- right_inverse = "",
+ inverse = "inverse",
+ left_compose = "compose",
+ lambda = "labmda",
+ },
+ linalg1 = {
+ vectorproduct = "vectorproduct",
+ scalarproduct = "scalarproduct",
+ outerproduct = "outerproduct",
+ transpose = "transpose",
+ determinant = "determinant",
+ vector_selector = "selector",
+ -- matrix_selector = "matrix_selector",
+ },
+ logic1 = {
+ equivalent = "equivalent",
+ ["not"] = "not",
+ ["and"] = "and",
+ -- big_and = "",
+ ["xor"] = "xor",
+ -- big_xor = "",
+ ["or"] = "or",
+ -- big-or = "",
+ implies = "implies",
+ ["true"] = "true",
+ ["false"] = "false",
+ },
+ nums1 = {
+ -- based_integer = "based_integer"
+ rational = "rational",
+ inifinity = "infinity",
+ e = "expenonentiale",
+ i = "imaginaryi",
+ pi = "pi",
+ gamma = "gamma",
+ NaN = "NaN",
+ },
+ relation1 = {
+ eq = "eq",
+ lt = "lt",
+ gt = "gt",
+ neq = "neq",
+ leq = "leq",
+ geq = "geq",
+ approx = "approx",
+ },
+ set1 = {
+ cartesian_product = "cartesianproduct",
+ empty_set = "emptyset",
+ map = "map",
+ size = "card",
+ -- suchthat = "suchthat",
+ set = "set",
+ intersect = "intersect",
+ -- big_intersect = "",
+ union = "union",
+ -- big_union = "",
+ setdiff = "setdiff",
+ subset = "subset",
+ ["in"] = "in",
+ notin = "notin",
+ prsubset = "prsubset",
+ notsubset = "notsubset",
+ notprsubset = "notprsubset",
+ },
+ veccalc1 = {
+ divergence = "divergence",
+ grad = "grad",
+ curl = "curl",
+ laplacian = "laplacian",
+ Laplacian = "laplacian",
+ },
+ calculus1 = {
+ diff = "diff",
+ -- nthdiff = "",
+ partialdiff = "partialdiff",
+ int = "int",
+ -- defint = "defint",
+ },
+ integer1 = {
+ factorof = "factorof",
+ factorial = "factorial",
+ quotient = "quotient",
+ remainder = "rem",
+ },
+ linalg2 = {
+ vector = "vector",
+ matrix = "matrix",
+ matrixrow = "matrixrow",
+ },
+ mathmkeys = {
+ -- equiv = "",
+ -- contentequiv = "",
+ -- contentequiv_strict = "",
+ },
+ rounding1 = {
+ ceiling = "ceiling",
+ floor = "floor",
+ -- trunc = "trunc",
+ -- round = "round",
+ },
+ setname1 = {
+ P = "primes",
+ N = "naturalnumbers",
+ Z = "integers",
+ rationals = "rationals",
+ R = "reals",
+ complexes = "complexes",
+ },
+ complex1 = {
+ -- complex_cartesian = "complex_cartesian", -- ci ?
+ real = "real",
+ imaginary = "imaginary",
+ -- complex_polar = "complex_polar", -- ci ?
+ argument = "arg",
+ conjugate = "conjugate",
+ },
+ interval1 = { -- not an apply
+ -- integer_interval = "integer_interval",
+ interval = "interval",
+ interval_oo = { tag = "interval", closure = "open" },
+ interval_cc = { tag = "interval", closure = "closed" },
+ interval_oc = { tag = "interval", closure = "open-closed" },
+ interval_co = { tag = "interval", closure = "closed-open" },
+ },
+ linalg3 = {
+ -- vector = "vector.column",
+ -- matrixcolumn = "matrixcolumn",
+ -- matrix = "matrix.column",
+ },
+ minmax1 = {
+ min = "min",
+ -- big_min = "",
+ max = "max",
+ -- big_max = "",
+ },
+ piece1 = {
+ piecewise = "piecewise",
+ piece = "piece",
+ otherwise = "otherwise",
+ },
+ error1 = {
+ -- unhandled_symbol = "",
+ -- unexpected_symbol = "",
+ -- unsupported_CD = "",
+ },
+ limit1 = {
+ -- limit = "limit",
+ -- both_sides = "both_sides",
+ -- above = "above",
+ -- below = "below",
+ -- null = "null",
+ tendsto = "tendsto",
+ },
+ list1 = {
+ -- map = "",
+ -- suchthat = "",
+ -- list = "list",
+ },
+ multiset1 = {
+ size = { tag = "card", type = "multiset" },
+ cartesian_product = { tag = "cartesianproduct", type = "multiset" },
+ empty_set = { tag = "emptyset", type = "multiset" },
+ -- multi_set = { tag = "multiset", type = "multiset" },
+ intersect = { tag = "intersect", type = "multiset" },
+ -- big_intersect = "",
+ union = { tag = "union", type = "multiset" },
+ -- big_union = "",
+ setdiff = { tag = "setdiff", type = "multiset" },
+ subset = { tag = "subset", type = "multiset" },
+ ["in"] = { tag = "in", type = "multiset" },
+ notin = { tag = "notin", type = "multiset" },
+ prsubset = { tag = "prsubset", type = "multiset" },
+ notsubset = { tag = "notsubset", type = "multiset" },
+ notprsubset = { tag = "notprsubset", type = "multiset" },
+ },
+ quant1 = {
+ forall = "forall",
+ exists = "exists",
+ },
+ s_dist = {
+ -- mean = "mean.dist",
+ -- sdev = "sdev.dist",
+ -- variance = "variance.dist",
+ -- moment = "moment.dist",
+ },
+ s_data = {
+ mean = "mean",
+ sdev = "sdev",
+ variance = "vriance",
+ mode = "mode",
+ median = "median",
+ moment = "moment",
+ },
+ transc1 = {
+ log = "log",
+ ln = "ln",
+ exp = "exp",
+ sin = "sin",
+ cos = "cos",
+ tan = "tan",
+ sec = "sec",
+ csc = "csc",
+ cot = "cot",
+ sinh = "sinh",
+ cosh = "cosh",
+ tanh = "tanh",
+ sech = "sech",
+ csch = "cscs",
+ coth = "coth",
+ arcsin = "arcsin",
+ arccos = "arccos",
+ arctan = "arctan",
+ arcsec = "arcsec",
+ arcscs = "arccsc",
+ arccot = "arccot",
+ arcsinh = "arcsinh",
+ arccosh = "arccosh",
+ arctanh = "arstanh",
+ arcsech = "arcsech",
+ arccsch = "arccsch",
+ arccoth = "arccoth",
+ },
+}
+
+function xml.functions.remapmmlcsymbol(e)
+ local at = e.at
+ local cd = at.cd
+ if cd then
+ cd = csymbols[cd]
+ if cd then
+ local tx = e.dt[1]
+ if tx and tx ~= "" then
+ local tg = cd[tx]
+ if tg then
+ at.cd = nil
+ at.cdbase = nil
+ e.dt = { }
+ if type(tg) == "table" then
+ for k, v in next, tg do
+ if k == "tag" then
+ e.tg = v
+ else
+ at[k] = v
+ end
+ end
+ else
+ e.tg = tg
+ end
+ end
+ end
+ end
+ end
+end
+
+function xml.functions.remapmmlbind(e)
+ e.tg = "apply"
+end
+
+function xml.functions.remapopenmath(e)
+ local tg = e.tg
+ if tg == "OMOBJ" then
+ e.tg = "math"
+ elseif tg == "OMA" then
+ e.tg = "apply"
+ elseif tg == "OMB" then
+ e.tg = "apply"
+ elseif tg == "OMS" then
+ local at = e.at
+ e.tg = "csymbol"
+ e.dt = { at.name or "unknown" }
+ at.name = nil
+ elseif tg == "OMV" then
+ local at = e.at
+ e.tg = "ci"
+ e.dt = { at.name or "unknown" }
+ at.name = nil
+ elseif tg == "OMI" then
+ e.tg = "ci"
+ end
+ e.rn = "mml"
+end
+
+function mathml.checked_operator(str)
+ context(simpleoperatorremapper(str))
+end
+
+function mathml.stripped(str)
+ context(strip(str))
+end
+
+function mathml.mn(id,pattern)
+ -- maybe at some point we need to interpret the number, but
+ -- currently we assume an upright font
+ local str = xmlcontent(getid(id)) or ""
+ local rep = gsub(str,"&.-;","")
+ local rep = gsub(rep,"(%s+)",utfchar(0x205F)) -- medspace e.g.: twenty one (nbsp is not seen)
+ local rep = gsub(rep,".",n_replacements)
+ context.mn(rep)
+end
+
+function mathml.mo(id)
+ local str = xmlcontent(getid(id)) or ""
+ local rep = gsub(str,"&.-;","") -- todo
+ context(simpleoperatorremapper(rep))
+end
+
+function mathml.mi(id)
+ -- we need to strip comments etc .. todo when reading in tree
+ local e = getid(id)
+ local str = e.dt
+ if type(str) == "string" then
+ local n = #str
+ if n == 0 then
+ -- nothing to do
+ elseif n == 1 then
+ local str = gsub(str[1],"&.-;","") -- bah
+ local rep = i_replacements[str]
+ if not rep then
+ rep = gsub(str,".",i_replacements)
+ end
+ context(rep)
+ -- context.mi(rep)
+ else
+ context.xmlflush(id) -- xmlsprint or so
+ end
+ else
+ context.xmlflush(id) -- xmlsprint or so
+ end
+end
+
+function mathml.mfenced(id) -- multiple separators
+ id = getid(id)
+ local left, right, separators = id.at.open or "(", id.at.close or ")", id.at.separators or ","
+ local l, r = l_replacements[left], r_replacements[right]
+ context.enabledelimiter()
+ if l then
+ context(l_replacements[left] or o_replacements[left] or "")
+ else
+ context(o_replacements["@l"])
+ context(left)
+ end
+ context.disabledelimiter()
+ local collected = lxml.filter(id,"/*") -- check the *
+ if collected then
+ local n = #collected
+ if n == 0 then
+ -- skip
+ elseif n == 1 then
+ xmlsprint(collected[1]) -- to be checked
+ else
+ local t = utf.split(separators,true)
+ for i=1,n do
+ xmlsprint(collected[i]) -- to be checked
+ if i < n then
+ local m = t[i] or t[#t] or ""
+ if m == "|" then
+ m = "\\enabledelimiter\\middle|\\relax\\disabledelimiter"
+ elseif m == doublebar then
+ m = "\\enabledelimiter\\middle|\\relax\\disabledelimiter"
+ elseif m == "{" then
+ m = "\\{"
+ elseif m == "}" then
+ m = "\\}"
+ end
+ context(m)
+ end
+ end
+ end
+ end
+ context.enabledelimiter()
+ if r then
+ context(r_replacements[right] or o_replacements[right] or "")
+ else
+ context(right)
+ context(o_replacements["@r"])
+ end
+ context.disabledelimiter()
+end
+
+--~ local function flush(e,tag,toggle)
+--~ if toggle then
+--~ context("^{")
+--~ else
+--~ context("_{")
+--~ end
+--~ if tag == "none" then
+--~ context("{}")
+--~ else
+--~ xmlsprint(e.dt)
+--~ end
+--~ if not toggle then
+--~ context("}")
+--~ else
+--~ context("}{}")
+--~ end
+--~ return not toggle
+--~ end
+
+local function flush(e,tag,toggle)
+ if tag == "none" then
+ -- if not toggle then
+ context("{}") -- {} starts a new ^_ set
+ -- end
+ elseif toggle then
+ context("^{")
+ xmlsprint(e.dt)
+ context("}{}") -- {} starts a new ^_ set
+ else
+ context("_{")
+ xmlsprint(e.dt)
+ context("}")
+ end
+ return not toggle
+end
+
+function mathml.mmultiscripts(id)
+ local done, toggle = false, false
+ for e in lxml.collected(id,"/*") do
+ local tag = e.tg
+ if tag == "mprescripts" then
+ context("{}")
+ done = true
+ elseif done then
+ toggle = flush(e,tag,toggle)
+ end
+ end
+ local done, toggle = false, false
+ for e in lxml.collected(id,"/*") do
+ local tag = e.tg
+ if tag == "mprescripts" then
+ break
+ elseif done then
+ toggle = flush(e,tag,toggle)
+ else
+ xmlsprint(e.dt)
+ done = true
+ end
+ end
+end
+
+local columnalignments = {
+ left = "flushleft",
+ right = "flushright",
+ center = "middle",
+}
+
+local rowalignments = {
+ top = "high",
+ bottom = "low",
+ center = "lohi",
+ baseline = "top",
+ axis = "lohi",
+}
+
+local frametypes = {
+ none = "off",
+ solid = "on",
+ dashed = "on",
+}
+
+-- crazy element ... should be a proper structure instead of such a mess
+
+function mathml.mcolumn(root)
+ root = getid(root)
+ local matrix, numbers = { }, 0
+ local function collect(m,e)
+ local tag = e.tg
+ if tag == "mi" or tag == "mn" or tag == "mo" or tag == "mtext" then
+ local str = xmltext(e)
+ str = gsub(str,"&.-;","")
+ for s in utfcharacters(str) do
+ m[#m+1] = { tag, s }
+ end
+ if tag == "mn" then
+ local n = utf.len(str)
+ if n > numbers then
+ numbers = n
+ end
+ end
+ elseif tag == "mspace" or tag == "mline" then
+ local str = e.at.spacing or ""
+ for s in utfcharacters(str) do
+ m[#m+1] = { tag, s }
+ end
+ -- elseif tag == "mline" then
+ -- m[#m+1] = { tag, e }
+ end
+ end
+ for e in lxml.collected(root,"/*") do
+ local m = { }
+ matrix[#matrix+1] = m
+ if e.tg == "mrow" then
+ -- only one level
+ for e in lxml.collected(e,"/*") do
+ collect(m,e)
+ end
+ else
+ collect(m,e)
+ end
+ end
+ context.halign()
+ context.bgroup()
+ context([[\hss\startimath\alignmark\stopimath\aligntab\startimath\alignmark\stopimath\cr]])
+ for i=1,#matrix do
+ local m = matrix[i]
+ local mline = true
+ for j=1,#m do
+ if m[j][1] ~= "mline" then
+ mline = false
+ break
+ end
+ end
+ if mline then
+ context.noalign([[\obeydepth\nointerlineskip]])
+ end
+ for j=1,#m do
+ local mm = m[j]
+ local tag, chr = mm[1], mm[2]
+ if tag == "mline" then
+ -- This code is under construction ... I need some real motivation
+ -- to deal with this kind of crap.
+--~ local n, p = true, true
+--~ for c=1,#matrix do
+--~ local mc = matrix[c][j]
+--~ if mc then
+--~ mc = mc[2]
+--~ if type(mc) ~= "string" then
+--~ n, p = false, false
+--~ break
+--~ elseif find(mc,"^[%d ]$") then -- rangecheck is faster
+--~ -- digit
+--~ elseif not find(mc,"^[%.%,]$") then -- rangecheck is faster
+--~ -- punctuation
+--~ else
+--~ n = false
+--~ break
+--~ end
+--~ end
+--~ end
+--~ if n then
+--~ chr = "\\mmlmcolumndigitrule"
+--~ elseif p then
+--~ chr = "\\mmlmcolumnpunctuationrule"
+--~ else
+--~ chr = "\\mmlmcolumnsymbolrule" -- should be widest char
+--~ end
+ chr = "\\hrulefill"
+ elseif tag == "mspace" then
+ chr = "\\mmlmcolumndigitspace" -- utfchar(0x2007)
+ end
+ if j == numbers + 1 then
+ context("\\aligntab")
+ end
+ local nchr = n_replacements[chr]
+ context(nchr or chr)
+ end
+ context.crcr()
+ end
+ context.egroup()
+end
+
+local spacesplitter = lpeg.tsplitat(" ")
+
+function mathml.mtable(root)
+ -- todo: align, rowspacing, columnspacing, rowlines, columnlines
+ root = getid(root)
+ local at = root.at
+ local rowalign = at.rowalign
+ local columnalign = at.columnalign
+ local frame = at.frame
+ local rowaligns = rowalign and lpegmatch(spacesplitter,rowalign)
+ local columnaligns = columnalign and lpegmatch(spacesplitter,columnalign)
+ local frames = frame and lpegmatch(spacesplitter,frame)
+ local framespacing = at.framespacing or "0pt"
+ local framespacing = at.framespacing or "-\\ruledlinewidth" -- make this an option
+
+ context.bTABLE { frame = frametypes[frame or "none"] or "off", offset = framespacing }
+ for e in lxml.collected(root,"/(mml:mtr|mml:mlabeledtr)") do
+ context.bTR()
+ local at = e.at
+ local col = 0
+ local rfr = at.frame or (frames and frames [#frames])
+ local rra = at.rowalign or (rowaligns and rowaligns [#rowaligns])
+ local rca = at.columnalign or (columnaligns and columnaligns[#columnaligns])
+ local ignorelabel = e.tg == "mlabeledtr"
+ for e in lxml.collected(e,"/mml:mtd") do -- nested we can use xml.collected
+ col = col + 1
+ if ignorelabel and col == 1 then
+ -- get rid of label, should happen at the document level
+ else
+ local at = e.at
+ local rowspan, columnspan = at.rowspan or 1, at.columnspan or 1
+ local cra = rowalignments [at.rowalign or (rowaligns and rowaligns [col]) or rra or "center"] or "lohi"
+ local cca = columnalignments[at.columnalign or (columnaligns and columnaligns[col]) or rca or "center"] or "middle"
+ local cfr = frametypes [at.frame or (frames and frames [col]) or rfr or "none" ] or "off"
+ context.bTD { align = format("{%s,%s}",cra,cca), frame = cfr, nx = columnspan, ny = rowspan }
+ context.startimath()
+ context.ignorespaces()
+ xmlcprint(e)
+ context.stopimath()
+ context.removeunwantedspaces()
+ context.eTD()
+ end
+ end
+ -- if e.tg == "mlabeledtr" then
+ -- context.bTD()
+ -- xmlcprint(xml.first(e,"/!mml:mtd"))
+ -- context.eTD()
+ -- end
+ context.eTR()
+ end
+ context.eTABLE()
+end
+
+function mathml.csymbol(root)
+ root = getid(root)
+ local at = root.at
+ local encoding = at.encoding or ""
+ local hash = url.hashed(lower(at.definitionUrl or ""))
+ local full = hash.original or ""
+ local base = hash.path or ""
+ local text = strip(xmltext(root) or "")
+ context.mmlapplycsymbol(full,base,encoding,text)
+end
+
+function mathml.menclosepattern(root)
+ root = getid(root)
+ local a = root.at.notation
+ if a and a ~= "" then
+ context("mml:enclose:",(gsub(a," +",",mml:enclose:")))
+ end
+end
+
+function xml.is_element(e,name)
+ return type(e) == "table" and (not name or e.tg == name)
+end
+
+function mathml.cpolar_a(root)
+ root = getid(root)
+ local dt = root.dt
+ context.mathopnolimits("Polar")
+ context.left(false,"(")
+ for k=1,#dt do
+ local dk = dt[k]
+ if xml.is_element(dk,"sep") then
+ context(",")
+ else
+ xmlsprint(dk)
+ end
+ end
+ context.right(false,")")
+end